[SOLVED]Exporting terrain to a mesh (with code)

nindim

05-03-2007 21:43:21

I need to be able to add a cave to my terrain, I realise this has been discussed many times before and it seems to me like the easiest way would be to export a tiles mesh data to mesh file and then edit it in a appropriate application, hide the tile in the PLSm2 and place the edited mesh tile at correct position. It seems as though getting the vertices of the terrain wouldnt be too difficult following the howto on the wiki located here:


[url=http://www.ogre3d.org/wiki/index.php/Us]http://www.ogre3d.org/wiki/index.php/Us ... ith_Newton


How though would I go about outputting this data to a file, I'm not too familiar with any particular format (bar milkshape), even then it seems like a pretty big job.

Another idea I had was to use the mesh decal class I have integrated into my game which creates a movableobject based on the terrain. I thought that maybe there was something already that could export these movableobjects to a .mesh or equivalent? Any pointers would be great.

Grom

06-03-2007 06:05:27

Writing a 3d file isn't too difficult, just check out http://www.wotsit.org/. Decide which modeling program you want to do the editing in and export a file that program can read. I'd use 3DS, it's pretty common amongst many editors, and it isn't actually too difficult to read/write. It operates on a system of chunks. Here is an old flipcode page that shows how simple loading a 3DS can be, and writing one isn't all that dissimilar.

If you successfully load all the vertex data let me know, I've been trying to get it working for awhile now.

nindim

06-03-2007 12:15:51

Ok, might have a go at it tonight, do you have any idea how the vertices are ordered when they come out of the code as per the wikied plsm2 and newton howto?

I think the way Tuan renders the terrain is using triangle strips. When using the approcach I linked to above does it extract the vertex data and spit out every vertex of every triangle or does it simply do the first 3 of the first triangle and then every vertex after that is another triangle?

Doesnt really matter which, it would just be good to know before I start trying to code the exporter. Havent had time to look at the wikied code yet but as I said, hopefully tonight!

nindim

06-03-2007 12:39:11

Looks to me like the obj format is really easy to use, I would only need to export the vertex positions and normals and then specify indexes used for each face or lgroup of faces. thanks for the link Grom, useful.

Grom

06-03-2007 12:44:31

the vertex data you get is in row major order, and you get both vertex & index data when you do it.

nindim

06-03-2007 12:49:13

Thanks for the info, hopefully will have somehting to show soon provided I get some free time.

nindim

07-03-2007 09:58:00

I've got it exporting to an obj file, still have to calculate vertex normals and clean up code but apart from that it's pretty much done.

nindim

07-03-2007 20:38:40

I'm having some problems managing to hide a tile. They are just renderables but I really dont know where to start, so much code!

I tried setting the mvisible bool of a paginglandscapetile (by making it public first) but there seems to be more to it than that. Is there an example I should look at in the plugin as to how to effectively hide a tile? Tried loking at the tilehide event but it seem empty....

Grom

08-03-2007 00:24:05

you loaded the vertex data per tile? how?!

Grom

08-03-2007 06:46:06

Wow. Well, I just got frustrated enough to start again from scratch and surprisingly I can now succesfully load all the tiles. I feel like a jackass.

nindim

08-03-2007 11:15:49

LOL, thats how it goes I guess, at least you got the problem fixed!

nindim

18-03-2007 21:57:56

Thought I'd post my implementation of exporting a terrain to a mesh, currently the code calculates normals, which I have since been advised isnt actually needed as they can be gotten in a similar way to the vertices/indices fo the terrain tile.

I can guarantee you that this is not the prettiest code in the world, but it gets the job done. Also, seeing as I'm not producing these meshes at (game) run time it's not optimised at all.

Anyway here it goes....


if(!mInput->mBuildMesh)
{
//hide the required tiles
for(int tileHideX = 0;tileHideX<8; tileHideX++)
{
for(int tileHideZ=0;tileHideZ<8;tileHideZ++)
{
int pageX=0;
int pageZ=0;
unsigned int tileX=tileHideX;
unsigned int tileZ=tileHideZ;

//ask reference to geometry to PLSM2
std::vector<void*> params;
int renderLevel=0;
params.push_back(&pageX);
params.push_back(&pageZ);
params.push_back(&tileX);
params.push_back(&tileZ);
mScnMgr->getOption("HideTile",&params);
}
}
}
else
{
ofstream fout("vertices.obj");//file we will output the mesh to
int indicesprocessed=0;//used as offset for indices if multiple tiles being output
for(int tileHideX=0;tileHideX<8;tileHideX++)
{
for(int tileHideZ=0; tileHideZ<8; tileHideZ++)
{
int pageX=0;//which page are we looking at?
int pageZ=0;//which page are we looking at?
unsigned int tileX=tileHideX;//which tile are we looking at?
unsigned int tileZ=tileHideZ;//which tile are we looking at?

//get reference to geometry from PLSM2
std::vector<void*> params;
int renderLevel=0;//the LOD you want the mesh to be output at, 0 is highest
//add the info we want to params
params.push_back(&pageX);
params.push_back(&pageZ);
params.push_back(&tileX);
params.push_back(&tileZ);
params.push_back(&renderLevel);
mScnMgr->getOption("PageGetTileVertexData_2",&params);//pass params to the PLSM2

//Recover the data from the getOption we just called
int* numVtx=((int*)params[5]);//the numebr of vertices
Vector3* vertices=((Vector3*)params[6]);//the actual vertices
IndexData* indexData=((IndexData*)params[7]);//the indices which describe the faces

int index_offset=0;//places the data in the correct part of the index array
unsigned long* indices;//where we will place the index data

///////////////////////////////START OUTPUT VERTICES/////////////////////////////////
int numberVertices = *numVtx;//the number of vertices
int myNumVerts = 0;//the number of vertices processed so far (debug)

for(int x =0; x < numberVertices; x++)
{
fout << "v " << vertices[x].x << " " << vertices[x].y
<< " " << vertices[x].z << "\n";

myNumVerts++;
}
///////////////////////////////END OUTPUT VERTICES/////////////////////////////////


////////////////////////////START CALCULATING INDICES//////////////////////////////
size_t numTris = indexData->indexCount / 3;
Ogre::HardwareIndexBufferSharedPtr ibuf = indexData->indexBuffer;

//indices = new unsigned long[indexData->indexCount];//allocate memory for indices
indices = new unsigned long[indexData->indexCount];//allocate memory for indices

//*hack* Only unlocked here as the next piece of code pLong locks the buffer,
//temporary solution for now
if(ibuf->isLocked() == true)
{
ibuf->unlock();
}

unsigned long* pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong);

size_t offset = indicesprocessed;//the offset into index array if more than one tile being outputted

for ( size_t k = 0; k < numTris*3; k++)//loop through all vertices
{
//populate the index array
//indices[index_offset++] = (static_cast<unsigned long>(pShort[k]) +
//static_cast<unsigned long>(offset))+1;
indices[index_offset++] = (static_cast<unsigned long>(pShort[k]) +
static_cast<unsigned long>(0)) + 1 + offset;
}
////////////////////////////FINISH CALCULATING INDICES////////////////////////////


////////////////////////////START CALCULATING NORMALS/////////////////////////////
Vector3 currentVertex;//the current vertex we are looking at
Vector3 vertex1;//the first (excluding current vertex) vertex in current face
Vector3 vertex2;//the second (excluding current vertex)vertex in current face
Vector3 vector1;//the vector from vertex1 to currentvertex
Vector3 vector2;//the vector from vertex2 to currentvertex
Vector3* Normal;//pointer to what will be start of array of normals
Normal = new Vector3[numberVertices];//allocate memory for normal array
int counter = 0;//used to loop through entirety of index array
int counter2 = 0;//used to loop through entirety of vertex array

for( counter2 = 0; counter2 < numberVertices; counter2++)//0 - 4224
{
counter = 0;
Normal[counter2] = 0;//set the current normal to all 0 as normals are accumulated

for(counter = 0; counter < numTris*3; counter++)//loop through indices
{
if(counter%3 == 0)//first of 3 pieces of info for index of a triangle
{
if((indices[counter]-1) == counter2 + offset)
{
currentVertex = vertices[(indices[counter+0]-1 - offset)];
vertex1 = vertices[(indices[counter+1]-1 - offset)];
vertex2 = vertices[(indices[counter+2]-1 - offset)];

vector1 = vertex1 - currentVertex;
vector2 = vertex2 - currentVertex;

if(vector1.z < vector2.z)
{
Normal[counter2] += vector2.crossProduct(vector1);
}

Normal[counter2] += vector1.crossProduct(vector2);
}
}
else if(counter%3 == 1)//first of 3 pieces of info for index of a triangle
{
if((indices[counter]-1) == counter2 + offset)
{
currentVertex = vertices[(indices[counter]-1 - offset)];
vertex1 = vertices[(indices[counter-1]-1 - offset)];
vertex2 = vertices[(indices[counter+1]-1 - offset)];

vector1 = vertex1 - currentVertex;
vector2 = vertex2 - currentVertex;

Normal[counter2] += vector2.crossProduct(vector1);
}
}
else if(counter%3 == 2)//first of 3 pieces of info for index of a triangle
{
if((indices[counter]-1) == counter2 + offset)
{
currentVertex = vertices[(indices[counter]-1 - offset)];
vertex1 = vertices[(indices[counter-1]-1 - offset)];
vertex2 = vertices[(indices[counter-2]-1 - offset)];

vector1 = vertex1 - currentVertex;
vector2 = vertex2 - currentVertex;

Normal[counter2] += vector2.crossProduct(vector1);
}
}
}
}

counter2 = 0;
for( counter2 = 0; counter2 < numberVertices; counter2++)//0 - 4224
{
//normalize the "normals" we calculated
Normal[counter2].normalise();
fout << "vn " << Normal[counter2].x << " " << Normal[counter2].y << " " << Normal[counter2].z << endl;
}
///////////////////////////FINISH CALCULATING NORMALS////////////////////////////


/////////////////////////////START OUTPUT INDICES////////////////////////////////
fout << endl;

for ( int k = 0; k < numTris*3; k++)
{

if(k%3==0)
{
fout << "f ";
}

//fout << indices[k] << "//" << indices[k] << " ";
fout << indices[k] << "//" << indices[k] << " ";

if(k%3==2)
{
fout << "\n";
}
}
/////////////////////////////FINISH OUTPUT INDICES////////////////////////////////

fout << endl;//place line break incase multiple tiles are being outputted

indicesprocessed+=myNumVerts;//update indicesprocessed for possible next tile

//cleanup
delete[] vertices;
delete[] Normal;
delete numVtx;
}
}
fout.close();//close the file we opened for output
}


