Browse Source

Merge pull request #290 from Jrius/matrix_anim_fix

Matrix anim fix
pull/297/head
Adam Johnson 3 years ago committed by GitHub
parent
commit
7458e4664f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 70
      korman/exporter/animation.py
  2. 5
      korman/exporter/camera.py
  3. 2
      korman/exporter/convert.py
  4. 2
      korman/exporter/material.py
  5. 2
      korman/exporter/mesh.py
  6. 2
      korman/exporter/physics.py
  7. 2
      korman/exporter/utils.py
  8. 5
      korman/properties/modifiers/avatar.py
  9. 6
      korman/properties/modifiers/gui.py
  10. 8
      korman/properties/modifiers/water.py
  11. 2
      korman/properties/prop_camera.py

70
korman/exporter/animation.py

@ -58,7 +58,7 @@ class AnimationConverter:
if isinstance(bo.data, bpy.types.Camera): if isinstance(bo.data, bpy.types.Camera):
applicators.append(self._convert_camera_animation(bo, so, obj_fcurves, data_fcurves, anim_name, start, end)) applicators.append(self._convert_camera_animation(bo, so, obj_fcurves, data_fcurves, anim_name, start, end))
else: else:
applicators.append(self._convert_transform_animation(bo, obj_fcurves, bo.matrix_basis, start=start, end=end)) applicators.append(self._convert_transform_animation(bo, obj_fcurves, bo.matrix_local, bo.matrix_parent_inverse, start=start, end=end))
if bo.plasma_modifiers.soundemit.enabled: if bo.plasma_modifiers.soundemit.enabled:
applicators.extend(self._convert_sound_volume_animation(bo.name, obj_fcurves, bo.plasma_modifiers.soundemit, start, end)) applicators.extend(self._convert_sound_volume_animation(bo.name, obj_fcurves, bo.plasma_modifiers.soundemit, start, end))
if isinstance(bo.data, bpy.types.Lamp): if isinstance(bo.data, bpy.types.Lamp):
@ -143,7 +143,7 @@ class AnimationConverter:
# If we exported any FOV animation at all, then we need to ensure there is an applicator # If we exported any FOV animation at all, then we need to ensure there is an applicator
# returned from here... At bare minimum, we'll need the applicator with an empty # returned from here... At bare minimum, we'll need the applicator with an empty
# CompoundController. This should be sufficient to keep CWE from crashing... # CompoundController. This should be sufficient to keep CWE from crashing...
applicator = self._convert_transform_animation(bo, obj_fcurves, bo.matrix_basis, applicator = self._convert_transform_animation(bo, obj_fcurves, bo.matrix_local, bo.matrix_parent_inverse,
allow_empty=has_fov_anim, start=start, end=end) allow_empty=has_fov_anim, start=start, end=end)
camera = self._mgr.find_create_object(plCameraModifier, so=so) camera = self._mgr.find_create_object(plCameraModifier, so=so)
camera.animated = applicator is not None camera.animated = applicator is not None
@ -338,9 +338,16 @@ class AnimationConverter:
applicator.channel = channel applicator.channel = channel
yield applicator yield applicator
def _convert_transform_animation(self, bo, fcurves, xform, *, allow_empty: Optional[bool] = False, def _convert_transform_animation(self, bo, fcurves, default_xform, adjust_xform, *, allow_empty: Optional[bool] = False,
start: Optional[int] = None, end: Optional[int] = None) -> Optional[plMatrixChannelApplicator]: start: Optional[int] = None, end: Optional[int] = None) -> Optional[plMatrixChannelApplicator]:
tm = self.convert_transform_controller(fcurves, bo.rotation_mode, xform, allow_empty=allow_empty, if adjust_xform != mathutils.Matrix.Identity(4):
self._exporter().report.warn(("{}: Transform animation is not local and may export incorrectly. " +
"Please use Alt-P -> Clear Parent Inverse before animating objects to avoid issues.").format(bo.name), indent=1)
else:
# Adjustment matrix is identity, just pass None instead...
adjust_xform = None
tm = self.convert_transform_controller(fcurves, bo.rotation_mode, default_xform, adjust_xform, allow_empty=allow_empty,
start=start, end=end) start=start, end=end)
if tm is None and not allow_empty: if tm is None and not allow_empty:
return None return None
@ -351,20 +358,62 @@ class AnimationConverter:
channel = plMatrixControllerChannel() channel = plMatrixControllerChannel()
channel.controller = tm channel.controller = tm
applicator.channel = channel applicator.channel = channel
channel.affine = utils.affine_parts(xform) channel.affine = utils.affine_parts(default_xform)
return applicator return applicator
def convert_transform_controller(self, fcurves, rotation_mode: str, xform, *, def convert_transform_controller(self, fcurves, rotation_mode: str, default_xform, adjust_xform, *,
allow_empty: Optional[bool] = False, allow_empty: Optional[bool] = False,
start: Optional[int] = None, start: Optional[int] = None,
end: Optional[int] = None) -> Union[None, plCompoundController]: end: Optional[int] = None) -> Union[None, plCompoundController]:
if not fcurves and not allow_empty: if not fcurves and not allow_empty:
return None return None
pos = self.make_pos_controller(fcurves, "location", xform.to_translation(), start=start, end=end) if adjust_xform is not None:
rot = self.make_rot_controller(fcurves, rotation_mode, xform, start=start, end=end) # We have to edit the keyframes to make the anim local..
scale = self.make_scale_controller(fcurves, "scale", xform.to_scale(), start=start, end=end) # In many cases this should work fine, but sometimes scale and rotation might
# still cause issues. Also, euler angles need to be converted to quaternion
# and back to eulers, which could cause issues. Not much we can do about it.
adjust_rotation = adjust_xform.to_quaternion()
adjust_scale = adjust_xform.to_scale()
# Helpers to adjust keyframes in case animation is not local (adjust_xform == identity)
def convert_pos_keyframe(pos):
# Position: can transform to local space without issues.
return tuple(adjust_xform * mathutils.Vector(pos))
def convert_rot_keyframe(rot):
# Rotation: may cause issues if scale is present.
if isinstance(rot, mathutils.Quaternion): # quaternion from an axis-angle
return adjust_rotation * rot
elif isinstance(rot, mathutils.Euler):
return (adjust_rotation * rot.to_quaternion()).to_euler(rot.order)
else: # tuple
if len(rot) == 4: # quat in a tuple
return (adjust_rotation * mathutils.Quaternion(rot))[:]
else: # XYZ euler in a tuple
rot = mathutils.Euler(rot, "XYZ").to_quaternion()
return (adjust_rotation * rot).to_euler("XYZ")[:]
def convert_scale_keyframe(scale):
# Scale: very likely to cause issues.
return [a * b for a, b in zip(adjust_scale, scale)]
convert_pos = convert_pos_keyframe
convert_rot = convert_rot_keyframe
convert_scale = convert_scale_keyframe
else:
# Don't change the keyframes at all, so we don't risk screwing them up.
convert_pos = None
convert_rot = None
convert_scale = None
pos = self.make_pos_controller(fcurves, "location", default_xform.to_translation(),
convert=convert_pos, start=start, end=end)
rot = self.make_rot_controller(fcurves, rotation_mode, default_xform,
convert=convert_rot, start=start, end=end)
scale = self.make_scale_controller(fcurves, "scale", default_xform.to_scale(),
convert=convert_scale, start=start, end=end)
if pos is None and rot is None and scale is None: if pos is None and rot is None and scale is None:
if not allow_empty: if not allow_empty:
return None return None
@ -490,7 +539,8 @@ class AnimationConverter:
default_xform = (default_xform[1], default_xform[0].x, default_xform[0].y, default_xform[0].z) default_xform = (default_xform[1], default_xform[0].x, default_xform[0].y, default_xform[0].z)
if convert is not None: if convert is not None:
convert = lambda x: convert(mathutils.Quaternion(x[1:4], x[0]))[:] convert_original = convert
convert = lambda x: convert_original(mathutils.Quaternion(x[1:4], x[0]))[:]
else: else:
convert = lambda x: mathutils.Quaternion(x[1:4], x[0])[:] convert = lambda x: mathutils.Quaternion(x[1:4], x[0])[:]

