Skip to main content
GameDev.net gamedev.net
🔒 Locked

Help with MOGL terrain chapter

Started by MARS_999 Apr 14, 2006 at 10:21 PM 8 replies 2k views
Original Post
MARS_999
MARS_999
I bought the "MOGLGP" book and am trying to implement the frustum culling that is covered. I am having some issues with the terrain rendering correclty... If I take out the if() to check for what patches are in view the whole terrain renders just fine, as one whole patch. Now I am guessing it has to do with the frutsum calculation code? Here is what I am using and maybe its correct and I am doing something else wrong?

#ifndef CFRUSTUM_H
#define CFRUSTUM_H

class CFrustum
{
public:
	float frustum[6][4];

	CFrustum(void)
	{
	}

	~CFrustum(void)
	{
	}

	void multMatrix(float a[16], float b[16], float r[16])
	{
		r[0] = (a[0] *b[0]) + (a[1] *b[4]) + (a[2] *b[8] ) + (a[3] *b[12]);
		r[1] = (a[0] *b[1]) + (a[1] *b[5]) + (a[2] *b[9] ) + (a[3] *b[13]);
		r[2] = (a[0] *b[2]) + (a[1] *b[6]) + (a[2] *b[10]) + (a[3] *b[14]);
		r[3] = (a[0] *b[3]) + (a[1] *b[7]) + (a[2] *b[11]) + (a[3] *b[15]);		
		r[4] = (a[4] *b[0]) + (a[5] *b[4]) + (a[6] *b[8] ) + (a[7] *b[12]);
		r[5] = (a[4] *b[1]) + (a[5] *b[5]) + (a[6] *b[9] ) + (a[7] *b[13]);
		r[6] = (a[4] *b[2]) + (a[5] *b[6]) + (a[6] *b[10]) + (a[7] *b[14]);
		r[7] = (a[4] *b[3]) + (a[5] *b[7]) + (a[6] *b[11]) + (a[7] *b[15]);		
		r[8] = (a[8] *b[0]) + (a[9] *b[4]) + (a[10]*b[8] ) + (a[11]*b[12]);
		r[9] = (a[8] *b[1]) + (a[9] *b[5]) + (a[10]*b[9] ) + (a[11]*b[13]);
		r[10]= (a[8] *b[2]) + (a[9] *b[6]) + (a[10]*b[10]) + (a[11]*b[14]);
		r[11]= (a[8] *b[3]) + (a[9] *b[7]) + (a[10]*b[11]) + (a[11]*b[15]);
		r[12]= (a[12]*b[0]) + (a[13]*b[4]) + (a[14]*b[8] ) + (a[15]*b[12]);
		r[13]= (a[12]*b[1]) + (a[13]*b[5]) + (a[14]*b[9] ) + (a[15]*b[13]);
		r[14]= (a[12]*b[2]) + (a[13]*b[6]) + (a[14]*b[10]) + (a[15]*b[14]);
		r[15]= (a[12]*b[3]) + (a[13]*b[7]) + (a[14]*b[11]) + (a[15]*b[15]);
	}

	void normPlane(float plane[4], float result[4])
	{
	    // Find the magnitude of the vector.
		double mag = sqrt((plane[0] * plane[0]) + (plane[1] * plane[1]) + (plane[2] * plane[2]));

		// Divide the values by the length.
		result[0] = float(plane[0] / mag);
		result[1] = float(plane[1] / mag);
		result[2] = float(plane[2] / mag);
		result[3] = float(plane[3] / mag);
	}

    void calculateFrustum(void)
	{
		// Get the projection matrix.
		float projection[16];
		glGetFloatv(GL_PROJECTION_MATRIX, projection);

		// Get the modelview matrix.
		float modelview[16];
		glGetFloatv(GL_MODELVIEW_MATRIX, modelview);

		// Before we can extract the sides of the frustum, we need to 
		// multiply the projection and modelview matrices together. 
		float result[16];
		multMatrix(modelview, projection, result);

		// Extract each side of the frustum and normalize that side.
		// Right side:
		frustum[0][0] = result[3]  - result[0];
		frustum[0][1] = result[7]  - result[4];
		frustum[0][2] =	result[11] - result[8];
		frustum[0][3] =	result[15] - result[12];
		normPlane(frustum[0], frustum[0]);

		// Left side:
		frustum[1][0] = result[3]  + result[0];
		frustum[1][1] = result[7]  + result[4];
		frustum[1][2] =	result[11] + result[8];
		frustum[1][3] =	result[15] + result[12];
		normPlane(frustum[1], frustum[1]);

		// Bottom:
		frustum[2][0] = result[3]  + result[1];
		frustum[2][1] = result[7]  + result[5];
		frustum[2][2] =	result[11] + result[9];
		frustum[2][3] =	result[15] + result[13];
		normPlane(frustum[2], frustum[2]);

		// Top:
		frustum[3][0] = result[3]  - result[1];
		frustum[3][1] = result[7]  - result[5];
		frustum[3][2] =	result[11] - result[9];
		frustum[3][3] =	result[15] - result[13];
		normPlane(frustum[3], frustum[3]);

		// Back:
		frustum[4][0] = result[3]  - result[2];
		frustum[4][1] = result[7]  - result[6];
		frustum[4][2] =	result[11] - result[10];
		frustum[4][3] =	result[15] - result[14];
		normPlane(frustum[4], frustum[4]);

		// Front:
		frustum[5][0] = result[3]  + result[2];
		frustum[5][1] = result[7]  + result[6];
		frustum[5][2] =	result[11] + result[10];
		frustum[5][3] =	result[15] + result[14];
		normPlane(frustum[5], frustum[5]);
	}