This is the PLSM2 edit that I added to enable the hiding of a tile:


if (strKey == "HideTile")
{
/**
* This, as the naem suggests, "hides" a tile.
* Usage: Pass in a std::vector<void*> Pointer to the getOption call containing at least 4 Elements
* [0](Ogre::unsigned int*) = X Index of the Page to retrieve data from
* [1](Ogre::unsigned int*) = Z Index of the Page to retrieve data from
* [2](Ogre::unsigned int*) = X Index of the Tile within the Page to retrieve data from
* [3](Ogre::unsigned int*) = Z Index of the Tile within the Page to retrieve data from
**/
unsigned int requestPageX = *static_cast<unsigned int *>((*static_cast<std::vector<void*>*>(pDestValue))[0]);
unsigned int requestPageZ = *static_cast<unsigned int *>((*static_cast<std::vector<void*>*>(pDestValue))[1]);
unsigned int requestTileX = *static_cast<unsigned int *>((*static_cast<std::vector<void*>*>(pDestValue))[2]);
unsigned int requestTileZ = *static_cast<unsigned int *>((*static_cast<std::vector<void*>*>(pDestValue))[3]);

PagingLandScapePage* page = mPageManager->getPage(requestPageX,requestPageZ);

if(page)
{
PagingLandScapeTile* tile = page->getTile(requestTileX,requestTileZ);
if(tile)
{
PagingLandScapeRenderable* rend = tile->getRenderable();
if(rend)
{
if(tile->mVisible)
{
rend->setInUse(false);
tile->mVisible=false;//not needed to hide tile but just in case

//mTileManager->getSceneManager()->getListenerManager ()->fireTileHide
//(requestPageX, requestPageZ, requestTileX, requestTileZ, tile->mWorldBounds);
return true;
}
}
}
}
return false;
}