More work on animations (still work in progress)

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/irrlicht@3680 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk
2009-06-30 12:51:38 +00:00
parent d9f3a197cc
commit 6cefc34b31
17 changed files with 448 additions and 31 deletions

View File

@@ -18,6 +18,8 @@ supertuxkart_SOURCES = \
animations/animation_manager.hpp \
animations/billboard_animation.cpp \
animations/billboard_animation.hpp \
animations/ipo.cpp \
animations/ipo.hpp \
animations/three_d_animation.cpp \
animations/three_d_animation.hpp \
audio/music.hpp \

View File

@@ -19,6 +19,62 @@
#include "animations/animation_base.hpp"
void AnimationBase::update(float dt)
#include "animations/ipo.hpp"
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
AnimationBase::AnimationBase(const XMLNode &node, float fps)
{
for(unsigned int i=0; i<node.getNumNodes(); i++)
{
Ipo *ipo = new Ipo(*node.getNode(i), fps);
m_all_ipos.push_back(ipo);
} // for i<getNumNodes()
m_playing = true;
} // AnimationBase
// ----------------------------------------------------------------------------
/** Stores the initial transform (in the IPOs actually). This is necessary
* for relative IPOs.
* \param xyz Position of the object.
* \param hpr Rotation of the object.
*/
void AnimationBase::setInitialTransform(const core::vector3df &xyz,
const core::vector3df &hpr)
{
std::vector<Ipo*>::iterator i;
for(i=m_all_ipos.begin(); i<m_all_ipos.end(); i++)
{
(*i)->setInitialTransform(xyz, hpr);
} // for i in m_all_ipos
} // setTransform
// ----------------------------------------------------------------------------
/** Resets all IPOs for this animation.
*/
void AnimationBase::reset()
{
std::vector<Ipo*>::iterator i;
for(i=m_all_ipos.begin(); i<m_all_ipos.end(); i++)
{
(*i)->reset();
} // for i in m_all_ipos
} // reset
// ----------------------------------------------------------------------------
/** Updates the time, position and rotation. Called once per framce.
* \param dt Time since last call.
* \param xyz Position to be updated.
* \param hpr Rotation to be updated.
*/
void AnimationBase::update(float dt, core::vector3df *xyz,
core::vector3df *hpr)
{
std::vector<Ipo*>::iterator i;
for(i=m_all_ipos.begin(); i<m_all_ipos.end(); i++)
{
(*i)->update(dt, xyz, hpr);
} // for i in m_all_ipos
} // float dt

View File

@@ -20,8 +20,15 @@
#ifndef HEADER_ANIMATION_BASE_HPP
#define HEADER_ANIMATION_BASE_HPP
/** A base class for all animations. */
#include <vector>
#include "irrlicht.h"
using namespace irr;
class XMLNode;
class Ipo;
/** A base class for all animations. */
class AnimationBase
{
private:
@@ -41,12 +48,20 @@ private:
/** The current time in the cycle of a cyclic animation. */
float m_current_time;
protected:
/** All IPOs for this animation. */
std::vector<Ipo*> m_all_ipos;
public:
AnimationBase() {}
virtual ~AnimationBase(){}
virtual void update(float dt);
AnimationBase(const XMLNode &node, float fps);
virtual ~AnimationBase() {}
virtual void update(float dt, core::vector3df *xyz, core::vector3df *hpr);
/** This needs to be implemented by the inheriting classes. It is called
* once per frame from the track. */
virtual void update(float dt) = 0;
void setInitialTransform(const core::vector3df &xyz,
const core::vector3df &hpr);
void reset();
}; // AnimationBase

View File