	bool pointInFrustum(float x, float y, float z)
	{
        // Loop through the sides of the frustum.
		for(int i = 0; i < 6; i++)
		{
			// Calculate the distance to the side.
			float dist = frustum[0] * x +
						 frustum[1] * y +
						 frustum[2] * z +
						 frustum[3];

			// If the distance was negative, the point is outside the frustum.
			if(dist <= 0)
				return false;
		}
		// the point was inside the frustum.
		return true;
	}
};

#endif


I am calling this in my rendering function everytime I render my terrain patch

glPushMatrix();
	frustum.calculateFrustum();
	glPopMatrix();

	float maxX = 0.0f, maxY = 0.0f, maxZ = 0.0f;
	float minX = 0.0f, minY = 0.0f, minZ = 0.0f;

	SetupTexturesTerrain();

	// Loop through the chunks.
	for(unsigned int z = 0; z < mapData.map_Z / (PATCH_SIZE - 1); z++)
	{
		for(unsigned int x = 0; x < mapData.map_X / (PATCH_SIZE - 1); x++)
		{

			maxX = patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].maxX;
			maxY = patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].maxY;
			maxZ = patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].maxZ;
			minX = patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].minX;
			minY = patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].minY;
			minZ = patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].minZ;

			// Check to see if the corners of the chunk are within the frustum.
			if (!frustum.pointInFrustum(maxX, maxY, maxZ) &&
				!frustum.pointInFrustum(minX, maxY, minZ) &&
				!frustum.pointInFrustum(minX, maxY, maxZ) &&
				!frustum.pointInFrustum(maxX, maxY, minZ) &&
				!frustum.pointInFrustum(maxX, minY, maxZ) &&
				!frustum.pointInFrustum(minX, minY, minZ) &&
				!frustum.pointInFrustum(minX, minY, maxZ) &&
				!frustum.pointInFrustum(maxX, minY, minZ))
				continue;

			// bind the buffer and draw the chunk.
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].index_buffer);
			glDrawElements(GL_TRIANGLE_STRIP, numberIndices, GL_UNSIGNED_INT, NULL);


		}
	}


Any help or ideas would be great.
Sordith
Sordith
Been waiting for some questions/comments about my chapter...

My first sugestion would be to check and make sure you're calculating your min and max values correctly. I have a bit of debug code to display the bounding boxes, just to make sure the values are correct. It can go just after your call to glDrawEments.

// Draw the bounding box.if (boundingBox){										glColor3f(1.0f, 0.0f, 0.0f);	glBegin(GL_LINES);		glVertex3f(maxX, maxY, maxZ);		glVertex3f(minX, maxY, maxZ);		glVertex3f(minX, maxY, maxZ);		glVertex3f(minX, maxY, minZ);		glVertex3f(minX, maxY, minZ);		glVertex3f(maxX, maxY, minZ);		glVertex3f(maxX, maxY, minZ);		glVertex3f(maxX, maxY, maxZ);									glVertex3f(maxX, minY, maxZ);		glVertex3f(minX, minY, maxZ);		glVertex3f(minX, minY, maxZ);		glVertex3f(minX, minY, minZ);		glVertex3f(minX, minY, minZ);		glVertex3f(maxX, minY, minZ);		glVertex3f(maxX, minY, minZ);		glVertex3f(maxX, minY, maxZ);									glVertex3f(maxX, maxY, maxZ);		glVertex3f(maxX, minY, maxZ);		glVertex3f(maxX, maxY, minZ);		glVertex3f(maxX, minY, minZ);		glVertex3f(minX, maxY, minZ);		glVertex3f(minX, minY, minZ);		glVertex3f(minX, maxY, maxZ);		glVertex3f(minX, minY, maxZ);	glEnd();}
MARS_999
MARS_999
Hmmm I have that code in and it doesn't render anything?? I am using GLSL to render my terrain and not sure if that is causing an issue with the bounding boxes not to render? I will look at this more tonight. Thanks
MARS_999
MARS_999
Ok here is my code version of yours to setup the VBO and IBO

