Esempio n. 1
0
/* Initialise the Trig tables */
bool trigInitialise(void)
{
	uint64_t test;
	uint32_t crc;
	uint32_t i;

	// Generate tables.
	STATIC_ASSERT(sizeof(trigSinTable)/sizeof(*trigSinTable) == 0x4001);
	for (i = 0; i != 0x4001; ++i)
	{
		trigSinTable[i] = (int)(0x10000*sin(i*(M_PI/0x8000)) + 0.5) - !!i;  // -!!i = subtract 1, unless i == 0.
	}
	STATIC_ASSERT(sizeof(trigAtanTable)/sizeof(*trigAtanTable) == 0x2001);
	for (i = 0; i != 0x2001; ++i)
	{
		trigAtanTable[i] = (int)(0x8000/M_PI*atan((double)i/0x2000) + 0.5);
	}

	// Check tables are correct.
	crc = ~crcSumU16(0, trigSinTable, sizeof(trigSinTable)/sizeof(*trigSinTable));
	ASSERT(crc == 0x6D3C8DB5, "Bad trigSinTable CRC = 0x%08X, sin function is broken.", crc);
	crc = ~crcSumU16(0, trigAtanTable, sizeof(trigAtanTable)/sizeof(*trigAtanTable));
	ASSERT(crc == 0xD2797F85, "Bad trigAtanTable CRC = 0x%08X, atan function is broken.", crc);

	// Test large and small square roots.
	for (test = 0x0000; test != 0x10000; ++test)
	{
		uint64_t lower = test*test;
		uint64_t upper = (test + 1)*(test + 1) - 1;
		ASSERT((uint32_t)iSqrt(lower) == test, "Sanity check failed, sqrt(%"PRIu64") gave %"PRIu32" instead of %"PRIu64"!", lower, i64Sqrt(lower), test);
		ASSERT((uint32_t)iSqrt(upper) == test, "Sanity check failed, sqrt(%"PRIu64") gave %"PRIu32" instead of %"PRIu64"!", upper, i64Sqrt(upper), test);
	}
	for (test = 0xFFFE0000; test != 0x00020000; test = (test + 1)&0xFFFFFFFF)
	{
		uint64_t lower = test*test;
		uint64_t upper = (test + 1)*(test + 1) - 1;
		ASSERT((uint32_t)i64Sqrt(lower) == test, "Sanity check failed, sqrt(%"PRIu64") gave %"PRIu32" instead of %"PRIu64"!", lower, i64Sqrt(lower), test);
		ASSERT((uint32_t)i64Sqrt(upper) == test, "Sanity check failed, sqrt(%"PRIu64") gave %"PRIu32" instead of %"PRIu64"!", upper, i64Sqrt(upper), test);
	}

	return true;
}
Esempio n. 2
0
/**
 * Check what the videocard + drivers support and divide the loaded map into sectors that can be drawn.
 * It also determines the lightmap size.
 */