@@ -25,17 +25,18 @@
#include "animations/three_d_animation.hpp"
#include "io/xml_node.hpp"
AnimationManager::AnimationManager(const XMLNode &node)
AnimationManager::AnimationManager(const std::string &track_name, const XMLNode &node)
{
for(unsigned int i=0; i<node.getNumNodes(); i++)
{
const XMLNode *anim_node = node.getNode(i);
std::string type;
anim_node->get("type", &type);
std::string type = anim_node->getName();
float fps=25;
anim_node->get("fps", &fps);
if(type=="anim_billboard")
m_all_animations.push_back(new BillboardAnimation(*anim_node));
else if(type=="animation_3d")
m_all_animations.push_back(new ThreeDAnimation(*anim_node));
m_all_animations.push_back(new BillboardAnimation(track_name, *anim_node, fps));
else if(type=="animations-IPO")
m_all_animations.push_back(new ThreeDAnimation(track_name, *anim_node, fps));
else
fprintf(stderr, "Unknown animation type '%s' - ignored.\n",
type.c_str());
@@ -43,5 +44,22 @@ AnimationManager::AnimationManager(const XMLNode &node)
} // AnimationManager
// ----------------------------------------------------------------------------
/** Resets all animations.
*/
void AnimationManager::reset()
{
std::vector<AnimationBase*>::iterator i;
for(i=m_all_animations.begin(); i!=m_all_animations.end(); i++)
(*i)->reset();
} // reset
// ----------------------------------------------------------------------------
/** Updates all animations. Called one per time step.
* \param dt Time since last call.
*/
void AnimationManager::update(float dt)
{
std::vector<AnimationBase*>::iterator i;
for(i=m_all_animations.begin(); i!=m_all_animations.end(); i++)
(*i)->update(dt);
} // update

View File

@@ -20,6 +20,7 @@
#ifndef HEADER_ANIMATION_MANAGER_HPP
#define HEADER_ANIMATION_MANAGER_HPP
#include <string>
#include <vector>
class AnimationBase;
@@ -32,8 +33,9 @@ private:
std::vector<AnimationBase*> m_all_animations;
public:
AnimationManager(const XMLNode &node);
AnimationManager(const std::string &track_name, const XMLNode &node);
void update(float dt);
void reset();
}; // AnimationManager
#endif

View File

@@ -22,9 +22,20 @@
class XMLNode;
/** A 2d billboard animation. */
BillboardAnimation::BillboardAnimation(const XMLNode &node)
BillboardAnimation::BillboardAnimation(const std::string &track_name,
const XMLNode &node, float fps)
: AnimationBase(node, fps)
{
} // BillboardAnimation
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
/** Update the animation, called one per time step.
* \param dt Time since last call. */
void BillboardAnimation::update(float dt)
{
// FIXME: not implemented yet.
core::vector3df xyz(0, 0, 0);
core::vector3df hpr(0, 0, 0);
AnimationBase::update(dt, &xyz, &hpr);
} // update

View File

@@ -20,6 +20,8 @@
#ifndef HEADER_BILLBOARD_ANIMATION_HPP
#define HEADER_BILLBOARD_ANIMATION_HPP
#include <string>
#include "animations/animation_base.hpp"
class XMLNode;
@@ -30,9 +32,9 @@ class BillboardAnimation : public AnimationBase
private:
public:
BillboardAnimation(const XMLNode &node);
BillboardAnimation(const std::string &track, const XMLNode &node, float fps);
virtual ~BillboardAnimation(){}
virtual void update(float dt);
}; // BillboardAnimation

149
src/animations/ipo.cpp Normal file
View File

