Game Development Community

Spherical Terrain in T3D

by Jack Stone · in Torque 3D Professional · 11/05/2014 (4:30 pm) · 29 replies

Hello,

As I mentioned here:
http://www.garagegames.com/community/forums/viewthread/140239/1#comment-862259

I am working on implementing spherical terrain in T3D. I have have found some equations here:

http://mathproofs.blogspot.ie/2005/07/mapping-cube-to-sphere.html

That work for generating the Z-component of the terrain. However, I am now stuck.

I need to modify the X and Y components of T3D's Terrain in order to generate the quad cube. Here is what I am doing:

Here is where I generate the terrain. The "Height" equation is correct. The length and width equations are not.
for ( S32 y = 0; y < blockSize; y++ )
{
	for ( S32 x = 0; x < blockSize; x++ )
	{

		F32 x1 = x+1;
		F32 y1 = y+1;

		x1 /= 256;
		y1 /= 256;

		height =  sqrt((F32)1 - ((x1*x1)/2) - ((y1*y1)/2) + (((x1*x1)*(y1*y1))/3) );


		F32 z1 = height;

		widthY =	sqrt((F32)1 - ((z1*z1)/2) - ((x1*x1)/2) + (((z1*z1)*(x1*x1))/3) );//x*2;
		lengthX =	sqrt((F32)1 - ((y1*y1)/2) - ((z1*z1)/2) + (((y1*y1)*(z1*z1))/3) );//x*2;

		lengthX *= 256;

		widthY *= 256;;

		height *= 256;

		Con::printf("setterraindata: %d %d %f %d" , x , y ,height, floatToFixed( height ));

		file->setWidthY( x, y, floatToFixed( widthY ) );
		file->setLengthX( x, y, floatToFixed( lengthX ) );

		file->setHeight( x, y, floatToFixed( height ) );


		count++;

	}
}

terrain->updateGrid( Point2I::Zero, Point2I( blockSize, blockSize ) );
terrain->updateGridMaterials( Point2I::Zero, Point2I( blockSize, blockSize ) );
terrain->registerObject( terrainName );

SimGroup *missionGroup;
if( Sim::findObject( "MissionGroup", missionGroup ) ){
	missionGroup->addObject( terrain );
}

In terrFile.h, I have copied and modified the set/get height functions:

inline void TerrainFile::setWidthY( U32 x, U32 y, U16 width )
{
   x %= mSize;
   y %= mSize;
   mWidthMap[ x + ( y * mSize ) ] = width;
}
inline void TerrainFile::setLengthX( U32 x, U32 y, U16 length )
{
   x %= mSize;
   y %= mSize;
   mLengthMap[ x + ( y * mSize ) ] = length;
}
inline void TerrainFile::setHeight( U32 x, U32 y, U16 height )
{
   x %= mSize;
   y %= mSize;
   mHeightMap[ x + ( y * mSize ) ] = height;
}

And finally, in terCell.cpp, the rendering code:

for ( U32 y = 0; y < smVBStride; y++ )
{
	for ( U32 x = 0; x < smVBStride; x++ )
	{

		// We clamp here to keep the geometry from reading across
		// one side of the height map to the other causing walls
		// around the edges of the terrain.
		//   terpoint.x = mClamp( mPoint.x + x * stepSize, 0, blockSize - 1 );
		//    terpoint.y = mClamp( mPoint.y + y * stepSize, 0, blockSize - 1 );
		gridPt.x = mClamp( mPoint.x + x * stepSize, 0, blockSize - 1 );
		gridPt.y = mClamp( mPoint.y + y * stepSize, 0, blockSize - 1 );

		// Setup this point.
		point.x = (F32)gridPt.x * squareSize;
		point.y = (F32)gridPt.y * squareSize;

		lengthX = fixedToFloat( file->getLengthX( gridPt.x, gridPt.y ) );
		widthY = fixedToFloat( file->getWidthY( gridPt.x, gridPt.y ) );
		height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );

		Point3F terdata = file->getterdata(count);

		vert->point.x = lengthX;// point.x;//terdata.x;//point.x*2;
		vert->point.y = widthY;//point.y;//terdata.y;//point.y;
		vert->point.z = height;//50.0755;//terdata.z;//height;

		count++;

		Con::printf("updatevertexbuffer: %f %f %f" ,widthY, lengthX,height);
		// Get the normal.
		mTerrain->getSmoothNormal( point, &normal, true, false );
		vert->normal = normal;

		// Get the tangent z.
		vert->tangentZ = fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ) - height;

		// Test the empty state for this vert.
		if ( file->isEmptyAt( gridPt.x, gridPt.y ) )
		{
			mHasEmpty = true;
			mEmptyVertexList.push_back( vbcounter );
		}

		vbcounter++;
		++vert;
	}
}

