[Bf-extensions-cvs] [6bd79aa] master: FBX export: Add support for dupliobjects (either children, or particles).

Bastien Montagne noreply at git.blender.org
Wed May 7 21:19:33 CEST 2014


Commit: 6bd79aa49a767fcf363f31297cc6c206ebff6a22
Author: Bastien Montagne
Date:   Wed May 7 20:59:28 2014 +0200
https://developer.blender.org/rBA6bd79aa49a767fcf363f31297cc6c206ebff6a22

FBX export: Add support for dupliobjects (either children, or particles).

Note only particles/dupliobjects existing in the current frame will be taken into account.

Dev note: the 'object' part of the code becomes really ugly, it will need a serious
refactor (probably using some kind of wrapper around Object/Bone/PoseBone/DupliObject),
nobody (not even me) will be able to follow this code soon.

===================================================================

M	io_scene_fbx/export_fbx_bin.py

===================================================================

diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index 9d0dfe4..9fa3654 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -35,7 +35,7 @@ from itertools import zip_longest, chain
 
 import bpy
 import bpy_extras
-from bpy.types import Object, Bone, PoseBone
+from bpy.types import Object, Bone, PoseBone, DupliObject
 from mathutils import Vector, Matrix
 
 from . import encode_bin, data_types
@@ -272,6 +272,11 @@ def get_blender_empty_key(obj):
     return "|".join((get_blenderID_key(obj), "Empty"))
 
 
+def get_blender_dupli_key(dup):
+    """Return dupli's key (Model only)."""
+    return "|".join((get_blenderID_key(dup.object), "Dupli", "".join(str(i) for i in dup.persistent_id)))
+
+
 def get_blender_bone_key(armature, bone):
     """Return bone's keys (Model and NodeAttribute)."""
     key = "|".join((get_blenderID_key(armature), get_blenderID_key(bone)))
@@ -311,14 +316,14 @@ def get_blender_anim_layer_key(scene, ref_id):
     return get_blender_anim_id_base(scene, ref_id) + "|AnimLayer"
 
 
-def get_blender_anim_curve_node_key(scene, ref_id, ID, fbx_prop_name):
+def get_blender_anim_curve_node_key(scene, ref_id, obj_key, fbx_prop_name):
     """Return (stack/layer, ID, fbxprop) curve node key."""
-    return "|".join((get_blender_anim_id_base(scene, ref_id), get_blenderID_key(ID), fbx_prop_name, "AnimCurveNode"))
+    return "|".join((get_blender_anim_id_base(scene, ref_id), obj_key, fbx_prop_name, "AnimCurveNode"))
 
 
-def get_blender_anim_curve_key(scene, ref_id, ID, fbx_prop_name, fbx_prop_item_name):
+def get_blender_anim_curve_key(scene, ref_id, obj_key, fbx_prop_name, fbx_prop_item_name):
     """Return (stack/layer, ID, fbxprop, item) curve key."""
