WIP: X3D HAnim with Blender bones (no animation yet present) #23
@ -18,6 +18,7 @@ material_cache = {}
|
|||||||
conversion_scale = 1.0
|
conversion_scale = 1.0
|
||||||
|
|
||||||
EPSILON = 0.0000001 # Very crude.
|
EPSILON = 0.0000001 # Very crude.
|
||||||
|
TIME_MULTIPLIER = 250
|
||||||
|
|
||||||
|
|
||||||
def imageConvertCompat(path):
|
def imageConvertCompat(path):
|
||||||
@ -359,6 +360,9 @@ class vrmlNode(object):
|
|||||||
'node_type',
|
'node_type',
|
||||||
'parent',
|
'parent',
|
||||||
'children',
|
'children',
|
||||||
|
'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',
|
'parent',
|
||||||
'array_data',
|
'array_data',
|
||||||
'reference',
|
'reference',
|
||||||
@ -1594,8 +1598,11 @@ def translateTexTransform(node, ancestry):
|
|||||||
|
|
||||||
def getFinalMatrix(node, mtx, ancestry, global_matrix):
|
def getFinalMatrix(node, mtx, ancestry, global_matrix):
|
||||||
|
|
||||||
transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() == 'Transform']
|
transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() in ('Transform', 'HAnimHumanoid', 'HAnimJoint', 'HAnimSite', 'HAnimDisplacer')]
|
||||||
if node.getSpec() == 'Transform':
|
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)
|
transform_nodes.append(node)
|
||||||
transform_nodes.reverse()
|
transform_nodes.reverse()
|
||||||
|
|
||||||
@ -1639,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.new(name, 'FLOAT_COLOR', 'CORNER')
|
||||||
bpymesh.color_attributes[name].data.foreach_set("color", color_data)
|
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.
|
# Assumes that the mesh has polygons.
|
||||||
def importMesh_ApplyColors(bpymesh, geom, ancestry):
|
def importMesh_ApplyColors(bpymesh, geom, ancestry):
|
||||||
colors = geom.getChildBySpec(['ColorRGBA', 'Color'])
|
colors = geom.getChildBySpec(['ColorRGBA', 'Color'])
|
||||||
@ -1907,6 +1924,80 @@ def importMesh_TriangleFanSet(geom, ancestry):
|
|||||||
bpymesh.polygons.foreach_set("vertices", [x for x in triangles()])
|
bpymesh.polygons.foreach_set("vertices", [x for x in triangles()])
|
||||||
return importMesh_FinalizeTriangleMesh(bpymesh, geom, ancestry)
|
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):
|
def importMesh_IndexedFaceSet(geom, ancestry):
|
||||||
# Saw the following structure in X3Ds: the first mesh has a huge set
|
# Saw the following structure in X3Ds: the first mesh has a huge set
|
||||||
@ -2503,6 +2594,8 @@ def importMesh_IndexedLineSet(geom, ancestry):
|
|||||||
nu.points.add(len(line) - 1) # the new nu has 1 point to begin with
|
nu.points.add(len(line) - 1) # the new nu has 1 point to begin with
|
||||||
for il, pt in zip(line, nu.points):
|
for il, pt in zip(line, nu.points):
|
||||||
pt.co[0:3] = points[il]
|
pt.co[0:3] = points[il]
|
||||||
|
# TODO
|
||||||
|
# processColors_IndexedLineSet(geom, ancestry, bpycurve, lines)
|
||||||
|
|
||||||
return bpycurve
|
return bpycurve
|
||||||
|
|
||||||
@ -2850,6 +2943,8 @@ def appearance_LoadImageTextureFile(ima_urls, node):
|
|||||||
bpyima = None
|
bpyima = None
|
||||||
for f in ima_urls:
|
for f in ima_urls:
|
||||||
dirname = os.path.dirname(node.getFilename())
|
dirname = os.path.dirname(node.getFilename())
|
||||||
|
if f.startswith('"'):
|
||||||
|
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,
|
bpyima = image_utils.load_image(f, dirname,
|
||||||
place_holder=False,
|
place_holder=False,
|
||||||
recursive=False,
|
recursive=False,
|
||||||
@ -3163,7 +3258,7 @@ def importShape_ProcessObject(
|
|||||||
# Can transform data or object, better the object so we can instance
|
# Can transform data or object, better the object so we can instance
|
||||||
# the data
|
# the data
|
||||||
# bpymesh.transform(getFinalMatrix(node))
|
# 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)
|
bpyob.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix)
|
||||||
bpycollection.objects.link(bpyob)
|
bpycollection.objects.link(bpyob)
|
||||||
bpyob.select_set(True)
|
bpyob.select_set(True)
|
||||||
@ -3171,6 +3266,8 @@ def importShape_ProcessObject(
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
bpyob["source_line_no"] = geom.lineno
|
bpyob["source_line_no"] = geom.lineno
|
||||||
|
|
||||||
|
return bpyob
|
||||||
|
|
||||||
|
|
||||||
def importText(geom, ancestry):
|
def importText(geom, ancestry):
|
||||||
fmt = geom.getChildBySpec('FontStyle')
|
fmt = geom.getChildBySpec('FontStyle')
|
||||||
@ -3253,6 +3350,7 @@ def importShape(bpycollection, node, ancestry, global_matrix):
|
|||||||
|
|
||||||
bpydata = None
|
bpydata = None
|
||||||
geom_spec = geom.getSpec()
|
geom_spec = geom.getSpec()
|
||||||
|
coord = geom.getChildBySpec('Coordinate')
|
||||||
|
|
||||||
# ccw is handled by every geometry importer separately; some
|
# ccw is handled by every geometry importer separately; some
|
||||||
# geometries are easier to flip than others
|
# geometries are easier to flip than others
|
||||||
@ -3266,14 +3364,127 @@ def importShape(bpycollection, node, ancestry, global_matrix):
|
|||||||
|
|
||||||
# There are no geometry importers that can legally return
|
# There are no geometry importers that can legally return
|
||||||
# no object. It's either a bpy object, or an exception
|
# no object. It's either a bpy object, or an exception
|
||||||
importShape_ProcessObject(
|
bpypo = importShape_ProcessObject(
|
||||||
bpycollection, vrmlname, bpydata, geom, geom_spec,
|
bpycollection, vrmlname, bpydata, geom, geom_spec,
|
||||||
node, bpymat, tex_has_alpha, texmtx,
|
node, bpymat, tex_has_alpha, texmtx,
|
||||||
ancestry, global_matrix)
|
ancestry, global_matrix)
|
||||||
|
|
||||||
|
if bpypo is None:
|
||||||
|
print('ImportX3D warning: importShape_ProcessObject did not return a shape to return for HAnim "%s"' % vrmlname)
|
||||||
else:
|
else:
|
||||||
print('\tImportX3D warning: unsupported type "%s"' % geom_spec)
|
print('\tImportX3D warning: unsupported type "%s"' % geom_spec)
|
||||||
|
bpypo = None
|
||||||
|
return [ geom, bpypo, coord ]
|
||||||
|
|
||||||
|
|
||||||
|
def importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, segments, jointSkin):
|
||||||
|
vrmlname = node.getDefName()
|
||||||
|
# print(vrmlname)
|
||||||
|
prefix = ''
|
||||||
|
if vrmlname:
|
||||||
|
first_underscore = vrmlname.find('_')
|
||||||
|
if first_underscore > 0:
|
||||||
|
prefix = vrmlname[:first_underscore+1]
|
||||||
|
else:
|
||||||
|
vrmlname = 'HAnimHumanoid'
|
||||||
|
|
||||||
Bujus_Krachus marked this conversation as resolved
|
|||||||
|
# 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)
|
||||||
|
bpy.context.view_layer.objects.active = skeleton
|
||||||
|
skeleton.select_set(True)
|
||||||
|
|
||||||
|
# Enter edit mode
|
||||||
Bujus_Krachus marked this conversation as resolved
Cedric Steiert
commented
like above could get simplified using like above could get simplified using `or`
|
|||||||
|
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:
|
||||||
Bujus_Krachus marked this conversation as resolved
Cedric Steiert
commented
better move the nl after the validation section better move the nl after the validation section
|
|||||||
|
first_joint_name = child.getDefName() or child.getFieldAsString('name', None, ancestry)
|
||||||
|
joint_center = child.getFieldAsFloatTuple('center', (0.0, 0.0, 0.0), ancestry)
|
||||||
|
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:
|
||||||
|
if not joint_name:
|
||||||
|
joint_name = vrmlname
|
||||||
|
new_segment = armature_data.edit_bones.new(joint_name)
|
||||||
|
child.blendData = child.blendObject = new_segment
|
||||||
|
matrix_world_inv = skeleton.matrix_world.inverted()
|
||||||
|
new_segment.head = joint_end
|
||||||
|
new_segment.tail = joint_start
|
||||||
|
# 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:
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
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() or child.getFieldAsString('name', None, ancestry) or parent_bone_name
|
||||||
|
segments.append((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)
|
||||||
|
# I don't understand reviewer's comment:
|
||||||
|
# "better move the nl after the validation section"
|
||||||
|
if skinCoordWeight is None:
|
||||||
|
skinCoordWeight = ()
|
||||||
|
if skinCoordIndex is None:
|
||||||
|
skinCoordIndex = ()
|
||||||
|
|
||||||
|
if not child_center:
|
||||||
|
child_center = [0, 0, 0]
|
||||||
|
joints.append((child_bone_name, tuple(child_center), tuple(parent_center), 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:
|
||||||
|
childname = child.getFieldAsString('name', '', ancestry)
|
||||||
|
if DEBUG:
|
||||||
|
print(f"Didn't find children, {children} for {childname}")
|
||||||
|
else:
|
||||||
|
print(f"Didn't find child, {child}")
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------
|
||||||
# Lighting
|
# Lighting
|
||||||
|
|
||||||
@ -3416,10 +3627,24 @@ def importTransform(bpycollection, node, ancestry, global_matrix):
|
|||||||
|
|
||||||
bpyob.matrix_world = getFinalMatrix(node, None, 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_type = 'PLAIN_AXES'
|
||||||
bpyob.empty_display_size = 0.2
|
bpyob.empty_display_size = 0.2
|
||||||
|
|
||||||
|
def importHAnimSegment(bpycollection, node, ancestry, global_matrix):
|
||||||
|
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)
|
||||||
|
bpyob.select_set(True)
|
||||||
|
|
||||||
|
bpyob.matrix_world = getFinalMatrix(node, None, ancestry, global_matrix)
|
||||||
|
|
||||||
|
bpyob.empty_display_type = 'PLAIN_AXES'
|
||||||
|
# so the EMPTY is not too annoying
|
||||||
|
bpyob.empty_display_size = 0.2
|
||||||
|
|
||||||
|
return bpyob
|
||||||
|
|
||||||
#def importTimeSensor(node):
|
#def importTimeSensor(node):
|
||||||
def action_fcurve_ensure(action, data_path, array_index):
|
def action_fcurve_ensure(action, data_path, array_index):
|
||||||
@ -3429,7 +3654,6 @@ def action_fcurve_ensure(action, data_path, array_index):
|
|||||||
|
|
||||||
return action.fcurves.new(data_path=data_path, index=array_index)
|
return action.fcurves.new(data_path=data_path, index=array_index)
|
||||||
|
|
||||||
|
|
||||||
def translatePositionInterpolator(node, action, ancestry):
|
def translatePositionInterpolator(node, action, ancestry):
|
||||||
key = node.getFieldAsArray('key', 0, ancestry)
|
key = node.getFieldAsArray('key', 0, ancestry)
|
||||||
keyValue = node.getFieldAsArray('keyValue', 3, ancestry)
|
keyValue = node.getFieldAsArray('keyValue', 3, ancestry)
|
||||||
@ -3437,26 +3661,62 @@ def translatePositionInterpolator(node, action, ancestry):
|
|||||||
loc_x = action_fcurve_ensure(action, "location", 0)
|
loc_x = action_fcurve_ensure(action, "location", 0)
|
||||||
loc_y = action_fcurve_ensure(action, "location", 1)
|
loc_y = action_fcurve_ensure(action, "location", 1)
|
||||||
loc_z = action_fcurve_ensure(action, "location", 2)
|
loc_z = action_fcurve_ensure(action, "location", 2)
|
||||||
|
if DEBUG:
|
||||||
|
print (f"key {key} keyValue {keyValue} {action}")
|
||||||
|
|
||||||
for i, time in enumerate(key):
|
for i, time in enumerate(key):
|
||||||
try:
|
try:
|
||||||
x, y, z = keyValue[i]
|
x, y, z = keyValue[i]
|
||||||
except:
|
if DEBUG:
|
||||||
|
print (f"i {i} x {x} y {y} z {z}")
|
||||||
|
except: # There's 4 exception possible here, so just wildcard
|
||||||
continue
|
continue
|
||||||
|
|
||||||
loc_x.keyframe_points.insert(time, x)
|
loc_x.keyframe_points.insert(time*TIME_MULTIPLIER, x)
|
||||||
loc_y.keyframe_points.insert(time, y)
|
loc_y.keyframe_points.insert(time*TIME_MULTIPLIER, y)
|
||||||
loc_z.keyframe_points.insert(time, z)
|
loc_z.keyframe_points.insert(time*TIME_MULTIPLIER, z)
|
||||||
|
|
||||||
|
for fcu in (loc_x, loc_y, loc_z):
|
||||||
|
for kf in fcu.keyframe_points:
|
||||||
|
kf.interpolation = 'BEZIER'
|
||||||
|
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
|
||||||
|
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*TIME_MULTIPLIER, x)
|
||||||
|
loc_y.keyframe_points.insert(time*TIME_MULTIPLIER, y)
|
||||||
|
loc_z.keyframe_points.insert(time*TIME_MULTIPLIER, z)
|
||||||
|
|
||||||
|
curoff += 3
|
||||||
|
|
||||||
for fcu in (loc_x, loc_y, loc_z):
|
for fcu in (loc_x, loc_y, loc_z):
|
||||||
for kf in fcu.keyframe_points:
|
for kf in fcu.keyframe_points:
|
||||||
kf.interpolation = 'LINEAR'
|
kf.interpolation = 'LINEAR'
|
||||||
|
|
||||||
|
def translateOrientationInterpolator(node, action, ancestry, to_node):
|
||||||
def translateOrientationInterpolator(node, action, ancestry):
|
|
||||||
key = node.getFieldAsArray('key', 0, ancestry)
|
key = node.getFieldAsArray('key', 0, ancestry)
|
||||||
keyValue = node.getFieldAsArray('keyValue', 4, ancestry)
|
keyValue = node.getFieldAsArray('keyValue', 4, ancestry)
|
||||||
|
|
||||||
|
node.rotation_mode = 'XYZ'
|
||||||
|
|
||||||
rot_x = action_fcurve_ensure(action, "rotation_euler", 0)
|
rot_x = action_fcurve_ensure(action, "rotation_euler", 0)
|
||||||
rot_y = action_fcurve_ensure(action, "rotation_euler", 1)
|
rot_y = action_fcurve_ensure(action, "rotation_euler", 1)
|
||||||
rot_z = action_fcurve_ensure(action, "rotation_euler", 2)
|
rot_z = action_fcurve_ensure(action, "rotation_euler", 2)
|
||||||
@ -3464,22 +3724,35 @@ def translateOrientationInterpolator(node, action, ancestry):
|
|||||||
for i, time in enumerate(key):
|
for i, time in enumerate(key):
|
||||||
try:
|
try:
|
||||||
x, y, z, w = keyValue[i]
|
x, y, z, w = keyValue[i]
|
||||||
except:
|
except: # There's 4 exception possible here, so just wildcard
|
||||||
continue
|
continue
|
||||||
|
|
||||||
mtx = translateRotation((x, y, z, w))
|
mtx = translateRotation((x, y, z, w))
|
||||||
eul = mtx.to_euler()
|
eul = mtx.to_euler()
|
||||||
rot_x.keyframe_points.insert(time, eul.x)
|
rot_x.keyframe_points.insert(time*TIME_MULTIPLIER, eul.x)
|
||||||
rot_y.keyframe_points.insert(time, eul.y)
|
rot_y.keyframe_points.insert(time*TIME_MULTIPLIER, eul.y)
|
||||||
rot_z.keyframe_points.insert(time, eul.z)
|
rot_z.keyframe_points.insert(time*TIME_MULTIPLIER, eul.z)
|
||||||
|
|
||||||
for fcu in (rot_x, rot_y, rot_z):
|
for fcu in (rot_x, rot_y, rot_z):
|
||||||
for kf in fcu.keyframe_points:
|
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)
|
||||||
|
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 * TIME_MULTIPLIER)
|
||||||
|
|
||||||
|
|
||||||
# Untested!
|
# Untested!
|
||||||
def translateScalarInterpolator(node, action, ancestry):
|
def translateScaleInterpolator(node, action, ancestry):
|
||||||
key = node.getFieldAsArray('key', 0, ancestry)
|
key = node.getFieldAsArray('key', 0, ancestry)
|
||||||
keyValue = node.getFieldAsArray('keyValue', 4, ancestry)
|
keyValue = node.getFieldAsArray('keyValue', 4, ancestry)
|
||||||
|
|
||||||
@ -3490,13 +3763,26 @@ def translateScalarInterpolator(node, action, ancestry):
|
|||||||
for i, time in enumerate(key):
|
for i, time in enumerate(key):
|
||||||
try:
|
try:
|
||||||
x, y, z = keyValue[i]
|
x, y, z = keyValue[i]
|
||||||
except:
|
except: # There's 4 exception possible here, so just wildcard
|
||||||
continue
|
continue
|
||||||
|
|
||||||
sca_x.keyframe_points.new(time, x)
|
sca_x.keyframe_points.insert(time*TIME_MULTIPLIER, x)
|
||||||
sca_y.keyframe_points.new(time, y)
|
sca_y.keyframe_points.insert(time*TIME_MULTIPLIER, y)
|
||||||
sca_z.keyframe_points.new(time, z)
|
sca_z.keyframe_points.insert(time*TIME_MULTIPLIER, 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: # There's 4 exception possible here, so just wildcard
|
||||||
|
continue
|
||||||
|
|
||||||
|
scalar.keyframe_points.insert(time*TIME_MULTIPLIER, s)
|
||||||
|
|
||||||
def translateTimeSensor(node, action, ancestry):
|
def translateTimeSensor(node, action, ancestry):
|
||||||
"""
|
"""
|
||||||
@ -3527,14 +3813,7 @@ def translateTimeSensor(node, action, ancestry):
|
|||||||
if loop:
|
if loop:
|
||||||
time_cu.extend = Blender.IpoCurve.ExtendTypes.CYCLIC # or - EXTRAP, CYCLIC_EXTRAP, CONST,
|
time_cu.extend = Blender.IpoCurve.ExtendTypes.CYCLIC # or - EXTRAP, CYCLIC_EXTRAP, CONST,
|
||||||
|
|
||||||
|
def importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh):
|
||||||
def importRoute(node, ancestry):
|
|
||||||
"""
|
|
||||||
Animation route only at the moment
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not hasattr(node, 'fields'):
|
|
||||||
return
|
|
||||||
|
|
||||||
routeIpoDict = node.getRouteIpoDict()
|
routeIpoDict = node.getRouteIpoDict()
|
||||||
|
|
||||||
@ -3542,11 +3821,63 @@ def importRoute(node, ancestry):
|
|||||||
try:
|
try:
|
||||||
action = routeIpoDict[act_id]
|
action = routeIpoDict[act_id]
|
||||||
except:
|
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
|
return action
|
||||||
|
|
||||||
# for getting definitions
|
# for getting definitions
|
||||||
defDict = node.getDefDict()
|
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]
|
||||||
|
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':
|
||||||
|
action = getIpo(to_id)
|
||||||
|
set_data_from_node = defDict[from_id]
|
||||||
|
to_node = defDict[to_id]
|
||||||
Bujus_Krachus marked this conversation as resolved
Cedric Steiert
commented
any specific error to be expected? any specific error to be expected?
|
|||||||
|
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
|
Handles routing nodes to each other
|
||||||
|
|
||||||
@ -3557,50 +3888,36 @@ ROUTE vpTs.fraction_changed TO vpOI.set_fraction
|
|||||||
ROUTE champFly001.bindTime TO vpTs.set_startTime
|
ROUTE champFly001.bindTime TO vpTs.set_startTime
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#from_id, from_type = node.id[1].split('.')
|
if len(node.fields) <= 0:
|
||||||
#to_id, to_type = node.id[3].split('.')
|
from_id = node.getFieldAsString("fromNode", None, ancestry)
|
||||||
|
from_type = node.getFieldAsString("fromField", None, ancestry)
|
||||||
#value_changed
|
to_id = node.getFieldAsString("toNode", None, ancestry)
|
||||||
set_position_node = None
|
to_type = node.getFieldAsString("toField", None, ancestry)
|
||||||
set_orientation_node = None
|
if from_id and from_type and to_id and to_type:
|
||||||
time_node = None
|
# 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:
|
for field in node.fields:
|
||||||
|
# print(f"return field {field}")
|
||||||
if field and field[0] == 'ROUTE':
|
if field and field[0] == 'ROUTE':
|
||||||
try:
|
|
||||||
from_id, from_type = field[1].split('.')
|
from_id, from_type = field[1].split('.')
|
||||||
to_id, to_type = field[3].split('.')
|
to_id, to_type = field[3].split('.')
|
||||||
except:
|
# print(f"ROUTE from {from_id}.{from_type} to {to_id}.{to_type}")
|
||||||
print("Warning, invalid ROUTE", field)
|
importRouteFromTo(node, from_id, from_type, to_id, to_type, ancestry, skeleton, hasMesh)
|
||||||
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)
|
|
||||||
|
|
||||||
|
def importSkinWeights(obj, joint, jointCoord, end):
|
||||||
|
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 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(
|
def load_web3d(
|
||||||
bpycontext,
|
bpycontext,
|
||||||
filepath,
|
filepath,
|
||||||
*,
|
*,
|
||||||
PREF_FLAT=False,
|
PREF_FLAT=False, # So Tranforms will be imported
|
||||||
PREF_CIRCLE_DIV=16,
|
PREF_CIRCLE_DIV=16,
|
||||||
file_unit='M',
|
file_unit='M',
|
||||||
global_scale=1.0,
|
global_scale=1.0,
|
||||||
@ -3637,6 +3954,33 @@ def load_web3d(
|
|||||||
# fill with tuples - (node, [parents-parent, parent])
|
# fill with tuples - (node, [parents-parent, parent])
|
||||||
all_nodes = root_node.getSerialized([], [])
|
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:
|
||||||
Bujus_Krachus marked this conversation as resolved
Cedric Steiert
commented
dead code dead code
|
|||||||
|
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)
|
||||||
Bujus_Krachus marked this conversation as resolved
Cedric Steiert
commented
dead code & print needed above? dead code & print needed above?
|
|||||||
|
elif spec.endswith('HAnimSite'):
|
||||||
|
site = node
|
||||||
|
|
||||||
for node, ancestry in all_nodes:
|
for node, ancestry in all_nodes:
|
||||||
#if 'castle.wrl' not in node.getFilename():
|
#if 'castle.wrl' not in node.getFilename():
|
||||||
# continue
|
# continue
|
||||||
@ -3652,12 +3996,82 @@ def load_web3d(
|
|||||||
# Note, include this function so the VRML/X3D importer can be extended
|
# Note, include this function so the VRML/X3D importer can be extended
|
||||||
# by an external script. - gets first pick
|
# by an external script. - gets first pick
|
||||||
pass
|
pass
|
||||||
if spec == 'Shape':
|
if spec in {'PointLight', 'DirectionalLight', 'SpotLight'}:
|
||||||
importShape(bpycollection, node, ancestry, global_matrix)
|
|
||||||
elif spec in {'PointLight', 'DirectionalLight', 'SpotLight'}:
|
|
||||||
importLamp(bpycollection, node, spec, ancestry, global_matrix)
|
importLamp(bpycollection, node, spec, ancestry, global_matrix)
|
||||||
elif spec == 'Viewpoint':
|
elif spec == 'Viewpoint':
|
||||||
importViewpoint(bpycollection, node, ancestry, global_matrix)
|
importViewpoint(bpycollection, node, ancestry, global_matrix)
|
||||||
|
elif spec == 'HAnimHumanoid':
|
||||||
|
joints = []
|
||||||
|
segments = []
|
||||||
|
jointSkin = {}
|
||||||
|
skeleton = importHAnimHumanoid(bpycollection, node, ancestry, global_matrix, joints, segments, jointSkin)
|
||||||
|
skinCoord = node.getChildBySpec('Coordinate')
|
||||||
|
if skinCoord:
|
||||||
|
#if skinCoord.getFieldAsString("containerField", None, ancestry) == "skinCoord":
|
||||||
|
if DEBUG:
|
||||||
|
print(f"Skin coord is {skinCoord}")
|
||||||
|
for shape in all_shapes:
|
||||||
|
if shape:
|
||||||
|
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():
|
||||||
|
if DEBUG:
|
||||||
|
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("No skinCoord, no skin weights, no skin animation")
|
||||||
|
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
print(f"mesh is {meshobj}")
|
||||||
|
#bpy.ops.object.mode_set(mode="EDIT")
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
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:
|
||||||
|
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()
|
||||||
|
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'):
|
||||||
|
# TODO Intended to be implemented
|
||||||
|
#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':
|
elif spec == 'Transform':
|
||||||
# Only use transform nodes when we are not importing a flat object hierarchy
|
# Only use transform nodes when we are not importing a flat object hierarchy
|
||||||
if PREF_FLAT == False:
|
if PREF_FLAT == False:
|
||||||
@ -3666,13 +4080,20 @@ def load_web3d(
|
|||||||
# These are delt with later within importRoute
|
# These are delt with later within importRoute
|
||||||
elif spec=='PositionInterpolator':
|
elif spec=='PositionInterpolator':
|
||||||
action = bpy.data.ipos.new('web3d_ipo', 'Object')
|
action = bpy.data.ipos.new('web3d_ipo', 'Object')
|
||||||
translatePositionInterpolator(node, action)
|
translatePositionInterpolator(node, action, ancestry)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# After we import all nodes, route events - anim paths
|
# After we import all nodes, route events - anim paths
|
||||||
|
if skeleton:
|
||||||
|
bpy.ops.object.mode_set(mode='POSE')
|
||||||
for node, ancestry in all_nodes:
|
for node, ancestry in all_nodes:
|
||||||
importRoute(node, ancestry)
|
importRoute(node, ancestry, skeleton, meshobj)
|
||||||
|
bpy.context.scene.frame_set(0)
|
||||||
|
|
||||||
|
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')
|
||||||
for node, ancestry in all_nodes:
|
for node, ancestry in all_nodes:
|
||||||
if node.isRoot():
|
if node.isRoot():
|
||||||
# we know that all nodes referenced from will be in
|
# we know that all nodes referenced from will be in
|
||||||
@ -3684,16 +4105,42 @@ def load_web3d(
|
|||||||
|
|
||||||
# Assign anim curves
|
# Assign anim curves
|
||||||
node = defDict[key]
|
node = defDict[key]
|
||||||
|
# print(f"key {key} action {action} node {node}")
|
||||||
|
bone = None
|
||||||
|
if skeleton:
|
||||||
|
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")
|
||||||
|
else:
|
||||||
|
print(f"There's no skeleton")
|
||||||
if node.blendData is None: # Add an object if we need one for animation
|
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)
|
# print(f"Adding some blendData to the node for {key}. Did you forget to add it?")
|
||||||
bpycollection.objects.link(bpyob)
|
node.blendData = node.blendObject = bpy.data.objects.new(key, None)
|
||||||
bpyob.select_set(True)
|
bpycollection.objects.link(node.blendObject)
|
||||||
|
node.blendObject.select_set(True)
|
||||||
|
|
||||||
if node.blendData.animation_data is None:
|
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()
|
node.blendData.animation_data_create()
|
||||||
|
else:
|
||||||
|
#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}")
|
||||||
node.blendData.animation_data.action = action
|
node.blendData.animation_data.action = action
|
||||||
|
else:
|
||||||
|
# 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}")
|
||||||
|
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")
|
||||||
|
pass
|
||||||
# Add in hierarchy
|
# Add in hierarchy
|
||||||
if PREF_FLAT is False:
|
if PREF_FLAT is False:
|
||||||
child_dict = {}
|
child_dict = {}
|
||||||
@ -3721,13 +4168,36 @@ def load_web3d(
|
|||||||
|
|
||||||
# Parent
|
# Parent
|
||||||
for parent, children in child_dict.items():
|
for parent, children in child_dict.items():
|
||||||
|
if parent and children:
|
||||||
for c in children:
|
for c in children:
|
||||||
|
if c:
|
||||||
|
if type(c) == type(parent):
|
||||||
c.parent = parent
|
c.parent = parent
|
||||||
|
else:
|
||||||
|
if isinstance(c, bpy.types.EditBone):
|
||||||
|
if DEBUG:
|
||||||
|
print(f"Child is EditBone")
|
||||||
|
if isinstance(parent, bpy.types.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:
|
||||||
|
print("Not a child")
|
||||||
|
else:
|
||||||
|
print("Children or parent may be None")
|
||||||
|
|
||||||
# update deps
|
# update deps
|
||||||
bpycontext.view_layer.update()
|
bpycontext.view_layer.update()
|
||||||
del child_dict
|
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(
|
def load_with_profiler(
|
||||||
context,
|
context,
|
||||||
@ -3756,7 +4226,7 @@ def load(context,
|
|||||||
|
|
||||||
# loadWithProfiler(operator, context, filepath, global_matrix)
|
# loadWithProfiler(operator, context, filepath, global_matrix)
|
||||||
load_web3d(context, filepath,
|
load_web3d(context, filepath,
|
||||||
PREF_FLAT=True,
|
PREF_FLAT=False, # So Tranforms will be imported
|
||||||
PREF_CIRCLE_DIV=16,
|
PREF_CIRCLE_DIV=16,
|
||||||
global_scale=global_scale,
|
global_scale=global_scale,
|
||||||
global_matrix=global_matrix,
|
global_matrix=global_matrix,
|
||||||
|
Loading…
Reference in New Issue
Block a user
like above could get simplified using
or