5
korman/exporter/camera.py

@ -132,7 +132,7 @@ class CameraConverter:
# specifying an actual center allows you to do interesting things like animate the center... # specifying an actual center allows you to do interesting things like animate the center...
# Fascinating! Therefore, we will expose the Plasma Object... # Fascinating! Therefore, we will expose the Plasma Object...
if props.circle_center is None: if props.circle_center is None:
brain.center = hsVector3(*bo.location) brain.center = hsVector3(*bo.matrix_world.translation)
else: else:
brain.centerObject = self._mgr.find_create_key(plSceneObject, bl=props.circle_center) brain.centerObject = self._mgr.find_create_key(plSceneObject, bl=props.circle_center)
# This flag has no effect in CWE, but I'm using it for correctness' sake # This flag has no effect in CWE, but I'm using it for correctness' sake
@ -204,7 +204,8 @@ class CameraConverter:
# path object, but it makes more sense to me to just animate the camera with # path object, but it makes more sense to me to just animate the camera with
# the details of the path... # the details of the path...
pos_fcurves = tuple(i for i in helpers.fetch_fcurves(bo, False) if i.data_path == "location") pos_fcurves = tuple(i for i in helpers.fetch_fcurves(bo, False) if i.data_path == "location")
pos_ctrl = self._exporter().animation.convert_transform_controller(pos_fcurves, bo.rotation_mode, bo.matrix_basis) pos_ctrl = self._exporter().animation.convert_transform_controller(pos_fcurves, bo.rotation_mode,
bo.matrix_local, bo.matrix_parent_inverse)
if pos_ctrl is None: if pos_ctrl is None:
raise ExportError("'{}': Rail Camera lacks appropriate rail keyframes".format(bo.name)) raise ExportError("'{}': Rail Camera lacks appropriate rail keyframes".format(bo.name))
path = plAnimPath() path = plAnimPath()

