Browse Source

Implement layer opacity animations

pull/10/head
Adam Johnson 9 years ago
parent
commit
e14ac4f903
  1. 58
      korman/exporter/animation.py
  2. 74
      korman/exporter/material.py
  3. 7
      korman/properties/prop_texture.py
  4. 14
      korman/ui/ui_texture.py

58
korman/exporter/animation.py

@ -61,7 +61,7 @@ class AnimationConverter:
# Ugh. Unfortunately, it appears Blender's default interpolation is bezier. So who knows if
# many users will actually see the benefit here? Makes me sad.
if bez_chans:
ctrl = self._make_scalar_controller(rot_curves, keyframes, bez_chans, default_xform.to_euler())
ctrl = self._make_scalar_compound_controller(rot_curves, keyframes, bez_chans, default_xform.to_euler())
else:
ctrl = self._make_quat_controller(rot_curves, keyframes, default_xform.to_euler())
return ctrl
@ -76,6 +76,14 @@ class AnimationConverter:
ctrl = self._make_scale_value_controller(scale_curves, keyframes, bez_chans, default_xform)
return ctrl
def make_scalar_leaf_controller(self, fcurve):
keyframes, bezier = self._process_fcurve(fcurve)
if not keyframes:
return None
ctrl = self._make_scalar_leaf_controller(keyframes, bezier)
return ctrl
def _make_point3_controller(self, fcurves, keyframes, bezier, default_xform):
ctrl = plLeafController()
subctrls = ("X", "Y", "Z")
@ -141,7 +149,7 @@ class AnimationConverter:
ctrl.keys = (exported_frames, keyframe_type)
return ctrl
def _make_scalar_controller(self, fcurves, keyframes, bez_chans, default_xform):
def _make_scalar_compound_controller(self, fcurves, keyframes, bez_chans, default_xform):
ctrl = plCompoundController()
subctrls = ("X", "Y", "Z")
for i in subctrls:
@ -176,6 +184,23 @@ class AnimationConverter:
getattr(ctrl, subctrl).keys = (my_keyframes, my_keyframes[0].type)
return ctrl
def _make_scalar_leaf_controller(self, keyframes, bezier):
ctrl = plLeafController()
keyframe_type = hsKeyFrame.kBezScalarKeyFrame if bezier else hsKeyFrame.kScalarKeyFrame
exported_frames = []
for keyframe in keyframes:
exported = hsScalarKey()
exported.frame = keyframe.frame_num
exported.frameTime = keyframe.frame_time
exported.inTan = keyframe.in_tan
exported.outTan = keyframe.out_tan
exported.type = keyframe_type
exported.value = keyframe.value
exported_frames.append(exported)
ctrl.keys = (exported_frames, keyframe_type)
return ctrl
def _make_scale_value_controller(self, fcurves, keyframes, bez_chans, default_xform):
subctrls = ("X", "Y", "Z")
keyframe_type = hsKeyFrame.kBezScaleKeyFrame if bez_chans else hsKeyFrame.kScaleKeyFrame
@ -218,6 +243,35 @@ class AnimationConverter:
ctrl.keys = (exported_frames, keyframe_type)
return ctrl
def _process_fcurve(self, fcurve):
"""Like _process_keyframes, but for one fcurve"""
keyframe_data = type("KeyFrameData", (), {})
fps = self._bl_fps
pi = math.pi
keyframes = {}
bezier = False
fcurve.update()
for fkey in fcurve.keyframe_points:
keyframe = keyframe_data()
frame_num, value = fkey.co
if fps == 30.0:
keyframe.frame_num = int(frame_num)
else:
keyframe.frame_num = int(frame_num * (30.0 / fps))
keyframe.frame_time = frame_num / fps
if fkey.interpolation == "BEZIER":
keyframe.in_tan = -(value - fkey.handle_left[1]) / (frame_num - fkey.handle_left[0]) / fps / (2 * pi)
keyframe.out_tan = (value - fkey.handle_right[1]) / (frame_num - fkey.handle_right[0]) / fps / (2 * pi)
bezier = True
else:
keyframe.in_tan = 0.0
keyframe.out_tan = 0.0
keyframe.value = value
keyframes[frame_num] = keyframe
final_keyframes = [keyframes[i] for i in sorted(keyframes)]
return (final_keyframes, bezier)
def _process_keyframes(self, fcurves):
"""Groups all FCurves for the same frame together"""
keyframe_data = type("KeyFrameData", (), {})