@@ -0,0 +1,149 @@
// $Id: ipo.cpp 1681 2008-04-09 13:52:48Z hikerstk $
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "animations/ipo.hpp"
#include "io/xml_node.hpp"
//static std::string Ipo::m_all_names[2] = {std::string("a"), std::string("b")};
Ipo::Ipo(const XMLNode &curve, float fps)
{
if(curve.getName()!="curve")
{
fprintf(stderr, "Expected 'curve' for animation, got '%s' --> Ignored.\n",
curve.getName().c_str());
return;
}
std::string type; curve.get("type", &type );
if(type=="RotX") m_channel=IPO_ROTX;
else if(type=="RotY") m_channel=IPO_ROTY;
else if(type=="RotZ") m_channel=IPO_ROTZ;
std::string interp; curve.get("interpolation", &interp);
if (interp=="const" ) m_interpolation = IP_CONST;
else if(interp=="linear") m_interpolation = IP_LINEAR;
else m_interpolation = IP_BEZIER;
m_min_time = 999999;
m_max_time = -1;
for(unsigned int i=0; i<curve.getNumNodes(); i++)
{
const XMLNode *node = curve.getNode(i);
Vec3 xy;
node->get2("c", &xy);
// Convert blender's frame number (1 ...) into time (0 ...)
float t = (xy.getX()-1)/fps;
if(t<m_min_time) m_min_time = t;
if(t>m_max_time) m_max_time = t;
xy.setX(t);
m_points.push_back(xy);
if(m_interpolation==IP_BEZIER)
{
Vec3 handle;
node->get2("h1", &handle);
handle.setX((xy.getX()-1)/fps);
m_handle1.push_back(handle);
node->get2("h2", &handle);
handle.setX((xy.getX()-1)/fps);
m_handle2.push_back(handle);
}
} // for i<getNumNodes()
reset();
} // Ipo
// ----------------------------------------------------------------------------
/** Stores the initial transform. This is necessary for relative IPOs.
* \param xyz Position of the object.
* \param hpr Rotation of the object.
*/
void Ipo::setInitialTransform(const core::vector3df &xyz,
const core::vector3df &hpr)
{
m_initial_xyz = xyz;
m_initial_hpr = hpr;
} // setInitialTransform
// ----------------------------------------------------------------------------
/** Resets the IPO for (re)starting an animation.
*/
void Ipo::reset()
{
m_time = 0;
} // reset
// ----------------------------------------------------------------------------
/** Updates the time of this ipo and interpolates the new position and
* rotation (taking the cycle length etc. into account).
* \param dt Time since last call.
* \param xyz The position that needs to be updated.
* \param hpr The rotation that needs to be updated.
*/
void Ipo::update(float dt, core::vector3df *xyz, core::vector3df *hpr)
{
m_time += dt;
if(m_time>m_max_time) m_time = 0;
switch(m_channel)
{
case Ipo::IPO_LOCX : xyz->X = get(); break;
case Ipo::IPO_LOCY : xyz->Y = get(); break;
case Ipo::IPO_LOCZ : xyz->Z = get(); break;
case Ipo::IPO_ROTX : hpr->Y = get()*10.0f; break;
case Ipo::IPO_ROTY : hpr->Y = get()*10.0f; break;
case Ipo::IPO_ROTZ : hpr->Z = get()*10.0f; break;
} // switch
} // update
// ----------------------------------------------------------------------------
/** Returns the interpolated value at the current time (which this objects
* keeps track of).
*/
float Ipo::get() const
{
if(m_time<m_min_time)
{
// FIXME: should take extend into account!
return 0;
}
unsigned int n=0;
// Search for the first point in the (sorted) array which is greater or equal
// to the current time.
// FIXME: we should store the last point to speed this up!
while(n<m_points.size()-1 && m_time >=m_points[n].getX())
n++;
n--;
switch(m_interpolation)
{
case IP_CONST : return m_points[n].getY();
case IP_LINEAR : {
float t = m_time-m_points[n].getX();
return m_points[n].getY() + t*(m_points[n+1].getY()-m_points[n].getY()) /
(m_points[n+1].getX()-m_points[n].getX());
}
case IP_BEZIER: { // FIXME: for now only:
return m_points[n].getY();
}
} // switch
// Keep the compiler happy:
return 0;
} // get
// ----------------------------------------------------------------------------

80
src/animations/ipo.hpp Normal file
View File

