[#23275] .3DS import script fails on some models.

- fixed reading meshes without faces.

also changed...
- read verts/faces/uvs in one struct.unpack(), should be a bit faster.
- removed mesh/material splitting, very confusing/slow code and not needed since the 16 material limit was removed.
- load image paths with bpy.path.resolve_ncase() since many 3ds's files has case mismatch with file names (applies to OBJ too).
This commit is contained in:
2010-08-12 02:24:12 +00:00
parent ac133d5d26
commit ffd65f49da
2 changed files with 85 additions and 140 deletions

View File

@@ -141,7 +141,7 @@ import os
import time
import struct
from import_scene_obj import unpack_face_list, load_image
from import_scene_obj import load_image
import bpy
import mathutils
@@ -312,10 +312,10 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
contextMaterial = None
contextMatrix_rot = None # Blender.mathutils.Matrix(); contextMatrix.identity()
#contextMatrix_tx = None # Blender.mathutils.Matrix(); contextMatrix.identity()
contextMesh_vertls = None
contextMesh_vertls = None # flat array: (verts * 3)
contextMesh_facels = None
contextMeshMaterials = {} # matname:[face_idxs]
contextMeshUV = None
contextMeshUV = None # flat array (verts * 2)
TEXTURE_DICT = {}
MATDICT = {}
@@ -334,112 +334,68 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
def putContextMesh(myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials):
materialFaces = set() # faces that have a material. Can optimize?
bmesh = bpy.data.meshes.new(contextObName)
if myContextMesh_vertls:
# Now make copies with assigned materils.
bmesh.add_geometry(len(myContextMesh_vertls)//3, 0, len(myContextMesh_facels))
bmesh.verts.foreach_set("co", myContextMesh_vertls)
def makeMeshMaterialCopy(matName, faces):
'''
Make a new mesh with only face the faces that use this material.
faces can be any iterable object - containing ints.
'''
eekadoodle_faces = []
for v1, v2, v3 in myContextMesh_facels:
eekadoodle_faces.extend([v3, v1, v2, 0] if v3 == 0 else [v1, v2, v3, 0])
bmesh.faces.foreach_set("verts_raw", eekadoodle_faces)
faceVertUsers = [False] * len(myContextMesh_vertls)
ok = 0
for fIdx in faces:
for vindex in myContextMesh_facels[fIdx]:
faceVertUsers[vindex] = True
if matName != None: # if matName is none then this is a set(), meaning we are using the untextured faces and do not need to store textured faces.
materialFaces.add(fIdx)
ok = 1
if not ok:
return
myVertMapping = {}
vertMappingIndex = 0
vertsToUse = [i for i in range(len(myContextMesh_vertls)) if faceVertUsers[i]]
myVertMapping = {ii: i for i, ii in enumerate(vertsToUse)}
tempName= '%s_%s' % (contextObName, matName) # matName may be None.
bmesh = bpy.data.meshes.new(tempName)
if matName == None:
img = None
if bmesh.faces and contextMeshUV:
bmesh.add_uv_texture()
uv_faces = bmesh.active_uv_texture.data[:]
else:
bmat = MATDICT[matName][1]
bmesh.add_material(bmat)
# bmesh.materials = [bmat]
try: img = TEXTURE_DICT[bmat.name]
except: img = None
uv_faces = None
# bmesh_verts = bmesh.verts
if len(vertsToUse):
bmesh.add_geometry(len(vertsToUse), 0, len(faces))
for mat_idx, (matName, faces) in enumerate(myContextMeshMaterials.items()):
if matName is None:
bmesh.add_material(None)
else:
bmat = MATDICT[matName][1]
bmesh.add_material(bmat) # can be None
img = TEXTURE_DICT.get(bmat.name)
# XXX why add extra vertex?
# bmesh_verts.extend( [Vector()] )
bmesh.verts.foreach_set("co", [x for tup in [myContextMesh_vertls[i] for i in vertsToUse] for x in tup])
# bmesh_verts.extend( [myContextMesh_vertls[i] for i in vertsToUse] )
if uv_faces and img:
for fidx in faces:
bmesh.faces[fidx].material_index = mat_idx
uf = uv_faces[fidx]
uf.image = img
uf.tex = True
else:
for fidx in faces:
bmesh.faces[fidx].material_index = mat_idx
# +1 because of DUMMYVERT
bmesh.faces.foreach_set("verts_raw", unpack_face_list([[myVertMapping[vindex] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces]))
# face_mapping = bmesh.faces.extend( [ [ bmesh_verts[ myVertMapping[vindex]+1] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces ], indexList=True )
if uv_faces:
for fidx, uf in enumerate(uv_faces):
face = myContextMesh_facels[fidx]
v1, v2, v3 = face
if bmesh.faces and (contextMeshUV or img):
bmesh.add_uv_texture()
for ii, i in enumerate(faces):
# eekadoodle
if v3 == 0:
v1, v2, v3 = v3, v1, v2
# Mapped index- faces may have not been added- if so, then map to the correct index
# BUGGY API - face_mapping is not always the right length
# map_index = face_mapping[ii]
uf.uv1 = contextMeshUV[v1 * 2:(v1 * 2) + 2]
uf.uv2 = contextMeshUV[v2 * 2:(v2 * 2) + 2]
uf.uv3 = contextMeshUV[v3 * 2:(v3 * 2) + 2]
# always a tri
if 1:
# if map_index != None:
targetFace = bmesh.faces[ii]
# targetFace = bmesh.faces[map_index]
ob = bpy.data.objects.new(tempName, bmesh)
SCN.objects.link(ob)
uf = bmesh.active_uv_texture.data[ii]
'''
if contextMatrix_tx:
ob.setMatrix(contextMatrix_tx)
'''
if contextMeshUV:
# v.index-1 because of the DUMMYVERT
uvs = [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]]
if contextMatrix_rot:
ob.matrix_world = contextMatrix_rot
if len(myContextMesh_facels[i]) == 3:
uf.uv1, uf.uv2, uf.uv3, uf.uv4 = uvs + [(0.0, 0.0)]
else:
uf.uv1, uf.uv2, uf.uv3, uf.uv4 = uvs
# targetFace.uv = [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]]
if img:
uf.image = img
# to get this image to show up in 'Textured' shading mode
uf.tex = True
# bmesh.transform(contextMatrix)
ob = bpy.data.objects.new(tempName, bmesh)
SCN.objects.link(ob)
# ob = SCN_OBJECTS.new(bmesh, tempName)
'''
if contextMatrix_tx:
ob.setMatrix(contextMatrix_tx)
'''
if contextMatrix_rot:
ob.matrix_world = contextMatrix_rot
importedObjects.append(ob)
bmesh.update()
# bmesh.calcNormals()
for matName, faces in myContextMeshMaterials.items():
makeMeshMaterialCopy(matName, faces)
if len(materialFaces) != len(myContextMesh_facels):
# Invert material faces.
makeMeshMaterialCopy(None, set(range(len( myContextMesh_facels ))) - materialFaces)
#raise 'Some UnMaterialed faces', len(contextMesh.faces)
importedObjects.append(ob)
bmesh.update()
#a spare chunk
new_chunk = chunk()
@@ -667,13 +623,9 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
new_chunk.bytes_read += 2
# print 'number of verts: ', num_verts
def getvert():
temp_data = struct.unpack('<3f', file.read(STRUCT_SIZE_3FLOAT))
new_chunk.bytes_read += STRUCT_SIZE_3FLOAT #12: 3 floats x 4 bytes each
return temp_data
#contextMesh.verts.extend( [Vector(),] ) # DUMMYVERT! - remove when blenders internals are fixed.
contextMesh_vertls = [getvert() for i in range(num_verts)]
contextMesh_vertls = struct.unpack('<%df' % (num_verts * 3), file.read(STRUCT_SIZE_3FLOAT * num_verts))
new_chunk.bytes_read += STRUCT_SIZE_3FLOAT * num_verts
# dummyvert is not used atm!
#print 'object verts: bytes read: ', new_chunk.bytes_read
@@ -684,15 +636,11 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
new_chunk.bytes_read += 2
#print 'number of faces: ', num_faces
def getface():
# print '\ngetting a face'
temp_data = file.read(STRUCT_SIZE_4UNSIGNED_SHORT)
new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each
v1,v2,v3,dummy = struct.unpack('<4H', temp_data)
return v1, v2, v3
contextMesh_facels = [ getface() for i in range(num_faces) ]
# print '\ngetting a face'
temp_data = file.read(STRUCT_SIZE_4UNSIGNED_SHORT * num_faces)
new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT * num_faces #4 short ints x 2 bytes each
contextMesh_facels = struct.unpack('<%dH' % (num_faces * 4), temp_data)
contextMesh_facels = [contextMesh_facels[i - 3:i] for i in range(3, (num_faces * 4) + 3, 4)]
elif (new_chunk.ID == OBJECT_MATERIAL):
# print 'elif (new_chunk.ID == OBJECT_MATERIAL):'
@@ -703,12 +651,11 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
num_faces_using_mat = struct.unpack('<H', temp_data)[0]
new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
def getmat():
temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
return struct.unpack('<H', temp_data)[0]
contextMeshMaterials[material_name]= [ getmat() for i in range(num_faces_using_mat) ]
temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * num_faces_using_mat)
new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * num_faces_using_mat
contextMeshMaterials[material_name]= struct.unpack("<%dH" % (num_faces_using_mat), temp_data)
#look up the material in all the materials
@@ -717,12 +664,9 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
num_uv = struct.unpack('<H', temp_data)[0]
new_chunk.bytes_read += 2
def getuv():
temp_data = file.read(STRUCT_SIZE_2FLOAT)
new_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each
return mathutils.Vector( struct.unpack('<2f', temp_data) )
contextMeshUV = [ getuv() for i in range(num_uv) ]
temp_data = file.read(STRUCT_SIZE_2FLOAT * num_uv)
new_chunk.bytes_read += STRUCT_SIZE_2FLOAT * num_uv
contextMeshUV = struct.unpack('<%df' % (num_uv * 2), temp_data)
elif (new_chunk.ID == OBJECT_TRANS_MATRIX):
# How do we know the matrix size? 54 == 4x4 48 == 4x3
@@ -806,7 +750,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
# FINISHED LOOP
# There will be a number of objects still not added
if contextMesh_facels != None:
if CreateBlenderObject:
putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials)
def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, APPLY_MATRIX=False):

View File

@@ -305,13 +305,14 @@ def load_image(imagepath, dirname):
if os.path.exists(imagepath):
return bpy.data.images.load(imagepath)
variants = [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))]
variants = [imagepath, os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))]
for path in variants:
if os.path.exists(path):
return bpy.data.images.load(path)
else:
print(path, "doesn't exist")
for filepath in variants:
for nfilepath in (filepath, bpy.path.resolve_ncase(filepath)):
if os.path.exists(nfilepath):
return bpy.data.images.load(nfilepath)
print(filepath, "doesn't exist")
# TODO comprehensiveImageLoad also searched in bpy.config.textureDir
return None