Code Snippets

From Horde3D Wiki
Jump to: navigation, search

What is this page?

This page of the wiki is a sharing zone for rough Horde example code.



Using Custom GL Code After Horde3D

Horde3D has a bad habbit of messing with the OpenGL states after it is finished, so it is necessary with a few third party libraries to make sure that the OpenGL state is at its default settings before rendering. Here is an example using CEGUI. It is assumed you can logically piece together what the main loop would look like.

Custom OpenGL after Horde3D.
void Engine::InitializeRenderer()
{
    glPushAttrib(GL_ALL_ATTRIB_BITS); // save default attributes

    LOG("Initializing rendering engine...",INFO);
    if(!h3dInit())
    {
        LOG("Error initializing Horde3D. Aborting.",FATAL);
        Kill();
    }
    LOG("Running the game.",INFO);

    glClearDepth(1.f);
    glClearColor(0.f, 0.f, 0.5f, 0.f);
    glEnable(GL_DEPTH_TEST);
}

void Engine::FinalizeRenderer()
{
    if(stage)
    {
        h3dRender(active_camera);
    }
    h3dFinalizeFrame();
    // attributes here are now messed up
}

void Engine::FinalizeGUI()
{
    glPopAttrib(); // pop back to default state since horde3d messed with the states.

    CEGUI::System::getSingleton().renderGUI();

    glPushAttrib(GL_ALL_ATTRIB_BITS); // save the default state again before going back to Horde3D
}


More procedurally generated content code

The following is similar but different to the Procedurally generated geometry tutorial