2
korman/exporter/convert.py

@ -245,7 +245,7 @@ class Exporter:
ci = self.mgr.add_object(ci_cls, bl=bl, so=so) ci = self.mgr.add_object(ci_cls, bl=bl, so=so)
# Now we have the "fun" work of filling in the CI # Now we have the "fun" work of filling in the CI
ci.localToWorld = utils.matrix44(bl.matrix_basis) ci.localToWorld = utils.matrix44(bl.matrix_world)
ci.worldToLocal = ci.localToWorld.inverse() ci.worldToLocal = ci.localToWorld.inverse()
ci.localToParent = utils.matrix44(bl.matrix_local) ci.localToParent = utils.matrix44(bl.matrix_local)
ci.parentToLocal = ci.localToParent.inverse() ci.parentToLocal = ci.localToParent.inverse()

2
korman/exporter/material.py

@ -779,7 +779,7 @@ class MaterialConverter:
# will probably want to steal it for diabolical purposes... In MOUL, root objects are # will probably want to steal it for diabolical purposes... In MOUL, root objects are
# allowed, but that introduces a gotcha with regard to animated roots and PotS. Also, # allowed, but that introduces a gotcha with regard to animated roots and PotS. Also,
# sharing root objects with a DCM seems to result in bad problems in game O.o # sharing root objects with a DCM seems to result in bad problems in game O.o
pl_env.position = hsVector3(*viewpt.location) pl_env.position = hsVector3(*viewpt.matrix_world.translation)
if layer is not None: if layer is not None:
layer.UVWSrc = plLayerInterface.kUVWReflect layer.UVWSrc = plLayerInterface.kUVWReflect

2
korman/exporter/mesh.py

@ -658,7 +658,7 @@ class MeshConverter(_MeshManager):
# FIXME: Can some of this be generalized? # FIXME: Can some of this be generalized?
geospan.props |= (plGeometrySpan.kWaterHeight | plGeometrySpan.kLiteVtxNonPreshaded | geospan.props |= (plGeometrySpan.kWaterHeight | plGeometrySpan.kLiteVtxNonPreshaded |
plGeometrySpan.kPropReverseSort | plGeometrySpan.kPropNoShadow) plGeometrySpan.kPropReverseSort | plGeometrySpan.kPropNoShadow)
geospan.waterHeight = bo.location[2] geospan.waterHeight = bo.matrix_world.translation[2]
return [_GeoSpan(bo, blmat, geospan)], None return [_GeoSpan(bo, blmat, geospan)], None
else: else:
geospans = [None] * len(materials) geospans = [None] * len(materials)