"LengthX" and "WidthY" are being read correctly, and seem to have the correct values, but the terrain looks like this:

http://phoenixgamedevelopment.com/blog/wp-content/uploads/2014/11/PhoenixGameDevelopment130-Nov.-05-20.10-1024x793.jpg

It looks symmetrical, but clearly distorted.

I think the main issue I am having is getting the X,Y,and Z cube coordinates for the input to the speherical mapping function. The X and Y come from iterating through the terrain height map, but what is Z?


Page «Previous 1 2
#1
11/05/2014 (11:36 pm)
Why do you want to do this exactly? If you're making some kind of "the little prince" mini planet, I wonder if you wouldn't be better off using a mesh object instead of a terrain?
#2
11/06/2014 (4:52 am)
I'm ideally shooting for realistic planets, actually? Now that raises a whole host of other issues, such as paging, distance from the origin, etc, but that's the direction I am going in.

For now, I'd like to get something that's at least 4k X 4k on each face of the quad cube. A mesh of that size, that's detailed enough for the player to walk on as a terrain, would have a ridiculously large polycount!

#3
11/06/2014 (9:26 am)
I have made some progress on this.

These images:

http://phoenixgamedevelopment.com/blog/wp-content/uploads/2014/11/PhoenixGameDevelopment140-Nov.-06-15.04-1024x793.jpg

http://phoenixgamedevelopment.com/blog/wp-content/uploads/2014/11/PhoenixGameDevelopment139-Nov.-06-13.59-1024x793.jpg

Show that I have now modified the X and Y axes to produce a more curved shape. However, the z axis is still distorted.

This is my new code:

