All pastes #906193 Raw Edit

Mine

public cpp v1 · immutable
·published 2008-02-16 12:50 UTC
rendered paste body
/*	Terrain - The terrain scene object. Handles terrain drawing, etc *////////////////////////////////////////////////////////////////////#include <Base.h>#include "Engine/Scene/Scene.h"#include "Terrain.h"#include "textureBlender.h"// Macros//////////////////////////////////////////////////#define CLAMP(a,b,c)	(a < b ? b : (a > c ? c : a))#define WRAP(x,y)		((unsigned)x % y)#define POINT(x, y)		pointptr[x + (y * pointsize)]// Helper Functions////////////////////////////////////////////////////SmoothStep - Return a scalar of 0.0 to 1.0, based on the given valuef32 SmoothStep (f32 val, f32 a, f32 b){  if (b == a)    return 0.0f;  val -= a;  val /= (b - a);  return CLAMP (val, 0.0f, 1.0f);}// Con/De-struction//////////////////////////////////////////////////Terrain::Terrain(CScene *owner){	//Initialize stuff	m_owner = owner;	m_zonesize = 0;	disp = 0;}Terrain::~Terrain(){		Cleanup();}//Cleanup - Frees all resourcesvoid Terrain::Cleanup(){	//If we have terrain loaded..	if (m_zonesize) 	{	//Free our video memory		(*m_vaarea).reset();		//Free the buffers for each zone		for (u32 x=0; x < ZONES; ++x)		{			delete [] m_zones[x].cells;			delete [] m_zones[x].verts;			delete m_zones[x].vertexBuf;		}	}}// Terrain functions////////////////////////////////////////////////////InitialBuild - Uses the map provided to calculate normals, layer data, etcvoid Terrain::InitialBuild(f32 high, f32 low){	// Normalize the elevations	///////////////////////////////////////////////////////	#define SCALE(x,y)	scale[x + (y * m_sizex)]	f32 *scale			= new f32[m_sizey * m_sizex];	celldata *pointptr	= m_cells;	u32 pointsize		= m_sizex;	//Copy them all to a temporary table	for (u32 y = 0; y < m_sizey; ++y) 		for (u32 x = 0; x < m_sizex; ++x) 			SCALE(x,y) = (POINT(x, y).height - low) / (high - low);	//Blend the values	for (u32 x = 0; x < m_sizex; x++) 	{		for (u32 y = 0; y < m_sizey; y++) 		{	//Take samples from some surrounding elevations			f32 smooth = 0.0f;			u32 samples = 0;			for (s32 xx = -BLEND_RANGE; xx <= BLEND_RANGE; xx++)				for (s32 yy = -BLEND_RANGE; yy <= BLEND_RANGE; yy++) 				{					smooth += SCALE(CLAMP((x + xx), 0, (m_sizex - 1)), CLAMP((y + yy), 0, (m_sizey - 1)));					samples++;				}			//Insert the nice, smooth value			POINT(x, y).height = (smooth / f32(samples)) * MAX_HEIGHT;		}	}  	// Calculate the layer data	//		- How much of each layer(sand,etc) the point contains	///////////////////////////////////////////////////////	for (u32 y = 0; y < m_sizey; ++y) 	{		for (u32 x = 0; x < m_sizex; ++x) 		{	//Get the current cell			celldata &c = POINT(x, y);			ZeroMemory(c.layeralphas, sizeof(u8) * (LAYER_COUNT));			// Calculate the normal			///////////////////////////////////////////////			//Find matching nearby points			s32 top		= WRAP((y - 1), m_sizey);			s32 bottom	= WRAP((y + 1), m_sizey);			s32 left	= WRAP((x - 1), m_sizex);			s32 right	= WRAP((x + 1), m_sizex);			//Calculate the scaled normal (IS THIS ACTUALLY REQUIRED?)			Vector3 normal, &cnorm = POINT(x, y).normal;;			normal.x = (SCALE(left, y) * MAX_HEIGHT) - (SCALE(right, y) * MAX_HEIGHT);			normal.y = 2.0f;			normal.z = (SCALE(x, top) * MAX_HEIGHT) - (SCALE(x, bottom) * MAX_HEIGHT);			normal = normal.direction();			//Calculate the normal used for rendering			cnorm.x = POINT(left, y).height - POINT(right, y).height;			cnorm.y = -2.0f;			cnorm.z = POINT(x, top).height - POINT(x, bottom).height;			cnorm = cnorm.direction();			// Determine the layer makeup of this cell			///////////////////////////////////////////////			//Sand exists in the lowest parts of the terrain			f32 sand = 0.0f;//SmoothStep(SCALE(x,y), 0.3f, 0.1f);			//Low grass likes lowlands and flat areas			f32 e = SmoothStep(normal.y, 0.75f, 1.0f);			f32 lgrass = 1.0f;//SmoothStep(SCALE(x,y), 0.45f, 0.25f);			lgrass = (e * lgrass) * 5.0f;			lgrass = 0.0f;//CLAMP(lgrass, 0, 1);			//Rock likes mild slopes and high elevations			e = 0.0f;//SmoothStep(normal.y, 0.8f, 0.5f);			e += SmoothStep(SCALE(x,y), 0.7f, 1.0f);			f32 rock = CLAMP(e, 0, 1);			//Dirt likes very steep slopes			f32 dirt = 0.0f;//SmoothStep(normal.y, 0.7f, 0.4f);			// Convert the obtained values into alphas for each texture			///////////////////////////////////////////////			f32 total = sand + lgrass + rock + dirt;			//Normal grass is what's left over			c.layeralphas[LAYER_GRASS]		= (1.0f - total) * 255;			total = G3D::max(total, 1.0f);			//Calculate the rest			c.layeralphas[LAYER_LOWGRASS]	= (lgrass / total) * 255;			c.layeralphas[LAYER_DIRT]		= (dirt / total) * 255;			c.layeralphas[LAYER_ROCK]		= (rock / total) * 255;			c.layeralphas[LAYER_SAND]		= (sand / total) * 255;		}	}	///////////////////////////////////////////////////////	// Compile each zone into a vertex buffer	///////////////////////////////////////////////////////	//Calculate the zone size	m_zonearea = m_sizex / ZONE_GRID;	m_zoneareasq = m_zonearea * m_zonearea;	m_zonesize = m_zonearea + 1;	m_zonesizesq = m_zonesize * m_zonesize;		//Allocate an appropriate chunk of video memory	u32 numsharedverticies = ZONES * ((m_zonesize + 1) * 2);	u32 numverticies = (m_sizex * m_sizey) + numsharedverticies;		m_vaarea = VARArea::create( numverticies * (sizeof(Vector2)	+	//Texture coordinates												sizeof(Vector3)	+	//Normals												sizeof(Vector3))	//Position								+ 8 );	//Padding	//Walk the zones	for (s32 y = 0; y < ZONE_GRID; ++y) 	{		for (s32 x = 0; x < ZONE_GRID; ++x)		{	//Get the associated zone			zone &z = m_zones[x + (y * ZONE_GRID)];			//For this zone, allocate a block of cells			z.cells = (celldata*)malloc(sizeof(celldata) * m_zonesizesq);			//Copy over all the cells a row at a time			celldata *zonecells = &m_cells[(x * m_zonearea) + ((y * m_zonearea) * m_sizex)];			for (u32 i = 0; i < m_zonesize; ++i)				memcpy(	&z.cells[i * m_zonesize], 						&zonecells[i * m_sizex], 						sizeof(celldata) * m_zonesize	);			//Compile the zone			CompileZone(z, x + (y * ZONE_GRID));		}	}	//Get rid of temporary assets	delete [] m_cells;	delete [] scale;}//CompileZone - Compiles a zone into a renderable formvoid Terrain::CompileZone(zone &z, u32 zoneidx){	//Calculate zone offset	u32 zone_ofs_x = (zoneidx % ZONE_GRID) * m_zonearea;	u32 zone_ofs_y = (zoneidx / ZONE_GRID) * m_zonearea;	//Allocate a block of memory for the verticies	z.verts = (terrainvertex*)malloc(sizeof(terrainvertex) * m_zonesizesq);	//Create the verticies based on our cell data	for (u32 y = 0; y < m_zonesize; ++y) 	{		for (u32 x = 0; x < m_zonesize; ++x)		{	//Convert into a vertex			terrainvertex &vert = z.verts[x + (y * m_zonesize)];			vert.pos.x = (zone_ofs_x + x);			vert.pos.y = z.cells[x + (y * m_zonesize)].height;			vert.pos.z = (zone_ofs_y + y);			//Bring over the normal data			vert.normal = z.cells[x + (y * m_zonesize)].normal;			//Set the texture coordinates			vert.texcoord.x = (f32(x) / m_zonearea);			vert.texcoord.y = 1.0f - (f32(y) / m_zonearea);		}	}	//Construct the overlay texture for this zone	textureBlender tB;	Array<u8> alphaMaps[LAYER_COUNT];	//add all our source layer textures	for (u32 i=0; i < LAYER_COUNT; ++i)	{	//Construct the alphamap		alphaMaps[i].resize(m_zoneareasq);		u8 *mapPtr = alphaMaps[i].getCArray(), *tmapPtr = mapPtr;				//Convert the celldata into an alphamap		for (u32 y=0; y < m_zonearea; ++y)			for (u32 x=0; x < m_zonearea; ++x)				*(tmapPtr++) = z.cells[x + (y * m_zonesize)].layeralphas[i];		tB.addTexture(i, m_layers[i], mapPtr);	}	//Blend everything to form a texture	tB.updateOpacity(m_zonesize);	z.zonetex = tB.blend(rd);	//Stick all the vertex data into a video buffer	z.vertexBuf = new VAR(z.verts, m_zonesizesq, m_vaarea);}// Generation functions////////////////////////////////////////////////////Generate_Circles - Creates some terrain using a circles algorithmbool Terrain::Generate_Circles(	s32 nIterations, 								s32 sizex, s32 sizey, 								f32 maxdispl, f32 mindispl,								s32 featuresize ){	//Clear our previous data	Cleanup();	//#Cells = area + 1 (make zone division possible)	m_sizex				= sizex+1;	m_sizey				= sizey+1;	//Allocate our cell buffer	m_cells = new celldata[m_sizex * m_sizey];	celldata *pointptr	= m_cells;	u32 pointsize		= m_sizex;	//Initialize some variables	srand(GetTickCount());	u32 halfX = sizex / 2;	u32 halfZ = sizey / 2;	//Initialize the cells	for (u32 y = 0; y < m_sizey; ++y) 	{		for (u32 x = 0; x < m_sizex; ++x) 		{	//Start at a height of 0			POINT(x, y).height = x + y;			POINT(x, y).shadow = false;		}	}	//Start performing the iterations	f32 high=m_sizey + m_sizex, low=0;	/*for (s32 k = 0; k < nIterations; ++k) 	{	//Pick a random location to create a circular displacement		f32 disp;		s32 x = ((f32)rand() / RAND_MAX) * (sizex - featuresize);		x += featuresize / 2;		s32 y = ((f32)rand() / RAND_MAX) * (sizey - featuresize);		y += featuresize / 2;		//Calculate a random displacement within limits		disp = ((f64)rand() / (f64)RAND_MAX) * maxdispl;		if (disp < mindispl)			disp = mindispl;		//Are we going to create a depression or an extrusion?		s32 dispSign = (((f32)rand() / RAND_MAX) > 0.5f) ? 1 : -1;		//Produce a random circle size		s32 circleSize = (f32)(rand() % (featuresize - 10)) + 10.0f;		for (s32 i = 0; i < sizey; ++i)		{			for (s32 j = 0; j < sizex; ++j) 			{	//Obtain the distance of this grid coordinate from the circle				f64 dist = (sqrt(f64( (i-x)*(i-x) + (j-y)*(j-y) )) * 2) / (f64)circleSize;				//If the 'circle' is close enough...				if (fabs(dist) <= 1)				{	//Add the deplacement for this cell					celldata &c = POINT(j, i);					//Add					c.height += ((disp / 2) * dispSign) + cos(dist * G3D::pi()) * ((disp / 2) * dispSign);					//Keep track of the highest/lowest points					high = G3D::max(high, c.height);					low = G3D::min(low, c.height);				}			}		}	}*/	//Build the map	InitialBuild(high, low);	return true;}//Load_Heightmap	- Extrapolates terrain from a given colour heightmapbool Terrain::Load_Heightmap(const char *mapname){	// Attempt to open the heightmap	///////////////////////////////////////////////////////	GImage heightmap;	try	{		heightmap.load(mapname);	} 	catch (const GImage::Error &err)	{	//Report it and give up		LOG("Terrain") << "Attempt to load heightmap " << err.filename << " failed. (" << err.reason << ")";		return false;	}	//Perform some checks	assert(heightmap.width < MAP_SIZE, "Map is too big!");	assert(heightmap.height < MAP_SIZE, "Map is too big!");	//Cleanup what we already have and continue	Cleanup();	//Done! Start gathering info	///////////////////////////////////////////////////////	f32 high=0, low=0;		//Used to normalize the data later	//#Cells = area + 1 (make zone division possible)	m_sizex				= heightmap.width + 1;	m_sizey				= heightmap.height + 1;		//Allocate our cell buffer	m_cells				= new celldata[m_sizex * m_sizey];	celldata *pointptr	= m_cells;	u32 pointsize		= m_sizex;	//Extract the height values	for (u32 y = 0; y < m_sizey; ++y) 	{		for (u32 x = 0; x < m_sizex; ++x) 		{	//Clamp the indices			u32 xx = CLAMP (x, 1, (m_sizex - 2));			u32 yy = CLAMP (y, 1, (m_sizey - 2));						//Get our pixel			Color3uint8 &pixel = heightmap.pixel3(xx, yy);			//Determine the height of this point			f32 e = (f32(pixel.r) / 30.0f) + (f32(pixel.g) / 48.0f);			//Store the info in the cell			POINT(xx, yy).height = e;			POINT(xx, yy).shadow = false;			//Keep track of the highest/lowest points			high = G3D::max(high, POINT(xx, yy).height);			low = G3D::min(low, POINT(xx, yy).height);		}	}	//Build the map	InitialBuild(high, low);	return true;}// Drawing functions////////////////////////////////////////////////////renderZone - Draws a zonevoid Terrain::renderZone(u32 idx){	//Create our index list, depicting what to draw	zone &z = m_zones[idx];	Array<u32> lIndex;	//TODO: Make the index list static? It should never change anyway	//Create the indexes	for (u32 y=0 ; y < (m_zonesize - 1) ; ++y)	{	u32 rowStart = (y * m_zonesize);		for (u32 x=0 ; x < (m_zonesize - 1); ++x)		{	//Add the appropriate indicies to the list			lIndex.append(rowStart + (x + 0) + (1 * m_zonesize));			lIndex.append(rowStart + (x + 1) + (1 * m_zonesize));			lIndex.append(rowStart + (x + 1) + (0 * m_zonesize));			lIndex.append(rowStart + (x + 0) + (0 * m_zonesize));		}	}	//Setup our texture	rd->setTexture(0, z.zonetex);	//Render our pre-prepared vertex array	rd->beginIndexedPrimitives();	{		rd->setTexCoordArray(0, *(z.vertexBuf), 0, 2);		rd->setNormalArray(*(z.vertexBuf), sizeof(Vector2));		rd->setVertexArray(*(z.vertexBuf), sizeof(Vector2) + sizeof(Vector3), 3);		rd->sendIndices(RenderDevice::QUADS, lIndex);	}	rd->endIndexedPrimitives();}//render - Draws the object the entity is representingvoid Terrain::render(){	//Show our texture	rd->pushState();	{		rd->push2D();			rd->setTexture(0, m_zones[disp].zonetex);			Draw::rect2D(Rect2D::xywh(0,0,512,512), rd);		rd->pop2D();		//Render a zone		for (u32 x=0; x < ZONES; ++x)		{	//Give each zone a nice colour			//glColor3f((x % 2 == 0) ? 1.0f : 0, (x % 3 == 1) ? 1.0f : 0, (x % 4 == 2) ? 1.0f : 0);			renderZone(x);		}	}	rd->popState();	//Do we have anything to render?	/*assert(m_bReady, "Terrain class used without being initialized.");	// We need to calculate the coordinates that the camera's frustum	// make with the terrain's maximally negatively displaced plane.	///////////////////////////////////////////////////////	//Get the frustum and camera details	CoordinateFrame coordf;	Vector3 dir = -Vector3::unitY();	m_owner->m_overhead.getCoordinateFrame(coordf);	//Create matricies to calculate the direction vectors emerging from the top left 	//and bottom right of the viewport	Matrix3 matt1 = Matrix3::fromEulerAnglesXYZ(	(-m_owner->m_overhead.getFieldOfView()) / 2.0f, 													0, 													(-m_owner->m_overhead.getFieldOfView()) / 2.0f	);	Matrix3 matt2 = Matrix3::fromEulerAnglesXYZ(	(m_owner->m_overhead.getFieldOfView()) / 2.0f, 													0, 													(m_owner->m_overhead.getFieldOfView()) / 2.0f	);	//Create a Ray from the camera's position heading to the top left of the near plane	Ray topLeft = Ray::fromOriginAndDirection(coordf.translation, dir * matt1);	//..and a ray from the camera to the bottom right of the near plane	Ray botRight = Ray::fromOriginAndDirection(coordf.translation, dir * matt2);	//Create a plane to represent the terrain at maximum negative displacement	Plane terrain(	Vector3::unitY(),						//UNIT_Y, since the normal is just vertically upwards					Vector3(0, -m_minDisplacement, 0)	);	//Since the plane is infinite, it can start at 0,0	//Obtain the point of intersection for both rays	Vector3	tL = topLeft.intersection(terrain),			bR = botRight.intersection(terrain);	//Let it have some leeway	tL.x += 20;	bR.x -= 20;	renderDevice->pushState();	//Setup the per-pixel shader	renderDevice->setShader(m_shader);	//Use the points of intersection to obtain the section of terrain to draw	renderSection(renderDevice,	(tL.x * (f32)SQUARE_SIZE), (tL.z * (f32)SQUARE_SIZE),								(bR.x * (f32)SQUARE_SIZE), (bR.z * (f32)SQUARE_SIZE)	);	//Restore shader	renderDevice->setShader(NULL);	renderDevice->popState();*/}//update - Updates the state of the entityvoid Terrain::update(){	}// Texturing functions////////////////////////////////////////////////////loadAssets - Loads assets related to terrain into memory (textures, etc.)void Terrain::loadAssets(){	//Load the layer textures	m_layers[LAYER_GRASS]		= Texture::fromFile("images\\terrain\\grassa512.bmp");	m_layers[LAYER_LOWGRASS]	= Texture::fromFile("images\\terrain\\grassb512.bmp");	m_layers[LAYER_SAND]		= Texture::fromFile("images\\terrain\\sand512.bmp");	m_layers[LAYER_ROCK]		= Texture::fromFile("images\\terrain\\rock512.bmp");	m_layers[LAYER_DIRT]		= Texture::fromFile("images\\terrain\\dirt512.bmp");	//Allow the texture blender to load assets	textureBlender::loadAssets();}/*	textureBlender - Used for blending textures together for the terrain *////////////////////////////////////////////////////////////////////#include <Base.h>#include "textureBlender.h"#include "Terrain.h"// Static Initialization//////////////////////////////////////////////////ShaderRef	textureBlender::m_blendShader;	//loadAssets - Loads all the external assets needed for this classvoid textureBlender::loadAssets(){	//Load our blending shader	m_blendShader = G3D::Shader::fromFiles("shaders/terrainblend.vert", "shaders/terrainblend.frag");	assert(m_blendShader->ok(), "Terrain texture blending shader failed to load."); }// Con/De-struction//////////////////////////////////////////////////textureBlender::textureBlender(){}textureBlender::~textureBlender(){}// Texture functions////////////////////////////////////////////////////blend - Blends the given textures together using the specified alpha mapsTextureRef textureBlender::blend(RenderDevice *rd, TextureRef oldSurface){	//Allocate a new texture to render to, if we don't have an old one to use	TextureRef renderSurface;	if (oldSurface.isNull())		renderSurface = Texture::createEmpty(TEXTURE_SIZE, TEXTURE_SIZE, "terrainTexture", TextureFormat::RGBA8, Texture::CLAMP, Texture::NO_INTERPOLATION, Texture::DIM_2D);	else		renderSurface = oldSurface;	//Setup a framebuffer	FramebufferRef fb = Framebuffer::create("terrainTexturer");	fb->set(Framebuffer::COLOR_ATTACHMENT0, renderSurface);	//Set the rendering device up for rendering to our framebuffer/texture	rd->pushState();	rd->push2D(fb);	//First, clear the scene	rd->setColorClearValue(Color3(0, 1, 0));    rd->clear();	//Render the opacity map stuff	for(s32 i=0; i < m_sources.size(); i += 4)	{	//Break if there's nothing to draw		if (m_sources[i + 0].source.isNull() &&			m_sources[i + 1].source.isNull() &&			m_sources[i + 2].source.isNull() &&			m_sources[i + 3].source.isNull()	)			break;		bool source2 = (m_sources.size() > i + 1);		bool source3 = (m_sources.size() > i + 2);		bool source4 = (m_sources.size() > i + 3);		//Load up the blending shader		rd->setShader(m_blendShader);	//Maybe move this call outside? faster?		m_blendShader->args.set("opacityMap",   m_opacity[i / 4]);		m_blendShader->args.set("source0",		m_sources[i + 0].source);			//Replace textures that don't exist with the first texture to keep glsl happy.		//The opacity map should ignore them regardless.		m_blendShader->args.set("source1",		m_sources[i + (source2 ? 1:0)].source);		m_blendShader->args.set("source2",		m_sources[i + (source3 ? 2:0)].source);		m_blendShader->args.set("source3",		m_sources[i + (source4 ? 3:0)].source);		//Render the texture		Draw::rect2D(Rect2D::xywh(0,0,TEXTURE_SIZE,TEXTURE_SIZE), rd, Color3::white());		break;	}	//Reset the render mode	rd->setPixelProgram(NULL);	rd->pop2D();	rd->popState();	fb->set(Framebuffer::COLOR_ATTACHMENT0, NULL);	// Return the surface	debugAssertGLOk();	return renderSurface;}//addTexture - Adds a texture to be blended using the specified alpha mapvoid textureBlender::addTexture(u32 materialIdx, TextureRef tex, u8 *alphas){	//Make a new sourcetexture and jam it into the array	SourceTexture s;	s.alpha = alphas;	s.source = tex;	//Resize the array if necessary	if (m_sources.size() <= (s32)materialIdx)		m_sources.resize(materialIdx + 1);	m_sources[materialIdx] = s;}//updateOpacity - Merges all the alpha maps into a single texturevoid textureBlender::updateOpacity(u32 alphamapsize){	//Ready the opacity texture array	u32 alphamapsizesq = (alphamapsize*alphamapsize);	m_opacity.clear();	m_opacity.resize((m_sources.size() / 4) + 1);		//4 Textures for each opacity map	//Build new maps	for(s32 rangeStart=0; rangeStart < m_sources.size(); rangeStart +=4)	{	//If we have a null on source #rangeStart, we are done.		if (!m_sources[rangeStart + 0].alpha) 			break;				bool source2 = (m_sources.size() > rangeStart + 1);		bool source3 = (m_sources.size() > rangeStart + 2);		bool source4 = (m_sources.size() > rangeStart + 3);		//Ready an image		GImage tempMap(alphamapsize, alphamapsize, 4);		u8 *tmpBits = tempMap.byte();		ZeroMemory(tmpBits, sizeof(u32) * alphamapsizesq);		// Copy everything relevant into the texture...		for(u32 x=0; x < alphamapsizesq; ++x)		{	//Always assume that the first alpha exists, but don't rely on the others			tmpBits[0] = m_sources[rangeStart + 0].alpha[x];			if (source2)				tmpBits[1] = m_sources[rangeStart + 1].alpha[x];							if (source3)				tmpBits[2] = m_sources[rangeStart + 2].alpha[x];			if (source4)				tmpBits[3] = m_sources[rangeStart + 3].alpha[x];			// Move to next chunk of data...			tmpBits += 4;		}		// Make a texture and store it...		m_opacity[rangeStart / 4] = Texture::fromGImage("terrainAlphaMap", tempMap);		//NOTE: Does having a constant name for all alpha textures mean that		//		it'll just always default for the first one commited?	}}