-    return "|".join((get_blender_anim_id_base(scene, ref_id), get_blenderID_key(ID), fbx_prop_name,
+    return "|".join((get_blender_anim_id_base(scene, ref_id), obj_key, fbx_prop_name,
                      fbx_prop_item_name, "AnimCurve"))
 
 
@@ -1004,6 +1009,15 @@ def fbx_template_def_animcurve(scene, settings, override_defaults=None, nbr_user
 
 
 ##### FBX objects generators. #####
+
+def dupli_list_create(obj, scene, settings='PREVIEW'):
+    # Sigh, why raise exception here? :/
+    try:
+        obj.dupli_list_create(scene, settings)
+    except:
+        pass
+
+
 def has_valid_parent(scene_data, obj):
     if isinstance(obj, PoseBone):
         obj = obj.bone
@@ -1027,14 +1041,15 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
     else in world space.
     Note local_space has precedence over global_space.
     If obj is a bone, and global_space is True, armature must be provided (it's the bone's armature object!).
+    obj can also be a DupliObject.
     Applies specific rotation to bones, lamps and cameras (conversion Blender -> FBX).
     """
     is_posebone = isinstance(obj, PoseBone)
     is_bone = is_posebone or isinstance(obj, Bone)
+    is_dupli = isinstance(obj, DupliObject)
     # Objects which are not bones and do not have any parent are *always* in global space (unless local_space is True!).
-    is_global = not local_space and (global_space or not (is_bone or has_valid_parent(scene_data, obj)))
+    is_global = not local_space and (global_space or not (is_bone or is_dupli or has_valid_parent(scene_data, obj)))
 
-    # Up till here, our matrix is in local space, time to bring it in its final desired space.
     if is_bone:
         bo = obj
         matrix = (bo.matrix if is_posebone else bo.matrix_local) * MAT_CONVERT_BONE
@@ -1047,7 +1062,13 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
             par_matrix = (bo.parent.matrix if is_posebone else bo.parent.matrix_local).copy()
             matrix = (par_matrix * MAT_CONVERT_BONE).inverted() * matrix
     else:
-        matrix = obj.matrix_local.copy()
+        if is_dupli:
+            parent = obj.id_data
+            # And here, we are in *world* space, go back to local (parent) space...
+            matrix = parent.matrix_world.inverted() * obj.matrix
+        else:
+            parent = obj.parent
+            matrix = obj.matrix_local.copy()
 
         # Lamps, and cameras need to be rotated (in local space!).
         if obj.type == 'LAMP':
@@ -1055,16 +1076,17 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
         elif obj.type == 'CAMERA':
             matrix = matrix * MAT_CONVERT_CAMERA
 
-        if obj.parent:
+        # Our matrix is in local space, time to bring it in its final desired space.
+        if parent:
             if is_global:
                 # Move matrix to global Blender space.
-                matrix = obj.parent.matrix_world * matrix
-            elif use_bake_space_transform(scene_data, obj.parent):
+                matrix = parent.matrix_world * matrix
+            elif use_bake_space_transform(scene_data, parent):
                 # Blender's and FBX's local space of parent may differ if we use bake_space_transform...
                 # Apply parent's *Blender* local space...
-                matrix = obj.parent.matrix_local * matrix
+                matrix = parent.matrix_local * matrix
                 # ...and move it back into parent's *FBX* local space.
-                par_mat = fbx_object_matrix(scene_data, obj.parent, local_space=True)
+                par_mat = fbx_object_matrix(scene_data, parent, local_space=True)
                 matrix = par_mat.inverted() * matrix
 
     if use_bake_space_transform(scene_data, obj):
@@ -1914,6 +1936,11 @@ def fbx_data_object_elements(root, obj, scene_data):
     """
     obj_type = b"Null"  # default, sort of empty...
     tobj = obj
+    if isinstance(obj, DupliObject):
+        obj_key = scene_data.objects[tuple(obj.persistent_id)][0]
+        obj = obj.object
+    else:
+        obj_key = scene_data.objects[obj]
     if isinstance(obj, Bone):
         obj_type = b"LimbNode"
         # Get PoseBone for transformations!
@@ -1924,9 +1951,8 @@ def fbx_data_object_elements(root, obj, scene_data):
         obj_type = b"Light"
     elif (obj.type == 'CAMERA'):
         obj_type = b"Camera"
-    obj_key = scene_data.objects[obj]
     model = elem_data_single_int64(root, b"Model", get_fbxuid_from_key(obj_key))
-    model.add_string(fbx_name_class(obj.name.encode(), b"Model"))
+    model.add_string(fbx_name_class(obj_key.encode(), b"Model"))
     model.add_string(obj_type)
 
     elem_data_single_int32(model, b"Version", FBX_MODELS_VERSION)
@@ -2211,9 +2237,15 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
     Generate animation data (a single AnimStack) from objects, for a given frame range.
     """
     if objects is not None:
-        # Add bones!
-        objects |= {bo for vo in objects if (isinstance(vo, bpy.types.Object) and vo.type == 'ARMATURE')
-                       for bo in vo.data.bones}
+        # Add bones and duplis!
+        for obj in tuple(objects):
+            if not isinstance(obj, Object):
+                continue
+            if obj.type == 'ARMATURE':
+                objects |= set(vo.data.bones)
+            dupli_list_create(obj, scene, 'RENDER')
+            objects |= {tuple(dup.persistent_id) for dup in obj.dupli_list if tuple(dup.persistent_id) in scene_data.objects}
+            obj.dupli_list_clear()
     else:
         objects = scene_data.objects.keys()
     bake_step = scene_data.settings.bake_anim_step
@@ -2236,15 +2268,31 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
     while currframe < f_end:
         real_currframe = currframe - f_start if start_zero else currframe
         scene.frame_set(int(currframe), currframe - int(currframe))
+
+        duplis = {}
+        for obj in objects:
+            if not isinstance(obj, Object):
+                continue
+            dupli_list_create(obj, scene, 'RENDER')
+            duplis.update((tuple(dup.persistent_id), dup) for dup in obj.dupli_list if tuple(dup.persistent_id) in objects)
         for obj in objects:
             # Get PoseBone from bone...
-            tobj = bone_map[obj] if isinstance(obj, Bone) else obj
+            if isinstance(obj, Bone):
+                tobj = bone_map[obj]
+            # Get DupliObject from its pid...
+            elif isinstance(obj, tuple):
+                tobj = duplis[obj]
+            else:
+                tobj = obj
             # We compute baked loc/rot/scale for all objects (rot being euler-compat with previous value!).
             p_rot = p_rots.get(tobj, None)
             loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, tobj, p_rot)
             p_rots[tobj] = rot
             tx = tuple(loc) + tuple(units_convert_iter(rot, "radian", "degree")) + tuple(scale)
             animdata[obj].append((real_currframe, tx, [False] * len(tx)))
+        for obj in objects:
+            if isinstance(obj, Object):
+                obj.dupli_list_clear()
         currframe += bake_step
 
     scene.frame_set(back_currframe, 0.0)
@@ -2263,6 +2311,10 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
                 if wrt:
                     curves[idx].append((currframe, val))
 
+        if isinstance(obj, tuple):
+            obj_key = scene_data.objects[obj][0]
+        else:
+            obj_key = scene_data.objects[obj]
         # Get PoseBone from bone...
         #tobj = bone_map[obj] if isinstance(obj, Bone) else obj
         #loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, tobj)
@@ -2272,9 +2324,9 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
         final_keys = OrderedDict()
         for idx, c in enumerate(curves):
             fbx_group, fbx_gname, fbx_item = fbx_names[idx]
-            fbx_item_key = get_blender_anim_curve_key(scene, ref_id, obj, fbx_group, fbx_item)
+            fbx_item_key = get_blender_anim_curve_key(scene, ref_id, obj_key, fbx_group, fbx_item)
             if fbx_group not in final_keys:
-                fbx_group_key = get_blender_anim_curve_node_key(scene, ref_id, obj, fbx_group)
+                fbx_group_key = get_blender_anim_curve_

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list