Using Splines in Ogre creating a manual mesh

Problems building or running the engine, queries about how to use features etc.
Post Reply
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Using Splines in Ogre creating a manual mesh

Post by gzmzhen »

So, basically what I want to do is to automatically create a road mesh given information like the center points, how many lanes it is, and what type of road (dirt, highway, etc.).

I've found several places online that allude to using splines to create a smooth element to these roads (going from 4 lanes to 2 lanes without extra "artistic" work, etc).

Is it possible to use Splines while creating a manual mesh in Ogre?

Nate
DigitalGhost
Halfling
Posts: 62
Joined: Mon Feb 07, 2005 9:47 pm

Post by DigitalGhost »

It's possible. I think there's a bezier demo, try to use the same idea.
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

Here's what I'm thinking for now:

I have splines, using Ogre::SimpleSpline, that run down the road and alongside each segment of the road. I say segment, because I'm using a database that breaks up curves into segments for "compression" purposes.

Then so far down each spline I can call Vector3 Ogre::SimpleSpline::interpolate(Real t) to get the next set points to add to the mesh.

There is a sample code that creates a cube with just vertex information:
http://www.ogre3d.org/wiki/index.php/GeneratingAMesh

So, I think I'll head in that direction if others think it will work.
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

Using this tutorial as a guidline: http://www.ogre3d.org/wiki/index.php/GeneratingAMesh

When specifying how the triangles layout, does the order of the verticies matter? Or will one way make them face down and the other upward? If so, is it counter-clockwise or clockwise that gives the front face?

Additionally, once I create each mesh like this, how would I go about texturing the mesh? Is it even possible if it has a lot of flexibility to its shape?

Thanks Again.
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

So you can see here that I've gotten it to work.

Image

However, I'm unsure of how to texture map the road now. I'd prefer to be able to map it just like you would find a road in real life, but I'm not sure how to go about mapping this.

I used the generating a mesh code, and currently it only supports changing the colors (which isn't what I'm looking for)...

Any suggestions are very welcome.

Thanks.
Last edited by gzmzhen on Sat Aug 19, 2006 9:58 pm, edited 1 time in total.
P
OGRE Expert User
OGRE Expert User
Posts: 421
Joined: Fri Jan 07, 2005 9:49 pm
Location: UK
x 2
Contact:

Post by P »

One possibility is to create a texture, tileable in the v direction per instance.

Then you assign a u coordinate of 0.0 and 1.0 for respectivly the left and right side of the road, and a v coordinate depending of the position in the spline.

Per instance, with a position in the spline for 0.0 to 1.0, you can do something like v = position * x; where x is the number of time you want your texture to repeat on the whole spline length.

Notice that u and v coordinates, aren't necessary between 0.0 and 1.0, a greater number will make it repeat, if you use the correct texture addressing mode (material parameter), which is on repeat by default
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

Ok, P - I like that idea. In fact, I think it will work very well.

However, I have no idea where I'd add u,v coordinate stuff inside of the "GeneratingAMesh" code. (see http://www.ogre3d.org/wiki/index.php/GeneratingAMesh)

I'm guessing I'd add it as an additional buffer. For instance, I simply removed the code for the 2nd buffer to get rid of just being able to define colors for each vertex.

However, I'm extremely lost as to where to go from here since this code doesn't really say where it came from. I'll check out the API after I get my current goal accomplished.
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

Here seems to be a solution to my problem:

Started here: http://www.ogre3d.org/phpBB2/viewtopic.php?t=23332
Actual solution here: http://www.ogre3d.org/phpBB2/viewtopic.php?t=23360&

This clearly states that I'll bind them to another channel.
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

Thank you very much P. That has worked extremely well. Now that I've finally got a bunch of things working together I'll post a shot of the benefits of using a spline. This road has 5 total control pts, and the rest is made automatically.

Image

Future plans include widening that road, creating pebble like valleys to the side of the road (you know the rocks on the sides of roads) and to make the road a bit wider along with a slightly more realistic texture.

Againt P, I can't thank you enough.
Last edited by gzmzhen on Mon Sep 11, 2006 12:19 am, edited 1 time in total.
DigitalGhost
Halfling
Posts: 62
Joined: Mon Feb 07, 2005 9:47 pm

Post by DigitalGhost »

very cool
ChrisKing
Kobold
Posts: 30
Joined: Tue Jan 16, 2007 4:16 am

Post by ChrisKing »

Nice road there.
How did you achieve the seamless texture at the blend? is it a tiled road too?
i read P's solution but i still don't understand how the v=position*x could achieve the seamless effect.
User avatar
stoneCold
OGRE Expert User
OGRE Expert User
Posts: 867
Joined: Fri Oct 01, 2004 9:13 pm
Location: Carinthia, Austria
x 1

