Browse Source

Refactor ATCAnim generation

Move all the logic into the AnimationConverter in preparation for
exporting animated sound volumes, spot cones, light color values, etc.
pull/40/head
Adam Johnson 9 years ago
parent
commit
dbeed6f660
  1. 102
      korman/exporter/animation.py
  2. 3
      korman/exporter/convert.py
  3. 4
      korman/nodes/node_messages.py
  4. 63
      korman/properties/modifiers/anim.py

102
korman/exporter/animation.py

@ -27,19 +27,103 @@ class AnimationConverter:
self._exporter = weakref.ref(exporter)
self._bl_fps = bpy.context.scene.render.fps
def convert_action2tm(self, action, default_xform):
"""Converts a Blender Action to a plCompoundController."""
def _convert_frame_time(self, frame_num):
return frame_num / self._bl_fps
def convert_object_animations(self, bo, so):
anim = bo.animation_data
if anim is None:
return
action = anim.action
if action is None:
return
fcurves = action.fcurves
if not fcurves:
return
# We're basically just going to throw all the FCurves at the controller converter (read: wall)
# and see what sticks. PlasmaMAX has some nice animation channel stuff that allows for some
# form of separation, but Blender's NLA editor is way confusing and appears to not work with
# things that aren't the typical position, rotation, scale animations.
applicators = []
applicators.append(self._convert_transform_animation(bo.name, fcurves, bo.matrix_basis))
# Check to make sure we have some valid animation applicators before proceeding.
if not any(applicators):
return
# There is a race condition in the client with animation loading. It expects for modifiers
# to be listed on the SceneObject in a specific order. D'OH! So, always use these funcs.
agmod, agmaster = self.get_anigraph_objects(bo, so)
atcanim = self._mgr.find_create_object(plATCAnim, so=so)
# Add the animation data to the ATC
for i in applicators:
if i is not None:
atcanim.addApplicator(i)
agmod.channelName = bo.name
agmaster.addPrivateAnim(atcanim.key)
# This was previously part of the Animation Modifier, however, there can be lots of animations
# Therefore we move it here.
markers = action.pose_markers
atcanim.name = "(Entire Animation)"
atcanim.start = self._convert_frame_time(action.frame_range[0])
atcanim.end = self._convert_frame_time(action.frame_range[1])
# Marker points
for marker in markers:
atcanim.setMarker(marker.name, self._convert_frame_time(marker.frame))
# Fixme? Not sure if we really need to expose this...
atcanim.easeInMin = 1.0
atcanim.easeInMax = 1.0
atcanim.easeInLength = 1.0
atcanim.easeOutMin = 1.0
atcanim.easeOutMax = 1.0
atcanim.easeOutLength = 1.0
def _convert_transform_animation(self, name, fcurves, xform):
pos = self.make_pos_controller(fcurves, xform)
rot = self.make_rot_controller(fcurves, xform)
scale = self.make_scale_controller(fcurves, xform)
if pos is None and rot is None and scale is None:
return None
# NOTE: plCompoundController is from Myst 5 and was backported to MOUL.
# Worry not however... libHSPlasma will do the conversion for us.
tm = plCompoundController()
tm.X = self.make_pos_controller(fcurves, default_xform)
tm.Y = self.make_rot_controller(fcurves, default_xform)
tm.Z = self.make_scale_controller(fcurves, default_xform)
return tm
tm.X = pos
tm.Y = rot
tm.Z = scale
applicator = plMatrixChannelApplicator()
applicator.enabled = True
applicator.channelName = name
channel = plMatrixControllerChannel()
channel.controller = tm
applicator.channel = channel
# Decompose the matrix into the 90s-era 3ds max affine parts sillyness
# All that's missing now is something like "(c) 1998 HeadSpin" oh wait...
affine = hsAffineParts()
affine.T = hsVector3(*xform.to_translation())
affine.K = hsVector3(*xform.to_scale())
affine.F = -1.0 if xform.determinant() < 0.0 else 1.0
rot = xform.to_quaternion()
affine.Q = utils.quaternion(rot)
rot.normalize()
affine.U = utils.quaternion(rot)
channel.affine = affine
return applicator
def get_anigraph_keys(self, bo=None, so=None):
mod = self._mgr.find_create_key(plAGModifier, so=so, bl=bo)
master = self._mgr.find_create_key(plAGMasterMod, so=so, bl=bo)
return mod, master
def get_anigraph_objects(self, bo=None, so=None):
mod = self._mgr.find_create_object(plAGModifier, so=so, bl=bo)
master = self._mgr.find_create_object(plAGMasterMod, so=so, bl=bo)
return mod, master
def make_matrix44_controller(self, pos_fcurves, scale_fcurves, default_pos, default_scale):
pos_keyframes, pos_bez = self._process_keyframes(pos_fcurves)

3
korman/exporter/convert.py

@ -207,6 +207,7 @@ class Exporter:
sceneobject = self.mgr.find_create_object(plSceneObject, bl=bl_obj)
self._export_actor(sceneobject, bl_obj)
export_fn(sceneobject, bl_obj)
self.animation.convert_object_animations(bl_obj, sceneobject)
# And now we puke out the modifiers...
for mod in bl_obj.plasma_modifiers.modifiers:
@ -256,6 +257,8 @@ class Exporter:
return True
if bo.parent is not None:
return True
if bo.animation_data is not None and bo.animation_data.action is not None:
return True
if bo.name in self.actors:
return True

4
korman/nodes/node_messages.py

