Skip to content

Commit

Permalink
Started implementing vertex animation export. Far from complete, impo…
Browse files Browse the repository at this point in the history
…rt->export tends to create garbled triangle messes. Shouldn't interfere with skeletal-only exports.
  • Loading branch information
Five-Damned-Dollarz committed Feb 25, 2021
1 parent 2ec7689 commit 31e97ec
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 15 deletions.
93 changes: 85 additions & 8 deletions src/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,6 @@ def from_armature(armature_object):
node.bounds_min=Vector((min(node.bounds_min.x, vertex.location.x), min(node.bounds_min.y, vertex.location.y), min(node.bounds_min.z, vertex.location.z)))
node.bounds_max=Vector((max(node.bounds_min.x, vertex.location.x), max(node.bounds_min.y, vertex.location.y), max(node.bounds_min.z, vertex.location.z)))

node.md_vert_count=len(node.md_vert_list)
# TODO: md_vert_list
# obj.data.shape_keys.key_blocks['d_anim_0'].data[0..n] seem to contain all mesh vertices

#print("Processed", node.name, node.bind_matrix)
node.child_count = len(bone.children)
model.nodes.append(node)
Expand Down Expand Up @@ -210,6 +206,11 @@ def from_armature(armature_object):

armature_object.animation_data.action = action

# TODO: make this optional for when we don't use vertex animation on anything
if mesh.shape_keys:
md_action=[md_action for md_action in bpy.data.actions if md_action.name=="d_" + action.name]
mesh.shape_keys.animation_data.action=md_action[0] if md_action else None

# This is only one action fyi!
fcurves = armature_object.animation_data.action.fcurves

Expand Down Expand Up @@ -265,13 +266,24 @@ def from_armature(armature_object):

keyframe = Animation.Keyframe()
keyframe.time = scaled_time
animation.keyframes.append(keyframe)

keyframe.bounds_min=Vector(mesh_object.bound_box[0]) # will using mesh_object here break if there's multiple mesh
keyframe.bounds_max=Vector(mesh_object.bound_box[6]) # objects using the same armature as a parent? DON'T DO THAT

animation.bounds_min=max([keyframe.bounds_min for keyframe in animation.keyframes])
animation.bounds_max=max([keyframe.bounds_max for keyframe in animation.keyframes])
animation.keyframes.append(keyframe)

# this is horrible, but works
animation.bounds_min=Vector((float("inf"), float("inf"), float("inf")))
animation.bounds_max=Vector((float("-inf"), float("-inf"), float("-inf")))

for keyframe in animation.keyframes:
animation.bounds_min.x=min(animation.bounds_min.x, keyframe.bounds_min.x)
animation.bounds_min.y=min(animation.bounds_min.y, keyframe.bounds_min.y)
animation.bounds_min.z=min(animation.bounds_min.z, keyframe.bounds_min.z)

animation.bounds_max.x=max(animation.bounds_max.x, keyframe.bounds_max.x)
animation.bounds_max.y=max(animation.bounds_max.y, keyframe.bounds_max.y)
animation.bounds_max.z=max(animation.bounds_max.z, keyframe.bounds_max.z)

# Okay let's start processing our transforms!
for node_index, (node, pose_bone) in enumerate(zip(model.nodes, armature_object.pose.bones)):
Expand Down Expand Up @@ -303,7 +315,72 @@ def from_armature(armature_object):
# End For

model.animations.append(animation)
# End For

''' Vertex Animations '''

# Has an issue, probably in the mesh transforms
# TODO: try undoing the mesh bind pose so all pieces are centered on [0,0,0] before bounds calculation, and compression

# Then most trivially, you find min and max of each dimension, set scales to (maxes-mins), subtract mins from all points, then divide by scales.
# And the transform is set by doing the same subtract and divide to the origin
# Later on, to reduce artifacts, instead of just doing that and then scaling back up to 255 blindly, you could iterate over possible values UP to 255, and check sum of error^2 for each one, and choose the one with lowest total error
# Idea being that if you had like three evenly spaced things, then 240 may give you perfect accuracy, while 255 will not
# When you're compressing, you can choose to compress to less than 255, and just use a larger scale and transform to compensate

if mesh.shape_keys and len(mesh.shape_keys.key_blocks)>1:
for animation in model.animations:
shape_keys=[shape_key for shape_key in mesh.shape_keys.key_blocks if shape_key.name.startswith(animation.name)]

for node_index, node in enumerate(model.nodes):
dirty_node=False

# get all vertices for this node
node_vertices=[vertex_index for vertex_index, vertex in enumerate(model.pieces[0].lods[0].vertices) if vertex.weights[0].node_index==node_index]

node.bounds_min=Vector((float("inf"), float("inf"), float("inf")))
node.bounds_max=Vector((float("-inf"), float("-inf"), float("-inf")))

if len(node_vertices)==0:
node.bounds_min=Vector()
node.bounds_max=Vector()

