1. Change IRRLICHT_DIR #define in main.cpp to your own irrlicht directory:
#define IRRLICHT_DIR "..."
2. Keyboard binding:
F1 => bring camera nearer to missile
F4 => start / restart projectile
F5 => pause projectile
F6 => resume projectile
Escape => quit
main.cpp:
Code: Select all
#include <irrlicht.h>
#include <iostream>
#include <deque>
#include "projectile_animator.h"
using namespace irr;
#ifndef IRRLICHT_PROPERTIES
#define IRRLICHT_PROPERTIES
#define IRRLICHT_DIR "/home/smso/downloads/irrlicht/irrlicht-1.7.2/"
#endif
IrrlichtDevice* device = 0;
scene::ICameraSceneNode* camera = 0;
scene::CSceneNodeAnimatorProjectile* projectile = 0;
scene::ISceneNode* missile = 0;
core::vector3df missileStartPos;
core::vector3df missileStartDir;
f32 missileSpeed;
std::deque<core::vector3df> trajectoryPoints;
////////////////////////////////////////////////////////////////////////////////
void addTrajectoryPoint
(
scene::ISceneNode* node,
std::deque<core::vector3df>& points,
u32 pointCountMax = 100
)
{
u32 pointCount = points.size();
if (pointCount >= pointCountMax)
{
points.pop_front();
}
node->updateAbsolutePosition();
points.push_back(node->getAbsolutePosition());
}
void drawTrajectory(const std::deque<core::vector3df>& points)
{
if (points.size() <= 1)
return;
video::SColor blue(128, 0, 0, 255);
// set mat:
video::SMaterial lineMaterial;
lineMaterial.Lighting = false;
lineMaterial.Thickness = 3.0f;
lineMaterial.FrontfaceCulling = false;
lineMaterial.BackfaceCulling = false;
//lineMaterial.MaterialType = video::EMT_SOLID;
lineMaterial.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
video::IVideoDriver* driver = device->getVideoDriver();
driver->setMaterial(lineMaterial);
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
for (u32 n=0; n<(points.size()-1); ++n)
{
core::vector3df p1 = points[n];
core::vector3df p2 = points[n+1];
if (p1.equals(p2))
continue;
driver->draw3DLine(p1, p2, blue);
}
}
void rotateCameraTowardsNodeAroundYAxis
(
scene::ICameraSceneNode* camera,
scene::ISceneNode* node,
f32 minDist,
bool moveCameraCloser,
bool rotateCameraBy90Deg
)
{
node->updateAbsolutePosition();
camera->updateAbsolutePosition();
// rotate to face target Node
core::vector3df targetPos = node->getAbsolutePosition();
core::vector3df cameraPos = camera->getAbsolutePosition();
core::vector3df delta = targetPos - cameraPos;
core::vector3df rot = delta.getHorizontalAngle();
//rot.X = 0.0f;
//rot.Z = 0.0f;
if (rotateCameraBy90Deg)
rot.Y -= 90.0f;
camera->setRotation(rot);
//rot = getVerticalAngle(delta);
//rot.X = 0.0f;
//rot.Y = 0.0f;
//camera->setRotation(rot);
// bring camera nearer to node:
//if (delta.getLengthSQ() > 10000.0f)
if (!moveCameraCloser)
return;
if (delta.getLengthSQ() < (minDist * minDist))
return;
delta *= 0.5f;
camera->setPosition(core::vector3df
(
cameraPos.X + delta.X,
cameraPos.Y + delta.Y,
cameraPos.Z + delta.Z
));
//node->updateAbsolutePosition();
//camera->updateAbsolutePosition();
}
////////////////////////////////////////////////////////////////////////////////
class EventReceiver: public IEventReceiver
{
public:
bool OnEvent(const SEvent& event)
{
if
(
(event.EventType == EET_KEY_INPUT_EVENT)
&& (event.KeyInput.PressedDown == true)
)
{
if (event.KeyInput.Key == irr::KEY_ESCAPE)
{
device->closeDevice();
return true;
}
else if (event.KeyInput.Key == irr::KEY_KEY_W)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_S)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_A)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_D)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_Q)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_E)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_Z)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_C)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_N)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_M)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_R)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_X)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_G)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_B)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_I)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_K)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_O)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_U)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_J)
{
}
else if (event.KeyInput.Key == irr::KEY_KEY_L)
{
}
else if (event.KeyInput.Key == irr::KEY_F1)
{
if (missile)
rotateCameraTowardsNodeAroundYAxis(camera, missile, 5.0f, true, false);
}
else if (event.KeyInput.Key == irr::KEY_F2)
{
}
else if (event.KeyInput.Key == irr::KEY_F3)
{
bool enabled = camera->isInputReceiverEnabled();
camera->setInputReceiverEnabled(!enabled);
device->getCursorControl()->setVisible(enabled);
}
else if (event.KeyInput.Key == irr::KEY_F4)
{
projectile->reset();
projectile->setPosition(missileStartPos);
projectile->setDirection(missileStartDir);
projectile->start();
trajectoryPoints.clear();
}
else if (event.KeyInput.Key == irr::KEY_F5)
{
projectile->pause(device->getTimer()->getTime());
}
else if (event.KeyInput.Key == irr::KEY_F6)
{
projectile->resume(device->getTimer()->getTime());
}
else if (event.KeyInput.Key == irr::KEY_F7)
{
}
else if (event.KeyInput.Key == irr::KEY_F8)
{
}
else if (event.KeyInput.Key == irr::KEY_F9)
{
}
else if (event.KeyInput.Key == irr::KEY_F10)
{
}
else if (event.KeyInput.Key == irr::KEY_F11)
{
}
else if (event.KeyInput.Key == irr::KEY_F12)
{
}
else if (event.KeyInput.Key == irr::KEY_UP)
{
}
else if (event.KeyInput.Key == irr::KEY_DOWN)
{
}
else if (event.KeyInput.Key == irr::KEY_LEFT)
{
}
else if (event.KeyInput.Key == irr::KEY_RIGHT)
{
}
else if (event.KeyInput.Key == irr::KEY_PRIOR)
{
}
else if (event.KeyInput.Key == irr::KEY_NEXT)
{
}
}
else
{
}
return false;
}
};
int main()
{
device = createDevice
(
video::EDT_OPENGL, core::dimension2d<u32>(1024, 768),
32,
false, false, false
);
if (device == 0)
return 1;
EventReceiver receiver;
device->setEventReceiver(&receiver);
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
io::IFileSystem* fs = device->getFileSystem();
fs->addFileArchive(IRRLICHT_DIR);
// load building:
scene::IMeshSceneNode* q3node = 0;
c8* filename = "media/map-20kdm2.pk3";
bool fileOk = fs->addZipFileArchive(filename);
if (!fileOk)
{
printf("Error adding file:\n%s\nto the file system\n\n", filename);
return -1;
}
io::path bspPath = "20kdm2.bsp";
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh(bspPath);
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
if (q3node)
q3node->setPosition(core::vector3df(-1350.0f, -130.0f, -1400.0f));
////////////////////////////////////////////////////////////////////////////
// setup missile:
missileStartPos = core::vector3df(450.0f, 400.0f, 260.0f);
core::vector3df dir(0.0f, 1.0f, 0.5f);
dir.normalize();
missileStartDir = dir;
missileSpeed = 200.0f;
// correct mesh so that forward is in z-dir
core::matrix4 mat;
mat.setRotationDegrees(core::vector3df(0.0f, 180.0f, 0.0f));
smgr->getMeshManipulator()->transform(smgr->getMesh("media/dwarf.x"), mat);
missile = smgr->addMeshSceneNode(smgr->getMesh("media/dwarf.x"));
for (u32 n=0; n<missile->getMaterialCount(); ++n)
missile->getMaterial(n).setFlag(video::EMF_LIGHTING, false);
missile->setScale(core::vector3df(1.0f));
missile->setPosition(missileStartPos);
//setupSmoke(missile);
////////////////////////////////////////////////////////////////////////////
// setup projectile:
projectile = new scene::CSceneNodeAnimatorProjectile(60000, missileSpeed); //lifespan, speed
scene::CSceneNodeAnimatorProjectile::alignNode(missile, missileStartDir);
////////////////////////////////////////////////////////////////////////////
// setup camera:
f32 camRotateSpeed = 100.0f;
f32 camMoveSpeed = 0.5f;
f32 camJumpSpeed = 3.0f;
camera = smgr->addCameraSceneNodeFPS(0, camRotateSpeed, camMoveSpeed, -1, 0, 0, false, camJumpSpeed);
//camera->setPosition(core::vector3df(50.0f, 50.0f, -60.0f));
//camera->setTarget(core::vector3df(-70.0f, 30.0f, -60.0f));
camera->setPosition(missile->getPosition() + core::vector3df(100.0f, 50.0f, 100.0f));
camera->setTarget(missile->getPosition());
////////////////////////////////////////////////////////////////////////////
// create skybox
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
smgr->addSkyBoxSceneNode
(
driver->getTexture("media/irrlicht2_up.jpg"),
driver->getTexture("media/irrlicht2_dn.jpg"),
driver->getTexture("media/irrlicht2_lf.jpg"),
driver->getTexture("media/irrlicht2_rt.jpg"),
driver->getTexture("media/irrlicht2_ft.jpg"),
driver->getTexture("media/irrlicht2_bk.jpg")
);
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
////////////////////////////////////////////////////////////////////////////
device->getCursorControl()->setVisible(false);
// ----------------------------------------------------
u32 currentTime;
u32 lastUpdate = device->getTimer()->getTime();
while (device->run())
if (device->isWindowActive())
{
currentTime = device->getTimer()->getTime();
if (currentTime > lastUpdate + 250)
{
addTrajectoryPoint(missile, trajectoryPoints);
lastUpdate = currentTime;
}
projectile->animateNode(missile, device->getTimer()->getTime());
driver->beginScene(true, true, 0);
smgr->drawAll();
// draw trajectory
drawTrajectory(trajectoryPoints);
driver->endScene();
}
else { device->yield(); }
if (projectile)
{
delete projectile;
projectile = 0;
}
//if (device)
//device->drop();
return 0;
}
Code: Select all
#include "projectile_animator.h"
namespace irr
{
namespace scene
{
//! constructor
CSceneNodeAnimatorProjectile::CSceneNodeAnimatorProjectile
(
u32 startTime,
u32 lifespan,
const core::vector3df& pos,
const core::vector3df& dir,
f32 speed
)
: ISceneNodeAnimatorFinishing(lifespan),
StartTime(startTime),
Lifespan(lifespan),
Pos(pos),
OrigPos(pos),
OldPos(pos),
Speed(speed),
Dir(dir),
//IsMaxHeightReached(false),
IsFirstRun(true),
IsStopped(true)
{
#ifdef _DEBUG
setDebugName("CSceneNodeAnimatorProjectile");
#endif
}
CSceneNodeAnimatorProjectile::CSceneNodeAnimatorProjectile
(
u32 lifespan,
f32 speed
)
: ISceneNodeAnimatorFinishing(lifespan),
StartTime(0),
Lifespan(lifespan),
Pos(core::vector3df(0.0f, 0.0f, 0.0f)),
OrigPos(core::vector3df(0.0f, 0.0f, 0.0f)),
OldPos(core::vector3df(0.0f, 0.0f, 0.0f)),
Speed(speed),
Dir(core::vector3df(0.0f, 0.0f, 1.0f)),
//IsMaxHeightReached(false),
IsFirstRun(true),
IsStopped(true)
{
#ifdef _DEBUG
setDebugName("CSceneNodeAnimatorProjectile");
#endif
}
void CSceneNodeAnimatorProjectile::alignNode(ISceneNode* node, const core::vector3df& dir)
{
core::vector3df heading(0.0f, 0.0f, 1.0f);
core::quaternion q;
q.rotationFromTo(heading, dir);
core::vector3df eulerRadians;
q.toEuler(eulerRadians);
core::vector3df rot = eulerRadians * core::RADTODEG;
node->setRotation(rot);
}
void CSceneNodeAnimatorProjectile::start()
{
if (!IsStopped)
return;
IsFirstRun = true;
IsStopped = false;
//IsMaxHeightReached = false;
}
//FIXME: abrupt change in orientation!
void CSceneNodeAnimatorProjectile::pause(u32 timeMs)
{
if (IsStopped)
return;
PauseTime = timeMs;
IsStopped = true;
}
void CSceneNodeAnimatorProjectile::resume(u32 timeMs)
{
if (!IsStopped)
return;
StartTime += timeMs - PauseTime;
IsStopped = false;
}
void CSceneNodeAnimatorProjectile::reset()
{
IsFirstRun = true;
IsStopped = true;
//IsMaxHeightReached = false;
}
//! animates a scene node
void CSceneNodeAnimatorProjectile::animateNode(ISceneNode* node, u32 timeMs)
{
if (!node)
return;
if ((IsStopped) && (!IsFirstRun)) // i.e. IsPaused
{
node->updateAbsolutePosition();
OldPos = node->getAbsolutePosition();
//OldPos = node->getPosition();
}
//if (IsMaxHeightReached)
//return;
if (IsStopped)
return;
// update internal state:
if (IsFirstRun)
{
StartTime = timeMs;
IsFirstRun = false;
return;
}
u32 dt = (timeMs - StartTime);
if (dt > Lifespan)
{
HasFinished = true;
IsStopped = true;
this->reset();
return;
}
// update node:
core::vector3df pos = this->getPosition(dt);
node->setPosition(pos);
core::vector3df rot;
if (this->getRotation(pos, OldPos, rot))
node->setRotation(rot);
// for debugging:
/***
if (pos.Y < OldPos.Y)
{
IsMaxHeightReached = true;
return;
}
***/
OldPos = pos;
}
//! Writes attributes of the scene node animator.
void CSceneNodeAnimatorProjectile::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{
out->addInt("StartTime", (s32)StartTime);
out->addInt("Lifespan", (s32)Lifespan);
out->addVector3d("Pos", Pos);
out->addVector3d("OrigPos", OrigPos);
out->addVector3d("OldPos", OldPos);
out->addVector3d("Dir", Dir);
out->addFloat("Speed", Speed);
}
//! Reads attributes of the scene node animator.
void CSceneNodeAnimatorProjectile::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
{
Speed = in->getAttributeAsFloat("Speed");
StartTime = u32(in->getAttributeAsInt("StartTime"));
Lifespan = u32(in->getAttributeAsInt("Lifespan"));
core::stringc pname = "Pos";
if (in->existsAttribute(pname.c_str()))
Pos = in->getAttributeAsVector3d(pname.c_str());
else
Pos = core::vector3df(0.0f, 0.0f, 0.0f);
pname = "OrigPos";
if (in->existsAttribute(pname.c_str()))
OrigPos = in->getAttributeAsVector3d(pname.c_str());
else
OrigPos = core::vector3df(0.0f, 0.0f, 0.0f);
pname = "OldPos";
if (in->existsAttribute(pname.c_str()))
OldPos = in->getAttributeAsVector3d(pname.c_str());
else
OldPos = core::vector3df(0.0f, 0.0f, 0.0f);
pname = "Dir";
if (in->existsAttribute(pname.c_str()))
Dir = in->getAttributeAsVector3d(pname.c_str());
else
Dir = core::vector3df(0.0f, 0.0f, 1.0f);
}
ISceneNodeAnimator* CSceneNodeAnimatorProjectile::createClone(ISceneNode* node, ISceneManager* newManager)
{
CSceneNodeAnimatorProjectile * newAnimator =
new CSceneNodeAnimatorProjectile(StartTime, Lifespan, Pos, Dir, Speed);
return newAnimator;
}
core::vector3df CSceneNodeAnimatorProjectile::getPosition(u32 dt)
{
f32 x = Dir.X * Speed * dt * 0.001f;
f32 z = Dir.Z * Speed * dt * 0.001f;
f32 uy = Dir.Y * Speed;
f32 y = uy * dt * 0.001f - GRAVITY * (dt * 0.001f) * (dt * 0.001f) * 0.5f;
return core::vector3df(x, y, z) + OrigPos;
}
bool CSceneNodeAnimatorProjectile::getRotation
(
const core::vector3df& pos,
const core::vector3df& oldPos,
core::vector3df& rot
)
{
if (pos.equals(oldPos))
return false;
core::vector3df heading(0.0f, 0.0f, 1.0f);
core::vector3df dir = pos - oldPos;
dir.normalize();
core::quaternion q;
q.rotationFromTo(heading, dir);
core::vector3df eulerRadians;
q.toEuler(eulerRadians);
rot = eulerRadians * core::RADTODEG;
return true;
}
//CSceneNodeAnimatorProjectile::
} // end namespace scene
} // end namespace irr
projectile_animator.h:
Code: Select all
#ifndef __C_SCENE_NODE_ANIMATOR_PROJECTILE_H_INCLUDED__
#define __C_SCENE_NODE_ANIMATOR_PROJECTILE_H_INCLUDED__
#include "ISceneNode.h"
//#include "irrArray.h"
#include "finishing_animator.h"
#ifndef SCENE_NODE_ANIMATOR_PROJECTILE_PROPERTIES
#define SCENE_NODE_ANIMATOR_PROJECTILE_PROPERTIES
#define GRAVITY 10.0f
#endif
namespace irr
{
namespace scene
{
class CSceneNodeAnimatorProjectile : public ISceneNodeAnimatorFinishing
{
public:
//! constructor
CSceneNodeAnimatorProjectile
(
u32 startTime,
u32 lifespan,
const core::vector3df& pos,
const core::vector3df& dir,
f32 speed
);
CSceneNodeAnimatorProjectile
(
u32 lifespan,
f32 speed
);
static
void alignNode(ISceneNode* node, const core::vector3df& dir);
virtual void start();
virtual void pause(u32 timeMs);
//virtual void pause();
virtual void resume(u32 timeMs);
//virtual void resume();
virtual void reset();
//! animates a scene node
virtual void animateNode(ISceneNode* node, u32 timeMs);
//! Writes attributes of the scene node animator.
virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const;
//! Reads attributes of the scene node animator.
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
//! Returns type of the scene node animator
virtual ESCENE_NODE_ANIMATOR_TYPE getType() const { return ESNAT_UNKNOWN; }
//! Creates a clone of this animator.
/** Please note that you will have to drop
(IReferenceCounted::drop()) the returned pointer after calling
this. */
virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0);
void setDirection(const core::vector3df& dir)
{
Dir = dir;
};
void setPosition(const core::vector3df& pos)
{
Pos = pos;
OrigPos = pos;
OldPos = pos;
}
protected:
u32 StartTime;
u32 PauseTime;
u32 Lifespan;
core::vector3df Pos;
core::vector3df OrigPos;
core::vector3df OldPos;
f32 Speed;
core::vector3df Dir;
//u32 ElapsedTime;
bool IsFirstRun;
bool IsStopped;
//bool IsMaxHeightReached;
core::vector3df getPosition(u32 dt);
bool getRotation
(
const core::vector3df& pos,
const core::vector3df& oldPos,
core::vector3df& rot
);
};
} // end namespace scene
} // end namespace irr
#endif
finishing_animator.h:
Code: Select all
// Copyright (C) 2002-2008 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#ifndef __I_SCENE_NODE_ANIMATOR_FINISHING_H_INCLUDED__
#define __I_SCENE_NODE_ANIMATOR_FINISHING_H_INCLUDED__
#include "ISceneNode.h"
namespace irr
{
namespace scene
{
//! This is an abstract base class for animators that have a discrete end time.
class ISceneNodeAnimatorFinishing : public ISceneNodeAnimator
{
public:
//! constructor
ISceneNodeAnimatorFinishing(u32 finishTime)
: FinishTime(finishTime), HasFinished(false) { }
virtual bool hasFinished(void) const { return HasFinished; }
protected:
u32 FinishTime;
bool HasFinished;
};
} // end namespace scene
} // end namespace irr
#endif