Post by stoneCold »

ChrisKing wrote:Nice road there.
How did you achieve the seamless texture at the blend? is it a tiled road too?
i read P's solution but i still don't understand how the v=position*x could achieve the seamless effect.
The solution is today still the same (except you could use ManualObject, which is easier to handle).
Just create the mesh along the spline...

Code: Select all

ManualObject::position(Ogre::SimpleSpline::interpolate(Real t))
create a Texture that is tileable in the U or V direction...(just googled this one)
Image
and at the ManualObject you do...

Code: Select all

ManualObject::texcoord(0.0, t * x) //left side vertex of road
ManualObject::texcoord(1.0, t * x) //right side vertex of road
where "t" is the same "t" you used above to interpolate the spline, and "x" is just a constant you set to whatever you want {0.5, 1.0, 1.5, 2.0, ...} to control how often the road texture tiles per segment.

greetings
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

In addition, I didn't quite use x as a position to tile, but I took the length between the last x to this position to get a regularly mapped road. (so the lines don't shorten or lengthen)

Nate
mklann
Kobold
Posts: 33
Joined: Sun Jan 21, 2007 7:24 pm

Post by mklann »

Hi gzmzhen!

Now, I think that this approach solves part of the problem. Although I haven't followed it through in detail I can see how I could create a street including elevated sidewalks.

I see one problem though that I already encountered when creating streets on an even terrain. What I did and to my understanding it's the same for the approach presented in the other thread, is creating co-planar surfaces for the road with respect to the terrain (the surface of the road is on top of the surface for the terrain). And when you are high above the terrain you get graphics artifacts because (as I understand it) the arithmetic precision of the numbers representing the mesh is insufficient to distinguish the two planes. So my guess was that you would somehow have to construct a single mesh, maybe by cutting out the terrain underneath the road or not constructing it in the first place.

As you might have gathered I'm not a modelling pro ;-) In any case I'd be happy to learn how it's done.

Markus
ChrisKing
Kobold
Posts: 30
Joined: Tue Jan 16, 2007 4:16 am

Post by ChrisKing »

stoneCold
Say if the tile texture example side white lines you gave is changed to broken white lines then it would not be a tile able (for bends) anymore? Definitely it will still work for straight roads.

something like this:

- - - - - - - - - - -
==========
- - - - - - - - - - -

If i am not wrong the solution using Ogre::SimpleSpline::interpolate is the same as this ?
http://www.freeworld3d.org/tutorials/roadsystem.html
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

ChrisKing:

So the texturing is not done by "tiling" per se, but rather the coefficient along the road has to deal with the length of the road from one point to the next. For example, I have pt1 and pt2 where pt2 comes after pt1. I can find the distance between the two simply by doing (pt2-pt1).length() and then use that times some scaling factor that scales the texture per distance.

This effectively makes bends look just fine.

The freeworld page you pointed to, is very similar to the method (that I felt was originally) created using the simpleSpline.

Markus,
So, the problem that you're having is (apparently) known as z-fighting. This doesn't occur on all graphics cards. For example, my ATI card at home does not have this problem at all, but my laptop, and work computers have this problem using the NVIDIA GeForce 7900 card.

If you find a solution that gets rid of this without destroying the underlying terrain mesh, sharing it with us would be great =).

One last thing, if you're thinking about implementing something like this keep in mind how you want to create road intersections... it is really a very tough problem to fix and our solution is quite sub-optimal. It uses a little computational geometry to create a clockwise ordering of the points on the ends of the roads at a particular intersection.

Note that my solution is using a road database to put things together, which allows us to have a dynamic road setup at any given execution of the program. So it is essential that we make intersections automatically as well.

Nate
stodge
Goblin
Posts: 217
Joined: Thu Oct 24, 2002 4:12 am
x 1
Contact:

Post by stodge »

Which scene manager is this (I like the look of the terrain)? And cool, I see you have lat/longs.... :D
What does the debugger tell you? You *are* using a debugger, right??
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

I actually took real height information from a NASA database... and then had to write my own version of a terrain manager. Unfortunately, the data was simply a raw file and didn't work with ogreNewt when I first tried to get it to work with any of the normal scene managers (terrain scene manager or the paging scene manager). I'm not sure if this would still be the case if you decided to use one of them.