for ( S32 y = 0; y < blockSize; y++ )
		{
			angle2 = 0+sqrt(mPow(F32(blockSize),F32(2))-mPow(F32((y-0)),F32(2))) ;//1.0;
			for ( S32 x = 0; x < blockSize; x++ )
			{
				F32 x1 = x ;
				F32 y1 = y ;

				x1 -=128;
				x1 /=128;

				y1 -=128;
				y1 /=128;

				height = sqrt((F32)1 - ((x1*x1)/2) - ((y1*y1)/2) + (((x1*x1)*(y1*y1))/3) );

				F32 z1 = height;//sqrt((F32)1 - ((x1*x1)/2) - ((y1*y1)/2) + (((x1*x1)*(y1*y1))/3) );

				widthY =	sqrt((F32)1 - ((z1*z1)/2) - ((x1*x1)/2) + (((z1*z1)*(x1*x1))/3) );//x*2;
				lengthX =	sqrt((F32)1 - ((y1*y1)/2) - ((z1*z1)/2) + (((y1*y1)*(z1*z1))/3) );//x*2;

				lengthX *= 256;
				widthY *= 256;
				height *= 256;

				file->setWidthY( x, y, floatToFixed( widthY ) );
				file->setLengthX( x, y, floatToFixed( lengthX ) );

				file->setHeight( x, y, floatToFixed( height ) );

I feel that I am close with this, if I could only get the z axis working!
#4
11/06/2014 (12:51 pm)
Very interesting!

A quick tip, though, if you want images to show up in your post or URLs to be clickable, use the MarkupLite syntax.

Makes things easier for your fans.
#5
11/06/2014 (5:38 pm)
I always wanted to do a "Halo" terrain. This could be modified to allow you to make a ring terrain like Halo. a Spherical terrain would be cool as you said making small planets that you could land on and explore.

or course you can always do a level load when someone wants to land on a planet and use the standard terrain maps.
#6
11/06/2014 (5:52 pm)
Thanks Simon, I keep forgetting to use Makup Lite! I must remember that in future. I only use code tags usually.

@Michael: Actually, the Halo terrain is easier! I had that working first, before I began working on the spherical terrain. It looks pretty awesome actually, just not what I wanted!

I have made a few games using the level load system, but it's really not optimal. It works, but I wanted to do it "right" you know? To get a seamless surface to orbit transition.
#7
11/06/2014 (6:04 pm)
Would definitely be interested in that Halo terrain, at least a rundown of how you achieved it or some screens to get inspired by.
#8
11/06/2014 (7:06 pm)
Yeah, having a real ringworld terrain would be pretty sweet! Actually not something I've seen done in a game before.
#9
11/07/2014 (1:05 pm)
Hello Jack. There was a time in the past, even before I came to this community, I was engaged in this issue, while I also like you trying to build a round planet and make the characters go understanding on all sides (it's quite troublesome) maybe now I can be a little useful in this regard, as I still have many decisions that I was once developed for my previous projects and prototypes, with different options out of the situation, but I worry that you do, it can be useless in the form in which it is now (it many archiving paper with my not legible records) in the language that you are unlikely ever even have heard, and to adapt, you need a lot of time and effort, and I do not know how much it is possible to combine something that I have with the code in the engine (plus to this there is a language barrier), but I think I might try to spend some of my time to try on, to describe and explain some of the ideas and techniques that might help to conquer this mountain.
Anyway, here is my attempt to give even be a grain of benefit from me ...

This highly abstract operations that can give you the effect you want, they relate only to geometric transformations, for the correct calculation distance and position in space of the vertices from the center of the planet to the planet's surface (radius), but aiming for at the marked vertex on a flat surface mesh sides of the cube ...

1) requires the cube with all labeling serifs (3D vectors) (in fact it can beat absolutely any form without interpretation cube, simply mesh) it is necessary generate keeping communication;

2) mark in it a singular point node "center"

3) create a simple data class for an example - "Planeta_Vektor_3D" with these fields ...
F32 X // local coordinate from the "center" to the point at povehnosti cube along the X axis
F32 Y // local coordinate from the "center" to the point at povehnosti cube along the Y axis
F32 Z // local coordinate from the "center" to the point at povehnosti cube along the Z axis
F32 N // normal
F32 R // percentage range relative to the normal


4) to cast a ray from the "center" to each of the vectors, and write down the coordinates of XYZ, the first intermediate buffer (consisting of an array of instances of the class "Planeta_Vektor_3D" (this is the local value of the vectors relatively from local "center" of the planet)), each such vector should be recorded as normal i.e. 1: 1 - (F32 N = 1.0;) by default.

5) At last, to accommodate all the vertices in the form of spheres (with the desired radius) for each vector individually, we need to calculate its value expressed in percent of the amount of normalized length (as a 1: 1) of each vector from the "center" to the desired radius the planet that you want to get at the start by default. after calculation need to record it in the appropriate field of your copy of the vector in the array (F32 R = â„–.â„–) (instead of the symbol â„–.â„– percentage which takes the length of the radius from the "normal" length of each individual personally vector)

6) and then to install and edit the specific surface of the planet at the height of the desired point for you, you must change the value of the normal leash (F32 N) and update each axis (XYZ) multiplying each value separately at the normal (F32 N) multiplied by the value of its interest (F32 P). and then you get it to work. =)

Notes: (F32 N) is the only number of all values in the intermediate array of which must be saved in the file of each set for each vertex. All other data must be generated each time the when the spawn "planet" scene based on the underlying data which were passed to the constructor class "planet" such as the desired number of divisions face of the cube in each axis, width and height and depth of the cube (this is optional; but the appropriateness of course create one all identical are equal to 1 by default), and after it is fully initialized basic mesh object "planet" with all the vertices, and the intermediate buffer is initialized, to this we must bring the data from the file as modifiers of normals (F32 N ) values, which is the desired values equivalent to (similar) in order which in the ordinary "terrain block" was (height Z). So will be set all the normalization value of the order, this order is always the same, and This then is how, the planet will always be created exactly the same as before saving the array of modified while editing the values of the normals (F32 N) in the file.
#10
11/08/2014 (5:06 pm)
@Simon and David:

I have some pictures of some early cylinder-like terrain. I haven't spent much time on this, since I am trting to create spherical terrain, but hopefully it will be of some interest:

phoenixgamedevelopment.com/blog/wp-content/uploads/2014/11/PhoenixGameDevelopment97-Oct.-31-18.19.jpg
phoenixgamedevelopment.com/blog/wp-content/uploads/2014/11/PhoenixGameDevelopment93-Oct.-31-17.52.jpg
phoenixgamedevelopment.com/blog/wp-content/uploads/2014/11/PhoenixGameDevelopment98-Oct.-31-18.19.jpg
Obviously the cylinder would need to be flipped inside out, and some actual detail added, but combined with some modifications to the gravity system, this could look pretty good.

@Olexiy:

Thank you so much for your information! That certainly will help! Have you implemented your solution in Torque? Or is it a custom engine or code base?

I am essentially doing what you suggest, and have the code *mostly* working, I am just getting corrupted/deformed data when rendering the Terrain. I suspect it has something to do with the order in which the datais being stored in the vertex buffer?
#11
11/09/2014 (10:48 am)
@Jack
Quote: Have you implemented your solution in Torque? Or is it a custom engine or code base?

No such solution in Torque, I did not implement, and no engine, it's just highly abstract formula, which I brought forth out of my previous older developments and calculation schemes, especially for you, I designed it in this form so that it has become easier to read as possible. On so much on how much I can do to adapt my translation for publication in the community.

Well about a corrupted or deformation, then yes, it is quite probable that may occured if places of joints of in large clusters on which separate terrain where several vertices that must be in sync will not be synchronized to the coordinates where they have a joint position and place must beat together recalculated at the time of editing (especially in cases where the corners of the cube already 3 vertices together), and for this it is necessary take into consideration in the code. But it's hard that either specifically to help here not watching the events and not knowing the internal code of the engine, and I did not know unfortunately.
#12
11/09/2014 (1:33 pm)
Hello,

Well, thank you very much for your work, that was very kind of you!

I believe I have reached something of an impasse with this concept. I need to modify the X and Y values in order to create the Quad Cube, and I can't seem to manage this without terrain deformation.

I believe the X and Y values are tied into to the rendering algorithm in some obscure way. I had believed that the system used a simple vertex list, and I could simple change the value of the X and Y points explicitly, but this seems to not be the case.

I will try to dig further into the terrain code to resolve this.
#13
11/10/2014 (8:22 am)
Huh, not to be a naysayer on all your hard work, and I do fully appreciate the joy of solving difficult math problems... but it seems you are rewriting some very complicated code for a gain that in the end would seem to be unnoticeable to the player, unless you are setting the game on a very small asteroid. Once you get all the way down to first person level on any planet like ours, big enough to provide normal Earth gravity, then there is virtually no difference whatsoever between a spherical planet and a flat plane.

If you are making a space game and you want smooth transition from space to planetary surface, then if I were you I would approach it from the point of view of LODs, where you have a round mesh object until you get low enough to not see the curvature anymore, and then switch out to a regular flat terrain.

Admittedly this is not as exciting as rewriting the terrain engine to accommodate spherical coordinates, and I wish you the best in that pursuit. I'm just speaking from the point of view of one who, after many years of bashing my head against hard math problems, has finally learned to pick my battles _very_ carefully and _always_ take the easier way out if there is one. :-)
#14
11/10/2014 (5:33 pm)

@Chris:

I have had that suggested to me before, and on my previous projects I always implemented some kind of surface to space transition, it is much easier.

However, there are some drawbacks to this, such as the difficulty in hiding the transition from "Space" to "Earth". Paging all of that terrain data into memory seamlessly is difficult, and you must make sure the terrain textures match the textures on the planet object, for example if the player enters orbit over an ocean on the 3d object, they should spawn on a part of the terrain that contains an ocean. In addition, the projects that I normally work on make extensive use of procedural content. Texturing terrain "on the fly" is quite easy, however, texturing a 3D Object dynamically is, as far as I'm aware, a lot more difficult.

