FBX exporter conversion almost done.

Unit tests:
- add a check that BKE_copy_images produces NULL filepath for images having type other than IMA_TYPE_IMAGE
- also expect NULL filepath for images having empty filename

Enhanced BKE_copy_images so the tests pass.
This commit is contained in:
2009-07-17 10:09:07 +00:00
parent 140f2b154f
commit 122104b3bb
4 changed files with 104 additions and 63 deletions

View File

@@ -297,6 +297,29 @@ def BPyMesh_meshWeight2List(ob):
return groupNames, vWeightList return groupNames, vWeightList
def BPyMesh_meshWeight2Dict(me, ob):
''' Takes a mesh and return its group names and a list of dicts, one dict per vertex.
using the group as a key and a float value for the weight.
These 2 lists can be modified and then used with dict2MeshWeight to apply the changes.
'''
vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist.
# Clear the vert group.
groupNames= [g.name for g in ob.vertex_groups]
# groupNames= me.getVertGroupNames()
for group in groupNames:
for vert_index, weight in me.getVertsFromGroup(group, 1): # (i,w) tuples.
vWeightDict[vert_index][group]= weight
# removed this because me may be copying teh vertex groups.
#for group in groupNames:
# me.removeVertGroup(group)
return groupNames, vWeightDict
def meshNormalizedWeights(me): def meshNormalizedWeights(me):
try: # account for old bad BPyMesh try: # account for old bad BPyMesh
groupNames, vWeightList = BPyMesh_meshWeight2List(me) groupNames, vWeightList = BPyMesh_meshWeight2List(me)
@@ -1498,7 +1521,7 @@ def write(filename, batch_objects = None, \
if my_mesh.blenTextures: do_textures = True if my_mesh.blenTextures: do_textures = True
else: do_textures = False else: do_textures = False
do_uvs = len(me.uv_layers) > 0 do_uvs = len(me.uv_textures) > 0
# do_uvs = me.faceUV # do_uvs = me.faceUV
@@ -2073,8 +2096,9 @@ def write(filename, batch_objects = None, \
origData = True origData = True
if tmp_ob_type != 'MESH': if tmp_ob_type != 'MESH':
# if tmp_ob_type != 'Mesh': # if tmp_ob_type != 'Mesh':
me = bpy.data.meshes.new() # me = bpy.data.meshes.new()
try: me.getFromObject(ob) try: me = ob.create_mesh(True, 'PREVIEW')
# try: me.getFromObject(ob)
except: me = None except: me = None
if me: if me:
meshes_to_clear.append( me ) meshes_to_clear.append( me )
@@ -2084,65 +2108,70 @@ def write(filename, batch_objects = None, \
# Mesh Type! # Mesh Type!
if EXP_MESH_APPLY_MOD: if EXP_MESH_APPLY_MOD:
# me = bpy.data.meshes.new() # me = bpy.data.meshes.new()
me = ob.create_mesh('PREVIEW') me = ob.create_mesh(True, 'PREVIEW')
# me.getFromObject(ob) # me.getFromObject(ob)
# so we keep the vert groups # so we keep the vert groups
if EXP_ARMATURE: # if EXP_ARMATURE:
orig_mesh = ob.data
# orig_mesh = ob.getData(mesh=1) # orig_mesh = ob.getData(mesh=1)
if len(ob.vertex_groups):
# if orig_mesh.getVertGroupNames(): # if orig_mesh.getVertGroupNames():
ob.copy().link(me) # ob.copy().link(me)
# If new mesh has no vgroups we can try add if verts are teh same # # If new mesh has no vgroups we can try add if verts are teh same
if not me.getVertGroupNames(): # vgroups were not kept by the modifier # if not me.getVertGroupNames(): # vgroups were not kept by the modifier
if len(me.verts) == len(orig_mesh.verts): # if len(me.verts) == len(orig_mesh.verts):
groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) # groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh)
BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) # BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
# print ob, me, me.getVertGroupNames() # print ob, me, me.getVertGroupNames()
meshes_to_clear.append( me ) meshes_to_clear.append( me )
origData = False origData = False
mats = me.materials mats = me.materials
else: else:
me = ob.getData(mesh=1) me = ob.data
# me = ob.getData(mesh=1)
mats = me.materials mats = me.materials
# Support object colors # # Support object colors
tmp_colbits = ob.colbits # tmp_colbits = ob.colbits
if tmp_colbits: # if tmp_colbits:
tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too. # tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too.
for i in xrange(16): # for i in xrange(16):
if tmp_colbits & (1<<i): # if tmp_colbits & (1<<i):
mats[i] = tmp_ob_mats[i] # mats[i] = tmp_ob_mats[i]
del tmp_ob_mats # del tmp_ob_mats
del tmp_colbits # del tmp_colbits
if me: if me:
# This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled. # # This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled.
# so strictly this is bad. but only in rare cases would it have negative results # # so strictly this is bad. but only in rare cases would it have negative results
# say with dupliverts the objects would rotate a bit differently # # say with dupliverts the objects would rotate a bit differently
if EXP_MESH_HQ_NORMALS: # if EXP_MESH_HQ_NORMALS:
BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines. # BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
texture_mapping_local = {} texture_mapping_local = {}
material_mapping_local = {} material_mapping_local = {}
if me.faceUV: if len(me.uv_textures) > 0:
uvlayer_orig = me.activeUVLayer # if me.faceUV:
for uvlayer in me.getUVLayerNames(): uvlayer_orig = me.active_uv_texture
me.activeUVLayer = uvlayer # uvlayer_orig = me.activeUVLayer
for f in me.faces: for uvlayer in me.uv_textures:
tex = f.image # for uvlayer in me.getUVLayerNames():
# me.activeUVLayer = uvlayer
for f, uf in zip(me.faces, uvlayer.data):
# for f in me.faces:
tex = uf.image
# tex = f.image
textures[tex] = texture_mapping_local[tex] = None textures[tex] = texture_mapping_local[tex] = None
try: mat = mats[f.mat] try: mat = mats[f.material_index]
# try: mat = mats[f.mat]
except: mat = None except: mat = None
materials[mat, tex] = material_mapping_local[mat, tex] = None # should use sets, wait for blender 2.5 materials[mat, tex] = material_mapping_local[mat, tex] = None # should use sets, wait for blender 2.5
me.activeUVLayer = uvlayer_orig # me.activeUVLayer = uvlayer_orig
else: else:
for mat in mats: for mat in mats:
# 2.44 use mat.lib too for uniqueness # 2.44 use mat.lib too for uniqueness
@@ -2155,9 +2184,12 @@ def write(filename, batch_objects = None, \
blenParentBoneName = None blenParentBoneName = None
# parent bone - special case # parent bone - special case
if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE: if (not armob) and ob.parent and ob.parent.type == 'ARMATURE' and \
ob.parent_type == 'BONE':
# if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE:
armob = ob.parent armob = ob.parent
blenParentBoneName = ob.parentbonename blenParentBoneName = ob.parent_bone
# blenParentBoneName = ob.parentbonename
if armob and armob not in ob_arms: if armob and armob not in ob_arms:
@@ -2181,7 +2213,11 @@ def write(filename, batch_objects = None, \
my_mesh.fbxBoneParent = blenParentBoneName # replace with my_bone instance later my_mesh.fbxBoneParent = blenParentBoneName # replace with my_bone instance later
ob_meshes.append( my_mesh ) ob_meshes.append( my_mesh )
# not forgetting to free dupli_list
if ob_base.dupli_list: ob_base.free_dupli_list()
if EXP_ARMATURE: if EXP_ARMATURE:
# now we have the meshes, restore the rest arm position # now we have the meshes, restore the rest arm position
for i, arm in enumerate(bpy.data.armatures): for i, arm in enumerate(bpy.data.armatures):
@@ -2217,7 +2253,8 @@ def write(filename, batch_objects = None, \
# fbxName, blenderObject, my_bones, blenderActions # fbxName, blenderObject, my_bones, blenderActions
#ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, []) #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, [])
for bone in my_arm.blenData.bones.values(): for bone in my_arm.blenData.bones:
# for bone in my_arm.blenData.bones.values():
my_bone = my_bone_class(bone, my_arm) my_bone = my_bone_class(bone, my_arm)
my_arm.fbxBones.append( my_bone ) my_arm.fbxBones.append( my_bone )
ob_bones.append( my_bone ) ob_bones.append( my_bone )
@@ -2662,7 +2699,8 @@ Connections: {''')
# Needed for scene footer as well as animation # Needed for scene footer as well as animation
render = sce.render render = sce.render_data
# render = sce.render
# from the FBX sdk # from the FBX sdk
#define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000)) #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000))
@@ -2671,8 +2709,10 @@ Connections: {''')
return int(0.5 + ((t/fps) * 46186158000)) return int(0.5 + ((t/fps) * 46186158000))
fps = float(render.fps) fps = float(render.fps)
start = render.sFrame start = sce.start_frame
end = render.eFrame # start = render.sFrame
end = sce.end_frame
# end = render.eFrame
if end < start: start, end = end, start if end < start: start, end = end, start
if start==end: ANIM_ENABLE = False if start==end: ANIM_ENABLE = False
@@ -2959,8 +2999,6 @@ Takes: {''')
bpy.data.remove_mesh(me) bpy.data.remove_mesh(me)
# me.verts = None # me.verts = None
# --------------------------- Footer # --------------------------- Footer
if world: if world:
m = world.mist m = world.mist
@@ -3025,7 +3063,8 @@ Takes: {''')
if EXP_IMAGE_COPY: if EXP_IMAGE_COPY:
copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ])
print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) print 'export finished in %.4f sec.' % (bpy.sys.time() - start_time)
# print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time)
return True return True
@@ -3338,11 +3377,14 @@ if __name__ == '__main__':
# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) # NOTES (all line numbers correspond to original export_fbx.py (under release/scripts)
# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print # - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print
# - get rid of cleanName somehow # - get rid of cleanName somehow
# - isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 # + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565
# - get rid of BPyObject_getObjectArmature, move it in RNA? # - get rid of BPyObject_getObjectArmature, move it in RNA?
# - BATCH_ENABLE and BATCH_GROUP options: line 327 # - BATCH_ENABLE and BATCH_GROUP options: line 327
# - implement all BPyMesh_* used here with RNA # - implement all BPyMesh_* used here with RNA
# - getDerivedObjects is not fully replicated with .dupli* funcs # - getDerivedObjects is not fully replicated with .dupli* funcs
# - talk to Campbell, this code won't work? lines 1867-1875
# - don't know what those colbits are, do we need them? they're said to be deprecated in DNA_object_types.h: 1886-1893
# - no hq normals: 1900-1901
# TODO # TODO

View File

@@ -2120,6 +2120,7 @@ void BKE_image_user_calc_imanr(ImageUser *iuser, int cfra, int fieldnr)
Copy list of images to dest_dir. Copy list of images to dest_dir.
paths is optional, if given, image paths for each image will be written in it. paths is optional, if given, image paths for each image will be written in it.
It will also contain NULLs for images that cannot be copied.
If an image file doesn't exist, NULL is added in paths. If an image file doesn't exist, NULL is added in paths.
Logic: Logic:
@@ -2161,6 +2162,13 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths)
while (link) { while (link) {
im= link->data; im= link->data;
LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData");
ld->data= NULL;
BLI_addtail(paths, ld);
if (!strcmp(im->name, "") || im->type != IMA_TYPE_IMAGE)
goto next;
BLI_strncpy(path, im->name, sizeof(path)); BLI_strncpy(path, im->name, sizeof(path));
/* expand "//" in filename and get absolute path */ /* expand "//" in filename and get absolute path */
@@ -2169,16 +2177,8 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths)
/* in unit tests, we don't want to modify the filesystem */ /* in unit tests, we don't want to modify the filesystem */
#ifndef WITH_UNIT_TEST #ifndef WITH_UNIT_TEST
/* proceed only if image file exists */ /* proceed only if image file exists */
if (!BLI_exists(path)) { if (!BLI_exists(path))
goto next;
if (paths) {
LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData");
ld->data= NULL;
BLI_addtail(paths, ld);
}
continue;
}
#endif #endif
/* get the directory part */ /* get the directory part */
@@ -2219,17 +2219,17 @@ void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths)
} }
#ifndef WITH_UNIT_TEST #ifndef WITH_UNIT_TEST
BLI_copy_fileops(path, dest_path); if (BLI_copy_fileops(path, dest_path) != 0)
goto next;
#endif #endif
if (paths) { if (paths) {
LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData");
len= strlen(dest_path) + 1; len= strlen(dest_path) + 1;
ld->data= MEM_callocN(len, "PathLinkData"); ld->data= MEM_callocN(len, "PathLinkData");
BLI_strncpy(ld->data, dest_path, len); BLI_strncpy(ld->data, dest_path, len);
BLI_addtail(paths, ld);
} }
next:
link= link->next; link= link->next;
} }
} }

View File

@@ -370,7 +370,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS); RNA_def_function_flag(func, FUNC_USE_CONTEXT|FUNC_USE_REPORTS);
RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers."); RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers.");
RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_property_flag(parm, PROP_REQUIRED);
parm= RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Mesh settings to apply."); parm= RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Modifier settings to apply.");
RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_property_flag(parm, PROP_REQUIRED);
parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export."); parm= RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object, remove it if it is only used for export.");
RNA_def_function_return(func, parm); RNA_def_function_return(func, parm);

View File

@@ -583,9 +583,8 @@ static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args)
/* convert filenames */ /* convert filenames */
ret= PyList_New(0); ret= PyList_New(0);
len= BLI_countlist(paths);
for(link= paths->first, i= 0; link; link++, i++) { for(link= paths->first; link; link= link->next) {
if (link->data) { if (link->data) {
item= PyUnicode_FromString(link->data); item= PyUnicode_FromString(link->data);
PyList_Append(ret, item); PyList_Append(ret, item);