The lat/long values are aligned by both the tigerDB (road information) and from the Nasa data that was cropped from their databases. (if I find the link to it I'll post it... but just do a little searching if you want to use specific data).
Raiden
Gnoblar
Posts: 13
Joined: Fri Jul 01, 2005 3:11 pm
Location: Madrid. Spain

Post by Raiden »

Wow gzmzhen very nice road!

Have you considered making a wiki article about creating roads from splines?

It would be a very valuable contribution for the community!
Upsilon
Gnoblar
Posts: 18
Joined: Fri Jan 26, 2007 9:01 am
Location: New York, USA

Post by Upsilon »

yeah, i'd like that.
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

It looks like the code I have already written is somewhat deprecated because of the new ManualObject item. I'll have to look into upgrading it - if I do so successfully I don't mind writing a wiki on it. I'll have to spend some time this weekend looking into it - I'm rather busy with classes until then.

Nate
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

So, I've finally upgraded the code to use Manual Mesh. However, I've not had time yet to put it on to the wiki. I think it would be nice to actually show where the calculations come from, and to describe what a user needs to do to get such code to work properly on their system.

Ultimately though, I'll post the code now. The code takes in the backbone spline, an OgreNewt World object (could be commented out) and the name of the road object (in case you plan on using more than one road). The function returns the actual vertices of the end-pts (for the purpose of making intersections later).

It works best if the ratio of distance between any two points on the spline is close to 1. The closer the better. There are quite a few constants defined in the code that worked for me, but may not work for you - so do fool around with them.

I tried to comment my code, but until I can make a wiki (maybe your questions will help me create it) please do post your questions and I will answer them as quickly as I can.

I hope you enjoy =),
Nate Bauernfeind

Code: Select all

