forked from Mirrors/openclonk
Introduce animation stack, adapt existing scripts
parent
eaab9e528f
commit
5dcf92121d
|
@ -204,6 +204,8 @@ set(OC_CLONK_SOURCES
|
|||
src/game/object/C4IDList.h
|
||||
src/game/object/C4InfoCore.cpp
|
||||
src/game/object/C4InfoCore.h
|
||||
src/game/object/C4MeshAnimation.cpp
|
||||
src/game/object/C4MeshAnimation.h
|
||||
src/game/object/C4Movement.cpp
|
||||
src/game/object/C4ObjectCom.cpp
|
||||
src/game/object/C4ObjectCom.h
|
||||
|
|
|
@ -251,6 +251,8 @@ src/game/object/C4IDList.cpp \
|
|||
src/game/object/C4IDList.h \
|
||||
src/game/object/C4InfoCore.cpp \
|
||||
src/game/object/C4InfoCore.h \
|
||||
src/game/object/C4MeshAnimation.cpp \
|
||||
src/game/object/C4MeshAnimation.h \
|
||||
src/game/object/C4Movement.cpp \
|
||||
src/game/object/C4ObjectCom.cpp \
|
||||
src/game/object/C4ObjectCom.h \
|
||||
|
|
|
@ -334,20 +334,29 @@ protected func CheckStuck()
|
|||
|
||||
/* Status */
|
||||
|
||||
// TODO: Make this more sophisticated, readd turn animation and other
|
||||
// adaptions
|
||||
public func IsClonk() { return true; }
|
||||
|
||||
|
||||
/*
|
||||
// Test to synchronize the walkanimation with the movement
|
||||
local OldPos;
|
||||
|
||||
static CLNK_WalkStates; // TODO: Well wasn't there once a patch, allowing arrys to be assigned to global static?
|
||||
static CLNK_HangleStates;
|
||||
static CLNK_SwimStates;
|
||||
*/
|
||||
|
||||
/* Walk */
|
||||
|
||||
static const CLNK_WalkStand = "Stand";
|
||||
static const CLNK_WalkWalk = "Walk";
|
||||
static const CLNK_WalkRun = "Run";
|
||||
|
||||
func StartWalk()
|
||||
{
|
||||
if(CLNK_WalkStates == nil)
|
||||
CLNK_WalkStates = ["Stand", "Walk", "Run", "StandTurn", "RunTurn"];
|
||||
// if(CLNK_WalkStates == nil)
|
||||
// CLNK_WalkStates = ["Stand", "Walk", "Run", "StandTurn", "RunTurn"];
|
||||
if(!GetEffect("IntWalk", this))
|
||||
AddEffect("IntWalk", this, 1, 1, this);
|
||||
}
|
||||
|
@ -357,6 +366,61 @@ func StopWalk()
|
|||
if(GetAction() != "Walk") RemoveEffect("IntWalk", this);
|
||||
}
|
||||
|
||||
func GetCurrentWalkAnimation()
|
||||
{
|
||||
var velocity = Distance(0,0,GetXDir(),GetYDir());
|
||||
if(velocity < 1) return CLNK_WalkStand;
|
||||
if(velocity < 10) return CLNK_WalkWalk;
|
||||
return CLNK_WalkRun;
|
||||
}
|
||||
|
||||
func GetWalkAnimationPosition(string anim)
|
||||
{
|
||||
// TODO: Choose proper starting positions, depending on the current
|
||||
// animation and its position: For Stand->Walk or Stand->Run, start
|
||||
// with a frame where one of the clonk's feets is on the ground and
|
||||
// the other one is in the air. For Walk->Run and Run->Walk, fade to
|
||||
// a state where its feets are at a similar position (just taking
|
||||
// over previous animation's position might work, using
|
||||
// GetAnimationPosition()). Walk->Stand is arbitrary I guess.
|
||||
// First parameter of Anim_Linear/Anim_AbsX is initial position.
|
||||
// Movement synchronization might also be tweaked somewhat as well.
|
||||
if(anim == CLNK_WalkStand)
|
||||
return Anim_Linear(0, 0, GetAnimationLength(anim), 35, ANIM_Loop);
|
||||
else if(anim == CLNK_WalkWalk)
|
||||
return Anim_AbsX(0, 0, GetAnimationLength(anim), 20);
|
||||
else if(anim == CLNK_WalkRun)
|
||||
return Anim_AbsX(0, 0, GetAnimationLength(anim), 50);
|
||||
}
|
||||
|
||||
func FxIntWalkStart(pTarget, iNumber, fTmp)
|
||||
{
|
||||
if(fTmp) return;
|
||||
// Always start in Stand for now... should maybe fade properly from previous animation instead
|
||||
var anim = "Stand"; //GetCurrentWalkAnimation();
|
||||
EffectVar(0, pTarget, iNumber) = anim;
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation(anim, 5, GetWalkAnimationPosition(anim), Anim_Const(1000));
|
||||
}
|
||||
|
||||
func FxIntWalkStop(pTarget, iNumber, fTmp)
|
||||
{
|
||||
if(fTmp) return;
|
||||
|
||||
// Remove all
|
||||
StopAnimation(GetRootAnimation(5));
|
||||
}
|
||||
|
||||
func FxIntWalkTimer(pTarget, iNumber)
|
||||
{
|
||||
var anim = GetCurrentWalkAnimation();
|
||||
if(anim != EffectVar(0, pTarget, iNumber))
|
||||
{
|
||||
EffectVar(0, pTarget, iNumber) = anim;
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation(anim, 5, GetWalkAnimationPosition(anim), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func FxIntWalkStart(pTarget, iNumber, fTmp)
|
||||
{
|
||||
if(fTmp) return;
|
||||
|
@ -488,11 +552,28 @@ func FxIntWalkTimer(pTarget, iNumber, iTime)
|
|||
}
|
||||
EffectVar(14, pTarget, iNumber) = iState;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Scale */
|
||||
|
||||
func StartScale()
|
||||
{
|
||||
// TODO: Tweak animation speed
|
||||
PlayAnimation("Scale", 5, Anim_Y(0, GetAnimationLength("Scale"), 0, 15), Anim_Const(1000));
|
||||
}
|
||||
|
||||
func StopScale()
|
||||
{
|
||||
StopAnimation(GetRootAnimation(5));
|
||||
}
|
||||
|
||||
/* Hangle */
|
||||
|
||||
func StartHangle()
|
||||
{
|
||||
if(CLNK_HangleStates == nil)
|
||||
CLNK_HangleStates = ["HangleStand", "Hangle"];
|
||||
/* if(CLNK_HangleStates == nil)
|
||||
CLNK_HangleStates = ["HangleStand", "Hangle"];*/
|
||||
if(!GetEffect("IntHangle", this))
|
||||
AddEffect("IntHangle", this, 1, 1, this);
|
||||
}
|
||||
|
@ -506,99 +587,95 @@ func FxIntHangleStart(pTarget, iNumber, fTmp)
|
|||
{
|
||||
EffectVar(10, pTarget, iNumber) = GetPhysical("Hangle");
|
||||
if(fTmp) return;
|
||||
AnimationPlay("Hangle", 0);
|
||||
AnimationPlay("HangleStand", 1000);
|
||||
EffectVar(0, pTarget, iNumber) = 0; // Phase
|
||||
EffectVar(1, pTarget, iNumber) = 1000; // HangleStand weight
|
||||
EffectVar(2, pTarget, iNumber) = 0; // Hangle weight
|
||||
EffectVar(4, pTarget, iNumber) = 0; // Oldstate
|
||||
EffectVar(5, pTarget, iNumber) = 0; // Remember if the last frame had COMD_Stop (so a single COMD_Stop frame doesn't stop the movement)
|
||||
EffectVar(6, pTarget, iNumber) = 1; // Scedule Stop
|
||||
EffectVar(7, pTarget, iNumber) = 0; // Hanging Pose (Front to player or Back)
|
||||
|
||||
// EffectVars:
|
||||
// 0: whether the clonk is currently moving or not (<=> current animation is Hangle or HangleStand)
|
||||
// 1: Current animation number
|
||||
// 6: Player requested the clonk to stop
|
||||
// 7: Whether the HangleStand animation is shown front-facing or back-facing
|
||||
// 10: Previous Hangle physical
|
||||
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation("HangleStand", 5, Anim_Linear(0, 0, 2000, 100, ANIM_Loop), Anim_Const(1000));
|
||||
}
|
||||
|
||||
func FxIntHangleStop(pTarget, iNumber, iReasonm, fTmp)
|
||||
{
|
||||
SetPhysical("Hangle", EffectVar(10, pTarget, iNumber), 2);
|
||||
if(fTmp) return;
|
||||
AnimationStop("Hangle");
|
||||
AnimationStop("HangleStand");
|
||||
StopAnimation(GetRootAnimation(5));
|
||||
}
|
||||
|
||||
func FxIntHangleTimer(pTarget, iNumber, iTime)
|
||||
{
|
||||
// Make a cosine movment speed (the clonk only moves whem he makes a "stroke")
|
||||
var iSpeed = 50-Cos(EffectVar(0, pTarget, iNumber)*360*2/1000, 50);
|
||||
SetPhysical("Hangle", EffectVar(10, pTarget, iNumber)/50*iSpeed, 2);
|
||||
var iState = 0;
|
||||
// (TODO: Instead of EffectVar(0, pTarget, iNumber) we should be able
|
||||
// to query the current animation... maybe via a to-be-implemented
|
||||
// GetAnimationName() engine function.
|
||||
|
||||
// Continue movement, if the clonk still has momentum
|
||||
if(GetComDir() == COMD_Stop && iSpeed>10)
|
||||
// If we are currently moving
|
||||
if(EffectVar(0, pTarget, iNumber))
|
||||
{
|
||||
EffectVar(6, pTarget, iNumber) = 1;
|
||||
if(GetDir())
|
||||
SetComDir(COMD_Right);
|
||||
else
|
||||
SetComDir(COMD_Left);
|
||||
// Use a cosine-shaped movement speed (the clonk only moves when he makes a "stroke")
|
||||
var iSpeed = 50-Cos(GetAnimationPosition(EffectVar(1, pTarget, iNumber))/10*360*2/1000, 50);
|
||||
SetPhysical("Hangle", EffectVar(10, pTarget, iNumber)/50*iSpeed, 2);
|
||||
|
||||
// Exec movement animation (TODO: Use Anim_Linear?)
|
||||
var position = GetAnimationPosition(EffectVar(1, pTarget, iNumber));
|
||||
position += (EffectVar(10, pTarget, iNumber)/6000*1000/(14*2));
|
||||
|
||||
SetAnimationPosition(EffectVar(1, pTarget, iNumber), Anim_Const(position % GetAnimationLength("Hangle")));
|
||||
Log("Moving, speed %d, new pos %d/%d", iSpeed, position, GetAnimationPosition(EffectVar(1, pTarget, iNumber)));
|
||||
|
||||
// Continue movement, if the clonk still has momentum
|
||||
if(GetComDir() == COMD_Stop && iSpeed>10)
|
||||
{
|
||||
// Make it stop after the current movement
|
||||
EffectVar(6, pTarget, iNumber) = 1;
|
||||
|
||||
if(GetDir())
|
||||
SetComDir(COMD_Right);
|
||||
else
|
||||
SetComDir(COMD_Left);
|
||||
}
|
||||
// Stop movement if the clonk has lost his momentum
|
||||
else if(iSpeed <= 10 && (GetComDir() == COMD_Stop || EffectVar(6, pTarget, iNumber)))
|
||||
{
|
||||
EffectVar(6, pTarget, iNumber) = 0;
|
||||
SetComDir(COMD_Stop);
|
||||
|
||||
// and remeber the pose (front or back)
|
||||
if(GetAnimationPosition(EffectVar(1, pTarget, iNumber)) > 2500 && GetAnimationPosition(EffectVar(1, pTarget, iNumber)) < 7500)
|
||||
EffectVar(7, pTarget, iNumber) = 1;
|
||||
else
|
||||
EffectVar(7, pTarget, iNumber) = 0;
|
||||
|
||||
// Change to HangleStand animation
|
||||
var begin = 4000*EffectVar(7, pTarget, iNumber);
|
||||
var end = 2000+begin;
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation("HangleStand", 5, Anim_Linear(begin, begin, end, 100, ANIM_Loop), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
|
||||
EffectVar(0, pTarget, iNumber) = 0;
|
||||
}
|
||||
}
|
||||
// Stop movement if clonk has lost his momentum
|
||||
else if(EffectVar(6, pTarget, iNumber))
|
||||
{
|
||||
EffectVar(6, pTarget, iNumber) = 0;
|
||||
SetComDir(COMD_Stop);
|
||||
// and remeber the pose (front or back)
|
||||
if(EffectVar(0, pTarget, iNumber) > 250 && EffectVar(0, pTarget, iNumber) < 750)
|
||||
EffectVar(7, pTarget, iNumber) = 1;
|
||||
else
|
||||
EffectVar(7, pTarget, iNumber) = 0;
|
||||
}
|
||||
|
||||
// Play stand animation when not moving
|
||||
if(GetComDir() == COMD_Stop && EffectVar(5, pTarget, iNumber))
|
||||
{
|
||||
AnimationSetState("HangleStand", ((iTime/5)%21)*100+4000*EffectVar(7, pTarget, iNumber), nil);
|
||||
iState = 1;
|
||||
}
|
||||
// When moving
|
||||
else
|
||||
{
|
||||
var iSpeed = EffectVar(10, pTarget, iNumber)/6000;
|
||||
if(EffectVar(4, pTarget, iNumber) != 2)
|
||||
EffectVar(0, pTarget, iNumber) = 100+500*EffectVar(7, pTarget, iNumber); // start with frame 100 or from the back hanging pose frame 600
|
||||
else EffectVar(0, pTarget, iNumber) += iSpeed*100/(14*2);
|
||||
if(EffectVar(0, pTarget, iNumber) > 1000) EffectVar(0, pTarget, iNumber) -= 1000;
|
||||
|
||||
AnimationSetState("Hangle", EffectVar(0, pTarget, iNumber)*10, nil);
|
||||
iState = 2;
|
||||
}
|
||||
|
||||
// Save wether he have COMD_Stop or not. So a single frame with COMD_Stop keeps the movement
|
||||
if(GetComDir() == COMD_Stop) EffectVar(5, pTarget, iNumber) = 1;
|
||||
else EffectVar(5, pTarget, iNumber) = 0;
|
||||
|
||||
// Blend between the animations: The actuall animations gains weight till it reaches 1000
|
||||
// the other animations lose weight until they are at 0
|
||||
for(var i = 1; i <= 2; i++)
|
||||
{
|
||||
if(i == iState)
|
||||
// We are currently not moving
|
||||
if(GetComDir() != COMD_Stop)
|
||||
{
|
||||
if(EffectVar(i, pTarget, iNumber) < 1000)
|
||||
EffectVar(i, pTarget, iNumber) += 200;
|
||||
Log("Switch to move");
|
||||
// Switch to move
|
||||
EffectVar(0, pTarget, iNumber) = 1;
|
||||
// start with frame 100 or from the back hanging pose frame 600
|
||||
var begin = 10*(100 + 500*EffectVar(7, pTarget, iNumber));
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation("Hangle", 5, Anim_Const(begin), Anim_Linear(0, 0, 1000, 5, ANIM_Remove));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(EffectVar(i, pTarget, iNumber) > 0)
|
||||
EffectVar(i, pTarget, iNumber) -= 200;
|
||||
}
|
||||
AnimationSetState(CLNK_HangleStates[i-1], nil, EffectVar(i, pTarget, iNumber));
|
||||
}
|
||||
EffectVar(4, pTarget, iNumber) = iState;
|
||||
}
|
||||
|
||||
/* Swim */
|
||||
|
||||
func StartSwim()
|
||||
{
|
||||
if(CLNK_SwimStates == nil)
|
||||
CLNK_SwimStates = ["SwimStand", "Swim", "SwimDive", "SwimTurn", "SwimDiveTurn", "SwimDiveUp", "SwimDiveDown"];
|
||||
/* if(CLNK_SwimStates == nil)
|
||||
CLNK_SwimStates = ["SwimStand", "Swim", "SwimDive", "SwimTurn", "SwimDiveTurn", "SwimDiveUp", "SwimDiveDown"];*/
|
||||
if(!GetEffect("IntSwim", this))
|
||||
AddEffect("IntSwim", this, 1, 1, this);
|
||||
}
|
||||
|
@ -611,6 +688,10 @@ func StopSwim()
|
|||
func FxIntSwimStart(pTarget, iNumber, fTmp)
|
||||
{
|
||||
if(fTmp) return;
|
||||
|
||||
EffectVar(0, pTarget, iNumber) = "SwimStand";
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation("SwimStand", 5, Anim_Linear(0, 0, GetAnimationLength("SwimStand"), 20, ANIM_Loop), Anim_Const(1000));
|
||||
/*
|
||||
for(var i = 0; i < GetLength(CLNK_SwimStates); i++)
|
||||
AnimationPlay(CLNK_SwimStates[i], 0);
|
||||
EffectVar(0, pTarget, iNumber) = 0; // Phase
|
||||
|
@ -623,129 +704,72 @@ func FxIntSwimStart(pTarget, iNumber, fTmp)
|
|||
|
||||
EffectVar(7, pTarget, iNumber) = GetDir(); // OldDir
|
||||
EffectVar(8, pTarget, iNumber) = 0; // Turn Phase
|
||||
AnimationSetState("SwimStand", 0, 1000);*/
|
||||
}
|
||||
|
||||
func FxIntSwimStop(pTarget, iNumber, iReason, fTmp)
|
||||
{
|
||||
if(fTmp) return;
|
||||
for(var i = 0; i < GetLength(CLNK_SwimStates); i++)
|
||||
AnimationStop(CLNK_SwimStates[i]);
|
||||
StopAnimation(GetRootAnimation(5));
|
||||
}
|
||||
|
||||
func FxIntSwimTimer(pTarget, iNumber, iTime)
|
||||
{
|
||||
DoEnergy(1); //TODO Remove this! Endless Energy while diving is only for the testers
|
||||
|
||||
if(EffectVar(7, pTarget, iNumber) != GetDir() && 0)
|
||||
{
|
||||
EffectVar(7, pTarget, iNumber) = GetDir();
|
||||
EffectVar(8, pTarget, iNumber) = 1;
|
||||
}
|
||||
|
||||
var iSpeed = Distance(0,0,GetXDir(),GetYDir());
|
||||
var iState = 0;
|
||||
|
||||
// TODO: Smaller transition time between dive<->swim, keep 15 for swimstand<->swim/swimstand<->dive
|
||||
|
||||
// Play stand animation when not moving
|
||||
if(Abs(GetXDir()) < 1 && EffectVar(5, pTarget, iNumber) && !GBackSemiSolid(0, -4))
|
||||
if(Abs(GetXDir()) < 1 && !GBackSemiSolid(0, -4))
|
||||
{
|
||||
AnimationSetState("SwimStand", ((iTime/1)%20)*100, nil);
|
||||
iState = 1;
|
||||
if(EffectVar(0, pTarget, iNumber) != "SwimStand")
|
||||
{
|
||||
EffectVar(0, pTarget, iNumber) = "SwimStand";
|
||||
EffectVar(1, pTarget, iNumber) = PlayAnimation("SwimStand", 5, Anim_Linear(0, 0, GetAnimationLength("SwimStand"), 20, ANIM_Loop), Anim_Linear(0, 0, 1000, 15, ANIM_Remove));
|
||||
}
|
||||
}
|
||||
// Swimming
|
||||
else if(!GBackSemiSolid(0, -4))
|
||||
{
|
||||
if(EffectVar(8, pTarget, iNumber))
|
||||
// Animation speed by X
|
||||
if(EffectVar(0, pTarget, iNumber) != "Swim")
|
||||
{
|
||||
AnimationSetState("SwimTurn", EffectVar(8, pTarget, iNumber)*100, nil);
|
||||
EffectVar(8, pTarget, iNumber) += 2;
|
||||
if(EffectVar(8, pTarget, iNumber) >= 40)
|
||||
{
|
||||
EffectVar(8, pTarget, iNumber) = 0;
|
||||
SetDir(EffectVar(7, pTarget, iNumber));
|
||||
}
|
||||
else
|
||||
SetDir(!EffectVar(7, pTarget, iNumber));
|
||||
iState = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectVar(0, pTarget, iNumber) += Abs(GetXDir())*40/(16*2);
|
||||
if(EffectVar(0, pTarget, iNumber) > 400) EffectVar(0, pTarget, iNumber) -= 400;
|
||||
|
||||
AnimationSetState("Swim", EffectVar(0, pTarget, iNumber)*10, nil);
|
||||
iState = 2;
|
||||
EffectVar(0, pTarget, iNumber) = "Swim";
|
||||
// TODO: Determine starting position from previous animation
|
||||
PlayAnimation("Swim", 5, Anim_X(0, 0, GetAnimationLength("Swim"), 25), Anim_Linear(0, 0, 1000, 15, ANIM_Remove));
|
||||
}
|
||||
}
|
||||
// Diving
|
||||
else
|
||||
{
|
||||
EffectVar(0, pTarget, iNumber) += iSpeed*40/(16*2);
|
||||
if(EffectVar(0, pTarget, iNumber) > 400) EffectVar(0, pTarget, iNumber) -= 400;
|
||||
|
||||
AnimationSetState("SwimDive", ((iTime/2)%20)*100, nil);
|
||||
AnimationSetState("SwimDiveUp", ((iTime/2)%20)*100, nil);
|
||||
AnimationSetState("SwimDiveDown", ((iTime/2)%20)*100, nil);
|
||||
iState = 3;
|
||||
}
|
||||
|
||||
// Save wether he have COMD_Stop or not. So a single frame with COMD_Stop keeps the movement
|
||||
if(GetComDir() == COMD_Stop) EffectVar(5, pTarget, iNumber) = 1;
|
||||
else EffectVar(5, pTarget, iNumber) = 0;
|
||||
|
||||
// Blend between the animations: The actuall animations gains weight till it reaches 1000
|
||||
// the other animations lose weight until they are at 0
|
||||
for(var i = 1; i <= 3; i++)
|
||||
{
|
||||
if(i == iState)
|
||||
if(EffectVar(0, pTarget, iNumber) != "SwimDive")
|
||||
{
|
||||
if(EffectVar(i, pTarget, iNumber) < 1000)
|
||||
EffectVar(i, pTarget, iNumber) += 100;
|
||||
EffectVar(0, pTarget, iNumber) = "SwimDive";
|
||||
// TODO: Determine starting position from previous animation
|
||||
EffectVar(2, pTarget, iNumber) = PlayAnimation("SwimDiveUp", 5, Anim_Linear(0, 0, GetAnimationLength("SwimDiveUp"), 40, ANIM_Loop), Anim_Linear(0, 0, 1000, 15, ANIM_Remove));
|
||||
EffectVar(3, pTarget, iNumber) = PlayAnimation("SwimDiveDown", 5, Anim_Linear(0, 0, GetAnimationLength("SwimDiveDown"), 40, ANIM_Loop), Anim_Const(500), EffectVar(2, pTarget, iNumber));
|
||||
EffectVar(1, pTarget, iNumber) = EffectVar(3, pTarget, iNumber) + 1;
|
||||
|
||||
// TODO: This should depend on which animation we come from
|
||||
// Guess for SwimStand we should fade from 0, otherwise from 90.
|
||||
EffectVar(4, pTarget, iNumber) = 90;
|
||||
}
|
||||
else
|
||||
|
||||
if(iSpeed)
|
||||
{
|
||||
if(EffectVar(i, pTarget, iNumber) > 0)
|
||||
EffectVar(i, pTarget, iNumber) -= 100;
|
||||
var iRot = Angle(-Abs(GetXDir()), GetYDir());
|
||||
EffectVar(4, pTarget, iNumber) += BoundBy(iRot - EffectVar(4, pTarget, iNumber), -4, 4);
|
||||
}
|
||||
AnimationSetState(CLNK_SwimStates[i-1], nil, EffectVar(i, pTarget, iNumber));
|
||||
|
||||
// TODO: Shouldn't weight go by sin^2 or cos^2 instead of linear in angle?
|
||||
var weight = 1000*EffectVar(4, pTarget, iNumber)/180;
|
||||
SetAnimationWeight(EffectVar(1, pTarget, iNumber), Anim_Const(1000 - weight));
|
||||
}
|
||||
// Adjust Swim direction
|
||||
if(iSpeed > 1)
|
||||
{
|
||||
var iRot = Angle(-Abs(GetXDir()), GetYDir());
|
||||
EffectVar(6, pTarget, iNumber) += BoundBy(iRot-EffectVar(6, pTarget, iNumber), -4, 4);
|
||||
}
|
||||
iRot = EffectVar(6, pTarget, iNumber);
|
||||
Message("%d", this, iRot);
|
||||
AnimationSetState("SwimDiveUp", nil, EffectVar(3, pTarget, iNumber)*iRot/180);
|
||||
AnimationSetState("SwimDive", nil, 0);
|
||||
AnimationSetState("SwimDiveDown", nil, EffectVar(3, pTarget, iNumber)-EffectVar(3, pTarget, iNumber)*iRot/180);
|
||||
if(iRot < 90 && 0)
|
||||
{
|
||||
AnimationSetState("SwimDiveUp", nil, 0);
|
||||
AnimationSetState("SwimDive", nil, EffectVar(3, pTarget, iNumber)*iRot/90);
|
||||
AnimationSetState("SwimDiveDown", nil, EffectVar(3, pTarget, iNumber)-EffectVar(3, pTarget, iNumber)*iRot/90);
|
||||
}
|
||||
else if(0)
|
||||
{
|
||||
AnimationSetState("SwimDiveUp", nil, EffectVar(3, pTarget, iNumber)*(iRot-90)/90);
|
||||
AnimationSetState("SwimDive", nil, EffectVar(3, pTarget, iNumber)-EffectVar(3, pTarget, iNumber)*(iRot-90)/90);
|
||||
AnimationSetState("SwimDiveDown", nil, 0);
|
||||
}
|
||||
|
||||
EffectVar(4, pTarget, iNumber) = iState;
|
||||
}
|
||||
|
||||
func StartScale()
|
||||
{
|
||||
if(!GetEffect("IntScale", this))
|
||||
AddEffect("IntScale", this, 1, 1, this);
|
||||
}
|
||||
|
||||
func StopScale()
|
||||
{
|
||||
if(GetAction() != "Scale") RemoveEffect("IntScale", this);
|
||||
}
|
||||
|
||||
/*
|
||||
func FxIntScaleStart(pTarget, iNumber, fTmp)
|
||||
{
|
||||
if(fTmp) return;
|
||||
|
@ -808,7 +832,7 @@ func FxIntScaleTimer(pTarget, iNumber, iTime)
|
|||
// AnimationSetState(CLNK_WalkStates[i-1], nil, EffectVar(i, pTarget, iNumber));
|
||||
}
|
||||
EffectVar(4, pTarget, iNumber) = iState;
|
||||
}
|
||||
}*/
|
||||
|
||||
/* Act Map */
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ Version=4,9,8
|
|||
Category=C4D_Structure|C4D_SelectBuilding|C4D_SelectKnowledge
|
||||
MaxUserSelect=3
|
||||
TimerCall=Wind2Turn
|
||||
Timer=1
|
||||
Timer=35
|
||||
Width=70
|
||||
Height=90
|
||||
Offset=-35,-45
|
||||
|
@ -22,4 +22,4 @@ BlastIncinerate=60
|
|||
LineConnect=C4D_PowerOutput|C4D_PowerGenerator
|
||||
Construction=1
|
||||
Rotate=1
|
||||
Float=1
|
||||
Float=1
|
||||
|
|
|
@ -9,23 +9,46 @@ public func GetGeneratorPriority() { return 256; }
|
|||
|
||||
/* Initialisierung */
|
||||
|
||||
local wind_anim;
|
||||
|
||||
protected func Initialize()
|
||||
{
|
||||
AnimationPlay("Turn");
|
||||
iRot = 0;
|
||||
wind_anim = PlayAnimation("Turn", 5, Anim_Const(0), Anim_Const(1000));
|
||||
// Set initial position
|
||||
Wind2Turn();
|
||||
return _inherited(...);
|
||||
}
|
||||
|
||||
local iRot;
|
||||
|
||||
local b;
|
||||
func Wind2Turn()
|
||||
{
|
||||
if(!Random(10))
|
||||
DoPower(Abs(GetWind()/4));
|
||||
iRot += GetWind()/2;
|
||||
if(iRot < 0) iRot += 3600;
|
||||
if(iRot >= 3600) iRot -= 3600;
|
||||
AnimationSetState("Turn", iRot*12000/3600);
|
||||
DoPower(Abs(GetWind()/3));
|
||||
|
||||
// Fade linearly in time until next timer call
|
||||
var start = 0;
|
||||
var end = GetAnimationLength("Turn");
|
||||
if(GetWind() < 0)
|
||||
{
|
||||
start = end;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
// Number of frames for one revolution: the more wind the more
|
||||
// revolutions per frame.
|
||||
var l = 7200/Abs(GetWind());
|
||||
|
||||
// Note ending is irrelevant since this is called again after 35 frames
|
||||
if(!b)
|
||||
if(l > 0)
|
||||
{
|
||||
b=1;
|
||||
SetAnimationPosition(wind_anim, Anim_Linear(GetAnimationPosition(wind_anim), start, end, l, ANIM_Loop));
|
||||
}
|
||||
else
|
||||
{
|
||||
b = 1;
|
||||
SetAnimationPosition(wind_anim, Anim_Const(GetAnimationPosition(wind_anim)));
|
||||
}
|
||||
}
|
||||
|
||||
func Definition(def) {
|
||||
|
|
|
@ -8,10 +8,14 @@ public func IsLorry() { return 1; }
|
|||
|
||||
public func IsToolProduct() { return 1; }
|
||||
|
||||
local drive_anim;
|
||||
local tremble_anim;
|
||||
|
||||
protected func Initialize()
|
||||
{
|
||||
AnimationPlay("Drive");
|
||||
AnimationPlay("Tremble");
|
||||
drive_anim = PlayAnimation("Drive", 5, Anim_Const(0), Anim_Const(500) /* ignored anyway */);
|
||||
tremble_anim = PlayAnimation("Tremble", 5, Anim_Const(0), Anim_Const(500));
|
||||
|
||||
iRotWheels = 0;
|
||||
iTremble = 0;
|
||||
}
|
||||
|
@ -133,16 +137,18 @@ local iTremble;
|
|||
|
||||
func TurnWheels()
|
||||
{
|
||||
// TODO: Use Anim_X(Dir), keep from timer=1
|
||||
// TODO: Could also use GetAnimationPosition() instead of these local variables...
|
||||
iRotWheels += GetXDir()*2000/100; // Einmal rum (20 frames mal 10fps) nach 10 m
|
||||
while(iRotWheels < 0) iRotWheels += 2000;
|
||||
while(iRotWheels > 2000) iRotWheels -= 2000;
|
||||
AnimationSetState("Drive", iRotWheels);
|
||||
SetAnimationPosition(drive_anim, Anim_Const(iRotWheels));
|
||||
if(Random(100) < Abs(GetXDir()))
|
||||
{
|
||||
iTremble += 100;
|
||||
if(iTremble < 0) iTremble += 2000;
|
||||
if(iTremble > 2000) iTremble -= 2000;
|
||||
AnimationSetState("Tremble", iTremble);
|
||||
SetAnimationPosition(tremble_anim, Anim_Const(iTremble));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
#strict 2
|
||||
|
||||
// Wrappers for conventient calls to PlayAnimation
|
||||
|
||||
global func Anim_Const(int Value)
|
||||
{
|
||||
return [C4AVP_Const, Value];
|
||||
}
|
||||
|
||||
global func Anim_Linear(int Position, int Begin, int End, int Length, int Ending)
|
||||
{
|
||||
return [C4AVP_Linear, Position, Begin, End, Length, Ending];
|
||||
}
|
||||
|
||||
global func Anim_X(int Position, int Begin, int End, int Length)
|
||||
{
|
||||
return [C4AVP_X, Position, Begin, End, Length];
|
||||
}
|
||||
|
||||
global func Anim_Y(int Position, int Begin, int End, int Length)
|
||||
{
|
||||
return [C4AVP_Y, Position, Begin, End, Length];
|
||||
}
|
||||
|
||||
global func Anim_AbsX(int Position, int Begin, int End, int Length)
|
||||
{
|
||||
return [C4AVP_AbsX, Position, Begin, End, Length];
|
||||
}
|
||||
|
||||
global func Anim_AbsY(int Position, int Begin, int End, int Length)
|
||||
{
|
||||
return [C4AVP_AbsY, Position, Begin, End, Length];
|
||||
}
|
||||
|
||||
global func Anim_XDir(int Begin, int End, int MaxXDir, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 10;
|
||||
return [C4AVP_XDir, Begin, End, MaxXDir, Prec];
|
||||
}
|
||||
|
||||
global func Anim_YDir(int Begin, int End, int MaxYDir, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 10;
|
||||
return [C4AVP_YDir, Begin, End, MaxYDir, Prec];
|
||||
}
|
||||
|
||||
global func Anim_RDir(int Begin, int End, int MaxRDir, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 10;
|
||||
return [C4AVP_RDir, Begin, End, MaxRDir, Prec];
|
||||
}
|
||||
|
||||
global func Anim_CosR(int Begin, int End, int Offset, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 1;
|
||||
return [C4AVP_CosR, Begin, End, Offset, Prec];
|
||||
}
|
||||
|
||||
global func Anim_SinR(int Begin, int End, int Offset, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 1;
|
||||
return [C4AVP_SinR, Begin, End, Offset, Prec];
|
||||
}
|
||||
|
||||
global func Anim_CosV(int Begin, int End, int Offset, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 1;
|
||||
return [C4AVP_CosV, Begin, End, Offset, Prec];
|
||||
}
|
||||
|
||||
global func Anim_SinV(int Begin, int End, int Offset, int Prec)
|
||||
{
|
||||
if(Prec == nil) Prec = 1;
|
||||
return [C4AVP_SinV, Begin, End, Offset, Prec];
|
||||
}
|
|
@ -45,6 +45,7 @@ void C4Action::Default()
|
|||
Facet.Default();
|
||||
FacetX=FacetY=0;
|
||||
t_attach=CNAT_None;
|
||||
Animation = NULL;
|
||||
}
|
||||
|
||||
void C4Action::CompileFunc(StdCompiler *pComp)
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <C4GameObjects.h>
|
||||
#include <C4RankSystem.h>
|
||||
#include <C4GraphicsResource.h>
|
||||
#include <C4MeshAnimation.h>
|
||||
|
||||
// Helper class to load additional ressources required for meshes from
|
||||
// a C4Group.
|
||||
|
@ -749,13 +750,14 @@ void C4GraphicsOverlay::UpdateFacet()
|
|||
}
|
||||
else
|
||||
{
|
||||
C4String* Animation = action->GetPropertyStr(P_Animation);
|
||||
C4String* AnimationName = action->GetPropertyStr(P_Animation);
|
||||
if(!AnimationName) return;
|
||||
|
||||
pMeshInstance = new StdMeshInstance(*pSourceGfx->Mesh);
|
||||
const StdMeshAnimation* Animation = pSourceGfx->Mesh->GetAnimationByName(AnimationName->GetData());
|
||||
if(!Animation) return;
|
||||
|
||||
pMeshInstance = new StdMeshInstance(*pSourceGfx->Mesh);
|
||||
pMeshInstance->PlayAnimation(Animation->GetData(), 1.0f);
|
||||
StdMeshInstance::AnimationRef ref(pMeshInstance, Animation->GetData());
|
||||
ref.SetPosition(iPhase * ref.GetAnimation().Length / action->GetPropertyInt(P_Length));
|
||||
pMeshInstance->PlayAnimation(*Animation, 0, NULL, new C4ValueProviderRef<int32_t>(iPhase, Animation->Length / action->GetPropertyInt(P_Length)), new C4ValueProviderConst(1.0f));
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -244,7 +244,7 @@ class C4GraphicsOverlay
|
|||
C4Object *GetOverlayObject() const { return pOverlayObj; }
|
||||
int32_t GetID() const { return iID; }
|
||||
void SetID(int32_t aID) { iID = aID; }
|
||||
void SetPhase(int32_t iToPhase); // TODO: This is not implemented(?) - Remember to set mesh animation position when it is
|
||||
void SetPhase(int32_t iToPhase);
|
||||
C4GraphicsOverlay *GetNext() const { return pNext; }
|
||||
void SetNext(C4GraphicsOverlay *paNext) { pNext = paNext; }
|
||||
bool IsPicture() { return eMode == MODE_Picture; }
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2010 Armin Burgmeier
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
||||
*
|
||||
* Portions might be copyrighted by other authors who have contributed
|
||||
* to OpenClonk.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
* See isc_license.txt for full license and disclaimer.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender.
|
||||
* See clonk_trademark_license.txt for full license.
|
||||
*/
|
||||
|
||||
#include "C4Include.h"
|
||||
#include "C4MeshAnimation.h"
|
||||
#include "C4Object.h"
|
||||
#include "C4ValueList.h"
|
||||
#include "C4Game.h"
|
||||
|
||||
StdMeshInstance::ValueProvider* CreateValueProviderFromArray(C4Object* pForObj, C4ValueArray& Data)
|
||||
{
|
||||
int32_t type = Data[0].getInt();
|
||||
switch(type)
|
||||
{
|
||||
case C4AVP_Const:
|
||||
return new C4ValueProviderConst(Data[1].getInt()/1000.0f);
|
||||
case C4AVP_Linear:
|
||||
return new C4ValueProviderLinear(Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, Data[3].getInt()/1000.0f, Data[4].getInt(), static_cast<C4AnimationEnding>(Data[5].getInt()));
|
||||
case C4AVP_X:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderX(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, Data[3].getInt()/1000.0f, Data[4].getInt());
|
||||
case C4AVP_Y:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderY(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, Data[3].getInt()/1000.0f, Data[4].getInt());
|
||||
case C4AVP_AbsX:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderAbsX(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, Data[3].getInt()/1000.0f, Data[4].getInt());
|
||||
case C4AVP_AbsY:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderAbsY(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, Data[3].getInt()/1000.0f, Data[4].getInt());
|
||||
case C4AVP_XDir:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderXDir(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_YDir:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderYDir(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_RDir:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderRDir(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_CosR:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderCosR(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_SinR:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderSinR(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_CosV:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderCosV(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_SinV:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderSinV(pForObj, Data[1].getInt()/1000.0f, Data[2].getInt()/1000.0f, itofix(Data[3].getInt(),Data[4].getInt()));
|
||||
case C4AVP_Action:
|
||||
if(!pForObj) return NULL;
|
||||
return new C4ValueProviderAction(pForObj);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
C4ValueProviderConst::C4ValueProviderConst(float value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
bool C4ValueProviderConst::Execute()
|
||||
{
|
||||
// Keep value we set in ctor
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderLinear::C4ValueProviderLinear(float pos, float begin, float end, unsigned int length, C4AnimationEnding ending):
|
||||
Begin(begin), End(end), Length(length), Ending(ending), LastTick(Game.FrameCounter)
|
||||
{
|
||||
Value = pos;
|
||||
}
|
||||
|
||||
bool C4ValueProviderLinear::Execute()
|
||||
{
|
||||
Value += (End - Begin) * (Game.FrameCounter - LastTick) / Length;
|
||||
LastTick = Game.FrameCounter;
|
||||
|
||||
assert( (End >= Begin && Value >= Begin) || (End <= Begin && Value <= Begin));
|
||||
while( (End > Begin && Value > End) || (End < Begin && Value < End))
|
||||
{
|
||||
switch(Ending)
|
||||
{
|
||||
case ANIM_Loop:
|
||||
Value -= (End - Begin);
|
||||
return true;
|
||||
case ANIM_Hold:
|
||||
Value = End;
|
||||
return true;
|
||||
case ANIM_Remove:
|
||||
Value = End;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderX::C4ValueProviderX(const C4Object* object, float pos, float begin, float end, unsigned int length):
|
||||
Object(object), Begin(begin), End(end), Length(length), LastX(fixtof(object->fix_x))
|
||||
{
|
||||
Value = pos;
|
||||
}
|
||||
|
||||
bool C4ValueProviderX::Execute()
|
||||
{
|
||||
const float obj_x = fixtof(Object->fix_x);
|
||||
Value += (End - Begin) * (obj_x - LastX) / Length; // TODO: Use xdir instead?
|
||||
LastX = obj_x;
|
||||
|
||||
if(End > Begin)
|
||||
{
|
||||
while(Value > End)
|
||||
Value -= (End - Begin);
|
||||
while(Value < Begin)
|
||||
Value += (End - Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(Value > Begin)
|
||||
Value -= (Begin - End);
|
||||
while(Value < End)
|
||||
Value += (Begin - End);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderY::C4ValueProviderY(const C4Object* object, float pos, float begin, float end, unsigned int length):
|
||||
Object(object), Begin(begin), End(end), Length(length), LastY(fixtof(object->fix_y))
|
||||
{
|
||||
Value = pos;
|
||||
}
|
||||
|
||||
bool C4ValueProviderY::Execute()
|
||||
{
|
||||
const float obj_y = fixtof(Object->fix_y);
|
||||
Value += (End - Begin) * (obj_y - LastY) / Length; // TODO: Use ydir instead?
|
||||
LastY = obj_y;
|
||||
|
||||
if(End > Begin)
|
||||
{
|
||||
while(Value > End)
|
||||
Value -= (End - Begin);
|
||||
while(Value < Begin)
|
||||
Value += (End - Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(Value > Begin)
|
||||
Value -= (Begin - End);
|
||||
while(Value < End)
|
||||
Value += (Begin - End);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderAbsX::C4ValueProviderAbsX(const C4Object* object, float pos, float begin, float end, unsigned int length):
|
||||
Object(object), Begin(begin), End(end), Length(length), LastX(fixtof(object->fix_x))
|
||||
{
|
||||
Value = pos;
|
||||
}
|
||||
|
||||
bool C4ValueProviderAbsX::Execute()
|
||||
{
|
||||
const float obj_x = fixtof(Object->fix_x);
|
||||
Value += (End - Begin) * fabs(obj_x - LastX) / Length;
|
||||
LastX = obj_x;
|
||||
|
||||
assert( (End >= Begin && Value >= Begin) || (End <= Begin && Value <= Begin));
|
||||
while( (End > Begin && Value > End) || (End < Begin && Value < End))
|
||||
Value -= (End - Begin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderAbsY::C4ValueProviderAbsY(const C4Object* object, float pos, float begin, float end, unsigned int length):
|
||||
Object(object), Begin(begin), End(end), Length(length), LastY(fixtof(object->fix_y))
|
||||
{
|
||||
Value = pos;
|
||||
}
|
||||
|
||||
bool C4ValueProviderAbsY::Execute()
|
||||
{
|
||||
const float obj_y = fixtof(Object->fix_y);
|
||||
Value += (End - Begin) * fabs(obj_y - LastY) / Length;
|
||||
LastY = obj_y;
|
||||
|
||||
assert( (End >= Begin && Value >= Begin) || (End <= Begin && Value <= Begin));
|
||||
while( (End > Begin && Value > End) || (End < Begin && Value < End))
|
||||
Value -= (End - Begin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderXDir::C4ValueProviderXDir(const C4Object* object, float begin, float end, FIXED max_xdir):
|
||||
Object(object), Begin(begin), End(end), MaxXDir(max_xdir)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
|
||||
bool C4ValueProviderXDir::Execute()
|
||||
{
|
||||
Value = Begin + (End - Begin) * Min<float>(fabs(fixtof(Object->xdir/MaxXDir)), 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderYDir::C4ValueProviderYDir(const C4Object* object, float begin, float end, FIXED max_ydir):
|
||||
Object(object), Begin(begin), End(end), MaxYDir(max_ydir)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
bool C4ValueProviderYDir::Execute()
|
||||
{
|
||||
Value = Begin + (End - Begin) * Min<float>(fabs(fixtof(Object->ydir/MaxYDir)), 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderRDir::C4ValueProviderRDir(const C4Object* object, float begin, float end, FIXED max_rdir):
|
||||
Object(object), Begin(begin), End(end), MaxRDir(max_rdir)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
bool C4ValueProviderRDir::Execute()
|
||||
{
|
||||
Value = Begin + (End - Begin) * Min<float>(fabs(fixtof(Object->rdir/MaxRDir)), 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderCosR::C4ValueProviderCosR(const C4Object* object, float begin, float end, FIXED offset):
|
||||
Object(object), Begin(begin), End(end), Offset(offset)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
bool C4ValueProviderCosR::Execute()
|
||||
{
|
||||
Value = Begin + (End - Begin) * cos(fixtof(Object->fix_r + Offset)); // TODO: Use fixed-point math if necessary
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderSinR::C4ValueProviderSinR(const C4Object* object, float begin, float end, FIXED offset):
|
||||
Object(object), Begin(begin), End(end), Offset(offset)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
bool C4ValueProviderSinR::Execute()
|
||||
{
|
||||
Value = Begin + (End - Begin) * sin(fixtof(Object->fix_r + Offset)); // TODO: Use fixed-point math if necessary
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderCosV::C4ValueProviderCosV(const C4Object* object, float begin, float end, FIXED offset):
|
||||
Object(object), Begin(begin), End(end), Offset(offset)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
bool C4ValueProviderCosV::Execute()
|
||||
{
|
||||
// TODO: Maybe we can optimize this by using cos(r) = x/sqrt(x*x+y*y), sin(r)=y/sqrt(x*x+y*y)
|
||||
// plus addition theorems for sin or cos.
|
||||
|
||||
int angle = Angle(0, 0, fixtoi(Object->xdir, 256), fixtoi(Object->ydir, 256));
|
||||
Value = Begin + (End - Begin) * cos(angle + fixtof(Offset)); // TODO: Use fixed-point math if necessary
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderSinV::C4ValueProviderSinV(const C4Object* object, float begin, float end, FIXED offset):
|
||||
Object(object), Begin(begin), End(end), Offset(offset)
|
||||
{
|
||||
Execute();
|
||||
}
|
||||
|
||||
bool C4ValueProviderSinV::Execute()
|
||||
{
|
||||
// TODO: Maybe we can optimize this by using cos(r) = x/sqrt(x*x+y*y), sin(r)=y/sqrt(x*x+y*y),
|
||||
// plus addition theorems for sin or cos.
|
||||
|
||||
int angle = Angle(0, 0, fixtoi(Object->xdir, 256), fixtoi(Object->ydir, 256));
|
||||
Value = Begin + (End - Begin) * sin(angle + fixtof(Offset)); // TODO: Use fixed-point math if necessary
|
||||
return true;
|
||||
}
|
||||
|
||||
C4ValueProviderAction::C4ValueProviderAction(const C4Object* object):
|
||||
Action(object->Action)
|
||||
{
|
||||
}
|
||||
|
||||
bool C4ValueProviderAction::Execute()
|
||||
{
|
||||
// TODO: We could cache these...
|
||||
const StdMeshAnimation* animation = Action.Animation->GetAnimation();
|
||||
const int32_t length = Action.pActionDef->GetPropertyInt(P_Length);
|
||||
const int32_t delay = Action.pActionDef->GetPropertyInt(P_Delay);
|
||||
|
||||
if(delay)
|
||||
Value = static_cast<float>(Action.Phase * delay + Action.PhaseDelay) / (delay * length) * animation->Length;
|
||||
else
|
||||
Value = static_cast<float>(Action.Phase) / length * animation->Length;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2010 Armin Burgmeier
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
||||
*
|
||||
* Portions might be copyrighted by other authors who have contributed
|
||||
* to OpenClonk.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
* See isc_license.txt for full license and disclaimer.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender.
|
||||
* See clonk_trademark_license.txt for full license.
|
||||
*/
|
||||
|
||||
/* Value providers for mesh animation, cf. StdMeshInstance */
|
||||
|
||||
#ifndef INC_C4MeshAnimation
|
||||
#define INC_C4MeshAnimation
|
||||
|
||||
#include "StdMesh.h"
|
||||
|
||||
class C4Action;
|
||||
class C4Object;
|
||||
class C4ValueArray;
|
||||
|
||||
enum C4AnimationValueProviderID
|
||||
{
|
||||
C4AVP_Const,
|
||||
C4AVP_Linear,
|
||||
C4AVP_X,
|
||||
C4AVP_Y,
|
||||
C4AVP_AbsX,
|
||||
C4AVP_AbsY,
|
||||
C4AVP_XDir,
|
||||
C4AVP_YDir,
|
||||
C4AVP_RDir,
|
||||
C4AVP_CosR,
|
||||
C4AVP_SinR,
|
||||
C4AVP_CosV,
|
||||
C4AVP_SinV,
|
||||
C4AVP_Action
|
||||
};
|
||||
|
||||
enum C4AnimationEnding
|
||||
{
|
||||
ANIM_Loop,
|
||||
ANIM_Hold,
|
||||
ANIM_Remove
|
||||
};
|
||||
|
||||
// Keep a constant value
|
||||
class C4ValueProviderConst: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderConst(float value);
|
||||
virtual bool Execute();
|
||||
};
|
||||
|
||||
// Interpolate linearly in time between two values
|
||||
class C4ValueProviderLinear: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderLinear(float pos, float begin, float end, unsigned int length, C4AnimationEnding ending);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const float Begin;
|
||||
const float End;
|
||||
const unsigned int Length;
|
||||
const C4AnimationEnding Ending;
|
||||
|
||||
int32_t LastTick;
|
||||
};
|
||||
|
||||
class C4ValueProviderX: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderX(const C4Object* object, float pos, float begin, float end, unsigned int length);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const unsigned int Length;
|
||||
|
||||
float LastX;
|
||||
};
|
||||
|
||||
class C4ValueProviderY: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderY(const C4Object* object, float pos, float begin, float end, unsigned int length);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const unsigned int Length;
|
||||
|
||||
float LastY;
|
||||
};
|
||||
|
||||
class C4ValueProviderAbsX: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderAbsX(const C4Object* object, float pos, float begin, float end, unsigned int length);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const unsigned int Length;
|
||||
|
||||
float LastX;
|
||||
};
|
||||
|
||||
class C4ValueProviderAbsY: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderAbsY(const C4Object* object, float pos, float begin, float end, unsigned int length);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const unsigned int Length;
|
||||
|
||||
float LastY;
|
||||
};
|
||||
|
||||
class C4ValueProviderXDir: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderXDir(const C4Object* object, float begin, float end, FIXED max_xdir);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED MaxXDir;
|
||||
};
|
||||
|
||||
class C4ValueProviderYDir: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderYDir(const C4Object* object, float begin, float end, FIXED max_ydir);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED MaxYDir;
|
||||
};
|
||||
|
||||
class C4ValueProviderRDir: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderRDir(const C4Object* object, float begin, float end, FIXED max_rdir);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED MaxRDir;
|
||||
};
|
||||
|
||||
class C4ValueProviderCosR: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderCosR(const C4Object* object, float begin, float end, FIXED offset);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED Offset;
|
||||
};
|
||||
|
||||
class C4ValueProviderSinR: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderSinR(const C4Object* object, float begin, float end, FIXED offset);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED Offset;
|
||||
};
|
||||
|
||||
class C4ValueProviderCosV: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderCosV(const C4Object* object, float begin, float end, FIXED offset);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED Offset;
|
||||
};
|
||||
|
||||
class C4ValueProviderSinV: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderSinV(const C4Object* object, float begin, float end, FIXED offset);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Object* const Object;
|
||||
const float Begin;
|
||||
const float End;
|
||||
const FIXED Offset;
|
||||
};
|
||||
|
||||
class C4ValueProviderAction: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderAction(const C4Object* object);
|
||||
virtual bool Execute();
|
||||
|
||||
private:
|
||||
const C4Action& Action;
|
||||
};
|
||||
|
||||
// Reference another value (which is convertible to float), and optionally scale it
|
||||
template<typename SourceT>
|
||||
class C4ValueProviderRef: public StdMeshInstance::ValueProvider
|
||||
{
|
||||
public:
|
||||
C4ValueProviderRef(const SourceT& ref, float scale):
|
||||
Ref(ref), Scale(scale) {}
|
||||
|
||||
virtual bool Execute()
|
||||
{
|
||||
Value = static_cast<float>(Ref) * Scale;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SourceT& Ref;
|
||||
float Scale;
|
||||
};
|
||||
|
||||
StdMeshInstance::ValueProvider* CreateValueProviderFromArray(C4Object* pForObj, C4ValueArray& Data);
|
||||
|
||||
#endif
|
|
@ -51,6 +51,7 @@
|
|||
#include <C4PlayerList.h>
|
||||
#include <C4GameObjects.h>
|
||||
#include <C4Record.h>
|
||||
#include <C4MeshAnimation.h>
|
||||
|
||||
void DrawVertex(C4Facet &cgo, int32_t tx, int32_t ty, int32_t col, int32_t contact)
|
||||
{
|
||||
|
@ -1098,6 +1099,8 @@ void C4Object::Execute()
|
|||
ExecLife();
|
||||
// Base
|
||||
ExecBase();
|
||||
// Animation
|
||||
if(pMeshInstance) pMeshInstance->ExecuteAnimation();
|
||||
// Timer
|
||||
Timer++;
|
||||
if (Timer>=Def->Timer)
|
||||
|
@ -2192,29 +2195,9 @@ C4Value C4Object::Call(const char *szFunctionCall, C4AulParSet *pPars, bool fPas
|
|||
bool C4Object::SetPhase(int32_t iPhase)
|
||||
{
|
||||
if (!Action.pActionDef) return false;
|
||||
|
||||
const int32_t length = Action.pActionDef->GetPropertyInt(P_Length);
|
||||
const int32_t delay = Action.pActionDef->GetPropertyInt(P_Delay);
|
||||
|
||||
Action.Phase=BoundBy<int32_t>(iPhase,0,length);
|
||||
Action.PhaseDelay = 0;
|
||||
|
||||
if(pMeshInstance)
|
||||
{
|
||||
C4String* AnimationName = Action.pActionDef->GetPropertyStr(P_Animation);
|
||||
if(AnimationName)
|
||||
{
|
||||
StdMeshInstance::AnimationRef ref(pMeshInstance, AnimationName->GetData());
|
||||
if(ref)
|
||||
{
|
||||
if(delay)
|
||||
ref.SetPosition(static_cast<float>(Action.Phase * delay + Action.PhaseDelay) / (delay * length) * ref.GetAnimation().Length);
|
||||
else
|
||||
ref.SetPosition(static_cast<float>(Action.Phase) / length * ref.GetAnimation().Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2733,9 +2716,10 @@ void C4Object::CompileFunc(StdCompiler *pComp)
|
|||
pComp->Value(TemporaryPhysical);
|
||||
}
|
||||
|
||||
// TODO: Animations / attached meshes
|
||||
|
||||
// Commands
|
||||
if(pComp->FollowName("Commands"))
|
||||
// Commands
|
||||
if(pComp->FollowName("Commands"))
|
||||
if(fCompiler)
|
||||
{
|
||||
C4Command *pCmd = NULL;
|
||||
|
@ -2761,41 +2745,45 @@ void C4Object::CompileFunc(StdCompiler *pComp)
|
|||
}
|
||||
}
|
||||
|
||||
// Compiling? Do initialization.
|
||||
if(fCompiler)
|
||||
{
|
||||
// add to def count
|
||||
Def->Count++;
|
||||
// Compiling? Do initialization.
|
||||
if(fCompiler)
|
||||
{
|
||||
// add to def count
|
||||
Def->Count++;
|
||||
|
||||
// set local variable names
|
||||
LocalNamed.SetNameList(&Def->Script.LocalNamed);
|
||||
// set local variable names
|
||||
LocalNamed.SetNameList(&Def->Script.LocalNamed);
|
||||
|
||||
// Set action (override running data)
|
||||
int32_t iTime=Action.Time;
|
||||
int32_t iPhase=Action.Phase;
|
||||
int32_t iPhaseDelay=Action.PhaseDelay;
|
||||
/* FIXME if (SetActionByName(Action.pActionDef->GetName(),0,0,false))
|
||||
{
|
||||
Action.Time=iTime;
|
||||
Action.Phase=iPhase; // No checking for valid phase
|
||||
Action.PhaseDelay=iPhaseDelay;
|
||||
}*/
|
||||
// Set action (override running data)
|
||||
int32_t iTime=Action.Time;
|
||||
int32_t iPhase=Action.Phase;
|
||||
int32_t iPhaseDelay=Action.PhaseDelay;
|
||||
/* FIXME if (SetActionByName(Action.pActionDef->GetName(),0,0,false))
|
||||
{
|
||||
Action.Time=iTime;
|
||||
Action.Phase=iPhase; // No checking for valid phase
|
||||
Action.PhaseDelay=iPhaseDelay;
|
||||
}*/
|
||||
|
||||
// if on fire but no effect is present (old-style savegames), re-incinerate
|
||||
int32_t iFireNumber;
|
||||
C4Value Par1, Par2, Par3, Par4;
|
||||
if (OnFire && !pEffects) new C4Effect(this, C4Fx_Fire, C4Fx_FirePriority, C4Fx_FireTimer, NULL, 0, Par1, Par2, Par3, Par4, false, iFireNumber);
|
||||
// Set Action animation by slot 0
|
||||
if(pMeshInstance)
|
||||
Action.Animation = pMeshInstance->GetRootAnimationForSlot(0);
|
||||
|
||||
// blit mode not assigned? use definition default then
|
||||
if (!BlitMode) BlitMode = Def->BlitMode;
|
||||
// if on fire but no effect is present (old-style savegames), re-incinerate
|
||||
int32_t iFireNumber;
|
||||
C4Value Par1, Par2, Par3, Par4;
|
||||
if (OnFire && !pEffects) new C4Effect(this, C4Fx_Fire, C4Fx_FirePriority, C4Fx_FireTimer, NULL, 0, Par1, Par2, Par3, Par4, false, iFireNumber);
|
||||
|
||||
// object needs to be resorted? May happen if there's unsorted objects in savegame
|
||||
if (Unsorted) Game.fResortAnyObject = true;
|
||||
// blit mode not assigned? use definition default then
|
||||
if (!BlitMode) BlitMode = Def->BlitMode;
|
||||
|
||||
// object needs to be resorted? May happen if there's unsorted objects in savegame
|
||||
if (Unsorted) Game.fResortAnyObject = true;
|
||||
|
||||
// initial OCF update
|
||||
SetOCF();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -3276,22 +3264,14 @@ bool C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2
|
|||
// such an animation.
|
||||
if(pMeshInstance)
|
||||
{
|
||||
C4String* Animation = Act ? Act->GetPropertyStr(P_Animation) : NULL;
|
||||
C4String* OldAnimation = LastAction ? LastAction->GetPropertyStr(P_Animation) : NULL;
|
||||
if(OldAnimation)
|
||||
pMeshInstance->StopAnimation(OldAnimation->GetData());
|
||||
if(Action.Animation) pMeshInstance->StopAnimation(Action.Animation);
|
||||
Action.Animation = NULL;
|
||||
|
||||
C4String* Animation = Act ? Act->GetPropertyStr(P_Animation) : NULL;
|
||||
if(Animation)
|
||||
{
|
||||
// overwrite existing animation, if any (maybe launched by script)
|
||||
StdMeshInstance::AnimationRef ref(pMeshInstance, Animation->GetData());
|
||||
if(ref)
|
||||
{
|
||||
ref.SetPosition(0.0f);
|
||||
ref.SetWeight(1.0f);
|
||||
}
|
||||
else
|
||||
pMeshInstance->PlayAnimation(Animation->GetData(), 1.0f);
|
||||
// note that weight is ignored
|
||||
Action.Animation = pMeshInstance->PlayAnimation(Animation->GetData(), 0, NULL, new C4ValueProviderAction(this), new C4ValueProviderConst(1.0f));
|
||||
}
|
||||
}
|
||||
// Stop previous act sound
|
||||
|
@ -4577,7 +4557,6 @@ void C4Object::ExecAction()
|
|||
if (pAction->GetPropertyInt(P_Delay))
|
||||
{
|
||||
Action.PhaseDelay+=iPhaseAdvance;
|
||||
bool set_new_action = false;
|
||||
if (Action.PhaseDelay >= pAction->GetPropertyInt(P_Delay))
|
||||
{
|
||||
// Advance Phase
|
||||
|
@ -4605,12 +4584,11 @@ void C4Object::ExecAction()
|
|||
{
|
||||
// Set new action
|
||||
SetActionByName(next_action, NULL, NULL, SAC_StartCall | SAC_EndCall);
|
||||
set_new_action = true;
|
||||
SetActionByName(next_action, NULL, NULL, SAC_StartCall | SAC_EndCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Update animation on mesh instance. If a new action was set,
|
||||
// then this will already have happened for the new action.
|
||||
if(pMeshInstance && !set_new_action)
|
||||
|
@ -4627,6 +4605,7 @@ void C4Object::ExecAction()
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -100,6 +100,7 @@ class C4Action
|
|||
C4Object *Target,*Target2;
|
||||
C4Facet Facet; // NoSave //
|
||||
int32_t FacetX,FacetY; // NoSave //
|
||||
StdMeshInstance::AnimationNode* Animation; // NoSave //
|
||||
public:
|
||||
void Default();
|
||||
void CompileFunc(StdCompiler *pComp);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include <C4Game.h>
|
||||
#include <C4GameObjects.h>
|
||||
#include <C4GameControl.h>
|
||||
#include <C4MeshAnimation.h>
|
||||
|
||||
//========================== Some Support Functions =======================================
|
||||
|
||||
|
@ -5545,42 +5546,108 @@ static C4Void FnDoNoCollectDelay(C4AulObjectContext *ctx, int change)
|
|||
return C4VNull;
|
||||
}
|
||||
|
||||
static bool FnAnimationPlay(C4AulContext *ctx, C4String *szAnimation, Nillable<long> weight)
|
||||
static Nillable<int> FnPlayAnimation(C4AulObjectContext *ctx, C4String *szAnimation, int iSlot, C4ValueArray* PositionProvider, C4ValueArray* WeightProvider, Nillable<int> iSibling)
|
||||
{
|
||||
if(!ctx->Obj) return C4VNull;
|
||||
if(!ctx->Obj->pMeshInstance) return C4VNull;
|
||||
if(iSlot == 0) return C4VNull; // Reserved for ActMap animations
|
||||
if(!PositionProvider) return C4VNull;
|
||||
if(!WeightProvider) return C4VNull;
|
||||
|
||||
StdMeshInstance::AnimationNode* s_node = NULL;
|
||||
if(!iSibling.IsNil())
|
||||
{
|
||||
if(!ctx->Obj) return false;
|
||||
if(!ctx->Obj->pMeshInstance) return false;
|
||||
|
||||
float w = 1.0f;
|
||||
if(!weight.IsNil()) w = weight / 1000.0f;
|
||||
|
||||
return ctx->Obj->pMeshInstance->PlayAnimation(szAnimation->GetData(), w);
|
||||
s_node = ctx->Obj->pMeshInstance->GetAnimationNodeByNumber(iSibling);
|
||||
if(!s_node || s_node->GetSlot() != iSlot) return C4VNull;
|
||||
}
|
||||
|
||||
static bool FnAnimationStop(C4AulContext *ctx, C4String *szAnimation)
|
||||
StdMeshInstance::ValueProvider* p_provider = CreateValueProviderFromArray(ctx->Obj, *PositionProvider);
|
||||
StdMeshInstance::ValueProvider* w_provider = CreateValueProviderFromArray(ctx->Obj, *WeightProvider);
|
||||
if(!p_provider || !w_provider)
|
||||
{
|
||||
if(!ctx->Obj) return false;
|
||||
if(!ctx->Obj->pMeshInstance) return false;
|
||||
return ctx->Obj->pMeshInstance->StopAnimation(szAnimation->GetData());
|
||||
delete p_provider;
|
||||
delete w_provider;
|
||||
return C4VNull;
|
||||
}
|
||||
|
||||
static bool FnAnimationSetState(C4AulContext *ctx, C4String *szAnimation, Nillable<long> position, Nillable<long> weight)
|
||||
{
|
||||
StdMeshInstance::AnimationNode* n_node = ctx->Obj->pMeshInstance->PlayAnimation(szAnimation->GetData(), iSlot, s_node, p_provider, w_provider);
|
||||
if(!n_node) return C4VNull;
|
||||
|
||||
return n_node->GetNumber();
|
||||
}
|
||||
|
||||
static bool FnStopAnimation(C4AulObjectContext *ctx, int iAnimationNumber)
|
||||
{
|
||||
if(!ctx->Obj) return false;
|
||||
if(!ctx->Obj->pMeshInstance) return false;
|
||||
StdMeshInstance::AnimationRef ref(ctx->Obj->pMeshInstance, szAnimation->GetData());
|
||||
if(!ref) return false;
|
||||
|
||||
if(!position.IsNil())
|
||||
{
|
||||
float pos = position / 1000.0f;
|
||||
if(pos > ref.GetAnimation().Length) return false;
|
||||
ref.SetPosition(pos);
|
||||
}
|
||||
|
||||
if(!weight.IsNil())
|
||||
ref.SetWeight(weight / 1000.0f);
|
||||
StdMeshInstance::AnimationNode* node = ctx->Obj->pMeshInstance->GetAnimationNodeByNumber(iAnimationNumber);
|
||||
// slot 0 is reserved for ActMap animations
|
||||
if(!node || node->GetSlot() == 0) return false;
|
||||
ctx->Obj->pMeshInstance->StopAnimation(node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static Nillable<int> FnGetRootAnimation(C4AulObjectContext *ctx, int iSlot)
|
||||
{
|
||||
if(!ctx->Obj) return C4VNull;
|
||||
if(!ctx->Obj->pMeshInstance) return C4VNull;
|
||||
StdMeshInstance::AnimationNode* node = ctx->Obj->pMeshInstance->GetRootAnimationForSlot(iSlot);
|
||||
if(!node) return C4VNull;
|
||||
return node->GetNumber();
|
||||
}
|
||||
|
||||
static Nillable<int> FnGetAnimationLength(C4AulObjectContext *ctx, C4String *szAnimation)
|
||||
{
|
||||
if(!ctx->Obj) return C4VNull;
|
||||
if(!ctx->Obj->pMeshInstance) return C4VNull;
|
||||
const StdMeshAnimation* animation = ctx->Obj->pMeshInstance->Mesh.GetAnimationByName(szAnimation->GetData());
|
||||
if(!animation) return C4VNull;
|
||||
return static_cast<int>(animation->Length * 1000.0f); // TODO: sync critical?
|
||||
}
|
||||
|
||||
static Nillable<int> FnGetAnimationPosition(C4AulObjectContext *ctx, int iAnimationNumber)
|
||||
{
|
||||
if(!ctx->Obj) return C4VNull;
|
||||
if(!ctx->Obj->pMeshInstance) return C4VNull;
|
||||
StdMeshInstance::AnimationNode* node = ctx->Obj->pMeshInstance->GetAnimationNodeByNumber(iAnimationNumber);
|
||||
if(!node || node->GetType() != StdMeshInstance::AnimationNode::LeafNode) return C4VNull;
|
||||
return static_cast<int>(node->GetPosition() * 1000.0f); // TODO: sync critical?
|
||||
}
|
||||
|
||||
static Nillable<int> FnGetAnimationWeight(C4AulObjectContext *ctx, int iAnimationNumber)
|
||||
{
|
||||
if(!ctx->Obj) return C4VNull;
|
||||
if(!ctx->Obj->pMeshInstance) return C4VNull;
|
||||
StdMeshInstance::AnimationNode* node = ctx->Obj->pMeshInstance->GetAnimationNodeByNumber(iAnimationNumber);
|
||||
if(!node || node->GetType() != StdMeshInstance::AnimationNode::LinearInterpolationNode) return C4VNull;
|
||||
return static_cast<int>(node->GetWeight() * 1000.0f); // TODO: sync critical?
|
||||
}
|
||||
|
||||
static bool FnSetAnimationPosition(C4AulObjectContext *ctx, int iAnimationNumber, C4ValueArray* PositionProvider)
|
||||
{
|
||||
if(!ctx->Obj) return false;
|
||||
if(!ctx->Obj->pMeshInstance) return false;
|
||||
StdMeshInstance::AnimationNode* node = ctx->Obj->pMeshInstance->GetAnimationNodeByNumber(iAnimationNumber);
|
||||
// slot 0 is reserved for ActMap animations
|
||||
if(!node || node->GetSlot() == 0 || node->GetType() != StdMeshInstance::AnimationNode::LeafNode) return false;
|
||||
StdMeshInstance::ValueProvider* p_provider = CreateValueProviderFromArray(ctx->Obj, *PositionProvider);
|
||||
if(!p_provider) return false;
|
||||
ctx->Obj->pMeshInstance->SetAnimationPosition(node, p_provider);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FnSetAnimationWeight(C4AulObjectContext *ctx, int iAnimationNumber, C4ValueArray* WeightProvider)
|
||||
{
|
||||
if(!ctx->Obj) return false;
|
||||
if(!ctx->Obj->pMeshInstance) return false;
|
||||
StdMeshInstance::AnimationNode* node = ctx->Obj->pMeshInstance->GetAnimationNodeByNumber(iAnimationNumber);
|
||||
// slot 0 is reserved for ActMap animations
|
||||
if(!node || node->GetSlot() == 0 || node->GetType() != StdMeshInstance::AnimationNode::LinearInterpolationNode) return false;
|
||||
StdMeshInstance::ValueProvider* w_provider = CreateValueProviderFromArray(ctx->Obj, *WeightProvider);
|
||||
if(!w_provider) return false;
|
||||
ctx->Obj->pMeshInstance->SetAnimationWeight(node, w_provider);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Nillable<long> FnAttachMesh(C4AulContext *ctx, C4ID idMesh, C4String* szParentBone, C4String* szChildBone, Nillable<long> scale)
|
||||
{
|
||||
|
@ -6074,9 +6141,14 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
|
|||
AddFunc(pEngine, "GetPlayerControlEnabled", FnGetPlayerControlEnabled);
|
||||
//FIXME new C4AulDefCastFunc(pEngine, "ScoreboardCol", C4V_C4ID, C4V_Int);
|
||||
|
||||
AddFunc(pEngine, "AnimationPlay", FnAnimationPlay);
|
||||
AddFunc(pEngine, "AnimationStop", FnAnimationStop);
|
||||
AddFunc(pEngine, "AnimationSetState", FnAnimationSetState);
|
||||
AddFunc(pEngine, "PlayAnimation", FnPlayAnimation);
|
||||
AddFunc(pEngine, "StopAnimation", FnStopAnimation);
|
||||
AddFunc(pEngine, "GetRootAnimation", FnGetRootAnimation);
|
||||
AddFunc(pEngine, "GetAnimationLength", FnGetAnimationLength);
|
||||
AddFunc(pEngine, "GetAnimationPosition", FnGetAnimationPosition);
|
||||
AddFunc(pEngine, "GetAnimationWeight", FnGetAnimationWeight);
|
||||
AddFunc(pEngine, "SetAnimationPosition", FnSetAnimationPosition);
|
||||
AddFunc(pEngine, "SetAnimationWeight", FnSetAnimationWeight);
|
||||
AddFunc(pEngine, "AttachMesh", FnAttachMesh);
|
||||
AddFunc(pEngine, "DetachMesh", FnDetachMesh);
|
||||
|
||||
|
@ -6425,6 +6497,25 @@ C4ScriptConstDef C4ScriptConstMap[]={
|
|||
{ "CSPF_NoEliminationCheck" ,C4V_Int, CSPF_NoEliminationCheck },
|
||||
{ "CSPF_Invisible" ,C4V_Int, CSPF_Invisible },
|
||||
|
||||
{ "C4AVP_Const" ,C4V_Int, C4AVP_Const },
|
||||
{ "C4AVP_Linear" ,C4V_Int, C4AVP_Linear },
|
||||
{ "C4AVP_X" ,C4V_Int, C4AVP_X },
|
||||
{ "C4AVP_Y" ,C4V_Int, C4AVP_Y },
|
||||
{ "C4AVP_AbsX" ,C4V_Int, C4AVP_AbsX },
|
||||
{ "C4AVP_AbsY" ,C4V_Int, C4AVP_AbsY },
|
||||
{ "C4AVP_XDir" ,C4V_Int, C4AVP_XDir },
|
||||
{ "C4AVP_YDir" ,C4V_Int, C4AVP_YDir },
|
||||
{ "C4AVP_RDir" ,C4V_Int, C4AVP_RDir },
|
||||
{ "C4AVP_CosR" ,C4V_Int, C4AVP_CosR },
|
||||
{ "C4AVP_SinR" ,C4V_Int, C4AVP_SinR },
|
||||
{ "C4AVP_CosV" ,C4V_Int, C4AVP_CosV },
|
||||
{ "C4AVP_SinV" ,C4V_Int, C4AVP_SinV },
|
||||
{ "C4AVP_Action" ,C4V_Int, C4AVP_Action },
|
||||
|
||||
{ "ANIM_Loop" ,C4V_Int, ANIM_Loop },
|
||||
{ "ANIM_Hold" ,C4V_Int, ANIM_Hold },
|
||||
{ "ANIM_Remove" ,C4V_Int, ANIM_Remove },
|
||||
|
||||
{ NULL, C4V_Any, 0} };
|
||||
|
||||
#define MkFnC4V (C4Value (*)(C4AulContext *cthr, C4Value*, C4Value*, C4Value*, C4Value*, C4Value*,\
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2009 Armin Burgmeier
|
||||
* Copyright (c) 2009-2010 Armin Burgmeier
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
||||
*
|
||||
* Portions might be copyrighted by other authors who have contributed
|
||||
|
@ -64,6 +64,18 @@ namespace
|
|||
}
|
||||
};
|
||||
|
||||
// Reset all animation list entries corresponding to node or its children
|
||||
void ClearAnimationListRecursively(std::vector<StdMeshInstance::AnimationNode*>& list, StdMeshInstance::AnimationNode* node)
|
||||
{
|
||||
list[node->GetNumber()] = NULL;
|
||||
|
||||
if(node->GetType() == StdMeshInstance::AnimationNode::LinearInterpolationNode)
|
||||
{
|
||||
ClearAnimationListRecursively(list, node->GetLeftChild());
|
||||
ClearAnimationListRecursively(list, node->GetRightChild());
|
||||
}
|
||||
}
|
||||
|
||||
// Generate matrix to convert the mesh from Ogre coordinate system to Clonk coordinate system.
|
||||
StdMeshMatrix CoordCorrection = StdMeshMatrix::Scale(-1.0f, 1.0f, 1.0f) * StdMeshMatrix::Rotate(M_PI/2.0f, 1.0f, 0.0f, 0.0f) * StdMeshMatrix::Rotate(M_PI/2.0f, 0.0f, 0.0f, 1.0f);
|
||||
StdMeshMatrix CoordCorrectionInverse = StdMeshMatrix::Inverse(CoordCorrection);
|
||||
|
@ -246,6 +258,18 @@ void StdMeshQuaternion::Normalize()
|
|||
z /= length;
|
||||
}
|
||||
|
||||
StdMeshQuaternion StdMeshQuaternion::Nlerp(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs, float w)
|
||||
{
|
||||
StdMeshQuaternion q;
|
||||
float c = lhs.w * rhs.w + lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
|
||||
if(c < 0.0f)
|
||||
q = lhs + w * (-rhs - lhs);
|
||||
else
|
||||
q = lhs + w * ( rhs - lhs);
|
||||
q.Normalize();
|
||||
return q;
|
||||
}
|
||||
|
||||
StdMeshTransformation StdMeshTransformation::Zero()
|
||||
{
|
||||
StdMeshTransformation t;
|
||||
|
@ -267,11 +291,11 @@ StdMeshTransformation StdMeshTransformation::Identity()
|
|||
|
||||
StdMeshTransformation StdMeshTransformation::Inverse(const StdMeshTransformation& transform)
|
||||
{
|
||||
StdMeshTransformation inv;
|
||||
inv.scale = 1.0f/transform.scale;
|
||||
inv.rotate = -transform.rotate;
|
||||
inv.translate = inv.rotate * (inv.scale * -transform.translate);
|
||||
return inv;
|
||||
StdMeshTransformation t;
|
||||
t.scale = 1.0f/transform.scale;
|
||||
t.rotate = -transform.rotate;
|
||||
t.translate = t.rotate * (t.scale * -transform.translate);
|
||||
return t;
|
||||
}
|
||||
|
||||
StdMeshTransformation StdMeshTransformation::Translate(float dx, float dy, float dz)
|
||||
|
@ -303,6 +327,15 @@ StdMeshTransformation StdMeshTransformation::Rotate(float angle, float rx, float
|
|||
return t;
|
||||
}
|
||||
|
||||
StdMeshTransformation StdMeshTransformation::Nlerp(const StdMeshTransformation& lhs, const StdMeshTransformation& rhs, float w)
|
||||
{
|
||||
StdMeshTransformation t;
|
||||
t.translate = (1 - w) * lhs.translate + w * rhs.translate;
|
||||
t.rotate = StdMeshQuaternion::Nlerp(lhs.rotate, rhs.rotate, w);
|
||||
t.scale = (1 - w) * lhs.scale + w * rhs.scale;
|
||||
return t;
|
||||
}
|
||||
|
||||
StdMeshMatrix StdMeshMatrix::Zero()
|
||||
{
|
||||
StdMeshMatrix m;
|
||||
|
@ -591,6 +624,16 @@ StdMeshQuaternion operator+(const StdMeshQuaternion& lhs, const StdMeshQuaternio
|
|||
return q;
|
||||
}
|
||||
|
||||
StdMeshQuaternion operator-(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs)
|
||||
{
|
||||
StdMeshQuaternion q;
|
||||
q.w = lhs.w - rhs.w;
|
||||
q.x = lhs.x - rhs.x;
|
||||
q.y = lhs.y - rhs.y;
|
||||
q.z = lhs.z - rhs.z;
|
||||
return q;
|
||||
}
|
||||
|
||||
StdMeshTransformation operator*(const StdMeshTransformation& lhs, const StdMeshTransformation& rhs)
|
||||
{
|
||||
StdMeshTransformation t;
|
||||
|
@ -754,6 +797,12 @@ StdMeshTransformation StdMeshTrack::GetTransformAt(float time) const
|
|||
// a) time > animation length
|
||||
// b) The track does not include a frame for the very end of the animation
|
||||
// Both is considered an error
|
||||
if(iter == Frames.end())
|
||||
{
|
||||
std::map<float, StdMeshKeyFrame>::const_iterator citer = iter; --citer;
|
||||
printf("Time: %f (%f-%f)\n", time, Frames.begin()->first, citer->first);
|
||||
if(time<=0.0) iter = Frames.begin();
|
||||
}
|
||||
assert(iter != Frames.end());
|
||||
|
||||
if(iter == Frames.begin())
|
||||
|
@ -774,6 +823,7 @@ StdMeshTransformation StdMeshTrack::GetTransformAt(float time) const
|
|||
transformation.rotate = weight1 * iter->second.Transformation.rotate + weight2 * prev_iter->second.Transformation.rotate; // TODO: slerp or renormalize
|
||||
transformation.translate = weight1 * iter->second.Transformation.translate + weight2 * prev_iter->second.Transformation.translate;
|
||||
return transformation;
|
||||
//return StdMeshTransformation::Nlerp(prev_iter->second.Transformation, iter->second.Transformation, weight1);
|
||||
}
|
||||
|
||||
StdMeshAnimation::StdMeshAnimation(const StdMeshAnimation& other):
|
||||
|
@ -1141,42 +1191,61 @@ const StdMeshAnimation* StdMesh::GetAnimationByName(const StdStrBuf& name) const
|
|||
return &iter->second;
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationRef::AnimationRef(StdMeshInstance* instance, const StdStrBuf& animation_name):
|
||||
Instance(instance), Anim(NULL)
|
||||
StdMeshInstance::AnimationNode::AnimationNode(const StdMeshAnimation* animation, ValueProvider* position):
|
||||
Type(LeafNode), Parent(NULL)
|
||||
{
|
||||
const StdMeshAnimation* animation = instance->Mesh.GetAnimationByName(animation_name);
|
||||
if(animation)
|
||||
Leaf.Animation = animation;
|
||||
Leaf.Position = position;
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationNode::AnimationNode(AnimationNode* child_left, AnimationNode* child_right, ValueProvider* weight):
|
||||
Type(LinearInterpolationNode), Parent(NULL)
|
||||
{
|
||||
LinearInterpolation.ChildLeft = child_left;
|
||||
LinearInterpolation.ChildRight = child_right;
|
||||
LinearInterpolation.Weight = weight;
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationNode::~AnimationNode()
|
||||
{
|
||||
switch(Type)
|
||||
{
|
||||
for(unsigned int i = 0; i < instance->Animations.size(); ++i)
|
||||
if(instance->Animations[i].MeshAnimation == animation)
|
||||
{ Anim = &instance->Animations[i]; break; }
|
||||
case LeafNode:
|
||||
delete Leaf.Position;
|
||||
break;
|
||||
case LinearInterpolationNode:
|
||||
delete LinearInterpolation.ChildLeft;
|
||||
delete LinearInterpolation.ChildRight;
|
||||
delete LinearInterpolation.Weight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationRef::AnimationRef(StdMeshInstance* instance, const StdMeshAnimation& animation):
|
||||
Instance(instance), Anim(NULL)
|
||||
bool StdMeshInstance::AnimationNode::GetBoneTransform(unsigned int bone, StdMeshTransformation& transformation)
|
||||
{
|
||||
for(unsigned int i = 0; i < instance->Animations.size(); ++i)
|
||||
if(instance->Animations[i].MeshAnimation == &animation)
|
||||
{ Anim = &instance->Animations[i]; break; }
|
||||
}
|
||||
StdMeshTransformation combine_with;
|
||||
StdMeshTrack* track;
|
||||
|
||||
const StdMeshAnimation& StdMeshInstance::AnimationRef::GetAnimation() const
|
||||
{
|
||||
return *Anim->MeshAnimation;
|
||||
}
|
||||
switch(Type)
|
||||
{
|
||||
case LeafNode:
|
||||
//if(!Leaf.Animation) return false;
|
||||
track = Leaf.Animation->Tracks[bone];
|
||||
if(!track) return false;
|
||||
transformation = track->GetTransformAt(Leaf.Position->Value);
|
||||
return true;
|
||||
case LinearInterpolationNode:
|
||||
if(!LinearInterpolation.ChildLeft->GetBoneTransform(bone, transformation))
|
||||
return LinearInterpolation.ChildRight->GetBoneTransform(bone, transformation);
|
||||
if(!LinearInterpolation.ChildRight->GetBoneTransform(bone, combine_with))
|
||||
return true; // First Child affects bone
|
||||
|
||||
void StdMeshInstance::AnimationRef::SetPosition(float position)
|
||||
{
|
||||
assert(position <= Anim->MeshAnimation->Length);
|
||||
Anim->Position = position;
|
||||
Instance->BoneTransformsDirty = true;
|
||||
}
|
||||
|
||||
void StdMeshInstance::AnimationRef::SetWeight(float weight)
|
||||
{
|
||||
Anim->Weight = weight;
|
||||
Instance->BoneTransformsDirty = true;
|
||||
transformation = StdMeshTransformation::Nlerp(transformation, combine_with, LinearInterpolation.Weight->Value);
|
||||
return true;
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
StdMeshInstance::StdMeshInstance(const StdMesh& mesh):
|
||||
|
@ -1202,6 +1271,9 @@ StdMeshInstance::~StdMeshInstance()
|
|||
{
|
||||
for(AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
|
||||
delete iter->Child;
|
||||
while(!AnimationStack.empty())
|
||||
StopAnimation(AnimationStack.front());
|
||||
assert(AnimationNodes.empty());
|
||||
}
|
||||
|
||||
void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
|
||||
|
@ -1228,50 +1300,172 @@ void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
|
|||
}
|
||||
}
|
||||
|
||||
bool StdMeshInstance::PlayAnimation(const StdStrBuf& animation_name, float weight)
|
||||
StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdStrBuf& animation_name, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight)
|
||||
{
|
||||
const StdMeshAnimation* animation = Mesh.GetAnimationByName(animation_name);
|
||||
if(!animation) return false;
|
||||
return PlayAnimation(*animation, weight);
|
||||
if(!animation) { delete position; delete weight; return NULL; }
|
||||
|
||||
return PlayAnimation(*animation, slot, sibling, position, weight);
|
||||
}
|
||||
|
||||
bool StdMeshInstance::PlayAnimation(const StdMeshAnimation& animation, float weight)
|
||||
StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdMeshAnimation& animation, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight)
|
||||
{
|
||||
for(unsigned int i = 0; i < Animations.size(); ++i)
|
||||
if(Animations[i].MeshAnimation == &animation)
|
||||
return false;
|
||||
// Default
|
||||
if(!sibling) sibling = GetRootAnimationForSlot(slot);
|
||||
assert(!sibling || sibling->Slot == slot);
|
||||
|
||||
Animation anim;
|
||||
anim.MeshAnimation = &animation;
|
||||
anim.Position = 0.0f;
|
||||
anim.Weight = weight;
|
||||
Animations.push_back(anim);
|
||||
// Find two subsequent numbers in case we need to create two nodes, so
|
||||
// script can deduce the second node.
|
||||
unsigned int Number1, Number2;
|
||||
for(Number1 = 0; Number1 < AnimationNodes.size(); ++Number1)
|
||||
if(AnimationNodes[Number1] == NULL && (!sibling || Number1+1 == AnimationNodes.size() || AnimationNodes[Number1+1] == NULL))
|
||||
break;
|
||||
/* for(Number2 = Number1+1; Number2 < AnimationNodes.size(); ++Number2)
|
||||
if(AnimationNodes[Number2] == NULL)
|
||||
break;*/
|
||||
Number2 = Number1 + 1;
|
||||
|
||||
BoneTransformsDirty = true;
|
||||
return true;
|
||||
}
|
||||
if(Number1 == AnimationNodes.size()) AnimationNodes.push_back(NULL);
|
||||
if(sibling && Number2 == AnimationNodes.size()) AnimationNodes.push_back(NULL);
|
||||
|
||||
bool StdMeshInstance::StopAnimation(const StdStrBuf& animation_name)
|
||||
{
|
||||
const StdMeshAnimation* animation = Mesh.GetAnimationByName(animation_name);
|
||||
if(!animation) return false;
|
||||
return StopAnimation(*animation);
|
||||
}
|
||||
AnimationNode* child = new AnimationNode(&animation, position);
|
||||
AnimationNodes[Number1] = child;
|
||||
child->Number = Number1;
|
||||
child->Slot = slot;
|
||||
|
||||
bool StdMeshInstance::StopAnimation(const StdMeshAnimation& animation)
|
||||
{
|
||||
for(std::vector<Animation>::iterator iter = Animations.begin();
|
||||
iter != Animations.end(); ++iter)
|
||||
if(sibling)
|
||||
{
|
||||
if(iter->MeshAnimation == &animation)
|
||||
AnimationNode* parent = new AnimationNode(child, sibling, weight);
|
||||
AnimationNodes[Number2] = parent;
|
||||
parent->Number = Number2;
|
||||
parent->Slot = slot;
|
||||
|
||||
child->Parent = parent;
|
||||
parent->Parent = sibling->Parent;
|
||||
parent->LinearInterpolation.ChildLeft = sibling;
|
||||
parent->LinearInterpolation.ChildRight = child;
|
||||
if(sibling->Parent)
|
||||
{
|
||||
Animations.erase(iter);
|
||||
BoneTransformsDirty = true;
|
||||
return true;
|
||||
if(sibling->Parent->LinearInterpolation.ChildLeft == sibling)
|
||||
sibling->Parent->LinearInterpolation.ChildLeft = parent;
|
||||
else
|
||||
sibling->Parent->LinearInterpolation.ChildRight = parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
// set new parent
|
||||
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, false);
|
||||
// slot must not be empty, since sibling uses same slot
|
||||
assert(iter != AnimationStack.end() && *iter != NULL);
|
||||
*iter = parent;
|
||||
}
|
||||
|
||||
sibling->Parent = parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete weight;
|
||||
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, true);
|
||||
assert(!*iter); // we have a sibling if slot is not empty
|
||||
*iter = child;
|
||||
}
|
||||
|
||||
BoneTransformsDirty = true;
|
||||
return child;
|
||||
}
|
||||
|
||||
void StdMeshInstance::StopAnimation(AnimationNode* node)
|
||||
{
|
||||
ClearAnimationListRecursively(AnimationNodes, node);
|
||||
|
||||
AnimationNode* parent = node->Parent;
|
||||
if(parent == NULL)
|
||||
{
|
||||
AnimationNodeList::iterator iter = GetStackIterForSlot(node->Slot, false);
|
||||
assert(iter != AnimationStack.end() && *iter == node);
|
||||
AnimationStack.erase(iter);
|
||||
delete node;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(parent->Type == AnimationNode::LinearInterpolationNode);
|
||||
|
||||
// Remove parent interpolation node and re-link
|
||||
AnimationNode* other_child;
|
||||
if(parent->LinearInterpolation.ChildLeft == node)
|
||||
{
|
||||
other_child = parent->LinearInterpolation.ChildRight;
|
||||
parent->LinearInterpolation.ChildRight = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
other_child = parent->LinearInterpolation.ChildLeft;
|
||||
parent->LinearInterpolation.ChildLeft = NULL;
|
||||
}
|
||||
|
||||
if(parent->Parent)
|
||||
{
|
||||
assert(parent->Parent->Type == AnimationNode::LinearInterpolationNode);
|
||||
if(parent->Parent->LinearInterpolation.ChildLeft == parent)
|
||||
parent->Parent->LinearInterpolation.ChildLeft = other_child;
|
||||
else
|
||||
parent->Parent->LinearInterpolation.ChildRight = other_child;
|
||||
other_child->Parent = parent->Parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationNodeList::iterator iter = GetStackIterForSlot(node->Slot, false);
|
||||
assert(iter != AnimationStack.end() && *iter == parent);
|
||||
*iter = other_child;
|
||||
|
||||
other_child->Parent = NULL;
|
||||
}
|
||||
|
||||
AnimationNodes[parent->Number] = NULL;
|
||||
// Recursively deletes parent and its descendants
|
||||
delete parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
while(AnimationNodes.back() == NULL)
|
||||
AnimationNodes.erase(AnimationNodes.end()-1);
|
||||
BoneTransformsDirty = true;
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationNode* StdMeshInstance::GetAnimationNodeByNumber(unsigned int number)
|
||||
{
|
||||
if(number >= AnimationNodes.size()) return NULL;
|
||||
return AnimationNodes[number];
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationNode* StdMeshInstance::GetRootAnimationForSlot(int slot)
|
||||
{
|
||||
AnimationNodeList::iterator iter = GetStackIterForSlot(slot, false);
|
||||
if(iter == AnimationStack.end()) return NULL;
|
||||
return *iter;
|
||||
}
|
||||
|
||||
void StdMeshInstance::SetAnimationPosition(AnimationNode* node, ValueProvider* position)
|
||||
{
|
||||
assert(node->GetType() == AnimationNode::LeafNode);
|
||||
delete node->Leaf.Position;
|
||||
node->Leaf.Position = position;
|
||||
|
||||
BoneTransformsDirty = true;
|
||||
}
|
||||
|
||||
void StdMeshInstance::SetAnimationWeight(AnimationNode* node, ValueProvider* weight)
|
||||
{
|
||||
assert(node->GetType() == AnimationNode::LinearInterpolationNode);
|
||||
delete node->LinearInterpolation.Weight; node->LinearInterpolation.Weight = weight;
|
||||
|
||||
BoneTransformsDirty = true;
|
||||
}
|
||||
|
||||
void StdMeshInstance::ExecuteAnimation()
|
||||
{
|
||||
// Iterate from the back since slots might get removed
|
||||
for(unsigned int i = AnimationStack.size(); i > 0; --i)
|
||||
ExecuteAnimationNode(AnimationStack[i-1]);
|
||||
}
|
||||
|
||||
const StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(const StdMesh& mesh, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, float scale)
|
||||
|
@ -1339,28 +1533,28 @@ void StdMeshInstance::UpdateBoneTransforms()
|
|||
// Compute transformation matrix for each bone.
|
||||
for(unsigned int i = 0; i < BoneTransforms.size(); ++i)
|
||||
{
|
||||
float accum_weight = 0.0f;
|
||||
StdMeshTransformation Transformation = StdMeshTransformation::Zero();
|
||||
|
||||
for(unsigned int j = 0; j < Animations.size(); ++j)
|
||||
{
|
||||
StdMeshTrack* track = Animations[j].MeshAnimation->Tracks[i];
|
||||
if(track)
|
||||
{
|
||||
accum_weight += Animations[j].Weight;
|
||||
StdMeshTransformation Track = track->GetTransformAt(Animations[j].Position);
|
||||
|
||||
Transformation.scale += Animations[j].Weight * Track.scale;
|
||||
Transformation.rotate += Animations[j].Weight * Track.rotate; // TODO: introduce animation stack, then slerp
|
||||
Transformation.translate += Animations[j].Weight * Track.translate;
|
||||
}
|
||||
}
|
||||
StdMeshTransformation Transformation;
|
||||
|
||||
const StdMeshBone& bone = Mesh.GetBone(i);
|
||||
const StdMeshBone* parent = bone.GetParent();
|
||||
assert(!parent || parent->Index < i);
|
||||
|
||||
if(!accum_weight)
|
||||
bool have_transform = false;
|
||||
for(unsigned int j = 0; j < AnimationStack.size(); ++j)
|
||||
{
|
||||
if(have_transform)
|
||||
{
|
||||
StdMeshTransformation other;
|
||||
if(AnimationStack[j]->GetBoneTransform(i, other))
|
||||
Transformation = StdMeshTransformation::Nlerp(Transformation, other, 1.0f); // TODO: Allow custom weighing for slot combination
|
||||
}
|
||||
else
|
||||
{
|
||||
have_transform = AnimationStack[j]->GetBoneTransform(i, Transformation);
|
||||
}
|
||||
}
|
||||
|
||||
if(!have_transform)
|
||||
{
|
||||
if(parent)
|
||||
BoneTransforms[i] = BoneTransforms[parent->Index];
|
||||
|
@ -1369,14 +1563,8 @@ void StdMeshInstance::UpdateBoneTransforms()
|
|||
}
|
||||
else
|
||||
{
|
||||
Transformation.scale *= 1.0f/accum_weight;
|
||||
Transformation.rotate.Normalize(); // renormalize. We can skip this if we decide to use slerp
|
||||
Transformation.translate *= 1.0f/accum_weight;
|
||||
|
||||
BoneTransforms[i] = StdMeshMatrix::Transform(bone.Transformation * Transformation * bone.InverseTransformation);
|
||||
|
||||
if(parent)
|
||||
BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
|
||||
if(parent) BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1413,7 +1601,7 @@ void StdMeshInstance::UpdateBoneTransforms()
|
|||
}
|
||||
}
|
||||
|
||||
// Update attachment's inverse bone transformations. Note this needs not to be done recursively.
|
||||
// Update attachment's attach transformations. Note this is done recursively.
|
||||
for(AttachedMeshList::iterator iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
|
||||
{
|
||||
iter->Child->UpdateBoneTransforms();
|
||||
|
@ -1440,6 +1628,90 @@ void StdMeshInstance::UpdateBoneTransforms()
|
|||
BoneTransformsDirty = false;
|
||||
}
|
||||
|
||||
StdMeshInstance::AnimationNodeList::iterator StdMeshInstance::GetStackIterForSlot(int slot, bool create)
|
||||
{
|
||||
// TODO: bsearch
|
||||
for(AnimationNodeList::iterator iter = AnimationStack.begin(); iter != AnimationStack.end(); ++iter)
|
||||
{
|
||||
if((*iter)->Slot == slot)
|
||||
{
|
||||
return iter;
|
||||
}
|
||||
else if((*iter)->Slot < slot)
|
||||
{
|
||||
if(!create)
|
||||
return AnimationStack.end();
|
||||
else
|
||||
return AnimationStack.insert(iter, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if(!create)
|
||||
return AnimationStack.end();
|
||||
else
|
||||
return AnimationStack.insert(AnimationStack.end(), NULL);
|
||||
}
|
||||
|
||||
bool StdMeshInstance::ExecuteAnimationNode(AnimationNode* node)
|
||||
{
|
||||
ValueProvider* provider = NULL;
|
||||
switch(node->GetType())
|
||||
{
|
||||
case AnimationNode::LeafNode:
|
||||
provider = node->GetPositionProvider();
|
||||
break;
|
||||
case AnimationNode::LinearInterpolationNode:
|
||||
provider = node->GetWeightProvider();
|
||||
break;
|
||||
}
|
||||
|
||||
const float old_value = provider->Value;
|
||||
if(!provider->Execute())
|
||||
{
|
||||
if(node->GetType() == AnimationNode::LeafNode) return false;
|
||||
|
||||
// Remove the child with less weight (normally weight reaches 0.0 or 1.0)
|
||||
if(node->GetWeight() > 0.5)
|
||||
{
|
||||
// Remove both children (by parent) if other wants to be deleted as well
|
||||
if(!ExecuteAnimationNode(node->GetRightChild())) return false;
|
||||
// Remove left child as it has less weight
|
||||
StopAnimation(node->LinearInterpolation.ChildLeft);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove both children (by parent) if other wants to be deleted as well
|
||||
if(!ExecuteAnimationNode(node->GetLeftChild())) return false;
|
||||
// Remove right child as it has less weight
|
||||
StopAnimation(node->LinearInterpolation.ChildRight);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(provider->Value != old_value) BoneTransformsDirty = true;
|
||||
|
||||
if(node->GetType() == AnimationNode::LinearInterpolationNode)
|
||||
{
|
||||
const bool left_result = ExecuteAnimationNode(node->GetLeftChild());
|
||||
const bool right_result = ExecuteAnimationNode(node->GetRightChild());
|
||||
|
||||
// Remove this node completely
|
||||
if(!left_result && !right_result)
|
||||
return false;
|
||||
|
||||
// Note that either of this also removes node
|
||||
if(!left_result)
|
||||
StopAnimation(node->GetLeftChild());
|
||||
if(!right_result)
|
||||
StopAnimation(node->GetRightChild());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StdMeshInstance::ReorderFaces()
|
||||
{
|
||||
for(unsigned int i = 0; i < Faces.size(); ++i)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2009 Armin Burgmeier
|
||||
* Copyright (c) 2009-2010 Armin Burgmeier
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
||||
*
|
||||
* Portions might be copyrighted by other authors who have contributed
|
||||
|
@ -80,7 +80,8 @@ struct StdMeshQuaternion
|
|||
float LenSqr() const { return w*w+x*x+y*y+z*z; }
|
||||
void Normalize();
|
||||
|
||||
//static StdMeshQuaternion Slerp(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs, float t);
|
||||
static StdMeshQuaternion Nlerp(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs, float w);
|
||||
//static StdMeshQuaternion Slerp(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs, float w);
|
||||
};
|
||||
|
||||
struct StdMeshTransformation
|
||||
|
@ -95,6 +96,10 @@ struct StdMeshTransformation
|
|||
static StdMeshTransformation Translate(float dx, float dy, float dz);
|
||||
static StdMeshTransformation Scale(float sx, float sy, float sz);
|
||||
static StdMeshTransformation Rotate(float angle, float rx, float ry, float rz);
|
||||
|
||||
// TODO: Might add path parameter if necessary
|
||||
static StdMeshTransformation Nlerp(const StdMeshTransformation& lhs, const StdMeshTransformation& rhs, float w);
|
||||
//static StdMeshQuaternion Slerp(const StdMeshTransformation& lhs, const StdMeshTransformation& rhs, float w);
|
||||
};
|
||||
|
||||
class StdMeshMatrix
|
||||
|
@ -128,6 +133,7 @@ StdMeshQuaternion operator*(const StdMeshQuaternion& lhs, float rhs);
|
|||
StdMeshQuaternion operator*(float lhs, const StdMeshQuaternion& rhs);
|
||||
StdMeshQuaternion& operator+=(StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs);
|
||||
StdMeshQuaternion operator+(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs);
|
||||
StdMeshQuaternion operator-(const StdMeshQuaternion& lhs, const StdMeshQuaternion& rhs);
|
||||
StdMeshTransformation operator*(const StdMeshTransformation& lhs, const StdMeshTransformation& rhs);
|
||||
|
||||
StdMeshVector operator-(const StdMeshVector& rhs);
|
||||
|
@ -300,9 +306,6 @@ private:
|
|||
|
||||
class StdMeshInstance
|
||||
{
|
||||
protected:
|
||||
struct Animation;
|
||||
|
||||
public:
|
||||
StdMeshInstance(const StdMesh& mesh);
|
||||
~StdMeshInstance();
|
||||
|
@ -316,32 +319,85 @@ public:
|
|||
FaceOrdering GetFaceOrdering() const { return CurrentFaceOrdering; }
|
||||
void SetFaceOrdering(FaceOrdering ordering);
|
||||
|
||||
// Public API to modify animation. Updates bone transforms on
|
||||
// destruction, so make sure to let this go out of scope before
|
||||
// relying on the values set.
|
||||
struct AnimationRef
|
||||
// Provider for animation position or weight.
|
||||
class ValueProvider
|
||||
{
|
||||
AnimationRef(StdMeshInstance* instance, const StdStrBuf& animation_name);
|
||||
AnimationRef(StdMeshInstance* instance, const StdMeshAnimation& animation);
|
||||
|
||||
operator void*() const { return Anim; } // for use in boolean expressions
|
||||
public:
|
||||
ValueProvider(): Value(0.0f) {}
|
||||
virtual ~ValueProvider() {}
|
||||
|
||||
const StdMeshAnimation& GetAnimation() const;
|
||||
// Return false if the corresponding node is to be removed or true
|
||||
// otherwise.
|
||||
virtual bool Execute() = 0;
|
||||
|
||||
void SetPosition(float position);
|
||||
void SetWeight(float weight);
|
||||
private:
|
||||
AnimationRef(const AnimationRef&); // noncopyable
|
||||
AnimationRef& operator=(const AnimationRef&); // noncopyable
|
||||
|
||||
StdMeshInstance* Instance;
|
||||
Animation* Anim;
|
||||
float Value; // Current provider value
|
||||
};
|
||||
|
||||
bool PlayAnimation(const StdStrBuf& animation_name, float weight);
|
||||
bool PlayAnimation(const StdMeshAnimation& animation, float weight);
|
||||
bool StopAnimation(const StdStrBuf& animation_name);
|
||||
bool StopAnimation(const StdMeshAnimation& animation);
|
||||
// A node in the animation tree
|
||||
// Can be either a leaf node, or interpolation between two other nodes
|
||||
class AnimationNode
|
||||
{
|
||||
friend class StdMeshInstance;
|
||||
public:
|
||||
enum NodeType { LeafNode, LinearInterpolationNode };
|
||||
|
||||
AnimationNode(const StdMeshAnimation* animation, ValueProvider* position);
|
||||
AnimationNode(AnimationNode* child_left, AnimationNode* child_right, ValueProvider* weight);
|
||||
~AnimationNode();
|
||||
|
||||
bool GetBoneTransform(unsigned int bone, StdMeshTransformation& transformation);
|
||||
|
||||
int GetSlot() const { return Slot; }
|
||||
unsigned int GetNumber() const { return Number; }
|
||||
NodeType GetType() const { return Type; }
|
||||
AnimationNode* GetParent() { return Parent; }
|
||||
|
||||
const StdMeshAnimation* GetAnimation() const { assert(Type == LeafNode); return Leaf.Animation; }
|
||||
ValueProvider* GetPositionProvider() { assert(Type == LeafNode); return Leaf.Position; }
|
||||
float GetPosition() const { assert(Type == LeafNode); return Leaf.Position->Value; }
|
||||
|
||||
AnimationNode* GetLeftChild() { assert(Type == LinearInterpolationNode); return LinearInterpolation.ChildLeft; }
|
||||
AnimationNode* GetRightChild() { assert(Type == LinearInterpolationNode); return LinearInterpolation.ChildRight; }
|
||||
ValueProvider* GetWeightProvider() { assert(Type == LinearInterpolationNode); return LinearInterpolation.Weight; }
|
||||
float GetWeight() const { assert(Type == LinearInterpolationNode); return LinearInterpolation.Weight->Value; }
|
||||
|
||||
protected:
|
||||
int Slot;
|
||||
unsigned int Number;
|
||||
NodeType Type;
|
||||
AnimationNode* Parent;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
const StdMeshAnimation* Animation;
|
||||
ValueProvider* Position;
|
||||
} Leaf;
|
||||
|
||||
struct
|
||||
{
|
||||
AnimationNode* ChildLeft;
|
||||
AnimationNode* ChildRight;
|
||||
ValueProvider* Weight;
|
||||
} LinearInterpolation;
|
||||
};
|
||||
};
|
||||
|
||||
AnimationNode* PlayAnimation(const StdStrBuf& animation_name, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight);
|
||||
AnimationNode* PlayAnimation(const StdMeshAnimation& animation, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight);
|
||||
void StopAnimation(AnimationNode* node);
|
||||
|
||||
AnimationNode* GetAnimationNodeByNumber(unsigned int number);
|
||||
AnimationNode* GetRootAnimationForSlot(int slot);
|
||||
|
||||
// Set new value providers for a node's position or weight - cannot be in
|
||||
// class AnimationNode since we need to mark BoneTransforms dirty.
|
||||
void SetAnimationPosition(AnimationNode* node, ValueProvider* position);
|
||||
void SetAnimationWeight(AnimationNode* node, ValueProvider* weight);
|
||||
|
||||
// Update animations' value providers; call once a frame
|
||||
void ExecuteAnimation();
|
||||
|
||||
struct AttachedMesh
|
||||
{
|
||||
|
@ -354,7 +410,7 @@ public:
|
|||
// Cache attach transformation, updated in UpdateBoneTransforms()
|
||||
StdMeshMatrix AttachTrans;
|
||||
};
|
||||
|
||||
|
||||
typedef std::list<AttachedMesh> AttachedMeshList;
|
||||
typedef AttachedMeshList::const_iterator AttachedMeshIter;
|
||||
|
||||
|
@ -394,19 +450,16 @@ public:
|
|||
const StdMesh& Mesh;
|
||||
|
||||
protected:
|
||||
typedef std::vector<AnimationNode*> AnimationNodeList;
|
||||
|
||||
AnimationNodeList::iterator GetStackIterForSlot(int slot, bool create);
|
||||
bool ExecuteAnimationNode(AnimationNode* node);
|
||||
void ReorderFaces();
|
||||
|
||||
FaceOrdering CurrentFaceOrdering;
|
||||
|
||||
struct Animation
|
||||
{
|
||||
const StdMeshAnimation* MeshAnimation;
|
||||
float Position;
|
||||
float Weight;
|
||||
};
|
||||
|
||||
std::vector<Animation> Animations;
|
||||
|
||||
AnimationNodeList AnimationNodes; // for simple lookup of animation nodes by their unique number
|
||||
AnimationNodeList AnimationStack; // contains top level nodes only, ordered by slot number
|
||||
std::vector<StdMeshMatrix> BoneTransforms;
|
||||
|
||||
// Vertices transformed according to current animation, for each submesh
|
||||
|
|
Loading…
Reference in New Issue