/*
这个例子展示如何实现移动和动画。
场景节点最基本的观点,就想我们看到的,是用户可以通过键盘影星移动操作。
当然,我们需要包含头文件,使用irr命名空间,并告诉连接器链接到.lib文件
*/
#include <irrlicht.h>
#include <iostream>
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
/*
在这个例子中,我们的目标之一就是用键盘操作移动场景节点,
我们储存一个指针,存储了我们想移动的场景节点。
其他指针是我们需要的时间接受方面的鬼火设备指针来操作场景节点及移动摄像机。
*/
scene::ISceneNode* node = 0;
IrrlichtDevice* device = 0;//可以不用赋值
/*
为了让让诸如鼠标键盘等输入设备或者gui时间发生时好像点击了ok键,
我们需要一个来自于事件接受的对象。
只有一个函数被覆盖:OnEvent。
这个函数会在事件触发是有引擎调用。
我们会用这个输入设备利用W、S两键去移动场景节点。
*/
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
/*
如果W/S键弹起时,我们先获得场景节点然后修改Y轴坐标。
这样当你按下W键的时候,节点就会上移,按下S的时候就会下移。
*/
if (node != 0 && event.EventType == irr::EET_KEY_INPUT_EVENT&&
!event.KeyInput.PressedDown)
/*
当节点不为0--------------------------------------------节点是从哪里获得的?是指当前的场景节点么?
&&
事件类型为EET_KEY_INPUT_EVENT
键盘输入事件,键盘事件同时也由设备创建然后传递到鬼火引擎的传递用户事件。
他们和鼠标事件使用相同的路径。
&&
按键按下为松开?
event.KeyInput.PressedDown:
struct SKeyInput irr::SEvent::KeyInput
PressedDown为keyInput结构体中的布尔变量
如果没有按下,则按键被松开?(if not pressed, then the key was left up)
*/
{
switch(event.KeyInput.Key)
/*
EKEY_CODE:枚举类型的键值 Key
记录被按下或谈起的键值
*/
{
case KEY_KEY_W:
case KEY_KEY_S:
{
core::vector3df v = node->getPosition();
v.Y += event.KeyInput.Key == KEY_KEY_W ? 2.0f : -2.0f;
node->setPosition(v);
}
return true;
}
}
return false;
}
};
/*
用于移动场景的事件接受器已经准备完。
接下来可以创建一个鬼火设备并且以我们希望的方式移动。
我们同样可以创建一些附加的场景节点,这样可以真是有其他可以移动场景产生动画的方式。
*/
int main()
{
//让用户选择渲染设备
video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 0;
}
//创建设备
MyEventReceiver receiver;
IrrlichtDevice* device = createDevice( driverType, core::dimension2d<s32>(640, 480),
16, false, false, false, &receiver);
if (device == 0)
return 1; //不能创建所选驱动器
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
/*
创建节点来显示W/S移动时的效果。
我们创建一个球节点,它是一个原始的几何图元。
我们将它放置在(0,0,30)并且分配一个材质给他,让它看起来好看点。
因为在场景中我们没有动态的灯光,我们无法照亮每个模型,换句话说,就是模型们都是黑的~
*/
node = smgr->addSphereSceneNode();
/*
用于为了测试目的在场景中加入一个球体。
返回指向所创建节点的指针。
此指针不可用drop函数销毁。(创建的函数名字未以create开始)
参数解释:
1 半径:球体的半径。(默认为0.5f)
2 多边形数:场景节点的父节点,若无父母可为空。(默认为16)
3 id号:节点的id号。可用于辨场景节点。(默认为0)
4 位置:相对于父节点的位置。(默认为(0,0,0))
5 旋转角度:场景节点的初始旋转角度。(默认为(1.0f,1.0f,1.0f))
6 缩放比例:场景节点的初始缩放比例。(默认为(1.0f,1.0f,1.0f))
*/
node->setPosition(core::vector3df(0,0,30));
node->setMaterialTexture(0, driver->getTexture("http://www.cnblogs.com/media/wall.bmp"));
/*
设置这个场景节点中新材质的相应层来源。
参数解释:
1 材质层:材质层的设置。必须是大于0且小于MATERIAL_MAX_TEXTURES的值。
2 材质:所要用的材质。
*/
node->setMaterialFlag(video::EMF_LIGHTING, false);
/*
为所有包含的材质赋一个新值。
参数解释:
1 标志:用于设置所有材质的标志。
2 新值:要设定所有材质的新值。
*/
/*
现在我们开始创建另一个节点,并用一个场景节点动画器移动它。
场景节点动画器可以修改场景节点,他可以依附于任何场景节点,如网格场景节点,广告牌,灯甚至摄像机节点。
场景节点动画器不但可以修改场景节点的位置,他还可以改变对象纹理的材质以产生动画效果。
我们创建一个立方体场景节点并且附加一个'fly circle'场景节动画器给它,让这个节点围绕我们的球节点飞行。
*/
scene::ISceneNode* n = smgr->addCubeSceneNode();
//创建用于测试的正方体节点。(其他参照球节点)
if (n)
{
n->setMaterialTexture(0, driver->getTexture("http://www.cnblogs.com/media/t351sml.jpg"));
n->setMaterialFlag(video::EMF_LIGHTING, false);
scene::ISceneNodeAnimator* anim =
smgr->createFlyCircleAnimator(core::vector3df(0,0,30), 20.0f);
/*
让物体随一点按环形旋转。
参数解释:
1 中心:环形中心。
2 半径:环形半径
3 速度:指定飞行速度。(默认0.01f)
4 方向:指定飞行方向。()
*/
n->addAnimator(anim);
anim->drop();
//在场景绑定了场景节点动画器之后,场景就可以进行动画了,若果之后不需要动画器就可以删除了。
}
/*
我们加入的最后一个可以添加场景节点动画器的场景节点是一个md2模型,
我们用一个'fly straight'的场景节点动画器,使它在两点间往返。
*/
scene::IAnimatedMeshSceneNode* anms = smgr->addAnimatedMeshSceneNode(smgr->getMesh("http://www.cnblogs.com/media/sydney.md2"));
/*
增加一个场景节点来渲染一个动画模型。
参数解释:
1 网格:要载入的动画模型的指针。
2 父节点:场景节点的父节点。如果没有可以为空。
3 id号:节点的id号。可以用于识别场景节点。
4 位置:应该放置与相对于其父节点的空间位置。
5 旋转:场景模型的初始旋转角度。
6 缩放:场景模型的初始缩放值。
7 失败时加载:如果指针为0则加载这个场景节点。
*/
if (anms)
{
scene::ISceneNodeAnimator* anim =
smgr->createFlyStraightAnimator(core::vector3df(100,0,60),
core::vector3df(-100,0,60), 2500, true);
/*
添加一个直线的场景节点动画器。
参数解释:
1 起始点:直线的起点。
2 结束点:直线的结点。
3 路程的时间:以毫秒的单位的时间,设定从起点到终点所需的时间。
4 循环:如果设置为false,则结点到达终点时停止。否则再回到起点。
*/
anms->addAnimator(anim);
//给要动画的节点添加一个动画器。
anim->drop();
/*
为了让模型看起来更好些,我们设置动画场景间循环,
将模型旋转180°,并调整动画速度和纹理。
为了使动画有正确的速度及帧数,
我们我们也可以调用 "anms->setMD2Animation(scene::EMAT_RUN)"
来完成'run'这个动画为替换'setFrameLoop'和"setAnimationSpeed",
但这是在md2模型动画的时候有效,所以你知道如何开始其他动画。
可是不要尝试将每秒的动画桢速率开的过高。
*/
anms->setMaterialFlag(video::EMF_LIGHTING, false);
anms->setFrameLoop(160, 183);
anms->setAnimationSpeed(40);
anms->setMD2Animation(scene::EMAT_RUN);
/*
开启一个md2动画。
使用这个方法可以轻易的开始md2网格的运动,攻击,结束(die?)等诸如此类的动画。
否则,没什么发生。
这个函数使用了一个字符串去来识别这个动画。
如果这个动画器是标准的md2动画器,
你可能希望使用EMD2_ANIMATION_TYPE美剧类型来开始这个动画。
*/
anms->setRotation(core::vector3df(0,180.0f,0));
anms->setMaterialTexture(0, driver->getTexture("http://www.cnblogs.com/media/sydney.bmp"));
}
/*
为了看到在这个场景中的环形移动,
我们创建一个第一人称射击类型的摄像机并且让鼠标光标不可见。
*/
scene::ICameraSceneNode * cam = smgr->addCameraSceneNodeFPS(0, 100.0f, 100.0f);
device->getCursorControl()->setVisible(false);
/*
添加一个彩色的鬼火logo
*/
device->getGUIEnvironment()->addImage(
driver->getTexture("http://www.cnblogs.com/media/irrlichtlogoalpha2.tga"),
core::position2d<s32>(10,10));
/*
我们已经准备妥当,现在就可以绘制了。
我们还要指定当前每秒的帧数以、驱动器的名字及窗口标题。
*/
int lastFPS = -1;
while(device->run())
{
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll(); // 绘制3d场景
device->getGUIEnvironment()->drawAll(); //绘制gui环境(the logo)
driver->endScene();
int fps = driver->getFPS();
//在这里声明fps应该是每次循环都会新建一个变量,为什么不再while之外声明变量,在里面调用,这样不会比较快么?
if (lastFPS != fps)
{
core::stringw tmp(L"Movement Example - Irrlicht Engine [");
tmp += driver->getName();
tmp += L"] fps: ";
tmp += fps;
device->setWindowCaption(tmp.c_str());
lastFPS = fps;
}
}
/*
最后,删除鬼火设备。
*/
device->drop();
return 0;
}