float** MeshBuilder::createRoadMesh(SimpleSpline &backbone, OgreNewt::World *mWorld, String rName)
{
	Ogre::ManualObject currRoad(rName + "obj");
	currRoad.begin("DrivingSim/RoadMat");

	// Road "Constants"
	Real roadWidth = 7.5; // m
	Real roadHeight = 0.3; // m
	Real averageDistPerSection = 15.0;
	int numRoadPiecesThisSegment;

	// TempRoad attributes
	Real dist;
	int numRoadPiecesTtl;

	// for keeping track of the sides
	Vector3 lrVector;
	Vector3 vertex;
	Vector3 lastPt;

	Real t; // for interpolation

	// save some time from all of the calls to backbone
	int numPoints;
	int numHorizontalPts = 5;
	int n, vertexOffset;

	// To add the mesh to the scene.
    Entity* rdEntity;
	SceneNode* rdSceneNode;
	OgreNewt::CollisionPrimitives::TreeCollision *treeCol;

	// to create intersections we need this info
	float **retVertices = new float*[2];
	retVertices[0] = new float[numHorizontalPts*3];
	retVertices[1] = new float[numHorizontalPts*3];

	// Gets the Road node if it's already there.
	try { 
		rdSceneNode = mSceneMgr->getSceneNode("RoadNode"); 
	}
	catch(Ogre::Exception e)
	{ 
		rdSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("RoadNode"); 
	}

	// check here if we can load it from saved cache / prebuilt

	// Define the vertices
	numPoints = backbone.getNumPoints();

	// we need to count the number of segments totally
	numRoadPiecesTtl = -2; // minus first two and last (intersections)
	for (int i = 0; i < numPoints-1; i++)
	{
		dist = (backbone.getPoint(i+1) - backbone.getPoint(i)).length();
		numRoadPiecesThisSegment = dist/averageDistPerSection;
		if (numRoadPiecesThisSegment == 0) numRoadPiecesThisSegment++;
		numRoadPiecesTtl += numRoadPiecesThisSegment;
	}

	if (numRoadPiecesTtl <= 0)
	{
		delete[] retVertices[0];
		delete[] retVertices[1];
		delete[] retVertices;
		return NULL;
	}

	float vCoord = 0;
	Vector3 uvOldPt = backbone.interpolate(0,0);
	Vector3 uvNewPt;
	Vector3 pos, front;
	int cnt=0;

	Real uCoord[5] = {0,0.03125,0.5,1-0.03125,1};
	Real lrVectorMultiplier[5] = {roadWidth*1.45, roadWidth, 0, -roadWidth, -roadWidth*1.45};

	Real currHeight;
	Vector3 currVertices[5];

	// for some reason the first section may only have 1 segment... in this case we need to have lastPt defined
	lastPt = backbone.interpolate(0,0.0);

	for (int i = 0; i < numPoints-1; i++)
	{		
		dist = (backbone.getPoint(i+1) - backbone.getPoint(i)).length();
		numRoadPiecesThisSegment = dist/averageDistPerSection;
		if (numRoadPiecesThisSegment == 0) numRoadPiecesThisSegment++;
	
		if (i == 0)
			n = 1;
		else
			n = 0;
		
//		Ogre::LogManager::getSingleton().logMessage("numRoadPiecesThisSeg = " + Ogre::StringConverter::toString(numRoadPiecesThisSegment));
		for (; n < numRoadPiecesThisSegment; n++)
		{
			t = (Real) n / (Real) (numRoadPiecesThisSegment);
			uvNewPt = backbone.interpolate(i,t);
			vCoord += (uvNewPt-uvOldPt).length()/mTerrainMesh->getTextureRepeat();
			uvOldPt = uvNewPt;

//			Ogre::LogManager::getSingleton().logMessage("Spline Pt: " + Ogre::StringConverter::toString(uvNewPt));
			// create vertices from just the backbone spline

			// move left or right
			// calc lrVector
			vertex = backbone.interpolate(i,t);	// vertex is being used as a temporary variable
			currHeight = mTerrainMesh->getHeight(vertex);

			lastPt.y = vertex.y = 0;

			// positive is left vector
			front = (vertex-lastPt);
			lrVector = front.crossProduct(Vector3::UNIT_Y);
			lrVector.normalise();
			// end of lrVector calculation

			for (int j = 0; j < numHorizontalPts; j++)
			{
				// create this side piece
				currVertices[j] = vertex;
				currVertices[j] += lrVector*lrVectorMultiplier[j];

				// update height
				currVertices[j].y = mTerrainMesh->getHeight(currVertices[j]);
				if (currVertices[j].y > currHeight && j != 0 && j != numHorizontalPts-1)
					currHeight = currVertices[j].y;
			}

			for (int j = 1; j < numHorizontalPts-1; j++)
					currVertices[j].y = currHeight + roadHeight;

			lastPt = currVertices[2];

			// finally add vertices
			for (int j = 0; j < numHorizontalPts; j++)
			{
				// get vertex from created values
				currRoad.position(currVertices[j]);
				currRoad.textureCoord(uCoord[j], vCoord);

				if (cnt < 1)
				{
					retVertices[0][j*3] = currVertices[j].x;
					retVertices[0][j*3+1] = currVertices[j].y;
					retVertices[0][j*3+2] = currVertices[j].z;
				}
				else if (cnt >= numRoadPiecesTtl)
				{
					int k = numHorizontalPts-j-1;
					retVertices[1][k*3] = currVertices[j].x;
					retVertices[1][k*3+1] = currVertices[j].y;
					retVertices[1][k*3+2] = currVertices[j].z;
				}
			}
			/*
			if (cnt < 1)
				Ogre::LogManager::getSingleton().logMessage("retVertices[0] set");
			else if (cnt >= numRoadPiecesTtl)
				Ogre::LogManager::getSingleton().logMessage("retVertices[1] set");
			*/
			cnt++;
		} // loop on n
	}

	for (n = 0; n < numRoadPiecesTtl; n++)
	{
		// create four: 0,1,4 0,4,3 1,2,5 1,5,4 (0->6 + offset)
		for (vertexOffset = n*numHorizontalPts; vertexOffset < (n+1)*numHorizontalPts-1; vertexOffset++) // vertexOffset
		{
			unsigned short face[3*2] = {vertexOffset + 0,vertexOffset + numHorizontalPts+1, vertexOffset + 1,
								vertexOffset + 0,vertexOffset + numHorizontalPts,vertexOffset + numHorizontalPts+1};

			for (int j=0; j<6; j++)
				currRoad.index(face[j]);
		}
	}
	currRoad.end();
	currRoad.convertToMesh(rName+"Mesh");

	// add mesh to scene
	rdEntity = mSceneMgr->createEntity(rName + "Ent", rName + "Mesh");
	rdEntity->setQueryFlags( ROAD_MASK );
	rdSceneNode = rdSceneNode->createChildSceneNode();
	rdSceneNode->attachObject(rdEntity);

	// ogreNewt
	treeCol = new OgreNewt::CollisionPrimitives::TreeCollision(mWorld, rdSceneNode, false);
	OgreNewt::Body* body = new OgreNewt::Body(mWorld, treeCol);
	delete treeCol;
	body->attachToNode(rdSceneNode);
	body->setPositionOrientation(rdSceneNode->getPosition(), rdSceneNode->getOrientation());

	return retVertices;
}
gzmzhen
Halfling
Posts: 79
Joined: Mon Feb 20, 2006 2:08 am
Location: CMU, Pittsburgh PA
Contact:

Post by gzmzhen »

And of course, here's a picture of our "delivered" project:

Image
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Nice! :)

This begs to be Wiki'ed! :D
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
Post Reply