bool initTerrain(void)
{
	int i, j, x, y, a, b, absX, absY;
	PIELIGHT colour[2][2], centerColour;
	int layer = 0;
	
	RenderVertex *geometry;
	RenderVertex *water;
	DecalVertex *decaldata;
	int geometrySize, geometryIndexSize;
	int waterSize, waterIndexSize;
	int textureSize, textureIndexSize;
	GLuint *geometryIndex;
	GLuint *waterIndex;
	GLuint *textureIndex;
	PIELIGHT *texture;
	int decalSize;
	int maxSectorSizeIndices, maxSectorSizeVertices;
	bool decreasedSize = false;

	// call VBO support hack before using it
	screen_EnableVBO();
	
	// this information is useful to prevent crashes with buggy opengl implementations
	glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GLmaxElementsVertices);
	glGetIntegerv(GL_MAX_ELEMENTS_INDICES,  &GLmaxElementsIndices);

	// testing for crappy cards
	debug(LOG_TERRAIN, "GL_MAX_ELEMENTS_VERTICES: %i", (int)GLmaxElementsVertices);
	debug(LOG_TERRAIN, "GL_MAX_ELEMENTS_INDICES:  %i", (int)GLmaxElementsIndices);
	
	// now we know these values, determine the maximum sector size achievable
	maxSectorSizeVertices = iSqrt(GLmaxElementsVertices/2)-1;
	maxSectorSizeIndices = iSqrt(GLmaxElementsIndices/12);

	debug(LOG_TERRAIN, "preferred sector size: %i", sectorSize);
	debug(LOG_TERRAIN, "maximum sector size due to vertices: %i", maxSectorSizeVertices);
	debug(LOG_TERRAIN, "maximum sector size due to indices: %i", maxSectorSizeIndices);
	
	if (sectorSize > maxSectorSizeVertices)
	{
		sectorSize = maxSectorSizeVertices;
		decreasedSize = true;
	}
	if (sectorSize > maxSectorSizeIndices)
	{
		sectorSize = maxSectorSizeIndices;
		decreasedSize = true;
	}
	if (decreasedSize)
	{
		if (sectorSize < 1)
		{
			debug(LOG_WARNING, "GL_MAX_ELEMENTS_VERTICES: %i", (int)GLmaxElementsVertices);
			debug(LOG_WARNING, "GL_MAX_ELEMENTS_INDICES:  %i", (int)GLmaxElementsIndices);
			debug(LOG_WARNING, "maximum sector size due to vertices: %i", maxSectorSizeVertices);
			debug(LOG_WARNING, "maximum sector size due to indices: %i", maxSectorSizeIndices);
			debug(LOG_ERROR, "Your graphics card and/or drivers do not seem to support glDrawRangeElements, needed for the terrain renderer.");
			debug(LOG_ERROR, "- Do other 3D games work?");
			debug(LOG_ERROR, "- Did you install the latest drivers correctly?");
			debug(LOG_ERROR, "- Do you have a 3D window manager (Aero/Compiz) running?");
			return false;
		}
		debug(LOG_WARNING, "decreasing sector size to %i to fit graphics card constraints", sectorSize);
	}

	// +4 = +1 for iHypot rounding, +1 for sector size rounding, +2 for edge of visibility
	terrainDistance = iHypot(visibleTiles.x/2, visibleTiles.y/2)+4+sectorSize/2;
	debug(LOG_TERRAIN, "visible tiles x:%i y: %i", visibleTiles.x, visibleTiles.y);
	debug(LOG_TERRAIN, "terrain view distance: %i", terrainDistance);
	
	/////////////////////
	// Create the sectors
	xSectors = (mapWidth +sectorSize-1)/sectorSize;
	ySectors = (mapHeight+sectorSize-1)/sectorSize;
	sectors = (Sector *)malloc(sizeof(Sector)*xSectors*ySectors);
	
	////////////////////
	// fill the geometry part of the sectors
	geometry = (RenderVertex *)malloc(sizeof(RenderVertex)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2);
	geometryIndex = (GLuint *)malloc(sizeof(GLuint)*xSectors*ySectors*sectorSize*sectorSize*12);
	geometrySize = 0;
	geometryIndexSize = 0;
	
	water = (RenderVertex *)malloc(sizeof(RenderVertex)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2);
	waterIndex = (GLuint *)malloc(sizeof(GLuint)*xSectors*ySectors*sectorSize*sectorSize*12);
	waterSize = 0;
	waterIndexSize = 0;
	for (x = 0; x < xSectors; x++)
	{
		for (y = 0; y < ySectors; y++)
		{
			sectors[x*ySectors + y].dirty = false;
			sectors[x*ySectors + y].geometryOffset = geometrySize;
			sectors[x*ySectors + y].geometrySize = 0;
			sectors[x*ySectors + y].waterOffset = waterSize;
			sectors[x*ySectors + y].waterSize = 0;
			
			setSectorGeometry(x, y, geometry, water, &geometrySize, &waterSize);
			
			sectors[x*ySectors + y].geometrySize = geometrySize - sectors[x*ySectors + y].geometryOffset;
			sectors[x*ySectors + y].waterSize = waterSize - sectors[x*ySectors + y].waterOffset;
			// and do the index buffers
			sectors[x*ySectors + y].geometryIndexOffset = geometryIndexSize;
			sectors[x*ySectors + y].geometryIndexSize = 0;
			sectors[x*ySectors + y].waterIndexOffset = waterIndexSize;
			sectors[x*ySectors + y].waterIndexSize = 0;
			
			for (i = 0; i < sectorSize; i++)
			{
				for (j = 0; j < sectorSize; j++)
				{
					if (x*sectorSize+i >= mapWidth || y*sectorSize+j >= mapHeight)
					{
						continue; // off map, so skip
					}

					/* One tile is composed of 4 triangles,
					 * we need _2_ vertices per tile (1)
					 * 		e.g. center and bottom left
					 * 	the other 3 vertices are from the adjacent tiles
					 * 	on their top and right.
					 * (1) The top row and right column of tiles need 4 vertices per tile
					 * 	because they do not have adjacent tiles on their top and right,
					 * 	that is why we add _1_ row and _1_ column to provide the geometry
					 * 	for these tiles.
					 * This is the source of the '*2' and '+1' in the index math below.
					 */
#define q(i,j,center) ((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + ((i)*(sectorSize+1)+(j))*2+(center))
					// First triangle
					geometryIndex[geometryIndexSize+0]  = q(i  ,j  ,1);	// Center vertex
					geometryIndex[geometryIndexSize+1]  = q(i  ,j  ,0);	// Bottom left
					geometryIndex[geometryIndexSize+2]  = q(i+1,j  ,0);	// Bottom right
					// Second triangle
					geometryIndex[geometryIndexSize+3]  = q(i  ,j  ,1);	// Center vertex
					geometryIndex[geometryIndexSize+4]  = q(i  ,j+1,0);	// Top left
					geometryIndex[geometryIndexSize+5]  = q(i  ,j  ,0);	// Bottom left
					// Third triangle
					geometryIndex[geometryIndexSize+6]  = q(i  ,j  ,1);	// Center vertex
					geometryIndex[geometryIndexSize+7]  = q(i+1,j+1,0);	// Top right
					geometryIndex[geometryIndexSize+8]  = q(i  ,j+1,0);	// Top left
					// Fourth triangle
					geometryIndex[geometryIndexSize+9]  = q(i  ,j  ,1);	// Center vertex
					geometryIndex[geometryIndexSize+10] = q(i+1,j  ,0);	// Bottom right
					geometryIndex[geometryIndexSize+11] = q(i+1,j+1,0);	// Top right
					geometryIndexSize += 12;
					if (isWater(i+x*sectorSize,j+y*sectorSize))
					{
						waterIndex[waterIndexSize+0]  = q(i  ,j  ,1);
						waterIndex[waterIndexSize+1]  = q(i  ,j  ,0);
						waterIndex[waterIndexSize+2]  = q(i+1,j  ,0);
						
						waterIndex[waterIndexSize+3]  = q(i  ,j  ,1);
						waterIndex[waterIndexSize+4]  = q(i  ,j+1,0);
						waterIndex[waterIndexSize+5]  = q(i  ,j  ,0);
						
						waterIndex[waterIndexSize+6]  = q(i  ,j  ,1);
						waterIndex[waterIndexSize+7]  = q(i+1,j+1,0);
						waterIndex[waterIndexSize+8]  = q(i  ,j+1,0);
						
						waterIndex[waterIndexSize+9]  = q(i  ,j  ,1);
						waterIndex[waterIndexSize+10] = q(i+1,j  ,0);
						waterIndex[waterIndexSize+11] = q(i+1,j+1,0);
						waterIndexSize += 12;
					}
				}
			}
			sectors[x*ySectors + y].geometryIndexSize = geometryIndexSize - sectors[x*ySectors + y].geometryIndexOffset;
			sectors[x*ySectors + y].waterIndexSize = waterIndexSize - sectors[x*ySectors + y].waterIndexOffset;
		}
	}
	glGenBuffers(1, &geometryVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, geometryVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(RenderVertex)*geometrySize, geometry, GL_DYNAMIC_DRAW); glError();
	free(geometry);
	
	glGenBuffers(1, &geometryIndexVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, geometryIndexVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint)*geometryIndexSize, geometryIndex, GL_STATIC_DRAW); glError();
	free(geometryIndex);
	
	glGenBuffers(1, &waterVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, waterVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(RenderVertex)*waterSize, water, GL_DYNAMIC_DRAW); glError();
	free(water);
	
	glGenBuffers(1, &waterIndexVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, waterIndexVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint)*waterIndexSize, waterIndex, GL_STATIC_DRAW); glError();
	free(waterIndex);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	
	////////////////////
	// fill the texture part of the sectors
	texture = (PIELIGHT *)malloc(sizeof(PIELIGHT)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*numGroundTypes);
	textureIndex = (GLuint *)malloc(sizeof(GLuint)*xSectors*ySectors*sectorSize*sectorSize*12*numGroundTypes);
	textureSize = 0;
	textureIndexSize = 0;
	for (layer = 0; layer < numGroundTypes; layer++)
	{
		for (x = 0; x < xSectors; x++)
		{
			for (y = 0; y < ySectors; y++)
			{
				if (layer == 0)
				{
					sectors[x*ySectors + y].textureOffset = (int *)malloc(sizeof(int)*numGroundTypes);
					sectors[x*ySectors + y].textureSize = (int *)malloc(sizeof(int)*numGroundTypes);
					sectors[x*ySectors + y].textureIndexOffset = (int *)malloc(sizeof(int)*numGroundTypes);
					sectors[x*ySectors + y].textureIndexSize = (int *)malloc(sizeof(int)*numGroundTypes);
				}

				sectors[x*ySectors + y].textureOffset[layer] = textureSize;
				sectors[x*ySectors + y].textureSize[layer] = 0;
				sectors[x*ySectors + y].textureIndexOffset[layer] = textureIndexSize;
				sectors[x*ySectors + y].textureIndexSize[layer] = 0;
				//debug(LOG_WARNING, "offset when filling %i: %i", layer, xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer);
				for (i = 0; i < sectorSize+1; i++)
				{
					for (j = 0; j < sectorSize+1; j++)
					{
						bool draw = false;
						bool off_map;

						// set transparency
						for (a=0;a<2;a++)
						{
							for(b=0;b<2;b++)
							{
								absX = x*sectorSize+i+a;
								absY = y*sectorSize+j+b;
								colour[a][b].rgba = 0x00FFFFFF; // transparent
								
								// extend the terrain type for the bottom and left edges of the map
								off_map = false;
								if (absX == mapWidth)
								{
									off_map = true;
									absX--;
								}
								if (absY == mapHeight)
								{
									off_map = true;
									absY--;
								}
								
								if (absX < 0 || absY < 0 || absX >= mapWidth || absY >= mapHeight)
								{
									// not on the map, so don't draw
									continue;
								}
								if (mapTile(absX,absY)->ground == layer)
								{
									colour[a][b].rgba = 0xFFFFFFFF;
									if (!off_map)
									{
										// if this point lies on the edge is may not force this tile to be drawn
										// otherwise this will give a bright line when fog is enabled
										draw = true;
									}
								}
							}
						}
						texture[xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer+((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + (i*(sectorSize+1)+j)*2)].rgba = colour[0][0].rgba;
						averageColour(&centerColour, colour[0][0], colour[0][1], colour[1][0], colour[1][1]);
						texture[xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer+((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + (i*(sectorSize+1)+j)*2+1)].rgba = centerColour.rgba;
						textureSize += 2;
						if ((draw) && i < sectorSize && j < sectorSize)
						{
							textureIndex[textureIndexSize+0]  = q(i  ,j  ,1);
							textureIndex[textureIndexSize+1]  = q(i  ,j  ,0);
							textureIndex[textureIndexSize+2]  = q(i+1,j  ,0);
							
							textureIndex[textureIndexSize+3]  = q(i  ,j  ,1);
							textureIndex[textureIndexSize+4]  = q(i  ,j+1,0);
							textureIndex[textureIndexSize+5]  = q(i  ,j  ,0);
							
							textureIndex[textureIndexSize+6]  = q(i  ,j  ,1);
							textureIndex[textureIndexSize+7]  = q(i+1,j+1,0);
							textureIndex[textureIndexSize+8]  = q(i  ,j+1,0);
							
							textureIndex[textureIndexSize+9]  = q(i  ,j  ,1);
							textureIndex[textureIndexSize+10] = q(i+1,j  ,0);
							textureIndex[textureIndexSize+11] = q(i+1,j+1,0);
							textureIndexSize += 12;
						}

					}
				}
				sectors[x*ySectors + y].textureSize[layer] = textureSize - sectors[x*ySectors + y].textureOffset[layer];
				sectors[x*ySectors + y].textureIndexSize[layer] = textureIndexSize - sectors[x*ySectors + y].textureIndexOffset[layer];
			}
		}
	}
	glGenBuffers(1, &textureVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, textureVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(PIELIGHT)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*numGroundTypes, texture, GL_STATIC_DRAW); glError();
	free(texture);
	
	glGenBuffers(1, &textureIndexVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, textureIndexVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint)*textureIndexSize, textureIndex, GL_STATIC_DRAW); glError();
	free(textureIndex);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	// and finally the decals
	decaldata = (DecalVertex *)malloc(sizeof(DecalVertex)*mapWidth*mapHeight*12);
	decalSize = 0;
	for (x = 0; x < xSectors; x++)
	{
		for (y = 0; y < ySectors; y++)
		{
			sectors[x*ySectors + y].decalOffset = decalSize;
			sectors[x*ySectors + y].decalSize = 0;
			setSectorDecals(x, y, decaldata, &decalSize);
			sectors[x*ySectors + y].decalSize = decalSize - sectors[x*ySectors + y].decalOffset;
		}
	}
	debug(LOG_TERRAIN, "%i decals found", decalSize/12);
	glGenBuffers(1, &decalVBO); glError();
	glBindBuffer(GL_ARRAY_BUFFER, decalVBO); glError();
	glBufferData(GL_ARRAY_BUFFER, sizeof(DecalVertex)*decalSize, decaldata, GL_STATIC_DRAW); glError();
	free(decaldata);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	
	lightmap_tex_num = 0;
	lightmapLastUpdate = 0;
	lightmapWidth = 1;
	lightmapHeight = 1;
	// determine the smallest power-of-two size we can use for the lightmap
	while (mapWidth > (lightmapWidth <<= 1)) {}
	while (mapHeight > (lightmapHeight <<= 1)) {}
	debug(LOG_TERRAIN, "the size of the map is %ix%i", mapWidth, mapHeight);
	debug(LOG_TERRAIN, "lightmap texture size is %ix%i", lightmapWidth, lightmapHeight);

	// Prepare the lightmap pixmap and texture
	lightmapPixmap = (GLubyte *)calloc(lightmapWidth * lightmapHeight, 3 * sizeof(GLubyte));
	if (lightmapPixmap == NULL)
	{
		debug(LOG_FATAL, "Out of memory!");
		abort();
		return false;
	}

	glGenTextures(1, &lightmap_tex_num);
	glBindTexture(GL_TEXTURE_2D, lightmap_tex_num);

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, lightmapWidth, lightmapHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, lightmapPixmap);

	terrainInitalised = true;

	glBindBuffer(GL_ARRAY_BUFFER, 0);  // HACK Must unbind GL_ARRAY_BUFFER (in this function, at least), otherwise text rendering may mysteriously crash.

	return true;
}