74
korman/exporter/material.py

@ -176,6 +176,9 @@ class MaterialConverter:
"IMAGE": self._export_texture_type_image,
"NONE": self._export_texture_type_none,
}
self._animation_exporters = {
"opacityCtl": self._export_layer_opacity_animation,
}
def export_material(self, bo, bm):
"""Exports a Blender Material as an hsGMaterial"""
@ -261,9 +264,80 @@ class MaterialConverter:
# Export the specific texture type
self._tex_exporters[texture.type](bo, hsgmat, layer, slot)
# Export any layer animations
layer = self._export_layer_animations(bo, bm, slot, idx, hsgmat, layer)
hsgmat.addLayer(layer.key)
return num_exported
def _export_layer_animations(self, bo, bm, tex_slot, idx, hsgmat, base_layer):
"""Exports animations on this texture and chains the Plasma layers as needed"""
def harvest_fcurves(bl_id, collection, data_path=None):
anim = bl_id.animation_data
if anim is not None:
action = anim.action
if action is not None:
if data_path is None:
collection.extend(action.fcurves)
else:
collection.extend([i for i in action.fcurves if i.data_path.startswith(data_path)])
return action
return None
# First, we must gather relevant FCurves from both the material and the texture itself
# Because, you know, that totally makes sense...
fcurves = []
mat_action = harvest_fcurves(bm, fcurves, "texture_slots[{}]".format(idx))
tex_action = harvest_fcurves(tex_slot.texture, fcurves)
# No fcurves, no animation
if not fcurves:
return base_layer
# Okay, so we have some FCurves. We'll loop through our known layer animation converters
# and chain this biotch up as best we can.
layer_animation = None
for attr, converter in self._animation_exporters.items():
ctrl = converter(bm, tex_slot, fcurves)
if ctrl is not None:
if layer_animation is None:
name = "{}_LayerAnim".format(base_layer.key.name)
layer_animation = self._mgr.add_object(plLayerAnimation, bl=bo, name=name)
setattr(layer_animation, attr, ctrl)
# Alrighty, if we exported any controllers, layer_animation is a plLayerAnimation. We need to do
# the common schtuff now.
if layer_animation is not None:
layer_animation.underLay = base_layer.key
fps = bpy.context.scene.render.fps
atc = layer_animation.timeConvert
if tex_action is not None:
start, end = tex_action.frame_range
else:
start, end = mat_action.frame_range
atc.begin = start / fps
atc.end = end / fps
layer_props = tex_slot.texture.plasma_layer
if not layer_props.anim_auto_start:
atc.flags |= plAnimTimeConvert.kStopped
if layer_props.anim_loop:
atc.flags |= plAnimTimeConvert.kLoop
atc.loopBegin = atc.begin
atc.loopEnd = atc.end
return layer_animation
# Well, we had some FCurves but they were garbage... Too bad.
return base_layer
def _export_layer_opacity_animation(self, bm, tex_slot, fcurves):
opacity_fcurve = next((i for i in fcurves if i.data_path == "plasma_layer.opacity" and i.keyframe_points), None)
ctrl = self._exporter().animation.make_scalar_leaf_controller(opacity_fcurve)
return ctrl
def _export_texture_type_environment_map(self, bo, hsgmat, layer, slot):
"""Exports a Blender EnvironmentMapTexture to a plLayer"""

7
korman/properties/prop_texture.py

@ -25,3 +25,10 @@ class PlasmaLayer(bpy.types.PropertyGroup):
min=0,
max=100,
subtype="PERCENTAGE")
anim_auto_start = BoolProperty(name="Auto Start",
description="Automatically start layer animation",
default=True)
anim_loop = BoolProperty(name="Loop",
description="Loop layer animation",
default=True)

14
korman/ui/ui_texture.py

@ -28,5 +28,17 @@ class PlasmaLayerPanel(TextureButtonsPanel, bpy.types.Panel):
bl_label = "Plasma Layer Options"
def draw(self, context):
layer_props = context.texture.plasma_layer
layout = self.layout
layout.prop(context.texture.plasma_layer, "opacity")
split = layout.split()
col = split.column()
col.label("Animation:")
col.enabled = context.texture.animation_data is not None or \
context.material.animation_data is not None
col.prop(layer_props, "anim_auto_start")
col.prop(layer_props, "anim_loop")
col = split.column()
col.label("General:")
col.prop(layer_props, "opacity", text="Opacity")

Loading…
Cancel
Save