Building a 2D grid of quads
void ScreenGridMesh( char*& data, int& fileSize, int& batchStart, int& batchCount, int& vertRStart, int& vertREnd )
{
	extern int appWidth;
	extern int appHeight;

	//This example splits the screen up into groups of 16 pixels, and create a quad for each group.
	int quadSize = 16;
	int quadsH = (appWidth +quadSize-1) / quadSize;//divide by 16, rounding up
	int quadsV = (appHeight+quadSize-1) / quadSize;
	int quads = quadsH*quadsV;


	int version = 5;//This is the current version of the GEO format (at the time of writing).

	int numJoints = 0;//I don't have code below for writing joints...
	int numMorphTargets = 0;//...or morph targets

//The lines that begin with //! or /*! could be used if you want a more complete vertex format (i.e. normal/binormal/tangent/joints/etc)
//!	int numVertexStreams = 8;
	int numVertexStreams = 2;//I'm just using position + tex-coord

	int numVertices = quads * 4;// I'm making 4 verts per quad
	int numTriangleIndices = quads * 6;//Each quad is 2 triangles. Each triangle is made up of 3 verts, so I need 6 indices to describe each quad.

	//These are all "magic numbers" that horde uses to interpret the data streams
	int vertPositionID = 0;
	int vertNormalID = 1;
	int vertTangentID = 2;
	int vertBiTangentID = 3;
	int vertJointIndexID = 4;
	int vertJointWeightID = 5;
	int vertTexCoordID = 6;
	int vertTexCoord2ID = 7;
	int positionElementSize = sizeof(float)*3;
	int normalElementSize   = sizeof(short)*3;
	int tangentElementSize  = sizeof(short)*3;
	int biTangentElementSize= sizeof(short)*3;
	int jointIndexElementSize = sizeof(char)*4;
	int jointWeightElementSize = sizeof(char)*4;
	int texCoordElementSize = sizeof(float)*2;
	int texCoord2ElementSize = sizeof(float)*2;

	batchStart = 0;
	batchCount = numTriangleIndices;
	vertRStart = 0;
	vertREnd = numVertices-1;
	
//!	int vertDataSize = positionElementSize+normalElementSize+
//!	                   tangentElementSize+biTangentElementSize+
//!	                   jointIndexElementSize+jointWeightElementSize+
//!	                   texCoordElementSize+texCoord2ElementSize;
	int vertDataSize = positionElementSize+texCoordElementSize;

	int quadDataSize = vertDataSize*4 + sizeof(int)*6;
//!	fileSize = sizeof(char)*4 + sizeof(int)*22 + quadDataSize*quads;
	fileSize = sizeof(char)*4 + sizeof(int)*10 + quadDataSize*quads;

	data = new char[fileSize];
	int written = 0;
	
#define ASSERT( c )	\
	if( !(c) ) { printf( "ASSERTION FAILED: %s\n", #c ); breakPlz(); }
#define WRITE_DATA( d, size )	\
	memcpy( &data[written], d, size ); written += size; ASSERT( written <= fileSize );

	//First write the header
	WRITE_DATA( "H3DG", sizeof(char)*4 );
	WRITE_DATA( &version, sizeof(int) );

	//Joint data would go here, if I had any
	WRITE_DATA( &numJoints, sizeof(int) );

	//Then write the vertex-stream header
	WRITE_DATA( &numVertexStreams, sizeof(int) );
	WRITE_DATA( &numVertices, sizeof(int) );

	//I'm making quads in this example, so all my loops are going to create 4 pieces of each data.

	//To begin with I write the positions:
	WRITE_DATA( &vertPositionID, sizeof(int) );
	WRITE_DATA( &positionElementSize, sizeof(int) );
	for( int y=0; y<quadsV; ++y )
	{
		for( int x=0; x<quadsH; ++x )
		{
			float positionA[3] = { ((x+0.0f)/(float)quadsH)*2.0f-1.0f, ((y+0.0f)/(float)quadsV)*2.0f-1.0f, 0.0f };
			float positionB[3] = { ((x+1.0f)/(float)quadsH)*2.0f-1.0f, ((y+0.0f)/(float)quadsV)*2.0f-1.0f, 0.0f };
			float positionC[3] = { ((x+1.0f)/(float)quadsH)*2.0f-1.0f, ((y+1.0f)/(float)quadsV)*2.0f-1.0f, 0.0f };
			float positionD[3] = { ((x+0.0f)/(float)quadsH)*2.0f-1.0f, ((y+1.0f)/(float)quadsV)*2.0f-1.0f, 0.0f };
			WRITE_DATA( positionA, sizeof(float)*3 );
			WRITE_DATA( positionB, sizeof(float)*3 );
			WRITE_DATA( positionC, sizeof(float)*3 );
			WRITE_DATA( positionD, sizeof(float)*3 );
		}
	}
	/*!
	//Normals/binormals/tangents should be floats in the 0-1 range, and then multiplied by 32767 and cast to 'short'. This is a form of compression.
	WRITE_DATA( &vertNormalID, sizeof(int) );
	WRITE_DATA( &normalElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		short n[] = { 0, 0, 32767 };
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
	}
	WRITE_DATA( &vertTangentID, sizeof(int) );
	WRITE_DATA( &tangentElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		short n[] = { 0, 32767, 0 };
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
	}
	WRITE_DATA( &vertBiTangentID, sizeof(int) );
	WRITE_DATA( &biTangentElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		short n[] = { 32767, 0, 0 };
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
		WRITE_DATA( n, sizeof(short)*3 );
	}

	//Each vertex can be influenced by 4 joints - the joint IDs are written here
	WRITE_DATA( &vertJointIndexID, sizeof(int) );
	WRITE_DATA( &jointIndexElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		char j[] = { 0, 0, 0, 0 };
		WRITE_DATA( j, sizeof(char)*4 );
		WRITE_DATA( j, sizeof(char)*4 );
		WRITE_DATA( j, sizeof(char)*4 );
		WRITE_DATA( j, sizeof(char)*4 );
	}

	//The 'percent' (out of 255, not out of 100) that each of the above joints influences the vertex:
	WRITE_DATA( &vertJointWeightID, sizeof(int) );
	WRITE_DATA( &jointWeightElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		ASSERT( sizeof(char) == 1 )
		char j[] = { 255, 0, 0, 0 };
		WRITE_DATA( j, sizeof(char)*4 );
		WRITE_DATA( j, sizeof(char)*4 );
		WRITE_DATA( j, sizeof(char)*4 );
		WRITE_DATA( j, sizeof(char)*4 );
	}
	*/
	//Tex-coords are pairs of floats
	WRITE_DATA( &vertTexCoordID, sizeof(int) );
	WRITE_DATA( &texCoordElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		float uvTl[] = { 0.0f, 1.0f };
		float uvBl[] = { 0.0f, 0.0f };
		float uvBr[] = { 1.0f, 0.0f };
		float uvTr[] = { 1.0f, 1.0f };
		WRITE_DATA( uvTl, sizeof(float)*2 );
		WRITE_DATA( uvBl, sizeof(float)*2 );
		WRITE_DATA( uvBr, sizeof(float)*2 );
		WRITE_DATA( uvTr, sizeof(float)*2 );
	}
	/*!
	//You can have a second set of tex-coords if you want
	WRITE_DATA( &vertTexCoord2ID, sizeof(int) );
	WRITE_DATA( &texCoord2ElementSize, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		float uvTl[] = { 0.0f, 1.0f };
		float uvBl[] = { 0.0f, 0.0f };
		float uvBr[] = { 1.0f, 0.0f };
		float uvTr[] = { 1.0f, 1.0f };
		WRITE_DATA( uvTl, sizeof(float)*2 );
		WRITE_DATA( uvBl, sizeof(float)*2 );
		WRITE_DATA( uvBr, sizeof(float)*2 );
		WRITE_DATA( uvTr, sizeof(float)*2 );
	}*/

	//Finally, time for the index buffer
	WRITE_DATA( &numTriangleIndices, sizeof(int) );
	for( int i=0; i!=quads; ++i )
	{
		int base = i * 4;
		int a = base, b = base+1, c = base+2, d=base+2, e=base+3, f=base;
		WRITE_DATA( &a, sizeof(int) );
		WRITE_DATA( &b, sizeof(int) );
		WRITE_DATA( &c, sizeof(int) );
		WRITE_DATA( &d, sizeof(int) );
		WRITE_DATA( &e, sizeof(int) );
		WRITE_DATA( &f, sizeof(int) );
	}

	//Morph targets would go here, if I had any
	WRITE_DATA( &numMorphTargets, sizeof(int) );

	ASSERT( written == fileSize );//ensure we didn't over/under-use the buffer
}
Example Usage
	// Build a geo
	H3DRes gridGeo = h3dAddResource( H3DResTypes::Geometry, "runtime/blahBlah.geo", H3DResFlags::NoQuery );
	int batchStart = 0, batchCount = 0, vertRStart = 0, vertREnd = 0;
	{
		int dataSize = 0;
		char* data = 0;
		ScreenGridMesh( data, dataSize, batchStart, batchCount, vertRStart, vertREnd );
		bool loadedData = h3dLoadResource( gridGeo, data, dataSize );
		delete [] lensBlurData;
	}
	
	// Load a material
	H3DRes material = h3dAddResource( H3DResTypes::Material, "materials/grid.material.xml", 0 );
	h3dutLoadResourcesFromDisk( _contentDir.c_str() );

	// Create a model/mesh from the procedural geo
	H3DNode model = h3dAddModelNode( H3DRootNode, "MyModel", gridGeo );
	H3DNode mesh = h3dAddMeshNode( model, "MyMesh", material, batchStart, batchCount, vertRStart, vertREnd );