2
korman/exporter/physics.py

@ -93,7 +93,7 @@ class PhysicsConverter:
if so.sim is None: if so.sim is None:
simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo) simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo)
physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo, name=name) physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo)
simIface.physical = physical.key simIface.physical = physical.key
physical.object = so.key physical.object = so.key

2
korman/exporter/utils.py

@ -103,8 +103,8 @@ def temporary_mesh_object(source : bpy.types.Object) -> bpy.types.Object:
obj = bpy.data.objects.new(source.name, source.to_mesh(bpy.context.scene, True, "RENDER")) obj = bpy.data.objects.new(source.name, source.to_mesh(bpy.context.scene, True, "RENDER"))
obj.draw_type = "WIRE" obj.draw_type = "WIRE"
obj.matrix_basis, obj.matrix_world = source.matrix_basis, source.matrix_world
obj.parent = source.parent obj.parent = source.parent
obj.matrix_local, obj.matrix_world = source.matrix_local, source.matrix_world
bpy.context.scene.objects.link(obj) bpy.context.scene.objects.link(obj)
try: try:

5
korman/properties/modifiers/avatar.py

@ -64,10 +64,7 @@ class PlasmaLadderModifier(PlasmaModifierProperties):
ladderVec = self.facing_object.matrix_world.translation - bo.matrix_world.translation ladderVec = self.facing_object.matrix_world.translation - bo.matrix_world.translation
else: else:
# Make our own artificial target -1.0 units back on the local Y axis. # Make our own artificial target -1.0 units back on the local Y axis.
world = bo.matrix_world.copy() ladderVec = mathutils.Vector((0, -1, 0)) * bo.matrix_world.inverted()
world.invert()
target = bo.location - (mathutils.Vector((0.0, 1.0, 0.0)) * world)
ladderVec = target - bo.matrix_local.translation
mod.ladderView = hsVector3(ladderVec.x, ladderVec.y, 0.0) mod.ladderView = hsVector3(ladderVec.x, ladderVec.y, 0.0)
mod.ladderView.normalize() mod.ladderView.normalize()

6
korman/properties/modifiers/gui.py

@ -230,7 +230,7 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
if self.clickable_region is None: if self.clickable_region is None:
with utils.bmesh_object("{}_Journal_ClkRgn".format(self.key_name)) as (rgn_obj, bm): with utils.bmesh_object("{}_Journal_ClkRgn".format(self.key_name)) as (rgn_obj, bm):
bmesh.ops.create_cube(bm, size=(6.0)) bmesh.ops.create_cube(bm, size=(6.0))
bmesh.ops.transform(bm, matrix=mathutils.Matrix.Translation(bo.location - rgn_obj.location), bmesh.ops.transform(bm, matrix=mathutils.Matrix.Translation(bo.matrix_world.translation - rgn_obj.matrix_world.translation),
space=rgn_obj.matrix_world, verts=bm.verts) space=rgn_obj.matrix_world, verts=bm.verts)
rgn_obj.plasma_object.enabled = True rgn_obj.plasma_object.enabled = True
rgn_obj.hide_render = True rgn_obj.hide_render = True
@ -466,8 +466,8 @@ class PlasmaLinkingBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
if self.clickable_region is None: if self.clickable_region is None:
with utils.bmesh_object("{}_LinkingBook_ClkRgn".format(self.key_name)) as (rgn_obj, bm): with utils.bmesh_object("{}_LinkingBook_ClkRgn".format(self.key_name)) as (rgn_obj, bm):
bmesh.ops.create_cube(bm, size=(6.0)) bmesh.ops.create_cube(bm, size=(6.0))
rgn_offset = mathutils.Matrix.Translation(self.clickable.location - bo.location) rgn_offset = mathutils.Matrix.Translation(self.clickable.matrix_world.translation - rgn_obj.matrix_world.translation)
bmesh.ops.transform(bm, matrix=rgn_offset, space=bo.matrix_world, verts=bm.verts) bmesh.ops.transform(bm, matrix=rgn_offset, space=rgn_obj.matrix_world, verts=bm.verts)
rgn_obj.plasma_object.enabled = True rgn_obj.plasma_object.enabled = True
rgn_obj.hide_render = True rgn_obj.hide_render = True
yield rgn_obj yield rgn_obj

