From 56ea511065c3a4fc25200042f667f204d5419ade Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 14 Oct 2024 08:02:07 -0500 Subject: [PATCH 1/9] X3D HAnim with Blender bones (no animation yet present) --- source/import_x3d.py | 256 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 2 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index 9079580..1eeb626 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -3274,6 +3274,136 @@ def importShape(bpycollection, node, ancestry, global_matrix): print('\tImportX3D warning: unsupported type "%s"' % geom_spec) +def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, segments, jointSkin): + vrmlname = node.getDefName() + # print(vrmlname) + prefix = '' + if vrmlname: + fus = vrmlname.find('_') + if fus > 0: + prefix = vrmlname[:fus+1] + else: + vrmlname = 'HAnimHumanoid' + + # Create armature and object + armature_data = bpy.data.armatures.new(prefix+"humanoid_root") + skeleton = bpy.data.objects.new(vrmlname, armature_data) + + # Link object to collection and make it active + bpycollection.objects.link(skeleton) + bpy.context.view_layer.objects.active = skeleton + skeleton.select_set(True) + + # Enter edit mode + bpy.ops.object.mode_set(mode='EDIT') + + # Store reference to the object on the node + bpyob = node.blendData = node.blendObject = skeleton + + + # Process children joints, including USE, if present + child = node.getChildBySpec('HAnimJoint') # 'HAnimJoint' + if child: + # first_joint_name = child.getFieldAsString('name', None, ancestry) + first_joint_name = child.getDefName() + if not first_joint_name: + first_joint_name = child.getFieldAsString('name', None, ancestry) + joint_center = child.getFieldAsFloatTuple('center', None, ancestry) + #if joint_center is None: + joint_center = (0, 0, 0) # may delete joint at 0 0 0 + print(f"Joint {first_joint_name} {joint_center}") + importHAnimJoint(joints, segments, child, ancestry, first_joint_name, parent_center=joint_center[:]) + + # Create bones for each joint + for joint_name, joint_start, joint_end, skinCoordWeight, skinCoordIndex in joints: + print(f"Creating {joint_name} {joint_start} {joint_end}") + # print(f"Joint {joint_name} {joint_start} {joint_end}") + if not joint_name: + joint_name = vrmlname + # bpy.ops.armature.bone_primitive_add(name=joint_name) + new_segment = armature_data.edit_bones.new(joint_name) + # new_segment = skeleton.data.edit_bones[joint_name] + #print(f"Created {joint_name} {new_segment}") + child.blendData = child.blendObject = new_segment + matrix_world_inv = skeleton.matrix_world.inverted() + new_segment.head = joint_end + new_segment.tail = joint_start + + # bpy.context.view_layer.objects.active = skeleton + #bpy.ops.object.mode_set(mode='POSE') + #pose_bone = skeleton.pose.bones.get(joint_name) + #if pose_bone: + # pose_bone.bone.select = True + # pose_bone.location = matrix_world_inv @ pose_bone.bone.matrix_local.inverted() @ mathutils.Vector(new_segment.head) + # pose_bone.location = mathutils.Vector(new_segment.head) + #else: + # print(f"There's no pose bone associated with {joint_name}") + if joint_name != vrmlname: + jointSkin[joint_name] = { + 'skinCoordWeight' : skinCoordWeight, + 'skinCoordIndex' : skinCoordIndex + } + #print(f"Adding {joint_name} {len(jointSkin[joint_name]['skinCoordIndex'])} indexes, {len(jointSkin[joint_name]['skinCoordWeight'])} weights") + for segment in segments: + parent_joint, child_joint = segment + if parent_joint in skeleton.data.edit_bones: + parent = skeleton.data.edit_bones[parent_joint] # some things don't have a parent + else: + parent = None + + if child_joint in skeleton.data.edit_bones: + child = skeleton.data.edit_bones[child_joint] + else: + child = armature_data.edit_bones.new(child_joint) + child.parent = parent + else: + print("Couldn't find child HAnimJoint") + + + getFinalMatrix(node, None, ancestry, global_matrix) + + return skeleton + + +def importHAnimJoints(joints, segments, children, ancestry, parent_bone_name, parent_center=[0, 0, 0]): + for child in children: + child_bone_name = child.getDefName() + if not child_bone_name: + child_bone_name = child.getFieldAsString('name', None, ancestry) + if not child_bone_name: + child_bone_name = parent_bone_name + segments.append((parent_bone_name, child_bone_name)) + # print(f"Segment IHAJs {parent_bone_name} {child_bone_name}") + importHAnimJoint(joints, segments, child, ancestry, parent_bone_name, parent_center) + +def importHAnimJoint(joints, segments, child, ancestry, parent_bone_name=None, parent_center=[0, 0, 0]): + if child: + child_bone_name = child.getDefName() + if not child_bone_name: + child_bone_name = child.getFieldAsString('name', None, ancestry) + if not child_bone_name: + child_bone_name = 'Armature' + child_center = child.getFieldAsFloatTuple('center', None, ancestry) + skinCoordWeight = child.getFieldAsArray('skinCoordWeight', 0, ancestry) + skinCoordIndex = child.getFieldAsArray('skinCoordIndex', 0, ancestry) + if skinCoordWeight is None: + skinCoordWeight = () + if skinCoordIndex is None: + skinCoordIndex = () + + if not child_center: + child_center = [0, 0, 0] + joints.append((child_bone_name, (child_center[0], child_center[1], child_center[2]), (parent_center[0], parent_center[1], parent_center[2]), skinCoordWeight, skinCoordIndex)) + + # print(f"Joint IHAJ {joints[-1]}") + children = child.getChildrenBySpec('HAnimJoint') + if children: + importHAnimJoints(joints, segments, children, ancestry, child_bone_name, child_center) + else: + print(f"Didn't find children, {children}") + else: + print(f"Didn't find child, {child}") + # ----------------------------------------------------------------------------------- # Lighting @@ -3420,6 +3550,22 @@ def importTransform(bpycollection, node, ancestry, global_matrix): bpyob.empty_display_type = 'PLAIN_AXES' bpyob.empty_display_size = 0.2 +def importHAnimSegment(bpycollection, node, ancestry, global_matrix): + name = node.getDefName() + if not name: + name = 'HAnimSegment' + + bpyob = node.blendData = node.blendObject = bpy.data.objects.new(name, None) + bpycollection.objects.link(bpyob) + bpyob.select_set(True) + + getFinalMatrix(node, None, ancestry, global_matrix) + + # so they are not too annoying + bpyob.empty_display_type = 'PLAIN_AXES' + bpyob.empty_display_size = 0.2 + + return bpyob #def importTimeSensor(node): def action_fcurve_ensure(action, data_path, array_index): @@ -3528,7 +3674,7 @@ def translateTimeSensor(node, action, ancestry): time_cu.extend = Blender.IpoCurve.ExtendTypes.CYCLIC # or - EXTRAP, CYCLIC_EXTRAP, CONST, -def importRoute(node, ancestry): +def importRoute(node, ancestry, skeleton=None, hasMesh=None): """ Animation route only at the moment """ @@ -3595,6 +3741,15 @@ ROUTE champFly001.bindTime TO vpTs.set_startTime time_node = defDict[to_id] translateTimeSensor(time_node, action, ancestry) +def importSkinWeights(obj, joint, jointCoord, end): + group = obj.vertex_groups.get(joint) + if group is None: + group = obj.vertex_groups.new(name=joint) + # print(f"Created group {joint}"); + print(f"Index {end} joint {joint}") + for wi in range(len(jointCoord['skinCoordIndex'])): + # print(f"Index {end} joint {joint} {jointCoord['skinCoordIndex'][wi]} weight {jointCoord['skinCoordWeight'][wi]}") + group.add([jointCoord['skinCoordIndex'][wi]], jointCoord['skinCoordWeight'][wi], 'REPLACE') def load_web3d( bpycontext, @@ -3637,6 +3792,25 @@ def load_web3d( # fill with tuples - (node, [parents-parent, parent]) all_nodes = root_node.getSerialized([], []) + all_shapes = [] + meshobj = None + shape = None + site = None + + for node, ancestry in all_nodes: + spec = node.getSpec() + + if spec.endswith('Shape'): + shape = importShape(bpycollection, node, ancestry, global_matrix) + if shape: + if shape[1]: + bpy.context.view_layer.objects.active = shape[1] + shape.append(site) + site = None + all_shapes.append(shape) + elif spec.endswith('HAnimSite'): + site = node + for node, ancestry in all_nodes: #if 'castle.wrl' not in node.getFilename(): # continue @@ -3658,6 +3832,75 @@ def load_web3d( importLamp(bpycollection, node, spec, ancestry, global_matrix) elif spec == 'Viewpoint': importViewpoint(bpycollection, node, ancestry, global_matrix) + elif spec == 'HAnimHumanoid': + joints = [] + segments = [] + jointSkin = {} + skeleton = importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, segments, jointSkin) + # skinCoord = node.getChildByName('skinCoord') # 'Coordinate' + skinCoord = node.getChildBySpec('Coordinate') + if skinCoord: + #if skinCoord.getFieldAsString("containerField", None, ancestry) == "skinCoord": + print(f"Skin coord is {skinCoord}") + for shape in all_shapes: + if shape: + print(f"Skin mesh is found") + if shape[0] and shape[1] and shape[2] and skinCoord.getRealNode().getDefName() == shape[2].getRealNode().getDefName(): + print("Got mesh obj") + meshobj = shape[1] + meshobj.modifiers.new(name='ArmatureToMesh', type='ARMATURE') + meshobj.modifiers['ArmatureToMesh'].object = skeleton + else: + print(f"DEFs match? missing shape[:]? skinCoord.getRealNode().getDefName() == shape[2].getRealNode().getDefName()") + else: + print(f"no shape {shape} ? all shapes is {all_shapes}") + + #else: + # print(f"Couldn't get containerField {skinCoord}") + else: + print("No skinCoord, no skin weights, no skin animation") + + + print(f"mesh is {meshobj}") + #bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.object.mode_set(mode="OBJECT") + #bmesh.from_edit_mesh(meshobj.data) + imported = [] + print(f"Number of segments {len(segments)}") + for segment in segments: + parent_joint, child_joint = segment + # print(f"Segment {parent_joint} {child_joint} loading weights") + if meshobj and child_joint not in imported: + importSkinWeights(meshobj, child_joint, jointSkin[child_joint], "child") + imported.append(child_joint) + if meshobj and parent_joint not in imported: + importSkinWeights(meshobj, parent_joint, jointSkin[parent_joint], "parent") + imported.append(parent_joint) + + elif spec in ('HAnimSegment'): + child_segment_name = node.getDefName() + hAnimSegment = importHAnimSegment(bpycollection, node, ancestry, global_matrix) + attachMesh(all_shapes, child_segment_name, hAnimSegment) # mesh is in all_shapes + hAnimSegment.parent = skeleton + hAnimSegment.parent_bone = parent_joint_name + hAnimSegment.parent_type = 'BONE' + elif spec in ('HAnimDisplacer'): + #if meshobj: + #importHAnimDisplacer(node, ancestry, meshobj, displacers) + pass + elif spec in ('HAnimHumanoid'): + humanoid_name = node.getDefName() + hAnimHumanoid = importTransform(bpycollection, node, ancestry, global_matrix) + elif spec in ('HAnimJoint'): + parent_joint_name = node.getDefName() + hAnimJoint = importTransform(bpycollection, node, ancestry, global_matrix) + elif spec in ('Group'): + group_name = node.getDefName() + group = importTransform(bpycollection, node, ancestry, global_matrix) + elif spec in ('HAnimSite'): + site_name = node.getDefName() + hAnimSite = importTransform(bpycollection, node, ancestry, global_matrix) + attachMesh(all_shapes, site_name, hAnimSite) # mesh is in all_shapes elif spec == 'Transform': # Only use transform nodes when we are not importing a flat object hierarchy if PREF_FLAT == False: @@ -3670,8 +3913,10 @@ def load_web3d( ''' # After we import all nodes, route events - anim paths + if skeleton: + bpy.ops.object.mode_set(mode='POSE') for node, ancestry in all_nodes: - importRoute(node, ancestry) + importRoute(node, ancestry, skeleton, meshobj) for node, ancestry in all_nodes: if node.isRoot(): @@ -3728,6 +3973,13 @@ def load_web3d( bpycontext.view_layer.update() del child_dict +def attachMesh(all_shapes, parent_name, parent_obj): + # print(f"Found parent {parent_name}") + for shape in all_shapes: + if shape and shape[0] and shape[1] and shape[2] and shape[3]: + if shape[3].getDefName() and parent_name == shape[3].getDefName(): # shape[3] is shape's parent node + meshobj = shape[1] + meshobj.parent = parent_obj def load_with_profiler( context, -- 2.30.2 From c94bcb4cde54016adf87fc48d6678bf01d842276 Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 14 Oct 2024 08:53:48 -0500 Subject: [PATCH 2/9] Fixed problem with skeleton not being lined up with skin --- source/import_x3d.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index 1eeb626..dc6c2a1 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -3288,6 +3288,7 @@ def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, se # Create armature and object armature_data = bpy.data.armatures.new(prefix+"humanoid_root") skeleton = bpy.data.objects.new(vrmlname, armature_data) + skeleton.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix) # Link object to collection and make it active bpycollection.objects.link(skeleton) @@ -3360,7 +3361,7 @@ def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, se print("Couldn't find child HAnimJoint") - getFinalMatrix(node, None, ancestry, global_matrix) + # getFinalMatrix(node, None, ancestry, global_matrix) return skeleton -- 2.30.2 From 5d4a5abf5d06c3268f1519f8fc35fd2ead13f498 Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 14 Oct 2024 12:26:07 -0500 Subject: [PATCH 3/9] Animation work for Joe (had to return the IndexedFaceSet) --- source/import_x3d.py | 288 ++++++++++++++++++++++++++++++++----------- 1 file changed, 213 insertions(+), 75 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index dc6c2a1..3d42d4d 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -18,6 +18,7 @@ material_cache = {} conversion_scale = 1.0 EPSILON = 0.0000001 # Very crude. +PREF_TIME_MULT = 250 def imageConvertCompat(path): @@ -359,6 +360,9 @@ class vrmlNode(object): 'node_type', 'parent', 'children', + 'skeleton', # no joints, segments or sites. TODO + 'skinCoord', + 'skin', 'parent', 'array_data', 'reference', @@ -1594,8 +1598,10 @@ def translateTexTransform(node, ancestry): def getFinalMatrix(node, mtx, ancestry, global_matrix): - transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() == 'Transform'] - if node.getSpec() == 'Transform': + transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() in ('Transform', 'HAnimHumanoid', 'HAnimJoint', 'HAnimSite', 'HAnimDisplacer')] + if node.getSpec() in ('Transform', 'HAnimHumanoid', 'HAnimJoint', 'HAnimSite', 'HAnimDisplacer'): + #transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() == 'Transform'] + #if node.getSpec() == 'Transform': transform_nodes.append(node) transform_nodes.reverse() @@ -3171,6 +3177,8 @@ def importShape_ProcessObject( if DEBUG: bpyob["source_line_no"] = geom.lineno + return bpyob + def importText(geom, ancestry): fmt = geom.getChildBySpec('FontStyle') @@ -3253,6 +3261,7 @@ def importShape(bpycollection, node, ancestry, global_matrix): bpydata = None geom_spec = geom.getSpec() + coord = geom.getChildBySpec('Coordinate') # ccw is handled by every geometry importer separately; some # geometries are easier to flip than others @@ -3266,12 +3275,17 @@ def importShape(bpycollection, node, ancestry, global_matrix): # There are no geometry importers that can legally return # no object. It's either a bpy object, or an exception - importShape_ProcessObject( + bpypo = importShape_ProcessObject( bpycollection, vrmlname, bpydata, geom, geom_spec, node, bpymat, tex_has_alpha, texmtx, ancestry, global_matrix) + + if bpypo is None: + print('ImportX3D warning: importShape_ProcessObject did not return a shape to return for HAnim "%s"' % vrmlname) else: print('\tImportX3D warning: unsupported type "%s"' % geom_spec) + bpypo = None + return [ geom, bpypo, coord ] def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, segments, jointSkin): @@ -3560,7 +3574,7 @@ def importHAnimSegment(bpycollection, node, ancestry, global_matrix): bpycollection.objects.link(bpyob) bpyob.select_set(True) - getFinalMatrix(node, None, ancestry, global_matrix) + bpyob.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix) # so they are not too annoying bpyob.empty_display_type = 'PLAIN_AXES' @@ -3576,7 +3590,6 @@ def action_fcurve_ensure(action, data_path, array_index): return action.fcurves.new(data_path=data_path, index=array_index) - def translatePositionInterpolator(node, action, ancestry): key = node.getFieldAsArray('key', 0, ancestry) keyValue = node.getFieldAsArray('keyValue', 3, ancestry) @@ -3591,19 +3604,51 @@ def translatePositionInterpolator(node, action, ancestry): except: continue - loc_x.keyframe_points.insert(time, x) - loc_y.keyframe_points.insert(time, y) - loc_z.keyframe_points.insert(time, z) + loc_x.keyframe_points.insert(time*PREF_TIME_MULT, x) + loc_y.keyframe_points.insert(time*PREF_TIME_MULT, y) + loc_z.keyframe_points.insert(time*PREF_TIME_MULT, z) for fcu in (loc_x, loc_y, loc_z): for kf in fcu.keyframe_points: kf.interpolation = 'LINEAR' -def translateOrientationInterpolator(node, action, ancestry): +def translateCoordinateInterpolator(node, action, ancestry): + key = node.getFieldAsArray('key', 0, ancestry) + keyValue = node.getFieldAsArray('keyValue', 0, ancestry) + offset = int(len(keyValue) / len(key) / 3) # values divide by times divided by axes + print(f"ci {offset} = {len(keyValue)} / {len(key)}") + loc_x = action_fcurve_ensure(action, "location", 0) + loc_y = action_fcurve_ensure(action, "location", 1) + loc_z = action_fcurve_ensure(action, "location", 2) + + curoff = 0 + for i, time in enumerate(key): # loop through time + # 0 1 2 + for off in range(offset): # for each data point + # 0 1 2 up to offset + # curoff = i*offset+off + #print(f" coordinate index {off} num coordinates {offset} time index {i} time {time} current offset {curoff}") + # then a vec3f + x = keyValue[curoff+0] + y = keyValue[curoff+1] + z = keyValue[curoff+2] + loc_x.keyframe_points.insert(time*PREF_TIME_MULT, x) + loc_y.keyframe_points.insert(time*PREF_TIME_MULT, y) + loc_z.keyframe_points.insert(time*PREF_TIME_MULT, z) + + curoff = curoff + 3 + + for fcu in (loc_x, loc_y, loc_z): + for kf in fcu.keyframe_points: + kf.interpolation = 'LINEAR' + +def translateOrientationInterpolator(node, action, ancestry, to_node): key = node.getFieldAsArray('key', 0, ancestry) keyValue = node.getFieldAsArray('keyValue', 4, ancestry) + node.rotation_mode = 'XYZ' + rot_x = action_fcurve_ensure(action, "rotation_euler", 0) rot_y = action_fcurve_ensure(action, "rotation_euler", 1) rot_z = action_fcurve_ensure(action, "rotation_euler", 2) @@ -3616,17 +3661,30 @@ def translateOrientationInterpolator(node, action, ancestry): mtx = translateRotation((x, y, z, w)) eul = mtx.to_euler() - rot_x.keyframe_points.insert(time, eul.x) - rot_y.keyframe_points.insert(time, eul.y) - rot_z.keyframe_points.insert(time, eul.z) + rot_x.keyframe_points.insert(time*PREF_TIME_MULT, eul.x) + rot_y.keyframe_points.insert(time*PREF_TIME_MULT, eul.y) + rot_z.keyframe_points.insert(time*PREF_TIME_MULT, eul.z) for fcu in (rot_x, rot_y, rot_z): for kf in fcu.keyframe_points: kf.interpolation = 'LINEAR' +def translateBoneOrientationInterpolator(node, action, ancestry, to_id=None, skeleton=None): + key = node.getFieldAsArray('key', 0, ancestry) + keyValue = node.getFieldAsArray('keyValue', 4, ancestry) + + pose_bone = None + if skeleton: + pose_bone = skeleton.pose.bones.get(to_id) + if pose_bone: + pose_bone.rotation_mode = 'AXIS_ANGLE' + for time, (x, y, z, w) in zip(key, keyValue): + pose_bone.rotation_axis_angle = (w, x, y, z) + pose_bone.keyframe_insert(data_path="rotation_axis_angle", frame=time * PREF_TIME_MULT) + # Untested! -def translateScalarInterpolator(node, action, ancestry): +def translateScaleInterpolator(node, action, ancestry): key = node.getFieldAsArray('key', 0, ancestry) keyValue = node.getFieldAsArray('keyValue', 4, ancestry) @@ -3640,10 +3698,23 @@ def translateScalarInterpolator(node, action, ancestry): except: continue - sca_x.keyframe_points.new(time, x) - sca_y.keyframe_points.new(time, y) - sca_z.keyframe_points.new(time, z) + sca_x.keyframe_points.insert(time*PREF_TIME_MULT, x) + sca_y.keyframe_points.insert(time*PREF_TIME_MULT, y) + sca_z.keyframe_points.insert(time*PREF_TIME_MULT, z) +def translateScalarInterpolator(node, action, ancestry, to_node, data_path): + key = node.getFieldAsArray('key', 0, ancestry) + keyValue = node.getFieldAsArray('keyValue', 0, ancestry) + + scalar = action_fcurve_ensure(action, data_path, 0) + + for i, time in enumerate(key): + try: + s = keyValue[i] + except: + continue + + scalar.keyframe_points.insert(time*PREF_TIME_MULT, s) def translateTimeSensor(node, action, ancestry): """ @@ -3674,14 +3745,7 @@ def translateTimeSensor(node, action, ancestry): if loop: time_cu.extend = Blender.IpoCurve.ExtendTypes.CYCLIC # or - EXTRAP, CYCLIC_EXTRAP, CONST, - -def importRoute(node, ancestry, skeleton=None, hasMesh=None): - """ - Animation route only at the moment - """ - - if not hasattr(node, 'fields'): - return +def importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh): routeIpoDict = node.getRouteIpoDict() @@ -3689,11 +3753,62 @@ def importRoute(node, ancestry, skeleton=None, hasMesh=None): try: action = routeIpoDict[act_id] except: - action = routeIpoDict[act_id] = bpy.data.actions.new('web3d_ipo') + action = routeIpoDict[act_id] = bpy.data.actions.new(act_id) + #print(f"return action {act_id} {action}") return action # for getting definitions defDict = node.getDefDict() + + if from_type == 'value_changed': + if to_type in ('set_translation', 'set_position'): # set translation may need some matrix multiplication + action = getIpo(to_id) + set_data_from_node = defDict[from_id] + print(f"Trying to create a position interpolator for joint {to_id} (may need something special?)") + translatePositionInterpolator(set_data_from_node, action, ancestry) + + if to_type in {'rotation', "set_rotation"} and defDict[to_id].getSpec() == 'TextureTransform': + action = getIpo(to_id) + set_data_from_node = defDict[from_id] + to_node = defDict[to_id] + translateScalarInterpolator(set_data_from_node, action, ancestry, to_node, "rotation") + elif to_type in {'set_orientation', 'rotation', "set_rotation"}: + action = getIpo(to_id) + set_data_from_node = defDict[from_id] + to_node = defDict[to_id] + if skeleton and skeleton.pose.bones.get(to_id): + print(f"Creating animation for joint {to_id}") + translateBoneOrientationInterpolator(set_data_from_node, action, ancestry, to_id, skeleton) + if not hasMesh: + print(f"Creating orientation animation for {to_id}") + translateOrientationInterpolator(set_data_from_node, action, ancestry, to_node) + + if to_type == 'set_scale': + action = getIpo(to_id) + set_data_from_node = defDict[from_id] + translateScaleInterpolator(set_data_from_node, action, ancestry) + + if to_type == 'set_point': + action = getIpo(to_id) + set_data_from_node = defDict[from_id] + translateCoordinateInterpolator(set_data_from_node, action, ancestry) + + elif from_type == 'bindTime': + action = getIpo(from_id) + time_node = defDict[to_id] + translateTimeSensor(time_node, action, ancestry) + +def importRoute(node, ancestry, skeleton=None, hasMesh=None): + """ + Animation route only at the moment + """ + + if node.getFieldAsString("fromNode", None, ancestry) and node.getFieldAsString("toNode", None, ancestry) and node.getFieldAsString("fromField", None, ancestry) and node.getFieldAsString("toField", None, ancestry): + pass + elif not hasattr(node, 'fields'): + # print(f"return not hasattr fields") + return + """ Handles routing nodes to each other @@ -3704,49 +3819,32 @@ ROUTE vpTs.fraction_changed TO vpOI.set_fraction ROUTE champFly001.bindTime TO vpTs.set_startTime """ - #from_id, from_type = node.id[1].split('.') - #to_id, to_type = node.id[3].split('.') - - #value_changed - set_position_node = None - set_orientation_node = None - time_node = None - - for field in node.fields: - if field and field[0] == 'ROUTE': - try: - from_id, from_type = field[1].split('.') - to_id, to_type = field[3].split('.') - except: - print("Warning, invalid ROUTE", field) - continue - - if from_type == 'value_changed': - if to_type == 'set_position': - action = getIpo(to_id) - set_data_from_node = defDict[from_id] - translatePositionInterpolator(set_data_from_node, action, ancestry) - - if to_type in {'set_orientation', 'rotation'}: - action = getIpo(to_id) - set_data_from_node = defDict[from_id] - translateOrientationInterpolator(set_data_from_node, action, ancestry) - - if to_type == 'set_scale': - action = getIpo(to_id) - set_data_from_node = defDict[from_id] - translateScalarInterpolator(set_data_from_node, action, ancestry) - - elif from_type == 'bindTime': - action = getIpo(from_id) - time_node = defDict[to_id] - translateTimeSensor(time_node, action, ancestry) + if len(node.fields) <= 0: + from_id = node.getFieldAsString("fromNode", None, ancestry) + from_type = node.getFieldAsString("fromField", None, ancestry) + to_id = node.getFieldAsString("toNode", None, ancestry) + to_type = node.getFieldAsString("toField", None, ancestry) + if from_id and from_type and to_id and to_type: + # print(f"ROUTE from {from_id}.{from_type} to {to_id}.{to_type}") + importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh) + else: + for field in node.fields: + # print(f"return field {field}") + if field and field[0] == 'ROUTE': + try: + from_id, from_type = field[1].split('.') + to_id, to_type = field[3].split('.') + # print(f"ROUTE from {from_id}.{from_type} to {to_id}.{to_type}") + importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh) + except: + print("Warning, invalid ROUTE", field[1], "to", field[3]) + continue def importSkinWeights(obj, joint, jointCoord, end): group = obj.vertex_groups.get(joint) if group is None: group = obj.vertex_groups.new(name=joint) - # print(f"Created group {joint}"); + # print(f"Created group {joint}") print(f"Index {end} joint {joint}") for wi in range(len(jointCoord['skinCoordIndex'])): # print(f"Index {end} joint {joint} {jointCoord['skinCoordIndex'][wi]} weight {jointCoord['skinCoordWeight'][wi]}") @@ -3794,10 +3892,18 @@ def load_web3d( all_nodes = root_node.getSerialized([], []) all_shapes = [] + skeleton = None meshobj = None shape = None site = None + displacers = {} + skinCoord = None + hAnimJoint = None + hAnimSegment = None + hAnimSite = None + group = None + # collect shapes for sites for node, ancestry in all_nodes: spec = node.getSpec() @@ -3906,19 +4012,24 @@ def load_web3d( # Only use transform nodes when we are not importing a flat object hierarchy if PREF_FLAT == False: importTransform(bpycollection, node, ancestry, global_matrix) - ''' # These are delt with later within importRoute + ''' elif spec=='PositionInterpolator': action = bpy.data.ipos.new('web3d_ipo', 'Object') translatePositionInterpolator(node, action) - ''' + ''' # After we import all nodes, route events - anim paths if skeleton: bpy.ops.object.mode_set(mode='POSE') for node, ancestry in all_nodes: importRoute(node, ancestry, skeleton, meshobj) + bpy.context.scene.frame_set(0) + if not skeleton and shape[1]: + bpy.context.view_layer.objects.active = shape[1] + + bpy.ops.object.mode_set(mode='OBJECT') for node, ancestry in all_nodes: if node.isRoot(): # we know that all nodes referenced from will be in @@ -3930,16 +4041,29 @@ def load_web3d( # Assign anim curves node = defDict[key] + # print(f"key {key} action {action} node {node}") + bone = None + if skeleton: + if skeleton and key in skeleton.pose.bones: + bone = skeleton.pose.bones[key] + else: + print(f"There's no pose bone associated with key {key}, probably using a regular interpolator") + else: + print(f"There's no skeleton") if node.blendData is None: # Add an object if we need one for animation - bpyob = node.blendData = node.blendObject = bpy.data.objects.new('AnimOb', None) # , name) - bpycollection.objects.link(bpyob) - bpyob.select_set(True) - - if node.blendData.animation_data is None: - node.blendData.animation_data_create() - - node.blendData.animation_data.action = action + node.blendData = node.blendObject = bpy.data.objects.new(key, None) + bpycollection.objects.link(node.blendObject) + node.blendObject.select_set(True) + if hasattr(node.blendData, "animation_data"): + if not node.blendData.animation_data: + node.blendData.animation_data_create() + if not node.blendData.animation_data.action: + node.blendData.animation_data.action = action + # to disable NLA, comment out these 3 lines + track = node.blendData.animation_data.nla_tracks.new() + track.name = "NLATRACK "+key + node.blendData.animation_data.nla_tracks[track.name].strips.new(name=key, start=0, action=bpy.data.actions[key]) # Add in hierarchy if PREF_FLAT is False: child_dict = {} @@ -3967,8 +4091,22 @@ def load_web3d( # Parent for parent, children in child_dict.items(): - for c in children: - c.parent = parent + if parent and children: + for c in children: + if c: + if type(c) == type(parent): + c.parent = parent + else: + if isinstance(c, bpy.types.EditBone): + print(f"Child is EditBone") + if isinstance(parent, bpy.types.EditBone): + print(f"Parent is EditBone") + c.parent = skeleton # Armature object + print(f"Can't handle parent-child relationship, child {c} type {type(c)}, parent {parent} type {type(parent)}") + else: + print("Not a child") + else: + print("Children or parent may be None") # update deps bpycontext.view_layer.update() -- 2.30.2 From db102e1c39ada02261f996686951ad5be99ae0fd Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 14 Oct 2024 14:32:22 -0500 Subject: [PATCH 4/9] Ball now animatione (PREF_FLAT=False) --- source/import_x3d.py | 54 +++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index 3d42d4d..5c098d5 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -2856,6 +2856,8 @@ def appearance_LoadImageTextureFile(ima_urls, node): bpyima = None for f in ima_urls: dirname = os.path.dirname(node.getFilename()) + if f.startswith('"'): + f = f[1:-1] # strip quotes bpyima = image_utils.load_image(f, dirname, place_holder=False, recursive=False, @@ -3169,7 +3171,7 @@ def importShape_ProcessObject( # Can transform data or object, better the object so we can instance # the data # bpymesh.transform(getFinalMatrix(node)) - bpyob = node.blendObject = bpy.data.objects.new(vrmlname, bpydata) + bpyob = node.blendData = node.blendObject = bpy.data.objects.new(vrmlname, bpydata) bpyob.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix) bpycollection.objects.link(bpyob) bpyob.select_set(True) @@ -3597,10 +3599,12 @@ def translatePositionInterpolator(node, action, ancestry): loc_x = action_fcurve_ensure(action, "location", 0) loc_y = action_fcurve_ensure(action, "location", 1) loc_z = action_fcurve_ensure(action, "location", 2) + print (f"key {key} keyValue {keyValue} {action}") for i, time in enumerate(key): try: x, y, z = keyValue[i] + print (f"i {i} x {x} y {y} z {z}") except: continue @@ -3610,8 +3614,7 @@ def translatePositionInterpolator(node, action, ancestry): for fcu in (loc_x, loc_y, loc_z): for kf in fcu.keyframe_points: - kf.interpolation = 'LINEAR' - + kf.interpolation = 'BEZIER' def translateCoordinateInterpolator(node, action, ancestry): key = node.getFieldAsArray('key', 0, ancestry) @@ -3667,7 +3670,7 @@ def translateOrientationInterpolator(node, action, ancestry, to_node): for fcu in (rot_x, rot_y, rot_z): for kf in fcu.keyframe_points: - kf.interpolation = 'LINEAR' + kf.interpolation = 'BEZIER' def translateBoneOrientationInterpolator(node, action, ancestry, to_id=None, skeleton=None): key = node.getFieldAsArray('key', 0, ancestry) @@ -3764,7 +3767,7 @@ def importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skelet if to_type in ('set_translation', 'set_position'): # set translation may need some matrix multiplication action = getIpo(to_id) set_data_from_node = defDict[from_id] - print(f"Trying to create a position interpolator for joint {to_id} (may need something special?)") + print(f"Trying to create a position interpolator for something from {from_id} to {to_id} (may need something special?)") translatePositionInterpolator(set_data_from_node, action, ancestry) if to_type in {'rotation', "set_rotation"} and defDict[to_id].getSpec() == 'TextureTransform': @@ -3933,9 +3936,10 @@ def load_web3d( # Note, include this function so the VRML/X3D importer can be extended # by an external script. - gets first pick pass - if spec == 'Shape': - importShape(bpycollection, node, ancestry, global_matrix) - elif spec in {'PointLight', 'DirectionalLight', 'SpotLight'}: + #if spec == 'Shape': + # importShape(bpycollection, node, ancestry, global_matrix) + #el + if spec in {'PointLight', 'DirectionalLight', 'SpotLight'}: importLamp(bpycollection, node, spec, ancestry, global_matrix) elif spec == 'Viewpoint': importViewpoint(bpycollection, node, ancestry, global_matrix) @@ -3984,13 +3988,13 @@ def load_web3d( importSkinWeights(meshobj, parent_joint, jointSkin[parent_joint], "parent") imported.append(parent_joint) - elif spec in ('HAnimSegment'): - child_segment_name = node.getDefName() - hAnimSegment = importHAnimSegment(bpycollection, node, ancestry, global_matrix) - attachMesh(all_shapes, child_segment_name, hAnimSegment) # mesh is in all_shapes - hAnimSegment.parent = skeleton - hAnimSegment.parent_bone = parent_joint_name - hAnimSegment.parent_type = 'BONE' + #elif spec in ('HAnimSegment'): + # child_segment_name = node.getDefName() + # hAnimSegment = importHAnimSegment(bpycollection, node, ancestry, global_matrix) + # attachMesh(all_shapes, child_segment_name, hAnimSegment) # mesh is in all_shapes + # hAnimSegment.parent = skeleton + # hAnimSegment.parent_bone = parent_joint_name + # hAnimSegment.parent_type = 'BONE' elif spec in ('HAnimDisplacer'): #if meshobj: #importHAnimDisplacer(node, ancestry, meshobj, displacers) @@ -4012,11 +4016,11 @@ def load_web3d( # Only use transform nodes when we are not importing a flat object hierarchy if PREF_FLAT == False: importTransform(bpycollection, node, ancestry, global_matrix) - # These are delt with later within importRoute ''' + # These are delt with later within importRoute elif spec=='PositionInterpolator': action = bpy.data.ipos.new('web3d_ipo', 'Object') - translatePositionInterpolator(node, action) + translatePositionInterpolator(node, action, ancestry) ''' # After we import all nodes, route events - anim paths @@ -4051,19 +4055,33 @@ def load_web3d( else: print(f"There's no skeleton") if node.blendData is None: # Add an object if we need one for animation + print(f"Adding some blendData to the node for {key}. Did you forget to add it?") node.blendData = node.blendObject = bpy.data.objects.new(key, None) bpycollection.objects.link(node.blendObject) node.blendObject.select_set(True) + print(f"Adding some animation data for {key}?") + node.blendData.animation_data_create() + else: + print(f"Node {node.blendData.name} has blendData") if hasattr(node.blendData, "animation_data"): if not node.blendData.animation_data: + print(f"Adding animation data for {node.blendData.name} 2 ") node.blendData.animation_data_create() + else: + print(f"Node {node.blendData.name} has animation_data") if not node.blendData.animation_data.action: + print(f"Setting an action {node.blendData.name}") node.blendData.animation_data.action = action + else: + print(f"Node {node.blendData.name} has actionnode. {node.blendData.animation_data.action}") # to disable NLA, comment out these 3 lines + print(f"Adding an nla_track to {node.blendData.name} {key}") track = node.blendData.animation_data.nla_tracks.new() track.name = "NLATRACK "+key node.blendData.animation_data.nla_tracks[track.name].strips.new(name=key, start=0, action=bpy.data.actions[key]) + else: + print(f"Node {node.blendData.name} has blendData, but no animation_data") # Add in hierarchy if PREF_FLAT is False: child_dict = {} @@ -4147,7 +4165,7 @@ def load(context, # loadWithProfiler(operator, context, filepath, global_matrix) load_web3d(context, filepath, - PREF_FLAT=True, + PREF_FLAT=False, PREF_CIRCLE_DIV=16, global_scale=global_scale, global_matrix=global_matrix, -- 2.30.2 From 3ba1e898f0d0c5f32ca060b2934a9d0f05da3009 Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 14 Oct 2024 15:02:08 -0500 Subject: [PATCH 5/9] This is not good, but I need to restore --- source/import_x3d.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index 5c098d5..0fdd131 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -3988,13 +3988,13 @@ def load_web3d( importSkinWeights(meshobj, parent_joint, jointSkin[parent_joint], "parent") imported.append(parent_joint) - #elif spec in ('HAnimSegment'): - # child_segment_name = node.getDefName() - # hAnimSegment = importHAnimSegment(bpycollection, node, ancestry, global_matrix) - # attachMesh(all_shapes, child_segment_name, hAnimSegment) # mesh is in all_shapes - # hAnimSegment.parent = skeleton - # hAnimSegment.parent_bone = parent_joint_name - # hAnimSegment.parent_type = 'BONE' + elif spec in ('HAnimSegment'): + child_segment_name = node.getDefName() + hAnimSegment = importHAnimSegment(bpycollection, node, ancestry, global_matrix) + attachMesh(all_shapes, child_segment_name, hAnimSegment) # mesh is in all_shapes + hAnimSegment.parent = skeleton + hAnimSegment.parent_bone = parent_joint_name + hAnimSegment.parent_type = 'BONE' elif spec in ('HAnimDisplacer'): #if meshobj: #importHAnimDisplacer(node, ancestry, meshobj, displacers) @@ -4060,7 +4060,7 @@ def load_web3d( bpycollection.objects.link(node.blendObject) node.blendObject.select_set(True) print(f"Adding some animation data for {key}?") - node.blendData.animation_data_create() + # node.blendData.animation_data_create() else: print(f"Node {node.blendData.name} has blendData") -- 2.30.2 From f5ba4c1a6c6e357da2b2b3bfd8224c013ade6353 Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 14 Oct 2024 18:25:23 -0500 Subject: [PATCH 6/9] Check for None before subscripting --- source/import_x3d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index 0fdd131..ed33960 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -4030,7 +4030,7 @@ def load_web3d( importRoute(node, ancestry, skeleton, meshobj) bpy.context.scene.frame_set(0) - if not skeleton and shape[1]: + if not skeleton and shape is not None and shape[1]: bpy.context.view_layer.objects.active = shape[1] bpy.ops.object.mode_set(mode='OBJECT') -- 2.30.2 From 7fa7ba52f1df7cfd6f22b2b866a5c83d8257b14a Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Tue, 15 Oct 2024 14:01:54 -0500 Subject: [PATCH 7/9] Commented out some loggging --- source/import_x3d.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index ed33960..dd6912f 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -4055,33 +4055,37 @@ def load_web3d( else: print(f"There's no skeleton") if node.blendData is None: # Add an object if we need one for animation - print(f"Adding some blendData to the node for {key}. Did you forget to add it?") + # print(f"Adding some blendData to the node for {key}. Did you forget to add it?") node.blendData = node.blendObject = bpy.data.objects.new(key, None) bpycollection.objects.link(node.blendObject) node.blendObject.select_set(True) - print(f"Adding some animation data for {key}?") + # print(f"Adding some animation data for {key}?") # node.blendData.animation_data_create() else: - print(f"Node {node.blendData.name} has blendData") + # print(f"Node {node.blendData.name} has blendData") + pass if hasattr(node.blendData, "animation_data"): if not node.blendData.animation_data: - print(f"Adding animation data for {node.blendData.name} 2 ") + #print(f"Adding animation data for {node.blendData.name} 2 ") node.blendData.animation_data_create() else: - print(f"Node {node.blendData.name} has animation_data") + #print(f"Node {node.blendData.name} has animation_data") + pass if not node.blendData.animation_data.action: - print(f"Setting an action {node.blendData.name}") + #print(f"Setting an action {node.blendData.name}") node.blendData.animation_data.action = action else: - print(f"Node {node.blendData.name} has actionnode. {node.blendData.animation_data.action}") + # print(f"Node {node.blendData.name} has actionnode. {node.blendData.animation_data.action}") + pass # to disable NLA, comment out these 3 lines - print(f"Adding an nla_track to {node.blendData.name} {key}") + # print(f"Adding an nla_track to {node.blendData.name} {key}") track = node.blendData.animation_data.nla_tracks.new() track.name = "NLATRACK "+key node.blendData.animation_data.nla_tracks[track.name].strips.new(name=key, start=0, action=bpy.data.actions[key]) else: - print(f"Node {node.blendData.name} has blendData, but no animation_data") + # print(f"Node {node.blendData.name} has blendData, but no animation_data") + pass # Add in hierarchy if PREF_FLAT is False: child_dict = {} @@ -4116,9 +4120,11 @@ def load_web3d( c.parent = parent else: if isinstance(c, bpy.types.EditBone): - print(f"Child is EditBone") + if DEBUG: + print(f"Child is EditBone") if isinstance(parent, bpy.types.EditBone): - print(f"Parent is EditBone") + if DEBUG: + print(f"Parent is EditBone") c.parent = skeleton # Armature object print(f"Can't handle parent-child relationship, child {c} type {type(c)}, parent {parent} type {type(parent)}") else: -- 2.30.2 From 1c8a12edd732dcf4c7abe0316b3a40131546b300 Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Thu, 17 Oct 2024 03:17:39 -0500 Subject: [PATCH 8/9] Some updates involving joint centers --- source/import_x3d.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index dd6912f..25c83dd 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -3325,15 +3325,13 @@ def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, se first_joint_name = child.getDefName() if not first_joint_name: first_joint_name = child.getFieldAsString('name', None, ancestry) - joint_center = child.getFieldAsFloatTuple('center', None, ancestry) - #if joint_center is None: - joint_center = (0, 0, 0) # may delete joint at 0 0 0 + joint_center = child.getFieldAsFloatTuple('center', (0.0, 0.0, 0.0), ancestry) print(f"Joint {first_joint_name} {joint_center}") importHAnimJoint(joints, segments, child, ancestry, first_joint_name, parent_center=joint_center[:]) # Create bones for each joint for joint_name, joint_start, joint_end, skinCoordWeight, skinCoordIndex in joints: - print(f"Creating {joint_name} {joint_start} {joint_end}") + # print(f"Creating {joint_name} {joint_start} {joint_end}") # print(f"Joint {joint_name} {joint_start} {joint_end}") if not joint_name: joint_name = vrmlname @@ -3417,7 +3415,9 @@ def importHAnimJoint(joints, segments, child, ancestry, parent_bone_name=None, p if children: importHAnimJoints(joints, segments, children, ancestry, child_bone_name, child_center) else: - print(f"Didn't find children, {children}") + childname = child.getFieldAsString('name', '', ancestry) + if DEBUG: + print(f"Didn't find children, {children} for {childname}") else: print(f"Didn't find child, {child}") @@ -3599,12 +3599,14 @@ def translatePositionInterpolator(node, action, ancestry): loc_x = action_fcurve_ensure(action, "location", 0) loc_y = action_fcurve_ensure(action, "location", 1) loc_z = action_fcurve_ensure(action, "location", 2) - print (f"key {key} keyValue {keyValue} {action}") + if DEBUG: + print (f"key {key} keyValue {keyValue} {action}") for i, time in enumerate(key): try: x, y, z = keyValue[i] - print (f"i {i} x {x} y {y} z {z}") + if DEBUG: + print (f"i {i} x {x} y {y} z {z}") except: continue @@ -3767,7 +3769,8 @@ def importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skelet if to_type in ('set_translation', 'set_position'): # set translation may need some matrix multiplication action = getIpo(to_id) set_data_from_node = defDict[from_id] - print(f"Trying to create a position interpolator for something from {from_id} to {to_id} (may need something special?)") + if DEBUG: + print(f"Trying to create a position interpolator for something from {from_id} to {to_id} (may need something special?)") translatePositionInterpolator(set_data_from_node, action, ancestry) if to_type in {'rotation', "set_rotation"} and defDict[to_id].getSpec() == 'TextureTransform': @@ -3780,10 +3783,10 @@ def importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skelet set_data_from_node = defDict[from_id] to_node = defDict[to_id] if skeleton and skeleton.pose.bones.get(to_id): - print(f"Creating animation for joint {to_id}") + # print(f"Creating animation for joint {to_id}") translateBoneOrientationInterpolator(set_data_from_node, action, ancestry, to_id, skeleton) if not hasMesh: - print(f"Creating orientation animation for {to_id}") + # print(f"Creating orientation animation for {to_id}") translateOrientationInterpolator(set_data_from_node, action, ancestry, to_node) if to_type == 'set_scale': @@ -3848,7 +3851,7 @@ def importSkinWeights(obj, joint, jointCoord, end): if group is None: group = obj.vertex_groups.new(name=joint) # print(f"Created group {joint}") - print(f"Index {end} joint {joint}") + # print(f"Index {end} joint {joint}") for wi in range(len(jointCoord['skinCoordIndex'])): # print(f"Index {end} joint {joint} {jointCoord['skinCoordIndex'][wi]} weight {jointCoord['skinCoordWeight'][wi]}") group.add([jointCoord['skinCoordIndex'][wi]], jointCoord['skinCoordWeight'][wi], 'REPLACE') @@ -3952,12 +3955,15 @@ def load_web3d( skinCoord = node.getChildBySpec('Coordinate') if skinCoord: #if skinCoord.getFieldAsString("containerField", None, ancestry) == "skinCoord": - print(f"Skin coord is {skinCoord}") + if DEBUG: + print(f"Skin coord is {skinCoord}") for shape in all_shapes: if shape: - print(f"Skin mesh is found") + if DEBUG: + print(f"Skin mesh is found") if shape[0] and shape[1] and shape[2] and skinCoord.getRealNode().getDefName() == shape[2].getRealNode().getDefName(): - print("Got mesh obj") + if DEBUG: + print("Got mesh obj") meshobj = shape[1] meshobj.modifiers.new(name='ArmatureToMesh', type='ARMATURE') meshobj.modifiers['ArmatureToMesh'].object = skeleton @@ -3972,12 +3978,14 @@ def load_web3d( print("No skinCoord, no skin weights, no skin animation") - print(f"mesh is {meshobj}") + if DEBUG: + print(f"mesh is {meshobj}") #bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="OBJECT") #bmesh.from_edit_mesh(meshobj.data) imported = [] - print(f"Number of segments {len(segments)}") + if DEBUG: + print(f"Number of segments {len(segments)}") for segment in segments: parent_joint, child_joint = segment # print(f"Segment {parent_joint} {child_joint} loading weights") -- 2.30.2 From eab885c0b127c8f3838e403ca4fb5e26d1ed3da6 Mon Sep 17 00:00:00 2001 From: John W Carlson Date: Mon, 21 Oct 2024 09:05:07 -0500 Subject: [PATCH 9/9] Changes per Bujus' review, plus commented out IndexedLineSet colors --- source/import_x3d.py | 259 +++++++++++++++++++++++++------------------ 1 file changed, 153 insertions(+), 106 deletions(-) diff --git a/source/import_x3d.py b/source/import_x3d.py index 25c83dd..eb2141a 100644 --- a/source/import_x3d.py +++ b/source/import_x3d.py @@ -18,7 +18,7 @@ material_cache = {} conversion_scale = 1.0 EPSILON = 0.0000001 # Very crude. -PREF_TIME_MULT = 250 +TIME_MULTIPLIER = 250 def imageConvertCompat(path): @@ -360,8 +360,8 @@ class vrmlNode(object): 'node_type', 'parent', 'children', - 'skeleton', # no joints, segments or sites. TODO - 'skinCoord', + 'skeleton', # TODO no joints, segments or sites yet. + 'skinCoord', # this is intentionally NOT skin_coord, but I don't know if HAnim should be here at all 'skin', 'parent', 'array_data', @@ -1600,6 +1600,7 @@ def getFinalMatrix(node, mtx, ancestry, global_matrix): transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() in ('Transform', 'HAnimHumanoid', 'HAnimJoint', 'HAnimSite', 'HAnimDisplacer')] if node.getSpec() in ('Transform', 'HAnimHumanoid', 'HAnimJoint', 'HAnimSite', 'HAnimDisplacer'): + # This comment is here so I can quick replace the above in testing #transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() == 'Transform'] #if node.getSpec() == 'Transform': transform_nodes.append(node) @@ -1645,6 +1646,16 @@ def set_new_float_color_attribute(bpymesh, color_data, name: str = "ColorPerCorn bpymesh.color_attributes.new(name, 'FLOAT_COLOR', 'CORNER') bpymesh.color_attributes[name].data.foreach_set("color", color_data) +# TODO not tested +def set_new_float_color_attribute_curve(bpycurve, color_data, name: str = "ColorPerCorner", convert_to_linear: bool = True): + if (convert_to_linear): + # convert color spaces to account for api changes from legacy to newer api + color_data = [srgb_to_linear(col_val) for col_val in color_data] + mat = bpy.data.materials.new(name="ColorMaterial") + mat.color = (1, 0, 0, 1) + curve_obj.data.materials.append(mat) + + # Assumes that the mesh has polygons. def importMesh_ApplyColors(bpymesh, geom, ancestry): colors = geom.getChildBySpec(['ColorRGBA', 'Color']) @@ -1913,6 +1924,80 @@ def importMesh_TriangleFanSet(geom, ancestry): bpymesh.polygons.foreach_set("vertices", [x for x in triangles()]) return importMesh_FinalizeTriangleMesh(bpymesh, geom, ancestry) +# TODO, not for this release +def processColors_IndexedLineSet(geom, ancestry, bpycurve, lines): + colors = geom.getChildBySpec(['ColorRGBA', 'Color']) + index = geom.getFieldAsArray('coordIndex', 0, ancestry) + if colors: + cco = [] + if colors.getSpec() == 'ColorRGBA': + rgb = colors.getFieldAsArray('color', 4, ancestry) + else: + # Array of arrays; no need to flatten + rgb = [c + [1.0] for c in colors.getFieldAsArray('color', 3, ancestry)] + + color_per_vertex = geom.getFieldAsBool('colorPerVertex', True, ancestry) + color_index = geom.getFieldAsArray('colorIndex', 0, ancestry) + has_color_index = len(color_index) != 0 + has_valid_color_index = index.count(-1) == color_index.count(-1) + + # rebuild a corrupted colorIndex field (assuming the end of face markers -1 are missing) + if has_color_index and not has_valid_color_index: + # remove all -1 beforehand to ensure clean working copy + color_index = [x for x in color_index if x != -1] + # copy all -1 from coordIndex to colorIndex + for i, v in enumerate(index): + if v == -1: + color_index.insert(i, -1) + + if color_per_vertex and has_color_index: # Color per vertex with index + cco = [cco for f in processPerVertexIndex(color_index) + for v in f + for cco in rgb[v]] + elif color_per_vertex: # Color per vertex without index + # use vertex value by default, however if lengths mismatch use the positional value to access rgb value + # ain't ideal by far, but should most likely work + try: + cco = [cco for f in lines + for v in f + for cco in rgb[v]] + except IndexError: + print("reattempting reading color_per_vertex without index by using positional value because vertex value failed") + cco = [cco for f in lines + for (i, v) in enumerate(f) + for cco in rgb[i]] + elif color_index: # Color per face with index + cco = [cco for (i, f) in enumerate(lines) + for j in f + for cco in rgb[color_index[i]]] + elif len(lines) > len(rgb): # Static color per face without index, when all lines have the same color. + # Exported from SOLIDWORKS, see: `blender/blender-addons#105398`. + cco = [cco for (i, f) in enumerate(lines) + for j in f + for cco in rgb[0]] + else: # Color per face without index + cco = [cco for (i, f) in enumerate(lines) + for j in f + for cco in rgb[i]] + + for i, spline in enumerate(bpycurve.splines): + # Example: Color based on spline index + # TODO do RGBA + if cco is not None: + color = cco[i * 3:(i+1) * 3] + color.append(1) # TODO includ transporency + else: + color = (1, 0, 0, 1) + print(f"Color = {color}") + # Apply color to the spline directly + spline.material_index = i # Assign a material index (optional) + + # Create a material for the spline + if len(bpy.data.materials) <= i: + mat = bpy.data.materials.new(name="ColorMaterial_" + str(i)) + mat.diffuse_color = color + bpycurve.materials.append(mat) + def importMesh_IndexedFaceSet(geom, ancestry): # Saw the following structure in X3Ds: the first mesh has a huge set @@ -2509,6 +2594,8 @@ def importMesh_IndexedLineSet(geom, ancestry): nu.points.add(len(line) - 1) # the new nu has 1 point to begin with for il, pt in zip(line, nu.points): pt.co[0:3] = points[il] + # TODO + # processColors_IndexedLineSet(geom, ancestry, bpycurve, lines) return bpycurve @@ -2857,7 +2944,7 @@ def appearance_LoadImageTextureFile(ima_urls, node): for f in ima_urls: dirname = os.path.dirname(node.getFilename()) if f.startswith('"'): - f = f[1:-1] # strip quotes + f = f[1:-1] # strip quotes (I want to strip both quotes, front and tail. I am not sure if this works) bpyima = image_utils.load_image(f, dirname, place_holder=False, recursive=False, @@ -3295,9 +3382,9 @@ def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, se # print(vrmlname) prefix = '' if vrmlname: - fus = vrmlname.find('_') - if fus > 0: - prefix = vrmlname[:fus+1] + first_underscore = vrmlname.find('_') + if first_underscore > 0: + prefix = vrmlname[:first_underscore+1] else: vrmlname = 'HAnimHumanoid' @@ -3321,44 +3408,26 @@ def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, se # Process children joints, including USE, if present child = node.getChildBySpec('HAnimJoint') # 'HAnimJoint' if child: - # first_joint_name = child.getFieldAsString('name', None, ancestry) - first_joint_name = child.getDefName() - if not first_joint_name: - first_joint_name = child.getFieldAsString('name', None, ancestry) + first_joint_name = child.getDefName() or child.getFieldAsString('name', None, ancestry) joint_center = child.getFieldAsFloatTuple('center', (0.0, 0.0, 0.0), ancestry) - print(f"Joint {first_joint_name} {joint_center}") + if DEBUG: + print(f"Joint {first_joint_name} {joint_center}") importHAnimJoint(joints, segments, child, ancestry, first_joint_name, parent_center=joint_center[:]) # Create bones for each joint for joint_name, joint_start, joint_end, skinCoordWeight, skinCoordIndex in joints: - # print(f"Creating {joint_name} {joint_start} {joint_end}") - # print(f"Joint {joint_name} {joint_start} {joint_end}") if not joint_name: joint_name = vrmlname - # bpy.ops.armature.bone_primitive_add(name=joint_name) new_segment = armature_data.edit_bones.new(joint_name) - # new_segment = skeleton.data.edit_bones[joint_name] - #print(f"Created {joint_name} {new_segment}") child.blendData = child.blendObject = new_segment matrix_world_inv = skeleton.matrix_world.inverted() new_segment.head = joint_end new_segment.tail = joint_start - - # bpy.context.view_layer.objects.active = skeleton - #bpy.ops.object.mode_set(mode='POSE') - #pose_bone = skeleton.pose.bones.get(joint_name) - #if pose_bone: - # pose_bone.bone.select = True - # pose_bone.location = matrix_world_inv @ pose_bone.bone.matrix_local.inverted() @ mathutils.Vector(new_segment.head) - # pose_bone.location = mathutils.Vector(new_segment.head) - #else: - # print(f"There's no pose bone associated with {joint_name}") - if joint_name != vrmlname: - jointSkin[joint_name] = { - 'skinCoordWeight' : skinCoordWeight, - 'skinCoordIndex' : skinCoordIndex - } - #print(f"Adding {joint_name} {len(jointSkin[joint_name]['skinCoordIndex'])} indexes, {len(jointSkin[joint_name]['skinCoordWeight'])} weights") + # if joint_name != vrmlname: + jointSkin[joint_name] = { + 'skinCoordWeight' : skinCoordWeight, + 'skinCoordIndex' : skinCoordIndex + } for segment in segments: parent_joint, child_joint = segment if parent_joint in skeleton.data.edit_bones: @@ -3375,20 +3444,13 @@ def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, se print("Couldn't find child HAnimJoint") - # getFinalMatrix(node, None, ancestry, global_matrix) - return skeleton def importHAnimJoints(joints, segments, children, ancestry, parent_bone_name, parent_center=[0, 0, 0]): for child in children: - child_bone_name = child.getDefName() - if not child_bone_name: - child_bone_name = child.getFieldAsString('name', None, ancestry) - if not child_bone_name: - child_bone_name = parent_bone_name + child_bone_name = child.getDefName() or child.getFieldAsString('name', None, ancestry) or parent_bone_name segments.append((parent_bone_name, child_bone_name)) - # print(f"Segment IHAJs {parent_bone_name} {child_bone_name}") importHAnimJoint(joints, segments, child, ancestry, parent_bone_name, parent_center) def importHAnimJoint(joints, segments, child, ancestry, parent_bone_name=None, parent_center=[0, 0, 0]): @@ -3401,6 +3463,8 @@ def importHAnimJoint(joints, segments, child, ancestry, parent_bone_name=None, p child_center = child.getFieldAsFloatTuple('center', None, ancestry) skinCoordWeight = child.getFieldAsArray('skinCoordWeight', 0, ancestry) skinCoordIndex = child.getFieldAsArray('skinCoordIndex', 0, ancestry) + # I don't understand reviewer's comment: + # "better move the nl after the validation section" if skinCoordWeight is None: skinCoordWeight = () if skinCoordIndex is None: @@ -3408,7 +3472,7 @@ def importHAnimJoint(joints, segments, child, ancestry, parent_bone_name=None, p if not child_center: child_center = [0, 0, 0] - joints.append((child_bone_name, (child_center[0], child_center[1], child_center[2]), (parent_center[0], parent_center[1], parent_center[2]), skinCoordWeight, skinCoordIndex)) + joints.append((child_bone_name, tuple(child_center), tuple(parent_center), skinCoordWeight, skinCoordIndex)) # print(f"Joint IHAJ {joints[-1]}") children = child.getChildrenBySpec('HAnimJoint') @@ -3563,14 +3627,12 @@ def importTransform(bpycollection, node, ancestry, global_matrix): bpyob.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix) - # so they are not too annoying + # so the EMPTY is not too annoying bpyob.empty_display_type = 'PLAIN_AXES' bpyob.empty_display_size = 0.2 def importHAnimSegment(bpycollection, node, ancestry, global_matrix): - name = node.getDefName() - if not name: - name = 'HAnimSegment' + name = node.getDefName() or node.getFieldAsString('name', None, ancestry) or 'HAnimSegment' bpyob = node.blendData = node.blendObject = bpy.data.objects.new(name, None) bpycollection.objects.link(bpyob) @@ -3578,8 +3640,8 @@ def importHAnimSegment(bpycollection, node, ancestry, global_matrix): bpyob.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix) - # so they are not too annoying bpyob.empty_display_type = 'PLAIN_AXES' + # so the EMPTY is not too annoying bpyob.empty_display_size = 0.2 return bpyob @@ -3607,12 +3669,12 @@ def translatePositionInterpolator(node, action, ancestry): x, y, z = keyValue[i] if DEBUG: print (f"i {i} x {x} y {y} z {z}") - except: + except: # There's 4 exception possible here, so just wildcard continue - loc_x.keyframe_points.insert(time*PREF_TIME_MULT, x) - loc_y.keyframe_points.insert(time*PREF_TIME_MULT, y) - loc_z.keyframe_points.insert(time*PREF_TIME_MULT, z) + loc_x.keyframe_points.insert(time*TIME_MULTIPLIER, x) + loc_y.keyframe_points.insert(time*TIME_MULTIPLIER, y) + loc_z.keyframe_points.insert(time*TIME_MULTIPLIER, z) for fcu in (loc_x, loc_y, loc_z): for kf in fcu.keyframe_points: @@ -3622,7 +3684,8 @@ def translateCoordinateInterpolator(node, action, ancestry): key = node.getFieldAsArray('key', 0, ancestry) keyValue = node.getFieldAsArray('keyValue', 0, ancestry) offset = int(len(keyValue) / len(key) / 3) # values divide by times divided by axes - print(f"ci {offset} = {len(keyValue)} / {len(key)}") + if DEBUG: + print(f"ci {offset} = {len(keyValue)} / {len(key)}") loc_x = action_fcurve_ensure(action, "location", 0) loc_y = action_fcurve_ensure(action, "location", 1) loc_z = action_fcurve_ensure(action, "location", 2) @@ -3638,11 +3701,11 @@ def translateCoordinateInterpolator(node, action, ancestry): x = keyValue[curoff+0] y = keyValue[curoff+1] z = keyValue[curoff+2] - loc_x.keyframe_points.insert(time*PREF_TIME_MULT, x) - loc_y.keyframe_points.insert(time*PREF_TIME_MULT, y) - loc_z.keyframe_points.insert(time*PREF_TIME_MULT, z) + loc_x.keyframe_points.insert(time*TIME_MULTIPLIER, x) + loc_y.keyframe_points.insert(time*TIME_MULTIPLIER, y) + loc_z.keyframe_points.insert(time*TIME_MULTIPLIER, z) - curoff = curoff + 3 + curoff += 3 for fcu in (loc_x, loc_y, loc_z): for kf in fcu.keyframe_points: @@ -3661,14 +3724,14 @@ def translateOrientationInterpolator(node, action, ancestry, to_node): for i, time in enumerate(key): try: x, y, z, w = keyValue[i] - except: + except: # There's 4 exception possible here, so just wildcard continue mtx = translateRotation((x, y, z, w)) eul = mtx.to_euler() - rot_x.keyframe_points.insert(time*PREF_TIME_MULT, eul.x) - rot_y.keyframe_points.insert(time*PREF_TIME_MULT, eul.y) - rot_z.keyframe_points.insert(time*PREF_TIME_MULT, eul.z) + rot_x.keyframe_points.insert(time*TIME_MULTIPLIER, eul.x) + rot_y.keyframe_points.insert(time*TIME_MULTIPLIER, eul.y) + rot_z.keyframe_points.insert(time*TIME_MULTIPLIER, eul.z) for fcu in (rot_x, rot_y, rot_z): for kf in fcu.keyframe_points: @@ -3681,11 +3744,11 @@ def translateBoneOrientationInterpolator(node, action, ancestry, to_id=None, ske pose_bone = None if skeleton: pose_bone = skeleton.pose.bones.get(to_id) - if pose_bone: - pose_bone.rotation_mode = 'AXIS_ANGLE' - for time, (x, y, z, w) in zip(key, keyValue): - pose_bone.rotation_axis_angle = (w, x, y, z) - pose_bone.keyframe_insert(data_path="rotation_axis_angle", frame=time * PREF_TIME_MULT) + if pose_bone: + pose_bone.rotation_mode = 'AXIS_ANGLE' + for time, (x, y, z, w) in zip(key, keyValue): + pose_bone.rotation_axis_angle = (w, x, y, z) + pose_bone.keyframe_insert(data_path="rotation_axis_angle", frame=time * TIME_MULTIPLIER) # Untested! @@ -3700,12 +3763,12 @@ def translateScaleInterpolator(node, action, ancestry): for i, time in enumerate(key): try: x, y, z = keyValue[i] - except: + except: # There's 4 exception possible here, so just wildcard continue - sca_x.keyframe_points.insert(time*PREF_TIME_MULT, x) - sca_y.keyframe_points.insert(time*PREF_TIME_MULT, y) - sca_z.keyframe_points.insert(time*PREF_TIME_MULT, z) + sca_x.keyframe_points.insert(time*TIME_MULTIPLIER, x) + sca_y.keyframe_points.insert(time*TIME_MULTIPLIER, y) + sca_z.keyframe_points.insert(time*TIME_MULTIPLIER, z) def translateScalarInterpolator(node, action, ancestry, to_node, data_path): key = node.getFieldAsArray('key', 0, ancestry) @@ -3716,10 +3779,10 @@ def translateScalarInterpolator(node, action, ancestry, to_node, data_path): for i, time in enumerate(key): try: s = keyValue[i] - except: + except: # There's 4 exception possible here, so just wildcard continue - scalar.keyframe_points.insert(time*PREF_TIME_MULT, s) + scalar.keyframe_points.insert(time*TIME_MULTIPLIER, s) def translateTimeSensor(node, action, ancestry): """ @@ -3837,30 +3900,24 @@ ROUTE champFly001.bindTime TO vpTs.set_startTime for field in node.fields: # print(f"return field {field}") if field and field[0] == 'ROUTE': - try: - from_id, from_type = field[1].split('.') - to_id, to_type = field[3].split('.') - # print(f"ROUTE from {from_id}.{from_type} to {to_id}.{to_type}") - importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh) - except: - print("Warning, invalid ROUTE", field[1], "to", field[3]) - continue + from_id, from_type = field[1].split('.') + to_id, to_type = field[3].split('.') + # print(f"ROUTE from {from_id}.{from_type} to {to_id}.{to_type}") + importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh) def importSkinWeights(obj, joint, jointCoord, end): - group = obj.vertex_groups.get(joint) - if group is None: - group = obj.vertex_groups.new(name=joint) + group = obj.vertex_groups.get(joint) or obj.vertex_groups.new(name=joint) # print(f"Created group {joint}") # print(f"Index {end} joint {joint}") - for wi in range(len(jointCoord['skinCoordIndex'])): - # print(f"Index {end} joint {joint} {jointCoord['skinCoordIndex'][wi]} weight {jointCoord['skinCoordWeight'][wi]}") - group.add([jointCoord['skinCoordIndex'][wi]], jointCoord['skinCoordWeight'][wi], 'REPLACE') + for weight_index in range(len(jointCoord['skinCoordIndex'])): + # print(f"Index {end} joint {joint} {jointCoord['skinCoordIndex'][weight_index]} weight {jointCoord['skinCoordWeight'][weight_index]}") + group.add([jointCoord['skinCoordIndex'][weight_index]], jointCoord['skinCoordWeight'][weight_index], 'REPLACE') def load_web3d( bpycontext, filepath, *, - PREF_FLAT=False, + PREF_FLAT=False, # So Tranforms will be imported PREF_CIRCLE_DIV=16, file_unit='M', global_scale=1.0, @@ -3939,9 +3996,6 @@ def load_web3d( # Note, include this function so the VRML/X3D importer can be extended # by an external script. - gets first pick pass - #if spec == 'Shape': - # importShape(bpycollection, node, ancestry, global_matrix) - #el if spec in {'PointLight', 'DirectionalLight', 'SpotLight'}: importLamp(bpycollection, node, spec, ancestry, global_matrix) elif spec == 'Viewpoint': @@ -3951,7 +4005,6 @@ def load_web3d( segments = [] jointSkin = {} skeleton = importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, segments, jointSkin) - # skinCoord = node.getChildByName('skinCoord') # 'Coordinate' skinCoord = node.getChildBySpec('Coordinate') if skinCoord: #if skinCoord.getFieldAsString("containerField", None, ancestry) == "skinCoord": @@ -3972,8 +4025,6 @@ def load_web3d( else: print(f"no shape {shape} ? all shapes is {all_shapes}") - #else: - # print(f"Couldn't get containerField {skinCoord}") else: print("No skinCoord, no skin weights, no skin animation") @@ -3982,19 +4033,19 @@ def load_web3d( print(f"mesh is {meshobj}") #bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="OBJECT") - #bmesh.from_edit_mesh(meshobj.data) imported = [] if DEBUG: print(f"Number of segments {len(segments)}") for segment in segments: parent_joint, child_joint = segment # print(f"Segment {parent_joint} {child_joint} loading weights") - if meshobj and child_joint not in imported: - importSkinWeights(meshobj, child_joint, jointSkin[child_joint], "child") - imported.append(child_joint) - if meshobj and parent_joint not in imported: - importSkinWeights(meshobj, parent_joint, jointSkin[parent_joint], "parent") - imported.append(parent_joint) + if meshobj: + if child_joint not in imported: + importSkinWeights(meshobj, child_joint, jointSkin[child_joint], "child") + imported.append(child_joint) + if parent_joint not in imported: + importSkinWeights(meshobj, parent_joint, jointSkin[parent_joint], "parent") + imported.append(parent_joint) elif spec in ('HAnimSegment'): child_segment_name = node.getDefName() @@ -4004,6 +4055,7 @@ def load_web3d( hAnimSegment.parent_bone = parent_joint_name hAnimSegment.parent_type = 'BONE' elif spec in ('HAnimDisplacer'): + # TODO Intended to be implemented #if meshobj: #importHAnimDisplacer(node, ancestry, meshobj, displacers) pass @@ -4056,7 +4108,7 @@ def load_web3d( # print(f"key {key} action {action} node {node}") bone = None if skeleton: - if skeleton and key in skeleton.pose.bones: + if key in skeleton.pose.bones: bone = skeleton.pose.bones[key] else: print(f"There's no pose bone associated with key {key}, probably using a regular interpolator") @@ -4067,11 +4119,6 @@ def load_web3d( node.blendData = node.blendObject = bpy.data.objects.new(key, None) bpycollection.objects.link(node.blendObject) node.blendObject.select_set(True) - # print(f"Adding some animation data for {key}?") - # node.blendData.animation_data_create() - else: - # print(f"Node {node.blendData.name} has blendData") - pass if hasattr(node.blendData, "animation_data"): if not node.blendData.animation_data: @@ -4179,7 +4226,7 @@ def load(context, # loadWithProfiler(operator, context, filepath, global_matrix) load_web3d(context, filepath, - PREF_FLAT=False, + PREF_FLAT=False, # So Tranforms will be imported PREF_CIRCLE_DIV=16, global_scale=global_scale, global_matrix=global_matrix, -- 2.30.2