@@ -0,0 +1,80 @@
// $Id: ipo.hpp 1681 2008-04-09 13:52:48Z hikerstk $
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_IPO_HPP
#define HEADER_IPO_HPP
#include <string>
#include <vector>
#include "utils/vec3.hpp"
class XMLNode;
/** A class to manage a single blender IPO curve. */
class Ipo
{
public:
/** All supported ipo types. */
enum IpoChannelType {IPO_LOCX, IPO_LOCY, IPO_LOCZ,
IPO_ROTX, IPO_ROTY, IPO_ROTZ,
IPO_MAX};
static std::string m_all_names[IPO_MAX];
private:
/** The type of this IPO. */
IpoChannelType m_channel;
/** The three interpolations defined by blender. */
enum {IP_CONST, IP_LINEAR, IP_BEZIER} m_interpolation;
/** The four extend types. */
enum {ET_CONST, ET_EXTRAP, ET_CYCLIC_EXTRAP, ET_CYCLIC} m_extend;
/** The actual control points. Using Vec3 is a bit of an overkill since
* the IPs are only 2d, but the amount of memory is small anyway. */
std::vector<Vec3> m_points;
/** Only used for bezier curves: the two handles. */
std::vector<Vec3> m_handle1, m_handle2;
/** Current time in cycle. */
float m_time;
/** Minium time when this animations starts, usually 0. */
float m_min_time;
/** Time this animation finishes (or cycles). */
float m_max_time;
/** Frames per second for this animation. */
float m_fps;
/** Stores the inital position of the object. */
core::vector3df m_initial_xyz;
/** Stores the inital rotation of the object. */
core::vector3df m_initial_hpr;
public:
Ipo(const XMLNode &curve, float fps);
void update(float dt, core::vector3df *xyz, core::vector3df *hpr);
float get() const;
void setInitialTransform(const core::vector3df &xyz,
const core::vector3df &hpr);
void reset();
}; // Ipo
#endif

View File

@@ -18,11 +18,43 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "animations/three_d_animation.hpp"
#include "io/xml_node.hpp"
ThreeDAnimation::ThreeDAnimation(const XMLNode &node)
#include <stdio.h>
#include "animations/ipo.hpp"
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "tracks/bezier_curve.hpp"
ThreeDAnimation::ThreeDAnimation(const std::string &track_name,
const XMLNode &node, float fps)
: AnimationBase(node, fps)
{
std::string model_name;
node.get("obj", &model_name);
std::string full_path = file_manager->getTrackFile(model_name, track_name);
m_mesh = irr_driver->getAnimatedMesh(full_path);
if(!m_mesh)
{
fprintf(stderr, "Warning: animated model '%s' not found, aborting.\n",
node.getName().c_str(), model_name.c_str());
exit(-1);
}
m_animated_node = irr_driver->addAnimatedMesh(m_mesh);
setInitialTransform(m_animated_node->getPosition(), m_animated_node->getRotation());
} // ThreeDAnimation
// ----------------------------------------------------------------------------
/** Updates position and rotation of this model. Called once per time step.
* \param dt Time since last call.
*/
void ThreeDAnimation::update(float dt)
{
core::vector3df xyz = m_animated_node->getPosition();
core::vector3df hpr = m_animated_node->getRotation();
AnimationBase::update(dt, &xyz, &hpr); //updates all IPOs
m_animated_node->setPosition(xyz);
m_animated_node->setRotation(hpr);
} // update

View File

@@ -20,18 +20,31 @@
#ifndef HEADER_THREE_D_ANIMATION_HPP
#define HEADER_THREE_D_ANIMATION_HPP
#include <string>
#include <vector>
#include "irrlicht.h"
using namespace irr;
#include "animations/animation_base.hpp"
class XMLNode;
class BezierCurve;
/** A virtual base class for all animations. */
class ThreeDAnimation : public AnimationBase
{
private:
/** Mesh of this animation. */
scene::IAnimatedMesh *m_mesh;
/** The scene node for the model. */
scene::IAnimatedMeshSceneNode *m_animated_node;
public:
ThreeDAnimation(const XMLNode &node);
virtual ~ThreeDAnimation(){}
ThreeDAnimation(const std::string &track_name,
const XMLNode &node, float fps);
virtual ~ThreeDAnimation(){}
virtual void update(float dt);
}; // ThreeDAnimation
#endif

View File

@@ -716,6 +716,10 @@
RelativePath="..\..\animations\billboard_animation.cpp"
>
</File>
<File
RelativePath="..\..\animations\ipo.cpp"
>
</File>
<File
RelativePath="..\..\animations\three_d_animation.cpp"
>
@@ -1330,6 +1334,10 @@
RelativePath="..\..\animations\billboard_animation.hpp"
>
</File>
<File
RelativePath="..\..\animations\ipo.hpp"
>
</File>
<File
RelativePath="..\..\animations\three_d_animation.hpp"
>

