diff --git a/bin/data/misc/dirt_test.jpg b/bin/data/misc/dirt_test.jpg new file mode 100644 index 0000000..85e8ad1 Binary files /dev/null and b/bin/data/misc/dirt_test.jpg differ diff --git a/src/Client/GUI/MCamera.h b/src/Client/GUI/MCamera.h new file mode 100644 index 0000000..187c6a6 --- /dev/null +++ b/src/Client/GUI/MCamera.h @@ -0,0 +1,392 @@ +/*-----------------------------------------------------------------------------* +| headerfile MCamera.h | +| | +| version 1.20 | +| date: (29.04.2007) | +| | +| author: Michal Švantner | +| | +| for Irrlicht engine | +| firsth person and orbiting cameras | +*-----------------------------------------------------------------------------*/ + +#ifndef MCAMERA_H +#define MCAMERA_H + +#include "irrlicht/irrlicht.h" +using namespace irr; + +class MCameraFPS +{ + scene::ICameraSceneNode* camera; + f32 rotationX; + f32 rotationY; + core::vector3df direction; + +public: + MCameraFPS(scene::ISceneManager* smgr) + { + camera = smgr->addCameraSceneNode(); + rotationX = 0.0f; + rotationY = 0.0f; + direction = core::vector3df(0,0,1); + } + + ~MCameraFPS(){} + + void turnRight(f32 i) + { + rotationY += i; + if(rotationY>=360)rotationY-=360; + if(rotationY<0)rotationY+=360; + + direction = core::vector3df(0,0,1); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (rotationX,rotationY,0)); + matrix.rotateVect(direction); + + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void turnLeft(f32 i) + { + rotationY -= i; + if(rotationY>=360)rotationY-=360; + if(rotationY<0)rotationY+=360; + + direction = core::vector3df(0,0,1); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (rotationX,rotationY,0)); + matrix.rotateVect(direction); + + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void turnUp(f32 i) + { + rotationX += i; + if(rotationX>=360)rotationX-=360; + if(rotationX<0)rotationX+=360; + + direction = core::vector3df(0,0,1); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (rotationX,rotationY,0)); + matrix.rotateVect(direction); + + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void turnDown(f32 i) + { + rotationX -= i; + if(rotationX>=360)rotationX-=360; + if(rotationX<0)rotationX+=360; + + direction = core::vector3df(0,0,1); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (rotationX,rotationY,0)); + matrix.rotateVect(direction); + + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void moveForward(f32 i) + { + core::vector3df step = core::vector3df(0,0,i); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (0,rotationY,0)); + matrix.rotateVect(step); + + camera->setPosition(camera->getPosition() + step); + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void moveBack(f32 i) + { + core::vector3df step = core::vector3df(0,0,-i); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (0,rotationY,0)); + matrix.rotateVect(step); + + camera->setPosition(camera->getPosition() + step); + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void moveRight(f32 i) + { + core::vector3df step = core::vector3df(i,0,0); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (0,rotationY,0)); + matrix.rotateVect(step); + + camera->setPosition(camera->getPosition() + step); + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void moveLeft(f32 i) + { + core::vector3df step = core::vector3df(-i,0,0); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (0,rotationY,0)); + matrix.rotateVect(step); + + camera->setPosition(camera->getPosition() + step); + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void setHeight(f32 i) + { + camera->setPosition(core::vector3df(camera->getPosition().X, i, camera->getPosition().Z)); + camera->setTarget(camera->getPosition() + direction); + camera->updateAbsolutePosition(); + } + + void setPosition(core::vector3df pos) + { + camera->setPosition(pos); + camera->updateAbsolutePosition(); + } + + core::vector3df getPosition() + { + return camera->getPosition(); + } + + core::vector3df getDirection() + { + return direction; + } + + core::vector3df getTarget() + { + return camera->getTarget(); + } + + f32 getHeading() + { + return rotationY; + } + + f32 getPitch() + { + return rotationX; + } + + f32 getFarValue() + { + return camera->getFarValue(); + } + + void setFarValue(f32 f) + { + camera->setFarValue(f); + } + + f32 getNearValue() + { + return camera->getNearValue(); + } + + void setNearValue(f32 n) + { + camera->setNearValue(n); + } + + f32 getFOV() + { + return camera->getFOV(); + } + + void setFOV(f32 v) + { + camera->setFOV(v); + } + + f32 getAspectRatio() + { + return camera->getAspectRatio(); + } + + void setAspectRatio(f32 a) + { + camera->setAspectRatio(a); + } + + scene::ISceneNode* getNode() + { + return camera; + } +}; + + + + +class MCameraOrbit +{ + scene::ICameraSceneNode* camera; + f32 rotationX; + f32 rotationY; + f32 distance; + + void update() + { + core::vector3df direction = core::vector3df(0,0,distance); + + core::matrix4 matrix; + matrix.setRotationDegrees(core::vector3df (rotationX,rotationY,0)); + matrix.rotateVect(direction); + + camera->setPosition(camera->getTarget() + direction); + camera->updateAbsolutePosition(); + } + +public: + MCameraOrbit(scene::ISceneManager* smgr) + { + camera = smgr->addCameraSceneNode(); + rotationX = 0.0f; + rotationY = 0.0f; + distance = 1.0f; + camera->setTarget(core::vector3df(0,0,0)); + update(); + } + + ~MCameraOrbit(){} + + void turnRight(f32 i) + { + rotationY += i; + if(rotationY>=360)rotationY-=360; + if(rotationY<0)rotationY+=360; + update(); + } + + void turnLeft(f32 i) + { + rotationY -= i; + if(rotationY>=360)rotationY-=360; + if(rotationY<0)rotationY+=360; + update(); + } + + void turnUp(f32 i) + { + rotationX += i; + if(rotationX>=360)rotationX-=360; + if(rotationX<0)rotationX+=360; + update(); + } + + void turnDown(f32 i) + { + rotationX -= i; + if(rotationX>=360)rotationX-=360; + if(rotationX<0)rotationX+=360; + update(); + } + + core::vector3df getTarget() + { + return camera->getTarget(); + } + + void setTarget(core::vector3df target) + { + camera->setTarget(target); + update(); + } + + core::vector3df getPosition() + { + return camera->getPosition(); + } + + f32 getDistance() + { + return distance; + } + + void setDistance(f32 i) + { + distance = i; + update(); + } + + void adjustDistance(f32 i) + { + distance += i; + update(); + } + + f32 getHeading() + { + return rotationY; + } + + f32 getPitch() + { + return rotationX; + } + + f32 getFarValue() + { + return camera->getFarValue(); + } + + void setFarValue(f32 f) + { + camera->setFarValue(f); + } + + f32 getNearValue() + { + return camera->getNearValue(); + } + + void setNearValue(f32 n) + { + camera->setNearValue(n); + } + + f32 getFOV() + { + return camera->getFOV(); + } + + void setFOV(f32 v) + { + camera->setFOV(v); + } + + f32 getAspectRatio() + { + return camera->getAspectRatio(); + } + + void setAspectRatio(f32 a) + { + camera->setAspectRatio(a); + } + + scene::ISceneNode* getNode() + { + return camera; + } +}; +#endif diff --git a/src/Client/GUI/MInput.h b/src/Client/GUI/MInput.h new file mode 100644 index 0000000..c5b4b19 --- /dev/null +++ b/src/Client/GUI/MInput.h @@ -0,0 +1,160 @@ +/*-----------------------------------------------------------------------------* +| headerfile MInput.h | +| | +| version 1.10 | +| date: (29.04.2007) | +| | +| author: Michal Švantner | +| | +| for Irrlicht engine | +| store keyboard and mouse input in structures "key" and "mouse" | +*-----------------------------------------------------------------------------*/ + +#ifndef MINPUT_H +#define MINPUT_H + +#include "irrlicht/irrlicht.h" +using namespace irr; + +struct IKeys +{ + bool pressed(EKEY_CODE keycode) + { + return code[keycode]; + } + + bool pressed_once(EKEY_CODE keycode) + { + if(code[keycode]) + { + code[keycode] = false; + return true; + } + return false; + } + + void reset() + { + for(s32 i=0; igetVideoDriver(); _smgr = _device->getSceneManager(); _guienv = _device->getGUIEnvironment(); + _timer = _device->getTimer(); //... _initialized = true; } @@ -123,6 +125,7 @@ void PseuGUI::Cancel(void) if(_scene) { + _scene->OnDelete(); delete _scene; _scene = NULL; } @@ -158,6 +161,9 @@ void PseuGUI::Run(void) while(_device && _device->run() && !_mustdie) { + _lastpasstime = _passtime; + _passtime = _timer->getTime() / 1000.0f; + _passtimediff = _passtime - _lastpasstime; // _HandleWindowResize(); // not yet used; doesnt work if (!_device->isWindowActive()) @@ -167,9 +173,13 @@ void PseuGUI::Run(void) try { + _UpdateSceneState(); + + if(_scene && _initialized) + _scene->OnUpdate(_passtimediff); + _driver->beginScene(true, true, 0); - _UpdateSceneState(); DrawCurrentScene(); _smgr->drawAll(); @@ -229,9 +239,13 @@ void PseuGUI::_UpdateSceneState(void) { if(_scenestate != _scenestate_new && _smgr) { - _smgr->clear(); if(_scene) + { + _scene->OnDelete(); delete _scene; + } + _smgr->clear(); + _guienv->clear(); _scenestate = _scenestate_new; @@ -250,10 +264,8 @@ void PseuGUI::_UpdateSceneState(void) void PseuGUI::DrawCurrentScene(void) { - if(!_initialized) - return; - if(_scene) - _scene->Draw(); + if(_scene && _initialized) + _scene->OnDraw(); } void PseuGUI::_HandleWindowResize(void) diff --git a/src/Client/GUI/PseuGUI.h b/src/Client/GUI/PseuGUI.h index ffc1b57..05613f8 100644 --- a/src/Client/GUI/PseuGUI.h +++ b/src/Client/GUI/PseuGUI.h @@ -54,6 +54,7 @@ public: ~PseuGUI(); void SetInstance(PseuInstance*); + inline PseuInstance *GetInstance(void) { return _instance; } void Run(void); void SetDriver(uint8); void SetResolution(uint16 x, uint16 y, uint16 depth=32); @@ -90,6 +91,8 @@ private: PseuInstance *_instance; SceneState _scenestate, _scenestate_new; Scene *_scene; + irr::ITimer *_timer; + float _passtime, _lastpasstime, _passtimediff; irr::core::dimension2d _screendimension; }; diff --git a/src/Client/GUI/Scene.cpp b/src/Client/GUI/Scene.cpp index 1f1541d..b8a6822 100644 --- a/src/Client/GUI/Scene.cpp +++ b/src/Client/GUI/Scene.cpp @@ -12,7 +12,20 @@ Scene::Scene(PseuGUI *g) guienv = gui->_guienv; } -void Scene::Draw(void) +void Scene::OnDraw(void) { device->yield(); } + +void Scene::OnDelete(void) +{ +} + +void Scene::OnUpdate(f32) +{ +} + +Scene::~Scene() +{ + DEBUG(logdebug("Scene::~Scene()")); +} diff --git a/src/Client/GUI/Scene.h b/src/Client/GUI/Scene.h index cad5669..c1ab379 100644 --- a/src/Client/GUI/Scene.h +++ b/src/Client/GUI/Scene.h @@ -18,8 +18,12 @@ class Scene friend class PseuGUI; public: Scene(PseuGUI *g); - virtual void Draw(void); + ~Scene(); + virtual void OnUpdate(f32); + virtual void OnDraw(void); + virtual void OnDelete(void); protected: + PseuGUI *gui; irr::IrrlichtDevice *device; irr::video::IVideoDriver* driver; @@ -31,18 +35,28 @@ class SceneGuiStart : public Scene { public: SceneGuiStart(PseuGUI *gui); - ~SceneGuiStart(); + void OnDelete(void); private: IGUIImage *irrlogo, *driverlogo; }; + +class ShTlTerrainSceneNode; +class MCameraFPS; +class MyEventReceiver; + class SceneWorld : public Scene { public: SceneWorld(PseuGUI *gui); - ~SceneWorld(); - void Draw(void); + void OnDraw(void); + void OnDelete(void); + void OnUpdate(f32); +private: + ShTlTerrainSceneNode *terrain; + MCameraFPS *camera; + MyEventReceiver *eventrecv; }; diff --git a/src/Client/GUI/SceneGuiStart.cpp b/src/Client/GUI/SceneGuiStart.cpp index 94605a3..8370abd 100644 --- a/src/Client/GUI/SceneGuiStart.cpp +++ b/src/Client/GUI/SceneGuiStart.cpp @@ -27,8 +27,7 @@ SceneGuiStart::SceneGuiStart(PseuGUI *gui) : Scene(gui) } -SceneGuiStart::~SceneGuiStart() +void SceneGuiStart::OnDelete(void) { - irrlogo->drop(); - driverlogo->drop(); + // not necessary to delete the images, because they are deleted by guienv->clear() } diff --git a/src/Client/GUI/SceneWorld.cpp b/src/Client/GUI/SceneWorld.cpp index c6eccb2..83a8d20 100644 --- a/src/Client/GUI/SceneWorld.cpp +++ b/src/Client/GUI/SceneWorld.cpp @@ -2,22 +2,134 @@ #include "PseuGUI.h" #include "PseuWoW.h" #include "Scene.h" +#include "MapTile.h" +#include "MapMgr.h" +#include "ShTlTerrainSceneNode.h" +#include "MCamera.h" +#include "MInput.h" +#include "WorldSession.h" +#include "World.h" +#include SceneWorld::SceneWorld(PseuGUI *g) : Scene(g) { + DEBUG(logdebug("SceneWorld: Initializing...")); + + s32 mapsize = 9 * 16; // 9 height floats in 16 chunks per tile per axis + s32 tilesize = UNITSIZE; + s32 meshsize = CHUNKSIZE; + vector3df terrainPos(0.0f, 0.0f, 0.0f); // TODO: use PseuWoW's world coords here? + + eventrecv = new MyEventReceiver(); + device->setEventReceiver(eventrecv); + + camera = new MCameraFPS(smgr); + camera->setNearValue(0.1f); + camera->setFarValue(tilesize*meshsize/2); + camera->setPosition(core::vector3df(mapsize*tilesize/2, 0, mapsize*tilesize/2) + terrainPos); + + terrain = new ShTlTerrainSceneNode(smgr,mapsize,mapsize,tilesize,meshsize); + terrain->drop(); + terrain->follow(camera->getNode()); + terrain->setMaterialTexture(0, driver->getTexture("data/misc/dirt_test.jpg")); + terrain->setDebugDataVisible(scene::EDS_FULL); + terrain->setMaterialFlag(video::EMF_LIGHTING, true); + terrain->setMaterialFlag(video::EMF_FOG_ENABLE, false); + terrain->setPosition(terrainPos); + + // randomize base color + for(s32 j=0; jgetSize().Height+1; j++) + for(s32 i=0; igetSize().Width+1; i++) + { + u32 g = (rand() % 150) + 80; + u32 r = (rand() % 50); + u32 b = (rand() % 50); + + terrain->setColor(i,j, video::SColor(255,r,g,b)); + } + + MapMgr *mapmgr = g->GetInstance()->GetWSession()->GetWorld()->GetMapMgr(); + + // TODO: better to do this with some ZThread Condition or FastMutex, but dont know how to. help plz! [FG] + if(!mapmgr->Loaded()) + { + logdebug("SceneWorld: Waiting until maps are loaded..."); + while(!mapmgr->Loaded()) + device->sleep(50); + } + + // something is not good here. we have terrain, but the chunks are read incorrectly. + // need to find out where which formula is wrong + // the current terrain renderer code is just a test to see if ADT files are read correctly. apparantly not :D + MapTile *maptile = mapmgr->GetCurrentTile(); + if(maptile) + { + // apply map height data + for(uint32 chx = 0; chx < 16; chx++) + for(uint32 chy = 0; chy < 16; chy++) + { + MapChunk *chunk = maptile->GetChunk(chx, chy); + std::stringstream ss; + DEBUG(logdebug("Apply MapChunk (%u, %u)",chx,chy)); + for(uint32 hy = 0; hy < 9; hy++) + { + for(uint32 hx = 0; hx < 9; hx++) + { + f32 h = chunk->hmap_rough[hy * 9 + hx]; + ss.precision(3); + ss << h << '\t'; + terrain->setHeight(9 * chx + hx, 9 * chy + hy, h); + } + ss << "\n"; + } + DEBUG(logdebug("\n%s\n",ss.str().c_str())); + } + } + else + { + logerror("SceneWorld: MapTile not loaded, can't apply heightmap!"); + } + + terrain->smoothNormals(); + + ILightSceneNode* light = smgr->addLightSceneNode(0, core::vector3df(0,0,0), + SColorf(255, 255, 255, 255), 1000.0f); + SLight ldata = light->getLightData(); + ldata.AmbientColor = video::SColorf(0.2f,0.2f,0.2f); + ldata.DiffuseColor = video::SColorf(1.0f,1.0f,1.0f); + ldata.Type = video::ELT_DIRECTIONAL; + ldata.Position = core::vector3df(-10,5,-5); + light->setLightData(ldata); + + driver->setFog(video::SColor(255,100,101,140), true, tilesize*meshsize/4, tilesize*(meshsize-4)/2, 0.05f); + + DEBUG(logdebug("SceneWorld: Init done!")); } -void SceneWorld::Draw(void) +void SceneWorld::OnUpdate(f32 timediff) { - // draw maps here + if(eventrecv->key.pressed(KEY_KEY_W)) camera->moveForward(50 * timediff); + if(eventrecv->key.pressed(KEY_KEY_S)) camera->moveBack(50 * timediff); + if(eventrecv->key.pressed(KEY_KEY_D)) camera->moveRight(50 * timediff); + if(eventrecv->key.pressed(KEY_KEY_A)) camera->moveLeft(50 * timediff); + // mouse directional control + camera->turnRight(5 * (device->getCursorControl()->getRelativePosition().X - 0.5f)); + camera->turnUp(5 * (device->getCursorControl()->getRelativePosition().Y - 0.5f)); + //device->getCursorControl()->setPosition(0.5f, 0.5f); + + // camera height control + if (eventrecv->mouse.wheel < 10) eventrecv->mouse.wheel = 10; + camera->setHeight( eventrecv->mouse.wheel + terrain->getHeight(camera->getPosition()) ); +} + +void SceneWorld::OnDraw(void) +{ // draw all objects gui->domgr.Update(); // iterate over DrawObjects, draw them and clean up - - // draw interface here - } -SceneWorld::~SceneWorld() +void SceneWorld::OnDelete(void) { -} \ No newline at end of file + DEBUG(logdebug("~SceneWorld()")); +} diff --git a/src/Client/GUI/ShTlTerrainSceneNode.cpp b/src/Client/GUI/ShTlTerrainSceneNode.cpp new file mode 100644 index 0000000..87aeeb6 --- /dev/null +++ b/src/Client/GUI/ShTlTerrainSceneNode.cpp @@ -0,0 +1,1317 @@ +#include "ShTlTerrainSceneNode.h" + +// constructor +ShTlTerrainSceneNode::ShTlTerrainSceneNode(scene::ISceneManager* pSceneManager, + s32 width, s32 height, f32 tilesize, s32 visiblesize, + scene::ISceneNode* parent, s32 id) + : scene::ISceneNode(pSceneManager->getRootSceneNode(), pSceneManager, id) +{ + Size.Width = width; + Size.Height = height; + + TileSize = tilesize; + + // make sure rendered terrain mesh is not larger than terrain itself + if(visiblesize > Size.Width) visiblesize = Size.Width; + if(visiblesize > Size.Height) visiblesize = Size.Height; + + MeshSize.Width = visiblesize; + MeshSize.Height = visiblesize; + + MeshPosition = core::vector2d(0,0); + + Fnode = NULL; + + ShStep = 1; + + // create data array + + Data.reset(Size.Width+1, Size.Height+1); + + for(s32 j=0; j(0,1); + UVdata(i,j).Vertex[1] = core::vector2d(0,0); + UVdata(i,j).Vertex[2] = core::vector2d(1,0); + UVdata(i,j).Vertex[3] = core::vector2d(1,1); + } + + // calculate number of sectors + // terrain mesh will be split to 1 or 3 or 5 sectors on each axis depending on size + s32 w = 1; + s32 h = 1; + if(MeshSize.Width >= 30) w = 3; + if(MeshSize.Height >= 30) h = 3; + if(MeshSize.Width >= 50) w = 5; + if(MeshSize.Height >= 50) h = 5; + + // create sectors + Sector.reset(w, h); + + // find size of sectors in tiles + w = MeshSize.Width / Sector.width(); + h = MeshSize.Height / Sector.height(); + + for(s32 j=0; j(w, h); + + // find size of center sector in tiles + w = MeshSize.Width - Sector(0,0).Size.Width * (Sector.width()-1); + h = MeshSize.Height - Sector(0,0).Size.Height * (Sector.height()-1); + + {s32 j= Sector.height()/2; + for(s32 i=0; i0) Sector(i,j).Offset.X = Sector(i-1,j).Offset.X + Sector(i-1,j).Size.Width; + else Sector(i,j).Offset.X = 0; + if(j>0) Sector(i,j).Offset.Y = Sector(i,j-1).Offset.Y + Sector(i,j-1).Size.Height; + else Sector(i,j).Offset.Y = 0; + } + + // fill sectors with tiles + for(s32 j=0; jColor = video::SColor(255,255,255,255); + Tile(i,j).Vertex[1]->Color = video::SColor(255,255,255,255); + Tile(i,j).Vertex[2]->Color = video::SColor(255,255,255,255); + Tile(i,j).Vertex[3]->Color = video::SColor(255,255,255,255); + } + + // setup material + video::SMaterial material; + material.AmbientColor = video::SColor(255,255,255,255); + material.DiffuseColor = video::SColor(255,255,255,255); + material.EmissiveColor = video::SColor(255,0,0,0); + material.Shininess = 0.0f; + material.SpecularColor = video::SColor(255,0,0,0); + material.MaterialType = video::EMT_DETAIL_MAP; + + Material.push_back(material); + + // create 2nd texture layer + + // find size of texture, must be power of two + s32 tw = 2; + while(tw < MeshSize.Width) tw = tw + tw; + s32 th = 2; + while(th < MeshSize.Height) th = th + th; + + // create texture + // turn of mipmaps othervise they would need to be regenerated each time texture is updated + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + + bool mmflag = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); + driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); + + CTexture = driver->addTexture(core::dimension2d(tw, th), "colortexture", video::ECF_A8R8G8B8); + + driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, mmflag); + + Material[0].Textures[1] = CTexture; + Material[0].TextureWrap[1] = video::ETC_CLAMP_TO_EDGE; + + // setup UV coordinates of vertices on 2nd texture layer + f32 ax = (f32)MeshSize.Width / CTexture->getSize().Width / MeshSize.Width; + f32 ay = (f32)MeshSize.Height/ CTexture->getSize().Height / MeshSize.Height; + f32 ry = 1.0f - (f32)MeshSize.Height/ CTexture->getSize().Height; + + u32 n = MeshSize.Height-1; + for(s32 j=0; jTCoords2 = core::vector2d(i*ax, ry+(j+1)*ay); + Tile(i,n).Vertex[1]->TCoords2 = core::vector2d(i*ax, ry+j*ay); + Tile(i,n).Vertex[3]->TCoords2 = core::vector2d((i+1)*ax, ry+(j+1)*ay); + Tile(i,n).Vertex[2]->TCoords2 = core::vector2d((i+1)*ax, ry+j*ay); + } + n--; + } + + // set update vertices flag for sectors + for(s32 j=0; jregisterNodeForRendering(this); + + ISceneNode::OnRegisterSceneNode(); +} + + + +// renders terrain +void ShTlTerrainSceneNode::render() +{ + // update position if needed + if(Fnode) centerAt(Fnode->getPosition()); + + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + if (!driver) return; + + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); + + driver->setMaterial(Material[0]); + + SectorsRendered = 0; + + // test if sectors are vissible + for(s32 j=0; jlock(); + + updateTexture(p, Sector(i,j)); + + Sector(i,j).UpdateTexture = false; + } + if(p) CTexture->unlock(); + + // render geometry + for(s32 j=0; jdrawIndexedTriangleList + (&Sector(i,j).Vertex[0], Sector(i,j).Vertex.size(), + &Sector(i,j).Index[0], Sector(i,j).Index.size()/3); + } + + // for debuging + if( DebugDataVisible == scene::EDS_OFF ) return; + + // wireframe overlay + if (DebugDataVisible == scene::EDS_MESH_WIRE_OVERLAY || DebugDataVisible == scene::EDS_FULL) + { + video::SMaterial m; + m.Lighting = false; + m.Wireframe = true; + m.BackfaceCulling = false; + m.AmbientColor = video::SColor(255,0,255,0); + m.DiffuseColor = video::SColor(255,0,255,0); + driver->setMaterial(m); + + for(s32 j=0; jdrawIndexedTriangleList + (&Sector(i,j).Vertex[0], Sector(i,j).Vertex.size(), + &Sector(i,j).Index[0], Sector(i,j).Index.size()/3); + } + } + } + + // terrain bounding box + if (DebugDataVisible == scene::EDS_BBOX || DebugDataVisible == scene::EDS_FULL) + { + video::SMaterial m; + m.Lighting = false; + driver->setMaterial(m); + + driver->draw3DBox(BoundingBox, video::SColor(0,255,255,255)); + } + + // sectors bounding boxes + if (DebugDataVisible == scene::EDS_BBOX_BUFFERS || DebugDataVisible == scene::EDS_FULL) + { + video::SMaterial m; + m.Lighting = false; + driver->setMaterial(m); + + for(s32 j=0; jdraw3DBox(Sector(i,j).BoundingBox, video::SColor(0,255,255,255)); + } + } + + // normals + if (DebugDataVisible == scene::EDS_NORMALS || DebugDataVisible == scene::EDS_FULL) + { + video::SMaterial m; + m.Lighting = false; + driver->setMaterial(m); + + for(s32 j=0; jdraw3DLine ( v[n].Pos, v[n].Pos + h, c ); + } + } + } +} + + + +// returns the axis aligned bounding box of terrain +const core::aabbox3d& ShTlTerrainSceneNode::getBoundingBox() const +{ + return BoundingBox; +} + + + +// recalculate terrain bounding box +void ShTlTerrainSceneNode::recalculateBoundingBox() +{ + BoundingBox.MinEdge.X = TileSize * MeshPosition.X; + BoundingBox.MinEdge.Y = Data(0,0).Height; + BoundingBox.MinEdge.Z = TileSize * MeshPosition.Y; + + BoundingBox.MaxEdge.X = TileSize * (MeshPosition.X + MeshSize.Width); + BoundingBox.MaxEdge.Y = Data(0,0).Height; + BoundingBox.MaxEdge.Z = TileSize * (MeshPosition.Y + MeshSize.Height); + + for(s32 j=0; j Data(i,j).Height) BoundingBox.MinEdge.Y = Data(i,j).Height; + if(BoundingBox.MaxEdge.Y < Data(i,j).Height) BoundingBox.MaxEdge.Y = Data(i,j).Height; + } + + + for(s32 j=0; j ShTlTerrainSceneNode::getSize() +{ + return Size; +} + + + +// return dimension of terrain tile +f32 ShTlTerrainSceneNode::getTileSize() +{ + return TileSize; +} + + + +// set new dimensions of terrain tiles +void ShTlTerrainSceneNode::setTileSize(f32 newsize) +{ + if(newsize != TileSize) + { + TileSize = newsize; + + update(); + } +} + + + +// returns dimension of rendered mesh in tiles +core::dimension2d ShTlTerrainSceneNode::getRenderedSize() +{ + return MeshSize; +} + + + +// return number of sectors +u32 ShTlTerrainSceneNode::getSectorCount() +{ + return Sector.width() * Sector.height(); +} + + + +// returns numner of sectors rendered last frame +u32 ShTlTerrainSceneNode::getSectorsRendered() +{ + return SectorsRendered; +} + + + +// return height of terrain spot at terrain coordinates +f32 ShTlTerrainSceneNode::getHeight(u32 w, u32 h) +{ + return Data(w,h).Height; +} + + + +// return height of terrain at any position +f32 ShTlTerrainSceneNode::getHeight(core::vector3df pos) +{ + // find position relative to terrain + pos = pos - getPosition(); + + if(pos.X>=0 && pos.X<(f32)Size.Width*TileSize && pos.Z>=0 && pos.Z<(f32)Size.Height*TileSize) + { + // calculatin coordinates of tile + s32 x = (s32) (pos.X / TileSize); + s32 y = (s32) (pos.Z / TileSize); + + // calculating position relative to tile + f32 xtil = pos.X - (x*TileSize); + f32 ytil = pos.Z - (y*TileSize); + + // finding trianle of tile + core::triangle3df triangle; + if(xtil<=ytil) + { + // upper left triangle of tile + triangle.pointA = core::vector3df(0, getHeight(x,y), 0); + triangle.pointB = core::vector3df(0, getHeight(x,y+1), TileSize); + triangle.pointC = core::vector3df(TileSize, getHeight(x+1,y+1), TileSize); + } + else + { + // lower right triangle of tile + triangle.pointA = core::vector3df(0, getHeight(x,y), 0); + triangle.pointB = core::vector3df(TileSize, getHeight(x+1,y+1), TileSize); + triangle.pointC = core::vector3df(TileSize, getHeight(x+1,y), 0); + } + + // calculating intersection with triangle + core::vector3df intersection; + triangle.getIntersectionWithLine(core::vector3df(xtil,0,ytil),core::vector3df(0,1,0),intersection); + + return intersection.Y + getPosition().Y; + } + else + return 0; +} + + + +// set relative height of terrain spot at terrain coordinates +void ShTlTerrainSceneNode::setHeight(u32 w, u32 h, f32 newheight) +{ + Data(w,h).Height = newheight; + + // recalculate bounding boxes + + if(newheight > BoundingBox.MaxEdge.Y) + { + BoundingBox.MaxEdge.Y = newheight; + + for(s32 j=0; j 0) // check if not out of array + v0 = core::vector3df(0, getHeight(w,h-1)-getHeight(w,h), -TileSize); + else + v0 = core::vector3df(0, 0, -TileSize); + // calculate vector to point -1,-1 + if(w > 0 && h > 0) // check if not out of array + v1 = core::vector3df(-TileSize, getHeight(w-1,h-1)-getHeight(w,h), -TileSize); + else + v1 = core::vector3df(-TileSize, 0, -TileSize); + n0 = v0.crossProduct(v1); + + // calculate vector to point -1,-1 + v0 = v1; + // calculate vector to point -1,0 + if(w > 0) + v1 = core::vector3df(-TileSize, getHeight(w-1,h)-getHeight(w,h), 0); + else + v1 = core::vector3df(-TileSize, 0, 0); + n1 = v0.crossProduct(v1); + + // calculate vector to point -1,0 + v0 = v1; + // calculate vector to point 0,1 + if(h < Size.Height) + v1 = core::vector3df(0, getHeight(w,h+1)-getHeight(w,h), TileSize); + else + v1 = core::vector3df(0, 0, TileSize); + n2 = v0.crossProduct(v1); + + // calculate vector to point 0,1 + v0 = v1; + // calculate vector to point 1,1 + if(w < Size.Width && h < Size.Height) + v1 = core::vector3df(TileSize, getHeight(w+1,h+1)-getHeight(w,h), TileSize); + else + v1 = core::vector3df(TileSize, 0, TileSize); + n3 = v0.crossProduct(v1); + + // calculate vector to point 1,1 + v0 = v1; + // calculate vector to point 1,0 + if(w < Size.Width) + v1 = core::vector3df(TileSize, getHeight(w+1,h)-getHeight(w,h), 0); + else + v1 = core::vector3df(TileSize, 0, 0); + n4 = v0.crossProduct(v1); + + // calculate vector to point 1,0 + v0 = v1; + // calculate vector to point 0,-1 + if(h > 0) + v1 = core::vector3df(0, getHeight(w,h-1)-getHeight(w,h), -TileSize); + else + v1 = core::vector3df(0, 0, -TileSize); + n5 = v0.crossProduct(v1); + + // calculate normals of 4 tiles around point + core::vector3df m0, m1, m2, m3; + m0 = (n1 - n0) /2 + n0; + m1 = n2; + m2 = (n4 - n3) /2 + n3; + m3 = n5; + + // calculate normals between oposing tiles + core::vector3df k0, k1; + k0 = (m2 - m0) /2 + m0; + k1 = (m3 - m1) /2 + m1; + + // calculate normal of point + core::vector3df n = (k1 - k0) /2 + k0; + n.normalize(); + setNormal(w,h,n); +} + +// recalculare normals of whole terrain making it look smooth under light +void ShTlTerrainSceneNode::smoothNormals() +{ + for(s32 j=0; j ShTlTerrainSceneNode::getTileUV(u32 w, u32 h, TILE_VERTEX corner) +{ + return UVdata(w,h).Vertex[corner]; +} + + + +// set texture coordinates of tile +void ShTlTerrainSceneNode::setTileUV(u32 w, u32 h, core::vector2d UVlowerLeft, + core::vector2d UVupperLeft, core::vector2d UVupperRight, + core::vector2d UVlowerRight) +{ + UVdata(w,h).Vertex[0] = UVlowerLeft; + UVdata(w,h).Vertex[1] = UVupperLeft; + UVdata(w,h).Vertex[2] = UVupperRight; + UVdata(w,h).Vertex[3] = UVlowerRight; +} + + + +// stretch texture over whole terrain +void ShTlTerrainSceneNode::stretchTexture(core::vector2d scale) +{ + f32 ax = scale.X / Size.Width; + f32 ay = scale.X / Size.Height; + + u32 n = Size.Height-1; + for(s32 j=0; j(i*ax, j*ay); + UVdata(i,n).Vertex[0] = core::vector2d(i*ax, j*ay+ay); + UVdata(i,n).Vertex[3] = core::vector2d(i*ax+ax, j*ay+ay); + UVdata(i,n).Vertex[2] = core::vector2d(i*ax+ax, j*ay); + } + n--; + } +} + + + +// stretch texture over every tile individualy +void ShTlTerrainSceneNode::stretchTextureOverTile(core::vector2d scale) +{ + for(s32 j=0; j(0, 0); + UVdata(i,j).Vertex[0] = core::vector2d(0, scale.Y); + UVdata(i,j).Vertex[3] = core::vector2d(scale.X, scale.Y); + UVdata(i,j).Vertex[2] = core::vector2d(scale.X, 0); + } +} + + + +// rotate texture of tile 90 degrees +void ShTlTerrainSceneNode::rotateTileTexture90(u32 w, u32 h) +{ + core::vector2d tmp = UVdata(w,h).Vertex[3]; + + UVdata(w,h).Vertex[3] = UVdata(w,h).Vertex[2]; + UVdata(w,h).Vertex[2] = UVdata(w,h).Vertex[1]; + UVdata(w,h).Vertex[1] = UVdata(w,h).Vertex[0]; + UVdata(w,h).Vertex[0] = tmp; +} + + + +// rotate texture of tile 180 degrees +void ShTlTerrainSceneNode::rotateTileTexture180(u32 w, u32 h) +{ + core::vector2d tmp = UVdata(w,h).Vertex[3]; + + UVdata(w,h).Vertex[3] = UVdata(w,h).Vertex[1]; + UVdata(w,h).Vertex[1] = tmp; + + tmp = UVdata(w,h).Vertex[2]; + + UVdata(w,h).Vertex[2] = UVdata(w,h).Vertex[0]; + UVdata(w,h).Vertex[0] = tmp; +} + + + +// rotate texture of tile 270 degrees +void ShTlTerrainSceneNode::rotateTileTexture270(u32 w, u32 h) +{ + core::vector2d tmp = UVdata(w,h).Vertex[3]; + + UVdata(w,h).Vertex[3] = UVdata(w,h).Vertex[0]; + UVdata(w,h).Vertex[0] = UVdata(w,h).Vertex[1]; + UVdata(w,h).Vertex[1] = UVdata(w,h).Vertex[2]; + UVdata(w,h).Vertex[2] = tmp; +} + + + +// flip (mirror) texture of tile horizontaly +void ShTlTerrainSceneNode::flipTileTextureHorizontal(u32 w, u32 h) +{ + core::vector2d tmp = UVdata(w,h).Vertex[3]; + + UVdata(w,h).Vertex[3] = UVdata(w,h).Vertex[0]; + UVdata(w,h).Vertex[0] = tmp; + + tmp = UVdata(w,h).Vertex[2]; + + UVdata(w,h).Vertex[2] = UVdata(w,h).Vertex[1]; + UVdata(w,h).Vertex[1] = tmp; +} + + + +// flip (mirror) texture of tile verticaly +void ShTlTerrainSceneNode::flipTileTextureVertical(u32 w, u32 h) +{ + core::vector2d tmp = UVdata(w,h).Vertex[3]; + + UVdata(w,h).Vertex[3] = UVdata(w,h).Vertex[2]; + UVdata(w,h).Vertex[2] = tmp; + + tmp = UVdata(w,h).Vertex[0]; + + UVdata(w,h).Vertex[0] = UVdata(w,h).Vertex[1]; + UVdata(w,h).Vertex[1] = tmp; +} + + + +// get color of tile at terrain coordinates +video::SColor ShTlTerrainSceneNode::getColor(u32 w, u32 h) +{ + return Data(w,h).Color; +} + + + +// set color of tile at terrain coordinates +void ShTlTerrainSceneNode::setColor(u32 w, u32 h, video::SColor newcolor) +{ + Data(w,h).Color = newcolor; +} + + + +// set rendered mesh position relative to terrain +void ShTlTerrainSceneNode::setMeshPosition(core::vector2d pos) +{ + // correct if out of terrain bounds + if(pos.X < 0) pos.X = 0; + if(pos.Y < 0) pos.Y = 0; + + if(pos.X > (s32)(Size.Width - MeshSize.Width)) pos.X = (s32)(Size.Width - MeshSize.Width); + if(pos.Y > (s32)(Size.Height - MeshSize.Height)) pos.Y = (s32)(Size.Height - MeshSize.Height); + + // update + if(pos.X > MeshPosition.X + ShStep || pos.X < MeshPosition.X - ShStep || + pos.Y > MeshPosition.Y + ShStep || pos.Y < MeshPosition.Y - ShStep) + { + MeshPosition = pos; + + update(); + } +} + + + +// center rendered mesh at 3d coordinates +void ShTlTerrainSceneNode::centerAt(core::vector3d pos) +{ + // find position relative to terrain + pos.X = pos.X - getPosition().X; + pos.Z = pos.Z - getPosition().Z; + + // offset center + pos.X = pos.X - TileSize * MeshSize.Width/2; + pos.Z = pos.Z - TileSize * MeshSize.Height/2; + + // find equivalent in tiles + s32 x = (s32)(pos.X / TileSize); + s32 y = (s32)(pos.Z / TileSize); + + setMeshPosition(core::vector2d(x,y)); +} + + + +// update rendered mesh +void ShTlTerrainSceneNode::update() +{ + // update position of bounding boxes + BoundingBox.MinEdge = core::vector3df(MeshPosition.X*TileSize, BoundingBox.MinEdge.Y, MeshPosition.Y*TileSize); + BoundingBox.MaxEdge = core::vector3df( (MeshPosition.X+MeshSize.Width)*TileSize, BoundingBox.MaxEdge.Y, (MeshPosition.Y+MeshSize.Height)*TileSize); + + for(s32 j=0; j line, core::vector3df &outIntersection) +{ + //get relative position of line to terrain + line.start = line.start - getPosition(); + line.end = line.end - getPosition(); + + // find 2d vector of line + core::vector2d vector; + vector.X = line.end.X - line.start.X; + vector.Y = line.end.Z - line.start.Z; + + // calculate variables used later in calculating coordinates on line + // used equations are: "x = y * a + c" and "y = x * b + d" + // where "a = x / y" and "b = y / x" + f32 a = 0; + if(vector.Y != 0) a = vector.X / vector.Y; + + f32 b = 0; + if(vector.X != 0) b = vector.Y / vector.X; + + // calculate starting and ending tiles + core::vector3d start; + start.X = (s32)(line.start.X / TileSize); + start.Y = (s32)(line.start.Z / TileSize); + + core::vector3d end; + end.X = (s32)(line.end.X / TileSize); + end.Y = (s32)(line.end.Z / TileSize); + + // some variables dependant on line orientation + s32 xs = 1; + s32 xc = 0; + if(vector.X < 0) + { + xs = -1; + xc = 1; + } + + s32 ys = 1; + s32 yc = 0; + if(vector.Y < 0) + { + ys = -1; + yc = 1; + } + + // calculate tiles which can intersect with line based on their 2d position + // then test for intersection + + // take y and calculate x coordinates of tile + if( core::abs_(vector.X) > core::abs_(vector.Y) ) + { + s32 x = start.X; + s32 y; + + for(y=start.Y+yc; y!=end.Y+yc; y+=ys) + { + f32 tmp = (f32)(y+ys) * TileSize - line.start.Z; + tmp = a * tmp + line.start.X; + s32 xn = (s32)(tmp / TileSize); + + for(x; x!=xn+xs; x+=xs) + if( getIntersectionWithTile(x, y-yc, line, outIntersection) ) + { + outIntersection += getPosition(); + return true; + } + + x = xn; + } + + for(x; x!=end.X+xs; x+=xs) + if( getIntersectionWithTile(x, y-yc, line, outIntersection) ) + { + outIntersection += getPosition(); + return true; + } + } + // take x and calculate y coordinates of tile + else + { + s32 y = start.Y; + s32 x; + + for(x=start.X+xc; x!=end.X+xc; x+=xs) + { + f32 tmp = (f32)(x+xs) * TileSize - line.start.X; + tmp = b * tmp + line.start.Z; + s32 yn = (s32)(tmp / TileSize); + + for(y; y!=yn+ys; y+=ys) + if( getIntersectionWithTile(x-xc, y, line, outIntersection) ) + { + outIntersection += getPosition(); + return true; + } + + y = yn; + } + + for(y; y!=end.Y+ys; y+=ys) + if( getIntersectionWithTile(x-xc, y, line, outIntersection) ) + { + outIntersection += getPosition(); + return true; + } + } + + return false; +} + + + +// load height data from texture +void ShTlTerrainSceneNode::loadHeightMap(const c8 *filename, f32 scale, u32 w, u32 h) +{ + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + if(!driver) return; + + video::IImage *image = driver->createImageFromFile(filename); + if(!image) return; + + s32 tw = image->getDimension().Width; + s32 th = image->getDimension().Height; + + s32 we = w + tw; + if(we > Size.Width+1) we = Size.Width+1; + s32 he = h + th; + if(he > Size.Height+1) he = Size.Height+1; + + tw = 0; + th = 0; + + for(s32 j=h; jgetPixel(tw, th); + + Data(i,j).Height = (f32)color.getLuminance()/255 * scale; + + tw++; + } + tw = 0; + th++; + } + + image->drop(); + + recalculateBoundingBox(); + + smoothNormals(); +} + + + +// load color data from texture +void ShTlTerrainSceneNode::loadColorMap(const c8 *filename, u32 w, u32 h) +{ + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + if(!driver) return; + + video::IImage *image = driver->createImageFromFile(filename); + if(!image) return; + + s32 tw = (u32)image->getDimension().Width; + s32 th = (u32)image->getDimension().Height; + + s32 we = w + tw; + if(we > Size.Width+1) we = Size.Width+1; + s32 he = h + th; + if(he > Size.Height+1) he = Size.Height+1; + + tw = 0; + th = 0; + + for(s32 j=h; jgetPixel(tw, th); + + Data(i,j).Color = color; + + tw++; + } + tw = 0; + th++; + } + + image->drop(); +} + + + +// return true if sector is on screen +bool ShTlTerrainSceneNode::isSectorOnScreen(TlTSector* sctr) +{ + // bounding box of sector + core::aabbox3d box = sctr->BoundingBox; + + // get absolute position of bounding box + box.MinEdge = box.MinEdge + getPosition(); + box.MaxEdge = box.MaxEdge + getPosition(); + + // get camera frustrum planes + const scene::SViewFrustum* frustrum = SceneManager->getActiveCamera()->getViewFrustum(); + core::plane3d Left = frustrum->planes[scene::SViewFrustum::VF_LEFT_PLANE]; + core::plane3d Right = frustrum->planes[scene::SViewFrustum::VF_RIGHT_PLANE]; + core::plane3d Top = frustrum->planes[scene::SViewFrustum::VF_TOP_PLANE]; + core::plane3d Bottom = frustrum->planes[scene::SViewFrustum::VF_BOTTOM_PLANE]; + core::plane3d Near = frustrum->planes[scene::SViewFrustum::VF_NEAR_PLANE]; + core::plane3d Far = frustrum->planes[scene::SViewFrustum::VF_FAR_PLANE]; + + // test sector bounding box against planes + s32 leftRel, rightRel, topRel, bottomRel, nearRel, farRel; + + nearRel = box.classifyPlaneRelation(Near); + if(nearRel == core::ISREL3D_FRONT) return false; + + leftRel = box.classifyPlaneRelation(Left); + if(leftRel == core::ISREL3D_FRONT) return false; + + rightRel = box.classifyPlaneRelation(Right); + if(rightRel == core::ISREL3D_FRONT) return false; + + bottomRel = box.classifyPlaneRelation(Bottom); + if(bottomRel == core::ISREL3D_FRONT) return false; + + farRel = box.classifyPlaneRelation(Far); + if(farRel == core::ISREL3D_FRONT) return false; + + topRel = box.classifyPlaneRelation(Top); + if(topRel == core::ISREL3D_FRONT) return false; + + return true; +} + + + +// update vertices of sector +void ShTlTerrainSceneNode::updateVertices(TlTSector §or) +{ + scene::SMeshBuffer* MeshBuffer=new scene::SMeshBuffer(); + scene::SMesh* Mesh=new scene::SMesh(); + + for(u32 j=sector.Offset.Y; jPos = core::vector3df(x*TileSize, Data(x,y).Height, y*TileSize); + Tile(i,j).Vertex[1]->Pos = core::vector3df(x*TileSize, Data(x,y+1).Height, (y+1)*TileSize); + Tile(i,j).Vertex[2]->Pos = core::vector3df( (x+1)*TileSize, Data(x+1,y+1).Height, (y+1)*TileSize); + Tile(i,j).Vertex[3]->Pos = core::vector3df( (x+1)*TileSize, Data(x+1,y).Height, y*TileSize); + + // update normals + Tile(i,j).Vertex[0]->Normal = Data(x,y).Normal; + Tile(i,j).Vertex[1]->Normal = Data(x,y+1).Normal; + Tile(i,j).Vertex[2]->Normal = Data(x+1,y+1).Normal; + Tile(i,j).Vertex[3]->Normal = Data(x+1,y).Normal; + + // update texture coordinates + Tile(i,j).Vertex[0]->TCoords = UVdata(x,y).Vertex[0]; + Tile(i,j).Vertex[1]->TCoords = UVdata(x,y).Vertex[1]; + Tile(i,j).Vertex[2]->TCoords = UVdata(x,y).Vertex[2]; + Tile(i,j).Vertex[3]->TCoords = UVdata(x,y).Vertex[3]; + + for (int z=0;z<4;z++) + MeshBuffer->Vertices.push_back(video::S3DVertex( + Tile(i,j).Vertex[z]->Pos, + core::vector3df(0,0,0), video::SColor(0,0,0,0), core::vector2df(0,0))); + } + + for (u32 z=0;zIndices.push_back( + sector.Index[z]); + } + + Mesh->addMeshBuffer(MeshBuffer); + MeshBuffer->drop(); + + Mesh->drop(); +} + + + +// update 2nd texture layer +void ShTlTerrainSceneNode::updateTexture(u32* p, TlTSector §or) +{ + u32 x, y; + + // in case created texure is larger than terrain mesh, update one more pixel + // on each axis to get rid of unused pixels at the border blended in to used ones + u32 w = 0; + if(MeshSize.Width < CTexture->getSize().Width) w = 1; + u32 h = 0; + if(MeshSize.Height < CTexture->getSize().Height) h = 1; + + for(u32 j=sector.Offset.Y; jgetSize().Height-1 - j; + for(u32 i=sector.Offset.X; igetSize().Width + i] = Data(x,y).Color.color; + } + } +} + + + +// test if 3d line colide with tile +// returns true if yes, false if not and store intersection in "outIntersection" vector +bool ShTlTerrainSceneNode::getIntersectionWithTile(s32 w, s32 h, core::line3d line, core::vector3df &outIntersection) +{ + // test if not out of terrain bounds + if(w < 0) return false; + if(h < 0) return false; + if(w > Size.Width-1) return false; + if(h > Size.Height-1) return false; + + // vertices + core::vector3df v0(w*TileSize, getHeight(w,h), h*TileSize); + core::vector3df v1(w*TileSize, getHeight(w,h+1), (h+1)*TileSize); + core::vector3df v2((w+1)*TileSize, getHeight(w+1,h+1), (h+1)*TileSize); + core::vector3df v3((w+1)*TileSize, getHeight(w+1,h), h*TileSize); + + // firsth test collision with tile bounding box + core::aabbox3d box; + box.reset(v0); + box.addInternalPoint(v1); + box.addInternalPoint(v2); + box.addInternalPoint(v3); + + if (!box.intersectsWithLine(line)) return false;; + + // test collision with tile itself + + core::triangle3df triangle; + + // test upper left trialgle of tile + triangle.set(v0,v1,v2); + bool collision1 = triangle.getIntersectionWithLimitedLine(line, outIntersection); + + // test lower right triangle of tile + triangle.set(v0,v2,v3); + core::vector3df intersect2; + bool collision2 = triangle.getIntersectionWithLimitedLine(line, intersect2); + + // compare results and decide what to return + if(collision2) + { + // if line collide with both triangles, test which colision is closer to start of line + if(collision1) + { + f32 distance1 = line.start.getDistanceFromSQ(outIntersection); + f32 distance2 = line.start.getDistanceFromSQ(intersect2); + if(distance2 < distance1) outIntersection = intersect2; + return true; + } + // if it collide only with second one + else + { + outIntersection = intersect2; + return true; + } + } + + return collision1; +} diff --git a/src/Client/GUI/ShTlTerrainSceneNode.h b/src/Client/GUI/ShTlTerrainSceneNode.h new file mode 100644 index 0000000..75d9c9e --- /dev/null +++ b/src/Client/GUI/ShTlTerrainSceneNode.h @@ -0,0 +1,317 @@ +/*-----------------------------------------------------------------------------* +| headerfile ShTlTerrainSceneNode.h | +| | +| version 2.00 | +| date: (29.04.2007) | +| | +| author: Michal Švantner | +| | +| Shifting Tiled Terrain Scene Node | +| | +| This node can render quit large terrain consisting of tiles by storing data | +| of individual tiles in array and rendering only part of terrain around | +| defined position and in defined size. This can be position of another scene | +| node defined by user and terrain node can be set to render around it | +| automaticaly. | +| It is done by creating mesh of given size which then glide over invisible | +| data skelet like a peace of soft cloth, updating its atributes from data | +| arrays each time it shifts to a new position. | +| | +| Diferent atributes of individual tiles can be adjusted, most of them on run | +| time. Namely texture coordinates, position of tile vertices, normals and | +| color. | +| | +| Scene node use 2 texture layers. | +| Firsth texture is standard texture which gives detail to terrain close by | +| and can by assigned to individual tiles by setting their UV coordinates. | +| User have complete freedom as howe to apply this texture to tiles. Texture | +| can for example consist of seweral subtextures and each can be set to | +| diferent tiles using UV coordinates. | +| | +| Second texture is created by terrain node internaly and is used to render | +| color of individual tiles. This adds some variability to terrain at distance.| +| Both textures are blended together using EMT_DETAIL_MAP material type. | +| | +| Normaly textures should be set in oposite order but there are some bugs with | +| updating texture under OpenGL so until it gets solved I have to stick with | +| detailmap applyed as firsth texture layer. Not perfect but working. | +| | +| This scene node also provide user with functions to get height at given | +| coordinates and test 3d line for collision useful for line of sight or bullet| +| collision testing. | +| | +| Rendered mesh is split in to seweral sectors which are culled individualy. | +| This exclude around 60% of polygoons from rendering. | +| | +| Writen for Irrlicht engine version 1.3 | +*-----------------------------------------------------------------------------*/ + +#ifndef SHTLTERRAINSCENENODE_H +#define SHTLTERRAINSCENENODE_H + +#include "irrlicht/irrlicht.h" +using namespace irr; + +#include "TlTMesh.h" + +// Shifting Tiled Terrain Scene Node class +class ShTlTerrainSceneNode : public scene::ISceneNode +{ + // dimensions of whole terrain + core::dimension2d Size; + + // bounding box of terrain mesh + core::aabbox3d BoundingBox; + + // terrain vertex data + array2d Data; + + // terrain tile UV data for 1th texture layer + array2d UVdata; + + // terrain mesh sectors + array2d Sector; + + // array of pointers to vertices of tiles + array2d Tile; + + // size of vissible terrain mesh + core::dimension2d MeshSize; + + // position of vissible terrain mesh relative to whole terrain in tiles + core::vector2d MeshPosition; + + // material + core::array Material; + + // size of terrain tiles + f32 TileSize; + + // node terrain mesh should be rendered around + scene::ISceneNode* Fnode; + + // color texture set as 2nd texture layer + video::ITexture* CTexture; + + // number of sectors rendered last frame + u32 SectorsRendered; + + // howe many tiles should be skiped before terrain mesh gets updated + s32 ShStep; + + // return true if sector is on screen + virtual bool isSectorOnScreen(TlTSector* sctr); + + // update vertices of sector + virtual void updateVertices(TlTSector §or); + + // update 2nd texture layer + virtual void updateTexture(u32* p, TlTSector §or); + + // return true if 3d line colide with tile + virtual bool getIntersectionWithTile(s32 w, s32 h, core::line3d line, + core::vector3df &outIntersection); + +public: + + // constructor + // \param smgr -pointer to scene manager + // \param width - width of terrain in tiles + // \param height - heighth of terrain in tiles + // \param tilesize -size of tile + // \param rendersize -size of rendered terrain mesh in tiles + // \param parent -parent scene node + // \param id -ID number + ShTlTerrainSceneNode(scene::ISceneManager* pSceneManager, s32 width, s32 height, + f32 tilesize, s32 rendersize, scene::ISceneNode* parent = 0, s32 id = -1); + + // destructor + ~ShTlTerrainSceneNode(); + + // frame + virtual void OnRegisterSceneNode(); + + // renders terrain + virtual void render(); + + // returns the axis aligned bounding box of terrain + virtual const core::aabbox3d& getBoundingBox() const; + + // recalculate terrain bounding box + virtual void recalculateBoundingBox(); + + // returns amount of materials used by terrain + // this terrain uses only one material + virtual u32 getMaterialCount(); + + // returns the material of terrain based on the zero based index + // \param i -index of material to return + virtual video::SMaterial& getMaterial(u32 i); + + // return number of tiles to skip before terrain mesh gets updated + virtual s32 getStep(); + + // set number of tiles to skip before terrain mesh gets updated, default is 1 + // updating slows down rendering and seting step higher will cause terrain to update less ofthen + // \param newstep -amount of tiles to skip before updating + virtual void setStep(u32 newstep); + + // return dimensions of whole terrain in tiles + virtual core::dimension2d getSize(); + + // return dimension of terrain tile + virtual f32 getTileSize(); + + // set new dimensions of terrain tile + // \param newsize -new size of tile + virtual void setTileSize(f32 newsize); + + // returns dimension of rendered mesh in tiles + virtual core::dimension2d getRenderedSize(); + + // return number of sectors into which terrain mesh is divided + virtual u32 getSectorCount(); + + // returns sectors rendered last frame + virtual u32 getSectorsRendered(); + + // return relative height of terrain spot at terrain coordinates + // \param w -width coordinate of spot in tiles + // \param h -height coordinate of spot in tiles + virtual f32 getHeight(u32 w, u32 h); + + // return height of terrain at any position + // \param pos -3d coordinates at which to get height of terrain + virtual f32 getHeight(core::vector3df pos); + + // set relative height of terrain spot at terrain coordinates + // \param w -width coordinate of spot in tiles + // \param h -height coordinate of spot in tiles + // \param newheight -new height of spot + virtual void setHeight(u32 w, u32 h, f32 newheight); + + // return normal of terrain at terrain coordinates + // \param w -width coordinate of spot in tiles + // \param h -height coordinate of spot in tiles + virtual core::vector3df getNormal(u32 w, u32 h); + + // set normal of terrain at terrain coordinates + // \param w -width coordinate of spot in tiles + // \param h -height coordinate of spot in tiles + // \param newnormal -new normal vector terrain spot + virtual void setNormal(s32 w, s32 h, core::vector3df newnormal); + + // recalculate normal at terrain coordinates + // \param w -width coordinate of spot in tiles + // \param h -height coordinate of spot in tiles + virtual void recalculateNormal(s32 w, s32 h); + + // recalculare normals of whole terrain making it look smooth under light + virtual void smoothNormals(); + + // get texture coordinates of tile corner + // \param w -width coordinate of tile + // \param h -height coordinate of tile + // \param corner -tile corner, can be LOWER_LEFT, UPPER_LEFT, UPPER_RIGHT, LOWER_RIGHT + virtual core::vector2d getTileUV(u32 w, u32 h, TILE_VERTEX corner); + + // set texture coordinates of tile + // \param w -width coordinate of tile + // \param h -height coordinate of tile + // \param UVLowerLeft -UV coordinates of lower left corner + // \param UVUpperLeft -UV coordinates of upper left corner + // \param UVUpperRight -UV coordinates of upper right corner + // \param UVLowerRight -UV coordinates of lower right corner + virtual void setTileUV(u32 w, u32 h, core::vector2d UVLowerLeft, + core::vector2d UVUpperLeft, core::vector2d UVUpperRight, + core::vector2d UVLowerRight); + + // stretch texture over whole terrain + // \param scale -scale of the texture stretched, 2,2 would stretch it twice + virtual void stretchTexture(core::vector2d scale = core::vector2d(1,1)); + + // stretch texture over every tile individualy + // \param scale -scale of the texture stretched, 0.5,0.5 would display half of texture over each tile + virtual void stretchTextureOverTile(core::vector2d scale = core::vector2d(1,1)); + + // rotate texture of tile 90 degrees + // \param w -width coordinate of tile + // \param h -height coordinate of tile + virtual void rotateTileTexture90(u32 w, u32 h); + + // rotate texture of tile 180 degrees + // \param w -width coordinate of tile + // \param h -height coordinate of tile + virtual void rotateTileTexture180(u32 w, u32 h); + + // rotate texture of tile 270 degrees + // \param w -width coordinate of tile + // \param h -height coordinate of tile + virtual void rotateTileTexture270(u32 w, u32 h); + + // flip (mirror) texture of tile horizontaly + // \param w -width coordinate of tile + // \param h -height coordinate of tile + virtual void flipTileTextureHorizontal(u32 w, u32 h); + + // flip (mirror) texture of tile verticaly + // \param w -width coordinate of tile + // \param h -height coordinate of tile + virtual void flipTileTextureVertical(u32 w, u32 h); + + // get color of tile at terrain coordinates + // \param w -width coordinate of tile + // \param h -height coordinate of tile + virtual video::SColor getColor(u32 w, u32 h); + + // set color of tile at terrain coordinates + // \param w -width coordinate of tile + // \param h -height coordinate of tile + // \param newcolor -new color of tile + virtual void setColor(u32 w, u32 h, video::SColor newcolor); + + // set rendered mesh position relative to terrain + // note that origin of mesh is in its lower left corner + // \param pos -new position of mesh relative to terrain in tiles + virtual void setMeshPosition(core::vector2d pos); + + // center rendered mesh at 3d coordinates + // \param pos -new position mesh should be rendered around + virtual void centerAt(core::vector3d pos); + + // update rendered mesh + virtual void update(); + + // set scene node terrain mesh should be automaticly rendered arround + // can be camera or player node for example + // \param node -scene node to automaticaly follow + virtual void follow(scene::ISceneNode* node); + + // cancel pervious function + // stop following scene node if any + virtual void stop(); + + // test if 3d line colide with terrain + // returns true if yes, false if not and store intersection in "outIntersection" vector + // \param line -3d line + // \param outIntersection -vector to store intersection point if any + virtual bool getIntersectionWithLine( core::line3d line, core::vector3df &outIntersection); + + // load height data from texture + // parameters allow to specify place where to load data, which makes possible + // to load terrain from seweral smaller textures + // \param filename -filename of texture + // \param scale -scale to apply at height data, if you want white color to be height of 10.5 set scale to 10.5 + // \param w -width coordinate of tile were to start loading + // \param h -height coordinate of tile were to start loading + virtual void loadHeightMap(const c8 *filename, f32 scale, u32 w = 0, u32 h = 0); + + // load color data from texture + // parameters allow to specify place where to load data, which makes possible + // to load terrain from seweral smaller textures + // \param filename -filename of texture + // \param w -width coordinate of tile were to start loading + // \param h -height coordinate of tile were to start loading + virtual void loadColorMap(const c8 *filename, u32 w = 0, u32 h = 0); +}; +#endif diff --git a/src/Client/GUI/TlTMesh.h b/src/Client/GUI/TlTMesh.h new file mode 100644 index 0000000..f274475 --- /dev/null +++ b/src/Client/GUI/TlTMesh.h @@ -0,0 +1,148 @@ +/*-----------------------------------------------------------------------------* +| headerfile TLTMesh.h | +| | +| version 1.00 | +| date: (17.04.2007) | +| | +| author: Michal Švantner | +| | +| Some structures used for Tiled Terrain | +| Writen for Irrlicht engine version 1.3 | +*-----------------------------------------------------------------------------*/ + +#ifndef TLTMESH_H +#define TLTMESH_H + +#include +using namespace irr; + + + +// dynamic 2d array +template class array2d +{ + T** data; + s32 w, h; + +public: + array2d() : w(0), h(0) {} + + array2d(int width, int height) : w(width), h(height) + { + data = new T*[w]; + for(int i=0; i Vertex[4]; +}; + + + +// structure which holds some data about spot on tiled terrain +struct TlTData +{ + f32 Height; + core::vector3df Normal; + video::SColor Color; +}; + + + +// structure which is used as meshbuffer for Tiled Terrain +class TlTSector +{ +public: + TlTSector() {}; + ~TlTSector() {}; + // position relative to whole mesh of which sector is part in tiles + core::vector2d Offset; + + // dimension of sector in tiles + core::dimension2d Size; + + // array of vertices + core::array Vertex; + + // array of indices + core::array Index; + + // axis aligned bounding box + core::aabbox3d BoundingBox; + + // update texture flag + bool UpdateTexture; + + // update vertices flag + bool UpdateVertices; + + // vissibility flag + bool isVissible; +}; +#endif diff --git a/src/Client/World/MapMgr.cpp b/src/Client/World/MapMgr.cpp index 30dfc6b..ab46545 100644 --- a/src/Client/World/MapMgr.cpp +++ b/src/Client/World/MapMgr.cpp @@ -7,6 +7,7 @@ MapMgr::MapMgr() { _tiles = new MapTileStorage(); _gridx = _gridy = _mapid = (-1); + _mapsLoaded = false; } MapMgr::~MapMgr() @@ -33,8 +34,8 @@ void MapMgr::Update(float x, float y, uint32 m) _gridx = _gridy = (-1); // must load tiles now } uint32 xg,yg; // MapTile IDs. Range 0..64 - xg = (uint32)( (ZEROPOINT - x) / TILESIZE); - yg = (uint32)( (ZEROPOINT - y) / TILESIZE); + xg = GetGridCoord(x); + yg = GetGridCoord(y); if(xg != _gridx || yg != _gridy) { _LoadNearTiles(xg,yg,m); @@ -47,6 +48,7 @@ void MapMgr::Update(float x, float y, uint32 m) void MapMgr::Flush(void) { + _mapsLoaded = false; for(uint32 i = 0; i < 4096; i++) _tiles->UnloadMapTile(i); logdebug("MAPMGR: Flushed all maps"); @@ -62,6 +64,7 @@ void MapMgr::_LoadNearTiles(uint32 gx, uint32 gy, uint32 m) _LoadTile(h,v,m); } } + _mapsLoaded = true; } void MapMgr::_LoadTile(uint32 gx, uint32 gy, uint32 m) @@ -115,11 +118,33 @@ void MapMgr::_UnloadOldTiles(void) } } +// Using forceLoad is VERY ineffective here, because the tile is removed again after the next Update() call! +MapTile *MapMgr::GetTile(uint32 xg, uint32 yg, bool forceLoad) +{ + MapTile *tile = _tiles->GetTile(xg,yg); + if(!tile) + { + _LoadTile(xg,yg,_mapid); + tile = _tiles->GetTile(xg,yg); + } + return tile; +} + +MapTile *MapMgr::GetCurrentTile(void) +{ + return GetTile(_gridx,_gridy); +} + +uint32 MapMgr::GetGridCoord(float f) +{ + return (ZEROPOINT - f) / TILESIZE; +} + float MapMgr::GetZ(float x, float y) { uint32 xg,yg; // MapTile IDs. Range 0..64 - xg = (uint32)( (ZEROPOINT - x) / TILESIZE); - yg = (uint32)( (ZEROPOINT - y) / TILESIZE); + xg = GetGridCoord(x); + yg = GetGridCoord(y); MapTile *tile = _tiles->GetTile(xg,yg); if(tile) { @@ -133,7 +158,4 @@ float MapMgr::GetZ(float x, float y) logerror("MapMgr::GetZ() called for not loaded MapTile (%u, %u) for (%f, %f)",xg,yg,x,y); return 0; } - - - diff --git a/src/Client/World/MapMgr.h b/src/Client/World/MapMgr.h index 12f1a59..1eabd7c 100644 --- a/src/Client/World/MapMgr.h +++ b/src/Client/World/MapMgr.h @@ -2,6 +2,7 @@ #define MAPMGR_H class MapTileStorage; +class MapTile; class MapMgr { @@ -11,6 +12,10 @@ public: void Update(float,float,uint32); void Flush(void); float GetZ(float,float); + uint32 GetGridCoord(float f); + MapTile *GetTile(uint32 xg, uint32 yg, bool forceLoad = false); + MapTile *GetCurrentTile(void); + inline bool Loaded(void) { return _mapsLoaded; } private: MapTileStorage *_tiles; @@ -19,6 +24,7 @@ private: void _UnloadOldTiles(void); uint32 _mapid; uint32 _gridx,_gridy; + bool _mapsLoaded; }; #endif \ No newline at end of file diff --git a/src/Client/World/World.h b/src/Client/World/World.h index 716c19c..29e2b12 100644 --- a/src/Client/World/World.h +++ b/src/Client/World/World.h @@ -17,6 +17,7 @@ public: void UpdatePos(float,float,uint32); void UpdatePos(float,float); inline float GetPosZ(float x, float y); + inline MapMgr *GetMapMgr(void) { return _mapmgr; } private: WorldSession *_session; diff --git a/src/Client/World/WorldSession.cpp b/src/Client/World/WorldSession.cpp index 3e589ab..c7cdd67 100644 --- a/src/Client/World/WorldSession.cpp +++ b/src/Client/World/WorldSession.cpp @@ -313,12 +313,12 @@ void WorldSession::_HandleAuthChallengeOpcode(WorldPacket& recvPacket) // recvPacket << ziped_UI_Plugins_Info // TODO: add addon data, simulate no addons. auth<<(uint32)0; // no addons? no idea, but seems to work. MaNGOS doesnt accept without this. - auth.SetOpcode(CMSG_AUTH_SESSION); + auth.SetOpcode(CMSG_AUTH_SESSION); SendWorldPacket(auth); - // note that if the sessionkey/auth is wrong or failed, the server sends the following packet UNENCRYPTED! - // so its not 100% correct to init the crypt here, but it should do the job if authing was correct + // note that if the sessionkey/auth is wrong or failed, the server sends the following packet UNENCRYPTED! + // so its not 100% correct to init the crypt here, but it should do the job if authing was correct _socket->InitCrypt(GetInstance()->GetSessionKey().AsByteArray(), 40); } @@ -939,6 +939,13 @@ void WorldSession::_HandleLoginVerifyWorldOpcode(WorldPacket& recvPacket) delete _world; _world = new World(this); _world->UpdatePos(x,y,m); + + // temp. solution to test terrain rendering + PseuGUI *gui = GetInstance()->GetGUI(); + if(gui) + { + gui->SetSceneState(SCENESTATE_WORLD); + } } ByteBuffer& operator>>(ByteBuffer& bb, WhoListEntry& e) diff --git a/src/Client/main.cpp b/src/Client/main.cpp index 0bc201d..ab176ff 100644 --- a/src/Client/main.cpp +++ b/src/Client/main.cpp @@ -1,7 +1,10 @@ +#include + #include "common.h" #include "main.h" #include "PseuWoW.h" + std::list instanceList; // TODO: move this to a "Master" class later @@ -68,12 +71,20 @@ void abortproc(void) } } +void _new_handler(void) +{ + logcritical("ERROR: Out of memory!"); + throw; +} + int main(int argc, char* argv[]) { try { + set_new_handler(_new_handler); _log_setcolor(true,LGREEN); printf("+----------------------------------+\n" "| (C) 2006,2007 Snowstorm Software |\n" + "| http://www.mangosclient.org |\n" "+----------------------------------+\n"); _log_resetcolor(true); diff --git a/src/Client/main.h b/src/Client/main.h index 1c5cd4d..cc09eda 100644 --- a/src/Client/main.h +++ b/src/Client/main.h @@ -6,6 +6,7 @@ void _UnhookSignals(void); void _OnSignal(int); void quitproc(void); void abortproc(void); +void _new_handler(void); int main(int,char**); #endif \ No newline at end of file diff --git a/src/PseuWoW.vcproj b/src/PseuWoW.vcproj index d18df29..7f30a2b 100644 --- a/src/PseuWoW.vcproj +++ b/src/PseuWoW.vcproj @@ -414,6 +414,12 @@ + + + + @@ -432,6 +438,15 @@ + + + + + +