Having spherical terrain also provides a seamless universe with no load zones which makes certain types of gameplay more realistic. I'm not just talking about asteroids and small planets, but have you ever played the game Imperium Galactica II? This game featured "Planetary guns" which fired from the planets surface at ships in orbit. This would be impossible if the planets surface is hidden behind a load zone.

The advantages of spherical terrain in terms of realism is, I feel, worth the effort of implementing it.

Having said that, I don't seem to be having much luck rewriting the terrain engine.

Does anyone know of a good T3D based vertex and index buffer tutorial? I am trying to understand how these work using very basic code:

TerrVertex *vert = mVertexBuffer.lock();

   Point2F point;


   vert->point.x = 0;
   vert->point.y = 0;
   vert->point.z = 0;

   ++vert;

   vert->point.x = 2;
   vert->point.y = 0;
   vert->point.z = 0;

   ++vert;

   vert->point.x = 1;
   vert->point.y = 2;
   vert->point.z = 0;

   ++vert;



   vert->point.x = 3;
   vert->point.y = 2;
   vert->point.z = 0;

   ++vert;



  mVertexBuffer.unlock();



   mPrimBuffer.set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" );

   GFXPrimitive *prim = mPrimBuffer.getPointer()->mPrimitiveArray;
   prim->type = GFXTriangleList;
   prim->numVertices = smVBSize;

   mTriCount = 0;

   // Lock and fill it up!
   U16 *idxBuff;
   mPrimBuffer.lock( &idxBuff );
   U32 counter = 0;
   U32 maxIndex = 0;


   idxBuff[0] = 0;
   idxBuff[1] = 1;
   idxBuff[2] = 2;

   idxBuff[3] = 0;
   idxBuff[4] = 2;
   idxBuff[5] = 3;


   idxBuff += 6;
   mTriCount += 2;
   maxIndex = 6;
   counter +=6;


   mPrimBuffer.unlock();
   prim->numPrimitives = mTriCount;

This produces something on the screen, but it's a badly distorted, stretched, mess. Is this vertex/primitive buffer code correct? Or is T3D's Terrain doing something unusual?
#15
11/11/2014 (10:03 am)
Well it sounds like you've spent a lot more time trying both options than I have, so I'll shut up and stand corrected. :-) Sorry I can't help you with the primbuffers, but looking forward to seeing your results. Good luck!
#16
11/12/2014 (12:20 pm)
Oh, not at all, you are absolutely correct! I suspect I will need to implement some kind of compromise system, like you suggested. This is proving to be harder than I thought!
#17
11/12/2014 (2:39 pm)
You may want to try pinging Bill Vee and searching this site, I believe he already has cracked this nut
www.garagegames.com/community/blogs/view/22324 - and several other links. I think he even released a demo at one point

Would be nice to see a full working release of this from anyone
#18
11/12/2014 (11:30 pm)
This shows a very simple vertex/index buffer usage github.com/GarageGames/Torque3D/blob/development/Engine/source/T3D/examples/rend...

Vertex/Index buffers in T3D use the classic lock/unlock method.

#19
11/13/2014 (4:06 am)
Not sure if it's of any help, but I believe T3D reverses the order of the indices.... I'm not explaining it very well and can't figure out how to describe atm lol, but I have some code that might be of use
#20
11/13/2014 (7:42 am)
@James: Yes, I came across Bill Vee's work, it seems he has done a lot with voxels too? I'll see if I can track him down.

@Timmy:
Thank you for that link, I have used the rendering example a number of time before, they are invaluable! I am finding it difficult however to transfer the skills I have learned from the rendermeshexample to the terrain system, it feels like there is something else going on with the terrain? The vertex/index buffer usage seems different.

On another note:
Call me crazy, but, would it be in any way feasible to just use render mesh example as the basis for a completely unique spherical terrain implementation? I could create the sphere as all one mesh, I wouldn't have to create six terrains and try to match them up.

The obvious (and very serious) issues would be that I would have to implement collision, culling, view distance, optimisations, editing, etc etc myself. Does anyone know offhand where basic collision and view distance optimisation for meshes is in terms of difficulty? Doable, or just madness?

@Lukas:

I have noticed that, I managed to figure that out through some trial and error! Thanks!
Page «Previous 1 2