Hi
As you may (skinkie) or not (hi koos) know, i am developing a video
compositing engine on top of clutter and gstreamer.
Attached is a preview (work in progress) of a simple animation wrapper
in python, with an effect example. It wraps the depth, opacity and
position behaviours. It features a "wait_for_completion" handler
(allow or not an animation to be fired before completion) and offers
some default parameters (overridable) that lets try them out fast.
It's far from complete, and it lacks a plugin architecture (on our
roadmap) but i'm very curious about what you guys may think of such an
approach.
I'd be glad to know:
* what language you will/want to develop your glSMIL player(s) in ?
* what you think of this preliminary proposal (it's not that elegant
nor scalable, yet i implemented a bunch of effects quite easily with
such wrappers)
* the thing with SMIL is that it's not taking 3D effects into account
(AFAIK). How to keep both worlds in the same rendering and compositing
engine ?
* for the SMIL effects, there are a lot of cropping-based effects, i
don't know how well clutter would perform by re-clipping incrementally
a texture using a timeline...
On my side, i am very interested in SMIL support as well, yet not only
for playback but rather for "recording"/animation archival purposes.
Please let me know when/if you join forces !
Cheers
Florent
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import clutter
class AnimatedEffectTemplate:
# in ms:
DEFAULT_DURATION = 3000
DEFAULT_FPS = 30
def __init__(self, duration=None, alphatype=None, looped=False):
if duration is None:
self.duration = self.DEFAULT_DURATION
else : self.duration = duration
self.timeline = clutter.Timeline(fps=self.DEFAULT_FPS, duration=self.duration)
if looped != False: self.timeline.set_loop(True)
if alphatype is None: self.alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func)
elif alphatype is "ramp_inc": self.alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func)
elif alphatype is "ramp_dec": self.alpha = clutter.Alpha(self.timeline, clutter.ramp_dec_func)
elif alphatype is "sine_inc": self.alpha = clutter.Alpha(self.timeline, clutter.sine_inc_func)
elif alphatype is "sine_dec": self.alpha = clutter.Alpha(self.timeline, clutter.sine_dec_func)
# TODO: changer par dict et clutter.Alpha.set_func et .set_timeline, compléter l'implémentation
class Effect:
def __init__(self, duration, alphatype, actor, looped, wait_for_completion):
self.actor = actor
self.looped = looped
self.effect_is_applied = False
self.autoremove_active = False
self.wait_for_completion = wait_for_completion
self.template = AnimatedEffectTemplate(duration=duration, alphatype=alphatype, looped=looped)
#self.effect = clutter.Behaviour()
def on_completion_cb(self, timeline):
self.effect.remove(self.actor)
self.template.timeline.disconnect_by_func(self.on_completion_cb)
self.effect_is_applied = False
self.autoremove_active = False
def run(self, stage, actor):
# First call, activate effect auto-removal after completion
if self.looped is False and self.autoremove_active is False:
self.template.timeline.connect('completed', self.on_completion_cb)
self.autoremove_active = True
# Apply effect and run it
if self.template.timeline.is_playing() is False and self.effect_is_applied is False:
self.effect.apply(self.actor)
self.template.timeline.start()
self.effect_is_applied = True
# Case where an effect not completed is called again
elif self.template.timeline.is_playing() is True:
if self.wait_for_completion is True:
print "Wait for the effect to finish before issuing a new call"
else :
self.template.timeline.rewind()
self.template.timeline.start()
class OpacityEffect(Effect):
def __init__(self, duration, alphatype, actor, looped=False, wait_for_completion=False, opacity_start=0, opacity_end=255):
Effect.__init__(self, duration=duration, alphatype=alphatype, actor=actor, looped=looped, wait_for_completion=wait_for_completion)
self.opacity_start = opacity_start
self.opacity_end = opacity_end
self.effect = clutter.BehaviourOpacity(alpha = self.template.alpha, opacity_start=self.opacity_start, opacity_end=self.opacity_end)
class RotateEffect(Effect):
def __init__(self, duration, alphatype, actor, looped=False, wait_for_completion=False, axis=None, angle_start=None, angle_end=None, direction=clutter.ROTATE_CW):
Effect.__init__(self, duration=duration, alphatype=alphatype, actor=actor, looped=looped, wait_for_completion=wait_for_completion)
self.angle_start = angle_start
self.angle_end = angle_end
self.x_center = self.y_center = self.z_center = 0
if axis is None or axis is "y":
self.axis = clutter.Y_AXIS
self.x_center = self.actor.get_width()/2
elif axis is "x":
self.axis = clutter.X_AXIS
self.y_center = self.actor.get_height()/2
elif axis is "z":
self.axis = clutter.Z_AXIS
self.x_center = self.actor.get_width()/2
self.y_center = self.actor.get_height()/2
self.effect = clutter.BehaviourRotate(alpha = self.template.alpha, axis=self.axis, direction=direction, angle_start=self.angle_start, angle_end=self.angle_end)
self.effect.set_center(self.x_center, self.y_center, self.z_center)
class DepthEffect(Effect):
def __init__(self, duration, alphatype, actor, looped=False, wait_for_completion=False, depth_start=0, depth_end=100):
Effect.__init__(self, duration=duration, alphatype=alphatype, actor=actor, looped=looped, wait_for_completion=wait_for_completion)
self.depth_start = depth_start
self.depth_end = depth_end
self.effect = clutter.BehaviourDepth(alpha = self.template.alpha, depth_start = self.depth_start, depth_end = self.depth_end)
class MoveEffect(Effect):
def __init__(self, duration, alphatype, actor, looped=False, wait_for_completion=False, x_move_offset=0, y_move_offset=0):
Effect.__init__(self, duration=duration, alphatype=alphatype, actor=actor, looped=looped, wait_for_completion=wait_for_completion)
self.knots = ( \
( self.actor.get_x(), self.actor.get_y() ), \
( self.actor.get_x()+x_move_offset, self.actor.get_y()+y_move_offset ), \
)
self.effect = clutter.BehaviourPath(alpha=self.template.alpha, knots=self.knots)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: [EMAIL PROTECTED]
# LGPL terms
import clutter
from effects import MoveEffect
class LinearHMoveOut(MoveEffect):
def __init__(self, duration, actor, looped):
MoveEffect.__init__(self, duration=duration, alphatype="ramp_inc", actor=actor, looped=looped, x_move_offset=400)
class LinearVMoveOut(MoveEffect):
def __init__(self, duration, actor, looped):
MoveEffect.__init__(self, duration=duration, alphatype="ramp_inc", actor=actor, looped=looped, y_move_offset=400)
class LinearHMoveIn(MoveEffect):
def __init__(self, duration, actor, looped, move_offset):
actor.set_position(actor.get_x()-move_offset,actor.get_y())
MoveEffect.__init__(self, duration=duration, alphatype="ramp_inc", actor=actor, looped=looped, x_move_offset=move_offset)
class SineVMoveIn(MoveEffect):
def __init__(self, duration, actor, looped, move_offset):
actor.set_position(actor.get_x(),actor.get_y()-move_offset)
MoveEffect.__init__(self, duration=duration, alphatype="sine_inc", actor=actor, looped=looped, y_move_offset=move_offset)
class SineMoveTo(MoveEffect):
def __init__(self, duration, actor, looped, x_move_offset, y_move_offset):
MoveEffect.__init__(self, duration=duration, alphatype="sine_inc", actor=actor, looped=looped, x_move_offset=x_move_offset, y_move_offset=y_move_offset)
if __name__ == '__main__':
stage = clutter.Stage()
stage.set_color(clutter.Color(0xcc, 0xcc, 0xcc, 0xff))
stage.set_size(640,480)
test_actor = clutter.label_new_with_text("Sans 22", "Test actor")
test_actor.set_position(200,200)
#test_move = LinearHMoveOut(duration=500, actor=test_actor, looped=False)
test_move = SineVMoveIn(duration=500, actor=test_actor, looped=False, move_offset=400)
#test_move = SineMoveTo(duration=500, actor=test_actor, looped=False, x_move_offset=100, y_move_offset=50)
stage.connect('key-press-event', test_move.run)
stage.add(test_actor)
stage.show_all()
clutter.main()