@ -178,9 +178,7 @@ class PlasmaAnimCmdMsgNode(PlasmaMessageNode, bpy.types.Node):
# (but obviously this is not wrong...)
target = exporter.mgr.find_create_key(plMsgForwarder, bl=obj, name=group.key_name)
else:
# remember, the AGModifier MUST exist first... so just in case...
exporter.mgr.find_create_key(plAGModifier, bl=obj, name=anim.key_name)
target = exporter.mgr.find_create_key(plAGMasterMod, bl=obj, name=anim.key_name)
_agmod_trash, target = exporter.animation.get_anigraph_keys(obj)
else:
material = bpy.data.materials.get(self.material_name, None)
if material is None:

63
korman/properties/modifiers/anim.py

@ -61,12 +61,9 @@ class PlasmaAnimationModifier(PlasmaModifierProperties):
action = _get_blender_action(bo)
markers = action.pose_markers
atcanim = exporter.mgr.find_create_object(plATCAnim, so=so, name=self.key_name)
atcanim = exporter.mgr.find_create_object(plATCAnim, so=so)
atcanim.autoStart = self.auto_start
atcanim.loop = self.loop
atcanim.name = "(Entire Animation)"
atcanim.start = _convert_frame_time(action.frame_range[0])
atcanim.end = _convert_frame_time(action.frame_range[1])
# Simple start and loop info
initial_marker = markers.get(self.initial_marker)
@ -86,51 +83,6 @@ class PlasmaAnimationModifier(PlasmaModifierProperties):
else:
atcanim.loopEnd = _convert_frame_time(action.frame_range[1])
# Marker points
for marker in markers:
atcanim.setMarker(marker.name, _convert_frame_time(marker.frame))
# Fixme? Not sure if we really need to expose this...
atcanim.easeInMin = 1.0
atcanim.easeInMax = 1.0
atcanim.easeInLength = 1.0
atcanim.easeOutMin = 1.0
atcanim.easeOutMax = 1.0
atcanim.easeOutLength = 1.0
# Now for the animation data. We're mostly just going to hand this off to the controller code
matrix = bo.matrix_basis
applicator = plMatrixChannelApplicator()
applicator.enabled = True
applicator.channelName = bo.name
channel = plMatrixControllerChannel()
channel.controller = exporter.animation.convert_action2tm(action, matrix)
applicator.channel = channel
atcanim.addApplicator(applicator)
# Decompose the matrix into the 90s-era 3ds max affine parts sillyness
# All that's missing now is something like "(c) 1998 HeadSpin" oh wait...
affine = hsAffineParts()
affine.T = hsVector3(*matrix.to_translation())
affine.K = hsVector3(*matrix.to_scale())
affine.F = -1.0 if matrix.determinant() < 0.0 else 1.0
rot = matrix.to_quaternion()
affine.Q = utils.quaternion(rot)
rot.normalize()
affine.U = utils.quaternion(rot)
channel.affine = affine
# We need both an AGModifier and an AGMasterMod
# NOTE: mandatory order--otherwise the animation will not work in game!
agmod = exporter.mgr.find_create_object(plAGModifier, so=so, name=self.key_name)
agmod.channelName = bo.name
agmaster = exporter.mgr.find_create_object(plAGMasterMod, so=so, name=self.key_name)
agmaster.addPrivateAnim(atcanim.key)
@property
def key_name(self):
return "{}_(Entire Animation)".format(self.id_data.name)
def _make_physical_movable(self, so):
sim = so.sim
if sim is not None:
@ -179,14 +131,11 @@ class PlasmaAnimationGroupModifier(PlasmaModifierProperties):
action = _get_blender_action(bo)
key_name = bo.plasma_modifiers.animation.key_name
# See above... AGModifier must always be inited first...
agmod = exporter.mgr.find_create_object(plAGModifier, so=so, name=key_name)
# The message forwarder is the guy that makes sure that everybody knows WTF is going on
msgfwd = exporter.mgr.find_create_object(plMsgForwarder, so=so, name=self.key_name)
# Now, this is da swhiz...
agmaster = exporter.mgr.find_create_object(plAGMasterMod, so=so, name=key_name)
agmod, agmaster = exporter.animation.get_anigraph_objects(bo, so)
agmaster.msgForwarder = msgfwd.key
agmaster.isGrouped, agmaster.isGroupMaster = True, True
for i in self.children:
@ -204,9 +153,8 @@ class PlasmaAnimationGroupModifier(PlasmaModifierProperties):
msg = "Animation Group '{}' specifies an object '{}' with no Plasma Animation modifier. Ignoring..."
exporter.report.warn(msg.format(self.key_name, i.object_name), indent=2)
continue
child_agmod = exporter.mgr.find_create_key(plAGModifier, bl=child_bo, name=child_animation.key_name)
child_agmaster = exporter.mgr.find_create_key(plAGMasterMod, bl=child_bo, name=child_animation.key_name)
msgfwd.addForwardKey(child_agmaster)
child_agmod, child_agmaster = exporter.animation.get_anigraph_objects(bo=child_bo)
msgfwd.addForwardKey(child_agmaster.key)
msgfwd.addForwardKey(agmaster.key)
@property
@ -241,8 +189,7 @@ class PlasmaAnimationLoopModifier(PlasmaModifierProperties):
action = _get_blender_action(bo)
markers = action.pose_markers
key_name = bo.plasma_modifiers.animation.key_name
atcanim = exporter.mgr.find_create_object(plATCAnim, so=so, name=key_name)
atcanim = exporter.mgr.find_create_object(plATCAnim, so=so)
for loop in self.loops:
start = markers.get(loop.loop_start)
end = markers.get(loop.loop_end)

Loading…
Cancel
Save