# get the bounds of the node
for keyframe_index, keyframe in enumerate(animation.keyframes):
for vertex_index in node_vertices:
temp_vert=shape_keys[keyframe_index].data[vertex_index]

if not temp_vert.co==mesh.shape_keys.key_blocks[0].data[vertex_index].co:
dirty_node=True

temp_vert=temp_vert.co

node.bounds_min.x=min(node.bounds_min.x, temp_vert.x)
node.bounds_min.y=min(node.bounds_min.y, temp_vert.y)
node.bounds_min.z=min(node.bounds_min.z, temp_vert.z)

node.bounds_max.x=max(node.bounds_max.x, temp_vert.x)
node.bounds_max.y=max(node.bounds_max.y, temp_vert.y)
node.bounds_max.z=max(node.bounds_max.z, temp_vert.z)

node.md_vert_list.extend(node_vertices if dirty_node else [])

scale=node.bounds_max-node.bounds_min

# compress vertices
for keyframe_index, keyframe in enumerate(animation.keyframes):
scaled_time=keyframe.time*get_framerate()
bpy.context.scene.frame_set(time, subframe=time-floor(time))

for vertex_index in node_vertices:
temp_loc=shape_keys[keyframe_index].data[vertex_index].co-node.bounds_min
temp_vert=Vector((temp_loc.x/scale.x, temp_loc.y/scale.y, temp_loc.z/scale.z))

animation.vertex_deformations.append(temp_vert)

for node in model.nodes:
# remove dupes, and count final
node.md_vert_list=list(dict.fromkeys(node.md_vert_list))
node.md_vert_count=len(node.md_vert_list)

''' AnimBindings '''
anim_binding = AnimBinding()
Expand Down
21 changes: 14 additions & 7 deletions src/writer_abc_v6_pc.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,17 @@ def __init__(self, name, data):
node.flags=self._flag_tris
break

if node.md_vert_count>0:
node.flags|=self._flag_deformation

buffer.extend(self._vector_to_bytes(node.bounds_min))
buffer.extend(self._vector_to_bytes(node.bounds_max))
buffer.extend(self._string_to_bytes(node.name))
buffer.extend(struct.pack('H', node.index))
buffer.extend(struct.pack('B', node.flags))
buffer.extend(struct.pack('I', node.md_vert_count))
for md_vert in node.md_vert_list:
buffer.extend(struct.pack('H', md_vert.vertex_index))
buffer.extend(struct.pack('H', md_vert))
buffer.extend(struct.pack('I', node.child_count))

sections.append(Section('Nodes', bytes(buffer)));
Expand All @@ -130,9 +133,9 @@ def __init__(self, name, data):
buffer=bytearray()

buffer.extend(struct.pack('I', len(model.animations)))
for anim in model.animations:
for anim_index, anim in enumerate(model.animations):
buffer.extend(self._string_to_bytes(anim.name))
buffer.extend(struct.pack('I', int(anim.keyframes[-1].time))) # final keyframe time; playing past final keyframe's time seems unpredictable
buffer.extend(struct.pack('I', int(anim.keyframes[-1].time))) # playing past final keyframe's time seems unpredictable
buffer.extend(self._vector_to_bytes(anim.bounds_min))
buffer.extend(self._vector_to_bytes(anim.bounds_max))
buffer.extend(struct.pack('I', len(anim.keyframes)))
Expand All @@ -142,16 +145,20 @@ def __init__(self, name, data):
buffer.extend(self._vector_to_bytes(anim.bounds_max))
buffer.extend(self._string_to_bytes(keyframe.string))

for node_transform_list in anim.node_keyframe_transforms:
for node_index, (node_transform_list, node) in enumerate(zip(anim.node_keyframe_transforms, model.nodes)):
for keyframe_transform in node_transform_list:
if model.flip_anim:
keyframe_transform.rotation.conjugate()
buffer.extend(self._transform_to_bytes(keyframe_transform))

# TODO: vertex animation data
#for node in model.nodes:
for keyframe_index, keyframe in enumerate(anim.keyframes):
for md_vert_index, md_vert in enumerate(node.md_vert_list):
index=keyframe_index*node.md_vert_count+md_vert_index
buffer.extend(struct.pack('BBB', int(anim.vertex_deformations[index].x*255), int(anim.vertex_deformations[index].y*255), int(anim.vertex_deformations[index].z*255)))

buffer.extend(self._vector_to_bytes(Vector((1, 1, 1)))) # TODO: node scale
buffer.extend(self._vector_to_bytes(Vector((0, 0, 0)))) # TODO: node transform
buffer.extend(self._vector_to_bytes((node.bounds_max-node.bounds_min)/255))
buffer.extend(self._vector_to_bytes(node.bounds_min))

sections.append(Section('Animation', bytes(buffer)))

Expand Down

0 comments on commit 31e97ec

Please sign in to comment.