8
korman/properties/modifiers/water.py

@ -114,7 +114,7 @@ class PlasmaSwimRegion(idprops.IDPropObjectMixin, PlasmaModifierProperties, bpy.
losdbs = ["kLOSDBSwimRegion"] losdbs = ["kLOSDBSwimRegion"]
member_group = "kGroupLOSOnly" if exporter.mgr.getVer() != pvMoul else "kGroupStatic" member_group = "kGroupLOSOnly" if exporter.mgr.getVer() != pvMoul else "kGroupStatic"
if bo.plasma_modifiers.water_basic.enabled: if bo.plasma_modifiers.water_basic.enabled:
exporter.physics.generate_flat_proxy(bo, so, z_coord=bo.location[2], exporter.physics.generate_flat_proxy(bo, so, z_coord=bo.matrix_world.translation[2],
member_group=member_group, member_group=member_group,
losdbs=losdbs) losdbs=losdbs)
else: else:
@ -125,7 +125,7 @@ class PlasmaSwimRegion(idprops.IDPropObjectMixin, PlasmaModifierProperties, bpy.
# Detector region bounds # Detector region bounds
if self.region is not None: if self.region is not None:
region_so = exporter.mgr.find_create_object(plSceneObject, bl=self.region) region_so = exporter.mgr.find_create_object(plSceneObject, bl=self.region)
# Good news: if this phys has already been exported, this is basically a noop # Good news: if this phys has already been exported, this is basically a noop
member_group = "kGroupDetector" if exporter.mgr.getVer() == "pvMoul" else "kGroupLOSOnly" member_group = "kGroupDetector" if exporter.mgr.getVer() == "pvMoul" else "kGroupLOSOnly"
exporter.physics.generate_physical(self.region, region_so, exporter.physics.generate_physical(self.region, region_so,
@ -258,7 +258,7 @@ class PlasmaWaterModifier(idprops.IDPropMixin, PlasmaModifierProperties, bpy.typ
# Stuff we expose # Stuff we expose
state = waveset.state state = waveset.state
state.rippleScale = self.ripple_scale state.rippleScale = self.ripple_scale
state.waterHeight = bo.location[2] state.waterHeight = bo.matrix_world.translation[2]
state.windDir = wind_dir state.windDir = wind_dir
state.specVector = hsVector3(self.noise / 100.0, self.specular_start, self.specular_end) state.specVector = hsVector3(self.noise / 100.0, self.specular_start, self.specular_end)
state.specularTint = hsColorRGBA(*self.specular_tint, alpha=self.specular_alpha) state.specularTint = hsColorRGBA(*self.specular_tint, alpha=self.specular_alpha)
@ -273,7 +273,7 @@ class PlasmaWaterModifier(idprops.IDPropMixin, PlasmaModifierProperties, bpy.typ
state.envCenter = dem.position state.envCenter = dem.position
state.envRefresh = dem.refreshRate state.envRefresh = dem.refreshRate
else: else:
state.envCenter = hsVector3(*bo.location) state.envCenter = hsVector3(*bo.matrix_world.translation)
state.envRefresh = 0.0 state.envRefresh = 0.0
state.envRadius = self.envmap_radius state.envRadius = self.envmap_radius

2
korman/properties/prop_camera.py

@ -225,7 +225,7 @@ class PlasmaCameraProperties(bpy.types.PropertyGroup):
"""Gets the circle camera radius for this camera when it is attached to the given Object""" """Gets the circle camera radius for this camera when it is attached to the given Object"""
assert bo is not None assert bo is not None
if self.circle_center is not None: if self.circle_center is not None:
vec = bo.location - self.circle_center.location vec = bo.matrix_world.translation - self.circle_center.matrix_world.translation
return vec.magnitude return vec.magnitude
return self.circle_radius_value return self.circle_radius_value

Loading…
Cancel
Save