I've got a rough rough solution for doing a terrain mesh that uses less and less detail as it gets further away from you. No height maps yet, there are a few bugs in it still (with bounding boxes and octave interlocking). I was hoping if I put it up, some of you might find it interesting and suggest improvements or even help me with it (remember I'm brand spanking new to C++). Also, I'm going to use the last of my newbie browny points and just paste it here (I've still got it all in the one file). Sorry, but it's very late here, and I'm too stuffed to put together a download package of it at my site.
Anyways, here you go, 40 hrs of blood sweat and tears spread out over 1200 lines of code...
Code: Select all
#include <iostream>
#include <Irrlicht.h>
#include <time.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#pragma comment(lib, "Irrlicht.lib")
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SETTINGS /////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// setup key engine objects
IrrlichtDevice* device = createDevice(EDT_DIRECT3D9,dimension2d<s32>(1024,768),32,false,false,false);
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();
ICursorControl* CursorControl;
int binaryRef[] = { 0,1,2,4,8,16,32,64,128,256,1024 }; // couldn't remember/figure out how to do binary from i *blush* ITS LATE!!!
// keyboard registry
bool keys[irr::KEY_KEY_CODES_COUNT];
// mouse registry
bool mouseDownL;
bool mouseDownM;
bool mouseDownR;
f32 lastWheelMovement;
position2d<f32> cursor;
position2d<f32> cursorOld;
position2d<f32> cursorDelta;
// camera registry
f32 cameraOrbit = 45;
f32 cameraAngle = 0;
f32 cameraDistance = 30;
f32 cameraOrbitOld = 0;
f32 cameraAngleOld = 0;
f32 cameraZoomMin = 5;
f32 cameraZoomMax = 500;
// player registry
f32 playerX = 0;
f32 playerY = 0;
f32 playerZ = 0;
vector3df playerPosition = vector3df(0,0,0);
f32 playerCompass = 30;
f32 playerTurnTo = 0;
f32 playerTurnSpeed = 2;
f32 playerMoveSpeed = 0.5;
// terrain node settings
static const s32 tNode_cellWidth = 1;
static const s32 tNode_cellsPerEdge = 16;
static const s32 tNode_width = tNode_cellWidth * tNode_cellsPerEdge;
static const s32 tNode_octaves = 7;
// terrain patterns
S3DVertex verticesF[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesN[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesNE[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesE[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesSE[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesS[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesSW[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesW[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
S3DVertex verticesNW[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
// [A]xis [A]ligned [B]ounding [B]ox in [3D] space
aabbox3d<f32> boxF,boxN,boxNE,boxE,boxSE,boxS,boxSW,boxW,boxNW;
// number of cells * 6 (each cell has two tris, each tri has 3 indices)
u16 indicesF[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesN[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesNE[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesE[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesSE[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesS[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesSW[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesW[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
u16 indicesNW[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASS : MyEventReceiver //////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class MyEventReceiver : public IEventReceiver
{
public:
virtual bool OnEvent(const SEvent& event)
{
if(event.EventType == irr::EET_KEY_INPUT_EVENT)
{
keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
return false;
}
if (event.EventType == EET_MOUSE_INPUT_EVENT)
{
// left mouse button state check
if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) mouseDownL = true;
if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) mouseDownL = false;
// middle mouse button state check
if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) mouseDownM = true;
if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) mouseDownM = false;
if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
{
cameraDistance -= event.MouseInput.Wheel * (cameraDistance / 20) * 2;
if(cameraDistance < cameraZoomMin) cameraDistance = cameraZoomMin;
if(cameraDistance > cameraZoomMax) cameraDistance = cameraZoomMax;
}
// right mouse button state check
if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
{
mouseDownR = true;
cursorOld.X = event.MouseInput.X;
cursorOld.Y = event.MouseInput.Y;
cursorDelta.X = 0;
cursorDelta.Y = 0;
cameraOrbitOld = cameraOrbit;
cameraAngleOld = cameraAngle;
}
if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) mouseDownR = false;
// mouse move check
if(event.MouseInput.Event == EMIE_MOUSE_MOVED)
{
// add condition that right mouse button be down
if(mouseDownR == true){
cursor.X = event.MouseInput.X;
cursor.Y = event.MouseInput.Y;
cursorDelta.X = cursor.X - cursorOld.X;
cursorDelta.Y = cursor.Y - cursorOld.Y;
if(cursorDelta.Y > 100) cursorDelta.Y = 100;
if(cursorDelta.Y < -100) cursorDelta.Y = -100;
cameraOrbit = (int)(cameraOrbitOld + cursorDelta.X) % 360;
cameraAngle = (int)(cameraAngleOld + cursorDelta.Y) % 360;
if(cameraAngle > 88) cameraAngle = 88;
if(cameraAngle < -88) cameraAngle = -88;
}
}
return false;
}
return false;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASS : CterrainNode /////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CterrainNode : public ISceneNode
{
private:
IVideoDriver *driver; // A link through to the driver object
SMaterial Material; // A material object of type SMaterial
vector3df originalPosition; // first placement of the terrain node (used for repositioning based on the tile intervals)
int nodeType; // 0=full, 1=N, 2=NE, 3=E, 4=SE, 5=S, 6=SW, 7=W, 8=NW
bool nodeDisplay;
// internal vertices, [A]xis [A]ligned [B]ounding [B]ox in [3D] space and indices array
S3DVertex vertices[((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1))];
aabbox3d<f32> box;
u16 indices[(tNode_cellsPerEdge * tNode_cellsPerEdge) * 6];
s32 defaultVertexColorR, defaultVertexColorG, defaultVertexColorB;
public:
/////////////////
// constructor //
/////////////////
CterrainNode(ISceneNode* parent, ISceneManager* mgr, s32 id, s32 nodeType) : ISceneNode(parent, mgr, id)
{
// put link to videoDriver into already prepared var of type IVideoDriver
driver = SceneManager->getVideoDriver();
// set material settings
Material.Wireframe = true;
Material.Lighting = false;
Material.BackfaceCulling = false;
// set node properties
nodeDisplay = true;
setNodeType(nodeType);
}
/////////////////////////
// OnRegisterSceneNode //
/////////////////////////
virtual void OnRegisterSceneNode()
{
if (IsVisible) ISceneNode::OnRegisterSceneNode();
}
///////////////
// OnAnimate //
///////////////
virtual void OnAnimate(u32 timeMs)
{
ISceneNode::OnAnimate(timeMs);
}
////////////
// render //
////////////
virtual void render()
{
driver->setMaterial(Material);
driver->setTransform(ETS_WORLD, AbsoluteTransformation);
driver->drawIndexedTriangleList(&vertices[0], (tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1),&indices[0],((tNode_cellsPerEdge*tNode_cellsPerEdge)*2));
}
////////////////////
// getBoundingBox //
////////////////////
virtual const aabbox3d<f32>& getBoundingBox() const
{
return box;
}
//////////////////////
// getMaterialCount //
//////////////////////
virtual u32 getMaterialCount()
{
return 1;
}
/////////////////
// getMaterial //
/////////////////
virtual SMaterial& getMaterial(s32 i)
{
return Material;
}
/////////////////////////
// setOriginalPosition //
/////////////////////////
virtual void setOriginalPosition(vector3df coords)
{
originalPosition = coords;
}
/////////////////////////
// getOriginalPosition //
/////////////////////////
virtual vector3df getOriginalPosition()
{
return originalPosition;
}
////////////////////
// setNodeDisplay //
////////////////////
virtual void setNodeDisplay(bool status)
{
nodeDisplay = status;
}
////////////////////
// getNodeDisplay //
////////////////////
virtual bool getNodeDisplay()
{
return nodeDisplay;
}
/////////////////
// getNodeType //
/////////////////
virtual int getNodeType()
{
return nodeType;
}
/////////////////
// setNodeType //
/////////////////
virtual void setNodeType(int nodeType)
{
int vCount = ((tNode_cellsPerEdge+1) * (tNode_cellsPerEdge+1));
int iCount = (tNode_cellsPerEdge * tNode_cellsPerEdge) * 6;
if(nodeType==0) {
for(int i=0; i<vCount; i++) vertices[i] = verticesF[i];
for(int i=0; i<iCount; i++) indices[i] = indicesF[i];
box = boxF;
}
if(nodeType==1) {
for(int i=0; i<vCount; i++) vertices[i] = verticesN[i];
for(int i=0; i<iCount; i++) indices[i] = indicesN[i];
box = boxN;
}
if(nodeType==2) {
for(int i=0; i<vCount; i++) vertices[i] = verticesNE[i];
for(int i=0; i<iCount; i++) indices[i] = indicesNE[i];
box = boxNE;
}
if(nodeType==3) {
for(int i=0; i<vCount; i++) vertices[i] = verticesE[i];
for(int i=0; i<iCount; i++) indices[i] = indicesE[i];
box = boxE;
}
if(nodeType==4) {
for(int i=0; i<vCount; i++) vertices[i] = verticesSE[i];
for(int i=0; i<iCount; i++) indices[i] = indicesSE[i];
box = boxSE;
}
if(nodeType==5) {
for(int i=0; i<vCount; i++) vertices[i] = verticesS[i];
for(int i=0; i<iCount; i++) indices[i] = indicesS[i];
box = boxS;
}
if(nodeType==6) {
for(int i=0; i<vCount; i++) vertices[i] = verticesSW[i];
for(int i=0; i<iCount; i++) indices[i] = indicesSW[i];
box = boxSW;
}
if(nodeType==7) {
for(int i=0; i<vCount; i++) vertices[i] = verticesW[i];
for(int i=0; i<iCount; i++) indices[i] = indicesW[i];
box = boxW;
}
if(nodeType==8) {
for(int i=0; i<vCount; i++) vertices[i] = verticesNW[i];
for(int i=0; i<iCount; i++) indices[i] = indicesNW[i];
box = boxNW;
}
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION : sphericalXYZ //////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
vector3df sphericalXYZ(f32 compassAngle, f32 elevationAngle, f32 radius){
compassAngle = compassAngle * -1;
elevationAngle = elevationAngle * -1;
elevationAngle = elevationAngle + 90;
f32 x = radius * cos(compassAngle * PI/180.0f ) * sin(elevationAngle * PI/180.0f );
f32 z = radius * sin(compassAngle * PI/180.0f ) * sin(elevationAngle * PI/180.0f );
f32 y = radius * cos(elevationAngle * PI/180.0f );
vector3df result;
result.X = x;
result.Y = y;
result.Z = z;
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION : int2string ////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
stringw int2string(int intToConvert){
char temp[50];
sprintf(temp,"%d",intToConvert);
return temp;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// MAIN /////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Setup ////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////
// setup system settings //
///////////////////////////
MyEventReceiver rv;
device->setEventReceiver(&rv);
for(int x=0; x<irr::KEY_KEY_CODES_COUNT; x++) keys[x] = false;
device->setWindowCaption(L"HUGE :: Habitat Ultramedia Game Engine");
IGUIStaticText* debug_panel = guienv->addStaticText(L"Debug Window",rect<s32>(5, 5, 200, 200),false,true,0,-1,false);
////////////////////////
// setup the "player" //
////////////////////////
// temporary player proxy
ISceneNode* cPivot1 = smgr->addCubeSceneNode(0.5,0,-1,vector3df(playerPosition.X,playerPosition.Y,playerPosition.Z));
cPivot1->setMaterialTexture(0,driver->getTexture("media/wall.bmp"));
cPivot1->setMaterialFlag(video::EMF_LIGHTING, false);
ISceneNode* cPivot2 = smgr->addCubeSceneNode(2,cPivot1,-1,vector3df(0,1,0));
cPivot2->setMaterialTexture(0,driver->getTexture("media/wall.bmp"));
cPivot2->setMaterialFlag(video::EMF_LIGHTING, false);
ISceneNode* cPivot3 = smgr->addCubeSceneNode(1,cPivot2,-1,vector3df(0,0,2));
cPivot3->setMaterialTexture(0,driver->getTexture("media/wall.bmp"));
cPivot3->setMaterialFlag(video::EMF_LIGHTING, false);
// setup the camera
ICameraSceneNode* myCamera = smgr->addCameraSceneNode(cPivot1, sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance), cPivot1->getPosition());
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Terrain Patterns /////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
s32 vIndex,iIndex;
s32 defaultVertexColorR = 0;
s32 defaultVertexColorG = 0;
s32 defaultVertexColorB = 0;
////////////////////
// Pattern : FULL //
////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<(tNode_cellsPerEdge+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesF[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxF.reset(verticesF[0].Pos);
for (s32 i=0; i<vIndex; i++) boxF.addInternalPoint(verticesF[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<tNode_cellsPerEdge; i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 currentVertex = (i*(tNode_cellsPerEdge+1)) + j;
indicesF[iIndex + 0] = currentVertex;
indicesF[iIndex + 1] = currentVertex+tNode_cellsPerEdge+1;
indicesF[iIndex + 2] = currentVertex+tNode_cellsPerEdge+2;
indicesF[iIndex + 3] = currentVertex;
indicesF[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesF[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
/////////////////////
// Pattern : NORTH //
/////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=(tNode_cellsPerEdge/2); i<(tNode_cellsPerEdge+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesN[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxN.reset(vector3df(0,0,0));
for (s32 i=0; i<vIndex; i++) boxN.addInternalPoint(verticesN[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 currentVertex = (i*(tNode_cellsPerEdge+1)) + j;
indicesN[iIndex + 0] = currentVertex;
indicesN[iIndex + 1] = currentVertex+tNode_cellsPerEdge+1;
indicesN[iIndex + 2] = currentVertex+tNode_cellsPerEdge+2;
indicesN[iIndex + 3] = currentVertex;
indicesN[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesN[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
//////////////////////////
// Pattern : NORTH EAST //
//////////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (f32 j=((tNode_cellsPerEdge/2)); j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesNE[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
for (f32 i=(tNode_cellsPerEdge/2); i<(tNode_cellsPerEdge+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesNE[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxNE.reset(vector3df(0,0,0));
for (s32 i=0; i<vIndex; i++) boxNE.addInternalPoint(verticesNE[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<(tNode_cellsPerEdge/2); j++){
s32 currentVertex = (i*((tNode_cellsPerEdge/2)+1)) + j;
s32 quarterEndRowShove = 0;
if(i == (tNode_cellsPerEdge/2) - 1) quarterEndRowShove = tNode_cellsPerEdge/2;
indicesNE[iIndex + 0] = currentVertex;
indicesNE[iIndex + 1] = currentVertex+(tNode_cellsPerEdge/2)+1+quarterEndRowShove;
indicesNE[iIndex + 2] = currentVertex+(tNode_cellsPerEdge/2)+2+quarterEndRowShove;
indicesNE[iIndex + 3] = currentVertex;
indicesNE[iIndex + 4] = currentVertex+(tNode_cellsPerEdge/2)+2+quarterEndRowShove;
indicesNE[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 part2root = tNode_cellsPerEdge/2 * ((tNode_cellsPerEdge/2)+1);
s32 currentVertex = part2root + (i*(tNode_cellsPerEdge+1)) + j;
indicesNE[iIndex + 0] = currentVertex;
indicesNE[iIndex + 1] = currentVertex+tNode_cellsPerEdge+1;
indicesNE[iIndex + 2] = currentVertex+tNode_cellsPerEdge+2;
indicesNE[iIndex + 3] = currentVertex;
indicesNE[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesNE[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
////////////////////
// Pattern : EAST //
////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<(tNode_cellsPerEdge+1); i++){
for (f32 j=(tNode_cellsPerEdge/2); j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesE[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxE.reset(vector3df(0,0,0));
for (s32 i=0; i<vIndex; i++) boxE.addInternalPoint(verticesE[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<tNode_cellsPerEdge; i++){
for (s32 j=0; j<(tNode_cellsPerEdge/2); j++){
s32 currentVertex = (i*((tNode_cellsPerEdge/2)+1)) + j;
indicesE[iIndex + 0] = currentVertex;
indicesE[iIndex + 1] = currentVertex+(tNode_cellsPerEdge/2)+1;
indicesE[iIndex + 2] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesE[iIndex + 3] = currentVertex;
indicesE[iIndex + 4] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesE[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
//////////////////////////
// Pattern : SOUTH EAST //
//////////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<((tNode_cellsPerEdge/2)+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesSE[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
for (f32 i=((tNode_cellsPerEdge/2)+1); i<(tNode_cellsPerEdge+1); i++){
for (f32 j=((tNode_cellsPerEdge/2)); j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesSE[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxSE.reset(vector3df(0,0,0));
for (s32 i=0; i<vIndex; i++) boxSE.addInternalPoint(verticesSE[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 currentVertex = (i*(tNode_cellsPerEdge+1)) + j;
indicesSE[iIndex + 0] = currentVertex;
indicesSE[iIndex + 1] = currentVertex+tNode_cellsPerEdge+1;
indicesSE[iIndex + 2] = currentVertex+tNode_cellsPerEdge+2;
indicesSE[iIndex + 3] = currentVertex;
indicesSE[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesSE[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<(tNode_cellsPerEdge/2); j++){
s32 part2root = ((tNode_cellsPerEdge/2) * (tNode_cellsPerEdge+1)) + (tNode_cellsPerEdge/2);
s32 currentVertex = part2root + (i*((tNode_cellsPerEdge/2)+1)) + j;
indicesSE[iIndex + 0] = currentVertex;
indicesSE[iIndex + 1] = currentVertex+(tNode_cellsPerEdge/2)+1;
indicesSE[iIndex + 2] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesSE[iIndex + 3] = currentVertex;
indicesSE[iIndex + 4] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesSE[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
/////////////////////
// Pattern : SOUTH //
/////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<((tNode_cellsPerEdge/2)+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesS[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxS.reset(verticesS[0].Pos);
for (s32 i=0; i<vIndex; i++) boxS.addInternalPoint(verticesS[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 currentVertex = (i*(tNode_cellsPerEdge+1)) + j;
indicesS[iIndex + 0] = currentVertex;
indicesS[iIndex + 1] = currentVertex+1;
indicesS[iIndex + 2] = currentVertex+tNode_cellsPerEdge+1;
indicesS[iIndex + 3] = currentVertex+1;
indicesS[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesS[iIndex + 5] = currentVertex+tNode_cellsPerEdge+1;
iIndex = iIndex + 6;
}
}
//////////////////////////
// Pattern : SOUTH WEST //
//////////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<((tNode_cellsPerEdge/2)+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesSW[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
for (f32 i=((tNode_cellsPerEdge/2)+1); i<(tNode_cellsPerEdge+1); i++){
for (f32 j=0; j<((tNode_cellsPerEdge/2)+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesSW[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxSW.reset(vector3df(0,0,0));
for (s32 i=0; i<vIndex; i++) boxSW.addInternalPoint(verticesSW[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 currentVertex = (i*(tNode_cellsPerEdge+1)) + j;
indicesSW[iIndex + 0] = currentVertex;
indicesSW[iIndex + 1] = currentVertex+tNode_cellsPerEdge+1;
indicesSW[iIndex + 2] = currentVertex+tNode_cellsPerEdge+2;
indicesSW[iIndex + 3] = currentVertex;
indicesSW[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesSW[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<(tNode_cellsPerEdge/2); j++){
s32 part2root = (((tNode_cellsPerEdge/2) * (tNode_cellsPerEdge+1))) + tNode_cellsPerEdge/2;
s32 currentVertex = part2root + (i*((tNode_cellsPerEdge/2)+1)) + j;
s32 quarterStartRowShove = 0;
if(i == 0) quarterStartRowShove = tNode_cellsPerEdge/2;
indicesSW[iIndex + 0] = currentVertex-quarterStartRowShove;
indicesSW[iIndex + 1] = currentVertex+(tNode_cellsPerEdge/2)+1;
indicesSW[iIndex + 2] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesSW[iIndex + 3] = currentVertex-quarterStartRowShove;
indicesSW[iIndex + 4] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesSW[iIndex + 5] = currentVertex-quarterStartRowShove+1;
iIndex = iIndex + 6;
}
}
////////////////////
// Pattern : WEST //
////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<(tNode_cellsPerEdge+1); i++){
for (f32 j=0; j<((tNode_cellsPerEdge/2)+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesW[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxW.reset(verticesW[0].Pos);
for (s32 i=0; i<vIndex; i++) boxW.addInternalPoint(verticesW[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<tNode_cellsPerEdge; i++){
for (s32 j=0; j<(tNode_cellsPerEdge/2); j++){
s32 currentVertex = (i*((tNode_cellsPerEdge/2)+1)) + j;
indicesW[iIndex + 0] = currentVertex;
indicesW[iIndex + 1] = currentVertex+(tNode_cellsPerEdge/2)+1;
indicesW[iIndex + 2] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesW[iIndex + 3] = currentVertex;
indicesW[iIndex + 4] = currentVertex+(tNode_cellsPerEdge/2)+2;
indicesW[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
//////////////////////////
// Pattern : NORTH WEST //
//////////////////////////
// create vertex objects in vertex array
vIndex = 0;
for (f32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (f32 j=0; j<((tNode_cellsPerEdge/2)+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesNW[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
for (f32 i=(tNode_cellsPerEdge/2); i<(tNode_cellsPerEdge+1); i++){
for (f32 j=0; j<(tNode_cellsPerEdge+1); j++){
f32 vx = j*tNode_cellWidth;
f32 vy = 0;
f32 vz = i*tNode_cellWidth;
verticesNW[vIndex] = S3DVertex(vx,vy,vz, 0,1,0, SColor(255,defaultVertexColorR,defaultVertexColorG,defaultVertexColorB),j,i);
vIndex++;
}
}
// setup the bounding box
boxNW.reset(vector3df(0,0,0));
for (s32 i=0; i<vIndex; i++) boxNW.addInternalPoint(verticesNW[i].Pos);
// setup indices
iIndex = 0;
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<(tNode_cellsPerEdge/2); j++){
s32 currentVertex = (i*((tNode_cellsPerEdge/2)+1)) + j;
s32 quarterEndRowShove = 0;
// if(i == (tNode_cellsPerEdge/2) - 1) quarterEndRowShove = tNode_cellsPerEdge/2;
indicesNW[iIndex + 0] = currentVertex;
indicesNW[iIndex + 1] = currentVertex+(tNode_cellsPerEdge/2)+1+quarterEndRowShove;
indicesNW[iIndex + 2] = currentVertex+(tNode_cellsPerEdge/2)+2+quarterEndRowShove;
indicesNW[iIndex + 3] = currentVertex;
indicesNW[iIndex + 4] = currentVertex+(tNode_cellsPerEdge/2)+2+quarterEndRowShove;
indicesNW[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
for (s32 i=0; i<(tNode_cellsPerEdge/2); i++){
for (s32 j=0; j<tNode_cellsPerEdge; j++){
s32 part2root = tNode_cellsPerEdge/2 * ((tNode_cellsPerEdge/2)+1);
s32 currentVertex = part2root + (i*(tNode_cellsPerEdge+1)) + j;
indicesNW[iIndex + 0] = currentVertex;
indicesNW[iIndex + 1] = currentVertex+tNode_cellsPerEdge+1;
indicesNW[iIndex + 2] = currentVertex+tNode_cellsPerEdge+2;
indicesNW[iIndex + 3] = currentVertex;
indicesNW[iIndex + 4] = currentVertex+tNode_cellsPerEdge+2;
indicesNW[iIndex + 5] = currentVertex+1;
iIndex = iIndex + 6;
}
}
//////////////////////////
// create terrain nodes //
//////////////////////////
ITextSceneNode* debugArray[(tNode_octaves*16)];
CterrainNode* myTerrain[(tNode_octaves*16)];
for(int i=0; i<(tNode_octaves*16); i++){
// setup octave
int octave = floor((double)i/16) + 1;
int octaveId = i - ((octave-1)*16);
// create terrain node
myTerrain[i] = new CterrainNode(smgr->getRootSceneNode(),smgr,i,0);
myTerrain[i]->setScale(vector3df(binaryRef[octave],binaryRef[octave],binaryRef[octave]));
// position terrain node
vector3df tPos;
tPos.X = (tNode_width*binaryRef[octave]*-2) + ((octaveId%4)*(tNode_width*binaryRef[octave]));
tPos.Y = 0; // octave * -3;
tPos.Z = (tNode_width*binaryRef[octave]*-1) + ((int)octaveId/4)*(tNode_width*binaryRef[octave]) ;
myTerrain[i]->setPosition(tPos);
myTerrain[i]->setOriginalPosition(tPos);
// stringw debugX = (stringw)tPos.X;
// stringw debugZ = (stringw)tPos.Z;
// stringw debugComp = debugX + (stringw)"," + debugZ;
// debugArray[i] = smgr->addTextSceneNode(guienv->getBuiltInFont(), stringw(debugComp).c_str(), video::SColor(255,255,255,255), myTerrain[i]);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEVICE : main loop ///////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
while(device->run() && device->isWindowActive()) {
/////////////////////////
// keyboard management //
/////////////////////////
// system keys
if(keys[KEY_ESCAPE]){
device->closeDevice();
}
// player keys
if(keys[KEY_KEY_W] || keys[KEY_KEY_S] || keys[KEY_KEY_A] || keys[KEY_KEY_D]){
// playerMoveDir
f32 playerMoveDir;
if(keys[KEY_KEY_W]) playerMoveDir = cameraOrbit;
if(keys[KEY_KEY_S]) playerMoveDir = cameraOrbit-180;
if(keys[KEY_KEY_A]) playerMoveDir = cameraOrbit-90;
if(keys[KEY_KEY_D]) playerMoveDir = cameraOrbit+90;
// player movement vector
vector3df playerStep = sphericalXYZ(playerMoveDir,0,playerMoveSpeed);
playerPosition.X -= playerStep.X;
playerPosition.Y -= playerStep.Y;
playerPosition.Z -= playerStep.Z;
// player rotation brain torture
if(keys[KEY_KEY_W]) playerTurnTo = cameraOrbit - 90;
if(keys[KEY_KEY_S]) playerTurnTo = cameraOrbit + 90;
if(keys[KEY_KEY_A]) playerTurnTo = cameraOrbit -180;
if(keys[KEY_KEY_D]) playerTurnTo = cameraOrbit;
if(playerTurnTo < 0) playerTurnTo += 360;
if(playerTurnTo >= 360) playerTurnTo -= 360;
if(playerCompass < 0) playerCompass = playerCompass + 360;
if(playerCompass >= 360) playerCompass = playerCompass - 360;
f32 playerTurnDir = 0;
f32 playerTurnDelta = 0;
if(playerCompass > playerTurnTo)
{
if(playerCompass - playerTurnTo < 180){
playerTurnDir = -1;
playerTurnDelta = playerCompass - playerTurnTo;
} else {
playerTurnDir = 1;
playerTurnDelta = (360 - playerCompass) + playerTurnTo;
}
}
if(playerCompass < playerTurnTo)
{
if(playerTurnTo - playerCompass < 180){
playerTurnDir = 1;
playerTurnDelta = playerTurnTo - playerCompass;
} else {
playerTurnDir = -1;
playerTurnDelta = (360 - playerTurnTo) + playerCompass;
}
}
f32 playerTurnAmount;
if(playerTurnDelta < playerTurnSpeed){
playerTurnAmount = playerTurnDelta;
} else {
playerTurnAmount = playerTurnSpeed;
}
playerCompass += (playerTurnAmount * playerTurnDir);
cPivot1->setPosition(vector3df(playerPosition.X,playerPosition.Y,playerPosition.Z));
cPivot2->setRotation(vector3df(0,playerCompass,0));
myCamera->setTarget(vector3df(playerPosition.X,playerPosition.Y,playerPosition.Z));
}
///////////////////////
// camera management //
///////////////////////
myCamera->setPosition(sphericalXYZ(cameraOrbit,cameraAngle,cameraDistance));
////////////////////////
// terrain management //
////////////////////////
for(int i=0; i<(tNode_octaves*16); i++){
// set octave
int octave = floor((double)i/16) + 1;
int octaveId = i - ((octave-1)*16);
// get reference positions
vector3df tNode_currentPos = myTerrain[i]->getPosition();
vector3df tNode_originalPos = myTerrain[i]->getOriginalPosition();
// get trigger field boundry
f32 triggerFieldX,triggerFieldZ;
triggerFieldX = tNode_originalPos.X - ((tNode_width*binaryRef[octave]) * 1.5);
triggerFieldZ = tNode_originalPos.Z - ((tNode_width*binaryRef[octave]) * 1.5);
// get distance between player and trigger field
f32 playerTriggerDeltaX = playerPosition.X - triggerFieldX;
f32 playerTriggerDeltaZ = playerPosition.Z - triggerFieldZ;
// round down the player trigger delta to a forbies number (4x4, the number of cells in an octave)
f32 playerTriggerDeltaInForbiesX, playerTriggerDeltaInForbiesZ;
playerTriggerDeltaInForbiesX = floor((double)(playerTriggerDeltaX / ((tNode_width*binaryRef[octave])*4)));
playerTriggerDeltaInForbiesZ = floor((double)(playerTriggerDeltaZ / ((tNode_width*binaryRef[octave])*4)));
// calculate the new position for the terrain node after being wrapped to the other side of its octave
f32 newX = tNode_originalPos.X + (playerTriggerDeltaInForbiesX * ((tNode_width*binaryRef[octave])*4));
f32 newZ = tNode_originalPos.Z + (playerTriggerDeltaInForbiesZ * ((tNode_width*binaryRef[octave])*4));
// calculate player tile delta
f32 playerTileDeltaX, playerTileDeltaZ;
if(playerPosition.X < newX){ playerTileDeltaX = newX - playerPosition.X; } else { playerTileDeltaX = playerPosition.X - newX - (tNode_width*binaryRef[octave]); }
if(playerPosition.Z < newZ){ playerTileDeltaZ = newZ - playerPosition.Z; } else { playerTileDeltaZ = playerPosition.Z - newZ - (tNode_width*binaryRef[octave]); }
// calculate player tile deltas in tile widths (for the current octave)
f32 ptdXinTiles = playerTileDeltaX / tNode_width;
f32 ptdZinTiles = playerTileDeltaZ / tNode_width;
// hide octave middle tiles that overlap with the next octave down
if(octave > 1 && (ptdXinTiles < 0.5 && ptdZinTiles < 0.5))
{
myTerrain[i]->setNodeDisplay(false);
} else {
myTerrain[i]->setNodeDisplay(true);
}
// change tile patterns to fit with off center lower octave
if(octave > 1)
{
bool chopped = false;
// set tile pattern to N?
if(ptdXinTiles < 0.5 && ptdZinTiles > 0.5 && ptdZinTiles < 1.5 && playerPosition.Z < newZ) { myTerrain[i]->setNodeType(1); chopped = true; }
if(ptdXinTiles > 0.5 && ptdXinTiles < 1.5 && ptdZinTiles > 0.5 && ptdZinTiles < 1.5 && playerPosition.Z < newZ && playerPosition.X < newX) { myTerrain[i]->setNodeType(2); chopped = true; }
if(ptdXinTiles > 0.5 && ptdXinTiles < 1.5 && ptdZinTiles < 0.5 && playerPosition.X < newX) { myTerrain[i]->setNodeType(3); chopped = true; }
if(ptdXinTiles > 0.5 && ptdXinTiles < 1.5 && ptdZinTiles > 0.5 && ptdZinTiles < 1.5 && playerPosition.Z > newZ && playerPosition.X < newX) { myTerrain[i]->setNodeType(4); chopped = true; }
if(ptdXinTiles < 0.5 && ptdZinTiles > 0.5 && ptdZinTiles < 1.5 && playerPosition.Z > newZ) { myTerrain[i]->setNodeType(5); chopped = true; }
if(ptdXinTiles > 0.5 && ptdXinTiles < 1.5 && ptdZinTiles > 0.5 && ptdZinTiles < 1.5 && playerPosition.Z > newZ && playerPosition.X > newX) { myTerrain[i]->setNodeType(6); chopped = true; }
if(ptdZinTiles < 0.5 && ptdXinTiles > 0.5 && ptdXinTiles < 1.5 && playerPosition.X > newX) { myTerrain[i]->setNodeType(7); chopped = true; }
if(ptdXinTiles > 0.5 && ptdXinTiles < 1.5 && ptdZinTiles > 0.5 && ptdZinTiles < 1.5 && playerPosition.Z < newZ && playerPosition.X > newX) { myTerrain[i]->setNodeType(8); chopped = true; }
// if no chopping was required and the tile is not full, make it full
int checkType = myTerrain[i]->getNodeType();
if(chopped == false && checkType != 0) myTerrain[i]->setNodeType(0);
}
// debug
// stringw debugComp = (stringw)ptdXinTiles + (stringw)"," + (stringw)ptdZinTiles;
// debugArray[i]->setText(stringw(debugComp).c_str());
// debugArray[i]->setTextColor(SColor(255,octave*40,octave*40,octave*40));
// if the terrain node needs to move...
if(newX != tNode_currentPos.X || newZ != tNode_currentPos.Z)
{
// transform terrain node
myTerrain[i]->setPosition(vector3df(newX,tNode_originalPos.Y,newZ));
myTerrain[i]->setScale(vector3df(binaryRef[octave],binaryRef[octave],binaryRef[octave]));
}
}
///////////////////
// debug display //
///////////////////
stringw displayFPS = (stringw)"FPS : " + int2string(driver->getFPS());
stringw displayDebug = displayFPS + "\n";
debug_panel->setText(stringw(displayDebug).c_str());
//////////////////
// render scene //
//////////////////
driver->beginScene(true, true, SColor(0,120,120,120));
for(int i=0; i<(tNode_octaves*16); i++)
{
if(myTerrain[i]->getNodeDisplay() == true) myTerrain[i]->render();
}
smgr->drawAll();
guienv->drawAll();
driver->endScene();
}
device->drop();
return 0;
}