void CTerrain::CreateVBOandIBO(void){	unsigned int z = 0;	unsigned int x = 0;	unsigned int currentIndex = 0;	unsigned int startx = 0;	unsigned int startz = 0;	unsigned int endx = 0;	unsigned int endz = 0;	float maxX = 0.0f, maxY = 0.0f, maxZ = 0.0f;	float minX = 0.0f, minY = 0.0f, minZ = 0.0f;	//this here will create the patch array so e.g. 1024 size map / (33 - 1) = 32 patch x 32 patch	patch = new Patch[(mapData.map_X / (PATCH_SIZE - 1)) * (mapData.map_Z / (PATCH_SIZE - 1))];		//initialize patch array to known values	for(z = 0; z < mapData.map_Z / (PATCH_SIZE - 1); z++)	{        for(x = 0; x < mapData.map_X / (PATCH_SIZE - 1); x++)		{			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].index_buffer = 0;			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].maxX = 0.0f;			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].maxY = 0.0f;			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].maxZ = 0.0f;			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].minX = 0.0f;			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].minY = 0.0f;			patch[z * (mapData.map_Z / (PATCH_SIZE - 1)) + x].minZ = 0.0f;		}	}	//Create vertex list.	if(vertex_buffer > 0)		glDeleteBuffers(1, &vertex_buffer);	glGenBuffers(1, &vertex_buffer);		//calculate the memory requirements for the VBO	vboObjectSizeArray = sizeof(float) * (sizeof(Vertex) / sizeof(float)) * mapData.map_X * mapData.map_Z;	//allocate memory for the vertex list.	glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);	glBufferData(GL_ARRAY_BUFFER, vboObjectSizeArray, terrain, GL_STATIC_DRAW);	if(!glIsBuffer(vertex_buffer))	{		MessageBox(NULL, "Initialization failed on VBO", "Failed to initalize vertex buffer", MB_OK);		//kill app here	}	// Calculate the offset in the array for the vertex, normal, texcoords ect...	vertexStride = sizeof(Vertex);	offsetForNormals = sizeof(float) * 3;	offsetForTangents = sizeof(float) * 6;	offsetForTexCoords = sizeof(float) * 9;	// Calculate the triangle index lists for each patch	for(unsigned int patchZ = 0; patchZ < mapData.map_Z / (PATCH_SIZE - 1); patchZ++)	{        for(unsigned int patchX = 0; patchX < mapData.map_X / (PATCH_SIZE - 1); patchX++)		{			// Create the index buffer.			if(patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].index_buffer > 0)				glDeleteBuffers(1, &patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].index_buffer);			glGenBuffers(1, &patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].index_buffer);			//allocate memory for the buffer.			numberIndices = ((PATCH_SIZE - 1) * (PATCH_SIZE * 2 + 2)) - 2;			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].index_buffer);			glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * numberIndices, NULL, GL_STATIC_DRAW);			// Fill triangle buffer.			unsigned int *triBuff = (unsigned int *)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);			currentIndex = 0;																											// Calculate the extents of the chunk.			startx = patchX * (PATCH_SIZE - 1);			startz = patchZ * (PATCH_SIZE - 1);			endx = startx + PATCH_SIZE;			endz = startz + PATCH_SIZE;			maxX = float(startx);			minX = float(startx);			maxY = terrain[startz * mapData.map_Z + startx].vy;			minY = terrain[startz * mapData.map_Z + startx].vy;			maxZ = float(startz);			minZ = float(startz);			// Loop through the chunk extents and create the list.			for(z = startz; z < endz - 1; z++)			{				for(x = startx; x < endx; x++)				{					// Update the min and max values.					maxX = maxX > x ? maxX : x;					minX = minX < x ? minX : x;					maxY = maxY > terrain[z * mapData.map_Z + x].vy ? maxY : terrain[z * mapData.map_Z + x].vy;					minY = minY < terrain[z * mapData.map_Z + x].vy ? minY : terrain[z * mapData.map_Z + x].vy;					maxZ = maxZ > z ? maxZ : z;					minZ = minZ < z ? minZ : z;					// Used for degenerate triangles.					if(x == startx && z != startz)						triBuff[currentIndex++] = x + (z * mapData.map_X);					triBuff[currentIndex++] = x + (z * mapData.map_X);					triBuff[currentIndex++] = x + ((z + 1) * mapData.map_X);					// Used for degenerate triangles.					if(x == endx - 1 && z != endz - 2)						triBuff[currentIndex++] = x + ((z + 1) * mapData.map_X);				}			}			glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);						// Assign the min and max values found.			patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].maxX = maxX;			patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].maxY = maxY;			patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].maxZ = maxZ;			patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].minX = minX;			patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].minY = minY;			patch[patchZ * (mapData.map_Z / (PATCH_SIZE - 1)) + patchX].minZ = minZ;		}	}}


BTW my near plane is 1.5 and far is 150.0

I tired everything I can think of and the terrain rendering is all jumping in and out of the view... Any help would be greatly appreciated! Thanks

[Edited by - MARS_999 on April 15, 2006 7:47:44 PM]
MARS_999
MARS_999
BTW your code doesn't compile under VC++ .net 2003 I had to type cast the sqrt() to float otherwise the compiler barks at me... :) I sent this to Dave along time ago and never heard anything about it...
Myopic Rhino
Myopic Rhino
Quote:
Original post by MARS_999
I sent this to Dave along time ago and never heard anything about it...
I've got a long list of updates that I need to post to the book's website. I got buried with work in the course of changing jobs from Qualcomm to ATI, but I'm nearly dug out now so the updates should be posted soon.
MARS_999
MARS_999
Thanks Dave for the info. On to the chapter for culling. I think I got it working, but not 100% sure. The terrain doesn't jump in and out and disapear anymore. So how can I determine how many polygons I am rendering vs. how many total I could have render for my terrain of 1025x1025 size? Also will this tutorial work with 1024x1024 map sizes or will I have to change the patch size from 33 to 32? I am not sure what kind of system you have but what kind of FPS are you getting with your brute force example? I am running aroud 30-40fps with a 1025x1025 heightmap using shaders, water, shadows ect... this is at 1600x1200 BTW. Thanks
Sordith
Sordith
Quote:
So how can I determine how many polygons I am rendering vs. how many total I could have render for my terrain of 1025x1025 size?

The number of triangles in a section of brute force terrain is (Height-1) * (Width*2), or (Height-1) * (Width*2+2) - 2 for a terrain using a single strip and degenerate triangles. (course, it's 4am, so my math could be off)

That means that you're full terrain has 2,101,246 polygons, and each patch has 2,174 polygons. You can find out how many polygons you are displaying by multiplying the number of patches displayed by 2,174.

I can't remember my framerates, but they were around the 20-30 fps mark, I believe.
MARS_999
MARS_999
Quote:
Original post by Sordith
Quote:
So how can I determine how many polygons I am rendering vs. how many total I could have render for my terrain of 1025x1025 size?

The number of triangles in a section of brute force terrain is (Height-1) * (Width*2), or (Height-1) * (Width*2+2) - 2 for a terrain using a single strip and degenerate triangles. (course, it's 4am, so my math could be off)

That means that you're full terrain has 2,101,246 polygons, and each patch has 2,174 polygons. You can find out how many polygons you are displaying by multiplying the number of patches displayed by 2,174.

I can't remember my framerates, but they were around the 20-30 fps mark, I believe.


Yeah I figured the 2million polygons was total but what I had on the screen at once. So in the rendering function the two for loops just increment some counter for each loop and multiply that by 2174 and I should have a total polygon count. I was wondering what hardware did you have when you got your fps and what resolution were you at? At 1024x768 I get around 80 fps! Now remember this is with some pretty intense shaders per pixel lighting and a very intense water shader... All in all I think these are really good fps just wondering. When I run it brute force and dump all 2 million polygons I get 0-5 fps! :) So the culling must be working. I can't seem to figure out why but I can't do the line rendering I need to do to see the boxes. Not sure if my shader is the cause or some other reason... Thanks BTW did you get that error message I sent Dave about the sqrt() barking about ambiguity? Thanks
MARS_999
MARS_999
This is for sordith, if you are still around? Anyway after I finally rendered my Bounding boxes, I have a gap on the z axis I think and the x axis boxes are all tightly packed but the z has a gap in between each one? I am coming up short with the math by one map height amount is what it looks like to me? Not sure if thats right or not, but for the most part I am using your code but with a

//my array is like this float *pointer = new float[width * height];//where as yours isfloat **pointer = new float*[width];//ect...

So when I index my vertex data array for the vertex Y axis I use this
index = (startz * mapData.map_Z + startx);maxX = float(startx);minX = float(startx);maxY = terrain[index].vy;minY = terrain[index].vy;maxZ = float(startz);minZ = float(startz);

I use the same indexing method for both sets of loops.... Any help would be greatly appreciated. Thanks

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.