View File

@@ -67,6 +67,7 @@ XMLNode::XMLNode(const std::string &filename)
XMLNode::~XMLNode()
{
} // ~XMLNode
// ----------------------------------------------------------------------------
/** Stores all attributes, and reads in all children.
* \param xml The XML reader.
@@ -114,6 +115,7 @@ const XMLNode *XMLNode::getNode(unsigned int i) const
{
return m_nodes[i];
} // getNode
// ----------------------------------------------------------------------------
/** Returns the node with the given name.
* \param s Name of the node to return.
@@ -126,6 +128,7 @@ const XMLNode *XMLNode::getNode(const std::string &s) const
}
return NULL;
} // getNode
// ----------------------------------------------------------------------------
/** Returns all nodes with the given name.
* \param s Name of the nodes to return.
@@ -186,6 +189,21 @@ int XMLNode::get(const std::string &attribute, Vec3 *value) const
return 1;
} // get(Vec3)
// ----------------------------------------------------------------------------
/** Only reads in a tuple and sets only x and y coordinates. Z is unchanges.
* This is used in reading blender's IPO data. */
int XMLNode::get2(const std::string &attribute, Vec3 *value) const
{
std::string s = "";
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
if(v.size()!=2) return 0;
value->setX((float)atof(v[0].c_str()));
value->setY((float)atof(v[1].c_str()));
return 1;
} // get2(Vec3)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, video::SColorf *color) const
{

View File

@@ -53,6 +53,7 @@ public:
int get(const std::string &attribute, float *value) const;
int get(const std::string &attribute, bool *value) const;
int get(const std::string &attribute, Vec3 *value) const;
int get2(const std::string &attribute,Vec3 *value) const;
int get(const std::string &attribute, core::vector3df *value) const;
int get(const std::string &attribute, video::SColorf *value) const;
int get(const std::string &attribute, std::vector<std::string> *value) const;

View File

@@ -489,6 +489,7 @@ void World::getDefaultCollectibles(int& collectible_type, int& amount )
void World::restartRace()
{
TimedRace::reset();
m_track->reset();
m_faster_music_active = false;
m_eliminated_karts = 0;
m_eliminated_players = 0;

View File

@@ -85,6 +85,13 @@ Track::~Track()
if(m_animation_manager) delete m_animation_manager;
} // ~Track
//-----------------------------------------------------------------------------
void Track::reset()
{
if(m_animation_manager)
m_animation_manager->reset();
} // reset
//-----------------------------------------------------------------------------
/** Removes the physical body from the world.
* Called at the end of a race.
@@ -523,11 +530,6 @@ void Track::loadTrack(const std::string &filename)
if(xml_node)
loadCurves(*xml_node);
xml_node = root->getNode("animations");
if(xml_node)
{
m_animation_manager = new AnimationManager(*xml_node);
}
// Set the correct paths
m_screenshot = file_manager->getTrackFile(m_screenshot, getIdent());
delete root;
@@ -875,6 +877,8 @@ void Track::update(float dt)
{
m_physical_objects[i]->update(dt);
}
if(m_animation_manager)
m_animation_manager->update(dt);
} // update
// ----------------------------------------------------------------------------
@@ -1028,6 +1032,10 @@ void Track::loadTrackModel()
// Height is needed if bit 2 (for z) is not set
itemCommand(xyz, type, /* need_height */ !XMLNode::hasZ(bits) );
}
else if(name=="animations")
{
m_animation_manager = new AnimationManager(getIdent(), *node);
}
else
{
fprintf(stderr, "Warning: element '%s' not found.\n",

View File

@@ -196,6 +196,7 @@ public:
float getTerrainHeight(const Vec3 &pos) const;
void createPhysicsModel();
void update(float dt);
void reset();
void handleExplosion(const Vec3 &pos, const PhysicalObject *mp) const;
/** Returns the graph of quads, mainly for the AI. */