Thread: Okay, giant issue, I think I'm treading into territory outside of cboards maybe...

  1. #1
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968

    Okay, giant issue, I think I'm treading into territory outside of cboards maybe...

    Okay, this issue has been daunting me for 4 months or so now. This issue has caused me to give up 3d programming, but I'm back baby!

    Now who can tell me how to make a centralized renderer? No, but seriously....

    Okay, jokes aside, I know all the concepts, I know how everything works, I know exactly how my ms3d loading code works (though not exactly everything it does), I know exactly how a centralized renderer works.

    But I suck at coding! No seriously, I can think really well, but coding is hard for me!

    Okay, let me show you the uber code, so you know where I'm coming from.

    MS3D.h
    Code:
    #ifndef MS3D_H
    #define MS3D_H
    #include <windows.h>																						
    #include <gl\glaux.h>						
    #include <vector>
    
    
    class MS3DModel
    {
    public:
    
    	MS3DModel();
    
    	virtual ~MS3DModel();
    
    
    	struct Vertex
    	{
    		char BoneID;
    		float Location[3];
    	};
    
    
    	int NumVertices;
    	Vertex * Vertices;
    
    	struct Triangle
    	{
    		float VertexNormals[3][3];
    		float Textures1[3], Textures2[3];
    		int VertexIndices[3];
    	};
    
    	int NumTriangles;
    	Triangle *Triangles;
    
    	struct Mesh
    	{
    		int MaterialIndex;
    		int NumTriangles;
    		int *TriangleIndices;
    	};
    
    	int NumMeshes;
    	Mesh *Meshes;
    
    	struct Material
    	{
    		float Ambient[4], Diffuse[4], Specular[4], Emissive[4];
    		float Shininess;
    		GLuint Texture;
    		char * TextureFilename;
    	};
    
    	int NumMaterials;
    	Material *Materials;
    
    	bool Load( const std::string & name );
    	void ReloadTextures();
    	void Draw();
    };
    
    #endif

    Okay, that is my ms3d model and the data structures it uses. I'll continue with more code, then do alot of explaining.

    MS3D.cpp
    Code:
    #include <windows.h>		// Header File For Windows
    #include <fstream>
    #include "MS3D.h"
    #include <iostream>
    #include "TextureManager.h"
    
    using namespace std;
    MS3DModel::MS3DModel()
    {
    	
    	NumMeshes = 0;
    	Meshes = NULL;
    	NumMaterials = 0;
    	Materials = NULL;
    	NumTriangles = 0;
    	Triangles = NULL;
    	
    	NumVertices = 0;
    	Vertices = NULL;
    }
    
    MS3DModel::~MS3DModel()
    {
    	
    	int i;
    	for ( i = 0; i < NumMeshes; i++ )
    		delete[] Meshes[i].TriangleIndices;
    	for ( i = 0; i < NumMaterials; i++ )
    		delete[] Materials[i].TextureFilename;
    
    	NumMeshes = 0;//
    	if ( Meshes != NULL )
    	{
    		delete[] Meshes;
    		Meshes = NULL;
    	}
    
    	NumMaterials = 0;
    	if ( Materials != NULL )
    	{
    		delete[] Materials;
    		Materials = NULL;
    	}
    
    	NumTriangles = 0;
    	if ( Triangles != NULL )
    	{
    		delete[] Triangles;
    		Triangles = NULL;
    	}
    	
    	NumVertices = 0;
    	if ( Vertices != NULL )
    	{
    		delete[] Vertices;
    		Vertices = NULL;
    	}
    }
    
    /* 
    	MS3D STRUCTURES 
    */
    
    // byte-align structures
    #ifdef _MSC_VER
    #	pragma pack( push, packing )
    #	pragma pack( 1 )
    #	define PACK_STRUCT
    #elif defined( __GNUC__ )
    #	define PACK_STRUCT	__attribute__((packed))
    #else
    #	error you must byte-align these structures with the appropriate compiler directives
    #endif
    
    typedef unsigned char byte;
    typedef unsigned short word;
    
    // File header
    struct MS3DHeader
    {
    	char m_ID[10];
    	int m_version;
    } PACK_STRUCT;
    
    // Vertex information
    struct MS3DVertex
    {
    	byte m_flags;
    	float m_vertex[3];
    	char m_boneID;
    	byte m_refCount;
    } PACK_STRUCT;
    
    // Triangle information
    struct MS3DTriangle
    {
    	word m_flags;
    	word m_vertexIndices[3];
    	float m_vertexNormals[3][3];
    	float m_s[3], m_t[3];
    	byte m_smoothingGroup;
    	byte m_groupIndex;
    } PACK_STRUCT;
    
    // Material information
    struct MS3DMaterial
    {
        char m_name[32];
        float m_ambient[4];
        float m_diffuse[4];
        float m_specular[4];
        float m_emissive[4];
        float m_shininess;	// 0.0f - 128.0f
        float m_transparency;	// 0.0f - 1.0f
        byte m_mode;	// 0, 1, 2 is unused now
        char m_texture[128];
        char m_alphamap[128];
    } PACK_STRUCT;
    
    //	Joint information
    struct MS3DJoint
    {
    	byte m_flags;
    	char m_name[32];
    	char m_parentName[32];
    	float m_rotation[3];
    	float m_translation[3];
    	word m_numRotationKeyframes;
    	word m_numTranslationKeyframes;
    } PACK_STRUCT;
    
    // Keyframe data
    struct MS3DKeyframe
    {
    	float m_time;
    	float m_parameter[3];
    } PACK_STRUCT;
    
    // Default alignment
    #ifdef _MSC_VER
    #	pragma pack( pop, packing )
    #endif
    
    #undef PACK_STRUCT
    
    bool MS3DModel::Load(const std::string & name)
    {
    	const char *ptr1 = 0;
    	ptr1= name.data ( );
    
    	ifstream inputFile( ptr1, ios::in | ios::binary);
    	if ( inputFile.fail())
    	{
    		MessageBox(NULL,"Model file not found.","Model Error",MB_OK);
    		return false;	// "Couldn't open the model file."
    	}
    
    	inputFile.seekg( 0, ios::end );
    	long fileSize = inputFile.tellg();
    	inputFile.seekg( 0, ios::beg );
    
    	byte *pBuffer = new byte[fileSize];
    	inputFile.read((char *)pBuffer, fileSize );
    	inputFile.close();
    
    	const byte *pPtr = pBuffer;
    	MS3DHeader *pHeader = ( MS3DHeader* )pPtr;
    	pPtr += sizeof( MS3DHeader );
    
    	if ( strncmp( pHeader->m_ID, "MS3D000000", 10 ) != 0 )
    	{
    		MessageBox(NULL,"Not a valid Milkshape3D model file.", "Model Error",MB_OK);
    		return false; // "Not a valid Milkshape3D model file."
    	}
    
    	if ( pHeader->m_version < 3 || pHeader->m_version > 7 )
    	{
    		MessageBox(NULL,"Not a valid Milkshape3D file version.","Model Error",MB_OK);
    		return false; // "Unhandled file version.  Milkshape3D Version 1.3 through 1.7 is supported." :)
    	}
    
    	int nVertices = *( word* )pPtr; 
    	NumVertices = nVertices;
    	Vertices = new Vertex[nVertices];
    	pPtr += sizeof( word );
    
    	int i;
    	for ( i = 0; i < nVertices; i++ )
    	{
    		MS3DVertex *pVertex = ( MS3DVertex* )pPtr;
    		Vertices[i].BoneID = pVertex->m_boneID;
    		memcpy( Vertices[i].Location, pVertex->m_vertex, sizeof( float )*3 );
    		pPtr += sizeof( MS3DVertex );
    	}
    
    
    	int nTriangles = *( word* )pPtr;
    	NumTriangles = nTriangles;
    	Triangles = new Triangle[nTriangles];
    	pPtr += sizeof( word );
    
    	for ( i = 0; i < nTriangles; i++ )
    	{
    		MS3DTriangle *pTriangle = ( MS3DTriangle* )pPtr;
    		int vertexIndices[3] = { pTriangle->m_vertexIndices[0], pTriangle->m_vertexIndices[1], pTriangle->m_vertexIndices[2] };
    		float t[3] = { 1.0f-pTriangle->m_t[0], 1.0f-pTriangle->m_t[1], 1.0f-pTriangle->m_t[2] };
    		memcpy( Triangles[i].VertexNormals, pTriangle->m_vertexNormals, sizeof( float )*3*3 );
    		memcpy( Triangles[i].Textures1, pTriangle->m_s, sizeof( float )*3 );
    		memcpy( Triangles[i].Textures2, t, sizeof( float )*3 );
    		memcpy( Triangles[i].VertexIndices, vertexIndices, sizeof( int )*3 );
    		pPtr += sizeof( MS3DTriangle );
    	}
    
    	int nGroups = *( word* )pPtr;
    	NumMeshes = nGroups;
    	Meshes = new Mesh[nGroups];
    	pPtr += sizeof( word );
    	for ( i = 0; i < nGroups; i++ )
    	{
    		pPtr += sizeof( byte );	// flags
    		pPtr += 32;				// name
    
    		word nTriangles = *( word* )pPtr;
    		pPtr += sizeof( word );
    		int *pTriangleIndices = new int[nTriangles];
    		for ( int j = 0; j < nTriangles; j++ )
    		{
    			pTriangleIndices[j] = *( word* )pPtr;
    			pPtr += sizeof( word );
    		}
    
    		char materialIndex = *( char* )pPtr;
    		pPtr += sizeof( char );
    	
    		Meshes[i].MaterialIndex = materialIndex;
    		Meshes[i].NumTriangles = nTriangles;
    		Meshes[i].TriangleIndices = pTriangleIndices;
    	}
    
    	int nMaterials = *( word* )pPtr;
    	NumMaterials = nMaterials;
    	Materials = new Material[nMaterials];
    	pPtr += sizeof( word );
    	for ( i = 0; i < nMaterials; i++ )
    	{
    		MS3DMaterial *pMaterial = ( MS3DMaterial* )pPtr;
    		memcpy( Materials[i].Ambient, pMaterial->m_ambient, sizeof( float )*4 );
    		memcpy( Materials[i].Diffuse, pMaterial->m_diffuse, sizeof( float )*4 );
    		memcpy( Materials[i].Specular, pMaterial->m_specular, sizeof( float )*4 );
    		memcpy( Materials[i].Emissive, pMaterial->m_emissive, sizeof( float )*4 );
    		Materials[i].Shininess = pMaterial->m_shininess;
    		Materials[i].TextureFilename = new char[strlen( pMaterial->m_texture )+1];
    		strcpy(Materials[i].TextureFilename, pMaterial->m_texture);
    		pPtr += sizeof( MS3DMaterial );
    	}
    
    	ReloadTextures();
    
    	delete[] pBuffer;
    
    	return true;
    }
    
    void MS3DModel::ReloadTextures()
    {
    	for ( int i = 0; i < NumMaterials; i++ )
    		if ( strlen(Materials[i].TextureFilename) > 0 )
    		{
    			std::string TextureFilename = Materials[i].TextureFilename;
    			int cutoff = TextureFilename.find_last_of('//');
    			TextureFilename = TextureFilename.substr(cutoff + 1);
    			Materials[i].Texture = LoadGLTexture( TextureFilename.c_str() );
    		}
    		else
    			Materials[i].Texture = 0;
    }
    
    void MS3DModel::Draw()
    {
    
    	GLboolean texEnabled = glIsEnabled( GL_TEXTURE_2D );
     
    	// Draw by group
    	for ( int i = 0; i < NumMeshes; i++ )
    	{
    
    		const int materialIndex = Meshes[i].MaterialIndex;
    		if ( materialIndex >= 0 )
    		{
    			glMaterialfv( GL_FRONT, GL_AMBIENT, Materials[materialIndex].Ambient );
    			glMaterialfv( GL_FRONT, GL_DIFFUSE, Materials[materialIndex].Diffuse );
    			glMaterialfv( GL_FRONT, GL_SPECULAR, Materials[materialIndex].Specular );
    			glMaterialfv( GL_FRONT, GL_EMISSION, Materials[materialIndex].Emissive );
    			glMaterialf( GL_FRONT, GL_SHININESS, Materials[materialIndex].Shininess );
     
    			if ( Materials[materialIndex].Texture > 0 )
    			{
    				glBindTexture( GL_TEXTURE_2D, Materials[materialIndex].Texture );
    				glEnable( GL_TEXTURE_2D );
    			}
    			else
    				glDisable( GL_TEXTURE_2D );
    		}
    		else
    		{
    			// Material properties?
    			glDisable( GL_TEXTURE_2D );
    		}
     
    		glBegin( GL_TRIANGLES );
    		{
    			for ( int j = 0; j < Meshes[i].NumTriangles; j++ )
    			{
    				const int triangleIndex = Meshes[i].TriangleIndices[j];
    				const MS3DModel::Triangle * pTri = &(Triangles[triangleIndex]);
     
    				for ( int k = 0; k < 3; k++ )
    				{
    					const int index = pTri->VertexIndices[k];
     
    					glNormal3fv( pTri->VertexNormals[k] );
    					glTexCoord2f( pTri->Textures1[k], pTri->Textures2[k] );
    					glVertex3fv( Vertices[index].Location );
    				}
    			}
    		}
    		glEnd();
    	}
     
    	if ( texEnabled )
    		glEnable( GL_TEXTURE_2D );
    	else
    		glDisable( GL_TEXTURE_2D );
    }
    Okay, now, turn that code into something that uses a single vertex array.






    GO!!!

    No, but seriously, lemme try to explain what I'm trying to do... And after I do that you'll be like, jeeze why don't you just do it yourself?

    I'M ADD THATS WHY!!!

    Okay, lets look at the header file first, this is our class for the ms3d object, it is an ms3d model, an ms3d model it is, it is simply that, and nothing more. See how it can draw itself? Well that is what I'm out to change, last time I checked humans don't animate themselves, they are created in a mommy, with some help from a daddy. Those two words are euphanisms here, the daddy would be our big bad logic engine while the mommy would be our rendering engine... The logic engine is all like, gimme a baby! And the rendering engine spits it out like wildfire.

    This leads me to want to make it so I can Load this object type into a universal format. Think of it this way... I want my mommy to be able to make mexican babies, asian babies, white babies, and black babies (for lack ofa better term?). Well, right now the way the model is loaded, the mommy would have to be tailored to support the ms3d model only! (or have a big state machine inside the renderer, and that would be silly!)... We wanna set it up so the ms3d model loads into a universal format

    Are you following me? Well, I think this is the best I've ever explained this before in my life!

    So, lets get more detailed here...

    A code snippet

    Code:
    	struct Vertex
    	{
    		char BoneID;
    		float Location[3];
    	};
    
    
    	int NumVertices;
    	Vertex * Vertices;
    
    	struct Triangle
    	{
    		float VertexNormals[3][3];
    		float Textures1[3], Textures2[3];
    		int VertexIndices[3];
    	};
    
    	int NumTriangles;
    	Triangle *Triangles;
    
    	struct Mesh
    	{
    		int MaterialIndex;
    		int NumTriangles;
    		int *TriangleIndices;
    	};
    
    	int NumMeshes;
    	Mesh *Meshes;
    
    	struct Material
    	{
    		float Ambient[4], Diffuse[4], Specular[4], Emissive[4];
    		float Shininess;
    		GLuint Texture;
    		char * TextureFilename;
    	};
    
    	int NumMaterials;
    	Material *Materials;
    That was taken from MS3D.h, this is how we store our information as is, this needs to be changed so we can send a single, count it ONE, dynamically allocated array to a renderer. We don't wanna draw by triangle anymore, we wanna draw with an index, we wanna draw pure vertex information.


    That right above there, is a HUGE MESS, theres like 4 differents structs in a single data type, BAH! I want one freakin indexed array, (or 2 or 3), in ONE structure, so I can send the ms3d model to the renderer and have it, say something like...

    Okay, lets see these vertices, BAM DRAW!!! Now lets do normals kiddies, BAM draw normals, mmk, what else do we need, color? COLORFIED! Oh, tex coords you say?! Texture applied! BAM!

    Does it sound too good to be true?! Well so far for me it is and that makes me VERY SAD!!! BECAUSE I CAN'T FIGURE IT OUT!!! GOD!

    OMG I started programming for 3 days and already it is driving me mad again!

    I can't believe this post isn't over yet!!! Okay, where was I.... Feel free to take 5, I need some -dedicated- help here.

    Okay, now comes the actual hard part, now whats going to happen with the loading code? Let me show you this beast of a function

    Code:
    bool MS3DModel::Load(const std::string & name)
    {
    	const char *ptr1 = 0;
    	ptr1= name.data ( );
    
    	ifstream inputFile( ptr1, ios::in | ios::binary);
    	if ( inputFile.fail())
    	{
    		MessageBox(NULL,"Model file not found.","Model Error",MB_OK);
    		return false;	// "Couldn't open the model file."
    	}
    
    	inputFile.seekg( 0, ios::end );
    	long fileSize = inputFile.tellg();
    	inputFile.seekg( 0, ios::beg );
    
    	byte *pBuffer = new byte[fileSize];
    	inputFile.read((char *)pBuffer, fileSize );
    	inputFile.close();
    
    	const byte *pPtr = pBuffer;
    	MS3DHeader *pHeader = ( MS3DHeader* )pPtr;
    	pPtr += sizeof( MS3DHeader );
    
    	if ( strncmp( pHeader->m_ID, "MS3D000000", 10 ) != 0 )
    	{
    		MessageBox(NULL,"Not a valid Milkshape3D model file.", "Model Error",MB_OK);
    		return false; // "Not a valid Milkshape3D model file."
    	}
    
    	if ( pHeader->m_version < 3 || pHeader->m_version > 7 )
    	{
    		MessageBox(NULL,"Not a valid Milkshape3D file version.","Model Error",MB_OK);
    		return false; // "Unhandled file version.  Milkshape3D Version 1.3 through 1.7 is supported." :)
    	}
    
    	int nVertices = *( word* )pPtr; 
    	NumVertices = nVertices;
    	Vertices = new Vertex[nVertices];
    	pPtr += sizeof( word );
    
    	int i;
    	for ( i = 0; i < nVertices; i++ )
    	{
    		MS3DVertex *pVertex = ( MS3DVertex* )pPtr;
    		Vertices[i].BoneID = pVertex->m_boneID;
    		memcpy( Vertices[i].Location, pVertex->m_vertex, sizeof( float )*3 );
    		pPtr += sizeof( MS3DVertex );
    	}
    
    
    	int nTriangles = *( word* )pPtr;
    	NumTriangles = nTriangles;
    	Triangles = new Triangle[nTriangles];
    	pPtr += sizeof( word );
    
    	for ( i = 0; i < nTriangles; i++ )
    	{
    		MS3DTriangle *pTriangle = ( MS3DTriangle* )pPtr;
    		int vertexIndices[3] = { pTriangle->m_vertexIndices[0], pTriangle->m_vertexIndices[1], pTriangle->m_vertexIndices[2] };
    		float t[3] = { 1.0f-pTriangle->m_t[0], 1.0f-pTriangle->m_t[1], 1.0f-pTriangle->m_t[2] };
    		memcpy( Triangles[i].VertexNormals, pTriangle->m_vertexNormals, sizeof( float )*3*3 );
    		memcpy( Triangles[i].Textures1, pTriangle->m_s, sizeof( float )*3 );
    		memcpy( Triangles[i].Textures2, t, sizeof( float )*3 );
    		memcpy( Triangles[i].VertexIndices, vertexIndices, sizeof( int )*3 );
    		pPtr += sizeof( MS3DTriangle );
    	}
    
    	int nGroups = *( word* )pPtr;
    	NumMeshes = nGroups;
    	Meshes = new Mesh[nGroups];
    	pPtr += sizeof( word );
    	for ( i = 0; i < nGroups; i++ )
    	{
    		pPtr += sizeof( byte );	// flags
    		pPtr += 32;				// name
    
    		word nTriangles = *( word* )pPtr;
    		pPtr += sizeof( word );
    		int *pTriangleIndices = new int[nTriangles];
    		for ( int j = 0; j < nTriangles; j++ )
    		{
    			pTriangleIndices[j] = *( word* )pPtr;
    			pPtr += sizeof( word );
    		}
    
    		char materialIndex = *( char* )pPtr;
    		pPtr += sizeof( char );
    	
    		Meshes[i].MaterialIndex = materialIndex;
    		Meshes[i].NumTriangles = nTriangles;
    		Meshes[i].TriangleIndices = pTriangleIndices;
    	}
    
    	int nMaterials = *( word* )pPtr;
    	NumMaterials = nMaterials;
    	Materials = new Material[nMaterials];
    	pPtr += sizeof( word );
    	for ( i = 0; i < nMaterials; i++ )
    	{
    		MS3DMaterial *pMaterial = ( MS3DMaterial* )pPtr;
    		memcpy( Materials[i].Ambient, pMaterial->m_ambient, sizeof( float )*4 );
    		memcpy( Materials[i].Diffuse, pMaterial->m_diffuse, sizeof( float )*4 );
    		memcpy( Materials[i].Specular, pMaterial->m_specular, sizeof( float )*4 );
    		memcpy( Materials[i].Emissive, pMaterial->m_emissive, sizeof( float )*4 );
    		Materials[i].Shininess = pMaterial->m_shininess;
    		Materials[i].TextureFilename = new char[strlen( pMaterial->m_texture )+1];
    		strcpy(Materials[i].TextureFilename, pMaterial->m_texture);
    		pPtr += sizeof( MS3DMaterial );
    	}
    
    	ReloadTextures();
    
    	delete[] pBuffer;
    
    	return true;
    }
    
    void MS3DModel::ReloadTextures()
    {
    	for ( int i = 0; i < NumMaterials; i++ )
    		if ( strlen(Materials[i].TextureFilename) > 0 )
    		{
    			std::string TextureFilename = Materials[i].TextureFilename;
    			int cutoff = TextureFilename.find_last_of('//');
    			TextureFilename = TextureFilename.substr(cutoff + 1);
    			Materials[i].Texture = LoadGLTexture( TextureFilename.c_str() );
    		}
    		else
    			Materials[i].Texture = 0;
    }
    Okay, so one of two things are going to happen. This code will double in size when I am done (meaning I'd have to load it this way and THEN put it into the data types I want...

    Or It can be nice and neat, and I can cut to the chase and load it straight into those structures. Oh how I would ever so want this to happen.

    Where do I begin? How do I even begin to work on this beast? All I really need are the following, A normal array, a texture coord array, and a vertex array. All of these need to be indexed, indexed to the point where I can input an index and it will draw only the arm or something (for animation purposes)..

    So I want my Final structure of data to look something liek this

    Code:
    Struct ModelDat
    {
    VertexArray[];
    NormalArray[];
    TexCoordArray[];
    }
    Wow thats a ton of data packed into a tiny bit of code!

    Alright, we have like 5 structures to begin with...

    So I see in our loading code we do the vertices here....

    Code:
    	int nVertices = *( word* )pPtr; 
    	NumVertices = nVertices;
    	Vertices = new Vertex[nVertices];
    	pPtr += sizeof( word );
    
    	int i;
    	for ( i = 0; i < nVertices; i++ )
    	{
    		MS3DVertex *pVertex = ( MS3DVertex* )pPtr;
    		Vertices[i].BoneID = pVertex->m_boneID;
    		memcpy( Vertices[i].Location, pVertex->m_vertex, sizeof( float )*3 );
    		pPtr += sizeof( MS3DVertex );
    	}
    Okay, so far it looks all fine and dandy, and I think I'm right, maybe. It is dynamically allocating vertices[i].location, and creating an array of vertices..

    I wonder if I stuck this array of vertices in a VA for opengl it would work? No, of course not, apparently I'm going to have to unshare the vertices. Apparently what I have right now isn't enough to properly light the model, you can't share normals I don't think, you need every one individually.

    So finally, you know problem number 1... I hope you're still with me.

    Now onto texture coords, this is where it gets strange...
    Code:
    	int nTriangles = *( word* )pPtr;
    	NumTriangles = nTriangles;
    	Triangles = new Triangle[nTriangles];
    	pPtr += sizeof( word );
    
    	for ( i = 0; i < nTriangles; i++ )
    	{
    		MS3DTriangle *pTriangle = ( MS3DTriangle* )pPtr;
    		int vertexIndices[3] = { pTriangle->m_vertexIndices[0], pTriangle->m_vertexIndices[1], pTriangle->m_vertexIndices[2] };
    		float t[3] = { 1.0f-pTriangle->m_t[0], 1.0f-pTriangle->m_t[1], 1.0f-pTriangle->m_t[2] };
    		memcpy( Triangles[i].VertexNormals, pTriangle->m_vertexNormals, sizeof( float )*3*3 );
    		memcpy( Triangles[i].Textures1, pTriangle->m_s, sizeof( float )*3 );
    		memcpy( Triangles[i].Textures2, t, sizeof( float )*3 );
    		memcpy( Triangles[i].VertexIndices, vertexIndices, sizeof( int )*3 );
    		pPtr += sizeof( MS3DTriangle );
    	}
    Apparently this is where the tex coords are dynamically allocated, well yes that is true. This is also where we create the index into our vertices, and our normals... Wow, nifty!

    So what's the problem? Well, it does it by TRIANGLE!!!! GAH!!! WTF!!! For ever triangle, GOD, why not vertices?! WHY GOD WHY!!! Okay, maybe I'm delirious here, but don't you need a normal per vertex for proper lighting? And don't you need a U/V element for every single vertex?

    And that is problem number two, which I believe stems from problem number 1...

    Now this is all fine and dandy if you're going to draw your model like this:
    Code:
    		glBegin( GL_TRIANGLES );
    		{
    			for ( int j = 0; j < Meshes[i].NumTriangles; j++ )
    			{
    				const int triangleIndex = Meshes[i].TriangleIndices[j];
    				const MS3DModel::Triangle * pTri = &(Triangles[triangleIndex]);
     
    				for ( int k = 0; k < 3; k++ )
    				{
    					const int index = pTri->VertexIndices[k];
     
    					glNormal3fv( pTri->VertexNormals[k] );
    					glTexCoord2f( pTri->Textures1[k], pTri->Textures2[k] );
    					glVertex3fv( Vertices[index].Location );
    				}
    			}
    		}
    		glEnd();
    	}
     
    	if ( texEnabled )
    		glEnable( GL_TEXTURE_2D );
    	else
    		glDisable( GL_TEXTURE_2D );
    }
    Hm, so yeah.... Look at that beautifully optimized ms3d code wonder... Yeah, optimized, but purely like, alone in its design, maybe if every model type worked this way, but that isn't the case!

    So MS3D Mode I'm sorry you must change!

    I wanna be able to draw you with a simple gldrawelements call for your normals vertices and tex coords...

    Soo, can anyone help me?

    -Shamino, the absent minded programmer.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  2. #2

    Join Date
    May 2005
    Posts
    1,042
    I simply do not fully understand:

    A) What you are trying to do, exactly...it seems you are trying to unify all of the data types so that it is easier to put it into a centralized renderer.

    B) Why you are trying to do it, this is somewhat answered, by myself, in part A...but what you could potentially get out of this doesn't seem like it will justify the time you are putting into this.

    So, with this specification:
    That right above there, is a HUGE MESS, theres like 4 differents structs in a single data type, BAH! I want one freakin indexed array, (or 2 or 3), in ONE structure, so I can send the ms3d model to the renderer and have it, say something like...

    Okay, lets see these vertices, BAM DRAW!!! Now lets do normals kiddies, BAM draw normals, mmk, what else do we need, color? COLORFIED! Oh, tex coords you say?! Texture applied! BAM!
    I can, for about the 400th time, show you how I set things up, because this is exactly the type of thing my simple renderer allows me to do.

    I have a few basic fundamental containers for basic primitives. I haven't started working with shaders, but when I do, there will be a shader container that holds all of the possible information for rendering a shader to the other fundamental data types.

    So, how would a MS3D model make it into my renderer? Here's how:

    //Container, defined by the renderer, how to hold a basic ms3d model
    //The renderer has an std::vector<MS3DModelRenderData*> called mMS3DRenderData
    PHP Code:
    struct    MS3DModelRenderData
    {
        
    float    mat[16];
        
    CMS3D_MODEL    *pModel;
    }; 
    How the renderer adds a ms3d model to itself, this is a member function of GLRenderer (implemented inline in the header):
    PHP Code:

        
    //Angles are in degrees
        
    void    AddMS3DModelToRenderer(Matrix4x4    &    Input,
                                         
    CMS3D_MODEL    *pModel)    
        {
            
    MS3DModelRenderData    *pData = new    MS3DModelRenderData;
            
            
    Input.FillGLMatrix(pData->mat);

            
    pData->pModel        =    pModel;
            
            
    this->mMS3DRenderData.push_back(pData);
        } 
    Somewhat ugly, but simple and works. GLRenderer has a function for rendering all of the ms3d models it has and is fairly similar to the actual drawing portion in 'your' code, so I will omit that part:
    PHP Code:

    void    GLRenderer
    ::RenderMS3DModels()
    {    
        if(!
    mMS3DRenderData.size())
        {
            return;
        }
        
        
    gpNTGLAPI->glActiveTextureARB(GL_TEXTURE0_ARB);
        
        
    NT_GL(glEnable(GL_TEXTURE_2D))

        
    gpNTGLAPI->glActiveTextureARB(GL_TEXTURE1_ARB);
        
        
    NT_GL(glDisable(GL_TEXTURE_2D))

        
    NT_GL(glDisable(GL_LIGHTING))    //to make sure we can always see the model

        
    R_NormalCullMode();

        
    gpNTGLAPI->glActiveTextureARB(GL_TEXTURE0_ARB);    //set the active texture unit

        
    std::vector<MS3DModelRenderData*>::iterator    ptr;
        
        
    MS3DModelRenderData    *pTempData=0;
        
        
    CMS3D_MODEL    *pTempModel=0;

        for(
    ptr mMS3DRenderData.begin(); ptr != mMS3DRenderData.end(); ptr++)
        {
            
    pTempData    =    *ptr;

            
    pTempModel    =    pTempData->pModel;
            
                
    NT_GL(glPushMatrix())
            
    NT_GL(glMultMatrixf(pTempData->mat))

    //model drawing omitted, essentially the same as what you have, I do not implement VA's either
        
    NT_GL(glPopMatrix())
        }
        
    R_QuakeCullMode();

    And here is the GLRenderScene() function:
    PHP Code:

    void    GLRenderer
    ::GLRenderScene()
    {
        
    NT_GL(glClear(GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT))
        
    NT_GL(glMatrixMode(GL_MODELVIEW))
        
    NT_GL(glLoadIdentity())

        
    /*
            -Generate the matrix to represent the current view direction and position
        */

        
    NT_GL(glMatrixMode(GL_MODELVIEW))
        
    NT_GL(glPushMatrix())
        
    NT_GL(glLoadIdentity())

        
    Vector3    View Export.mpPlayer.mpCam->mPosition Export.mpPlayer.mpCam->mView;//Export.mpCam->mPosition + Export.mpCam->mView;
        
    Vector3    Pos Export.mpPlayer.mpCam->mPosition;
        
    Vector3    Up Export.mpPlayer.mpCam->mUp;

    //    gluLookAt(Pos.x,Pos.y, Pos.z, View.x, View.y, View.z, Up.x, Up.y, Up.z); 

    #if    1
        
    R_GenerateViewMatrix(RAD2DEG(Export.mpPlayer.mpCam->xRadians),RAD2DEG(Export.mpPlayer.mpCam->yRadians),0,Pos);    
    #elif    0
        
    Matrix4x4    X,Y,trans,final;
        
    X.InitFromRadians(-Export.mpPlayer.mpCam->xRadians,0,0);
        
    Y.InitFromRadians(0,-Export.mpPlayer.mpCam->yRadians,0);
        
    trans.SetTranslation(&(Pos * -1.0f));
        final = 
    trans;
        
    R_Replace4x4GLMatrix(final,GL_MODELVIEW);
    #elif    0
        
        
    Quaternion    temp;
    //    temp.InitFromRadians(Export.mpPlayer.mpCam->xRadians,Export.mpPlayer.mpCam->yRadians,0);
        
    Vector3    tempView(0,0,-1);
        
    Vector3    Strafe(1,0,0);
        
    Quaternion32RotateVector3Radians(&tempView,Export.mpPlayer.mpCam->xRadians,Export.mpPlayer.mpCam->yRadians,0);
        
    Quaternion32RotateVector3Radians(&Strafe,Export.mpPlayer.mpCam->xRadians,Export.mpPlayer.mpCam->yRadians,0);
        
    Vector3 up CrossProduct(&Strafe,&tempView);

        
    Vector3    eye Pos tempView;

        
    gluLookAt(Pos.x,Pos.y,Pos.z,eye.x,eye.y,eye.z,0,1,0);
    #endif

    #if    1
        
    R_BuildBillboardMatrix(Export.mpPlayer.mpCam->mStrafe,View,Up,mBillBoardMatrix);
    #else
        
    Matrix3x3    Rot_Only;
        
    Rot_Only.InitFromRadians(-Export.mpPlayer.mpCam->xRadians,-Export.mpPlayer.mpCam->yRadians,0);
        
    Rot_Only.Invert();
        
    Matrix4x4    ToSend;
        
    ToSend  Rot_Only;
        
    ToSend.FillGLMatrix(mBillBoardMatrix);
    #endif
        
        /*
            -Render everything that has been sent to the renderer
        */
        
    RenderQuake3SingleTexturedFaces();
        
    RenderQuake3MultiTexturedFaces();

        
    RenderMS3DModels();
        
    RenderAllDebugSpheres();
        
    RenderallDebugWireframeSpheres();
        
    RenderAllMatrixDebugSpheres();
        
    RenderAllDebugLines();
        
    RenderAllDebugBoxes();
        
    RenderAllDebugPoints();
        
    RenderAllDebugTriangles();

        
    RenderBasicQuads();
        
    RenderSkyBox();
        
    RenderSkySphere();
        
    RenderAlphaBlendedQuads();    //NEEDS TO COME LAST!!!
        
    RenderAllDebugBillboards();    //needs to come last also cuz it now has alpha blend stuff
        
    NT_GL(glPopMatrix())

        
    /*
            -This needs to come last because Text is drawn ontop of everything else (otherwise it can be depth-tested away)
        */
        
    gpTextManager->DrawText();
        
        
    SwapBuffers(gpNTGLAPI->NT_DEVICE_CONTEXT);
        
        
    /*
            -This clears the containers so they can be re-filled for the next scene
        */
        
    ResetScene();
        
    NT_GL(glMatrixMode(GL_MODELVIEW))
        
    NT_GL(glLoadIdentity())

    And here is an example of how an object accesses the renderer functionality:

    PHP Code:
    void    Hovertank::Render()
    {
        if(
    gpFrustum->SphereInFrustum(CurrentState.mPosition,mRadius))
        {
            
    Matrix4x4    final_render_mat;
            
    final_render_mat.SetTranslation(&CurrentState.mPosition);
            
    final_render_mat    =    CurrentState.mMat_Orientation;
            
    gpGLRenderer->AddMS3DModelToRenderer(final_render_mat,mpModel);
            
    gpGLRenderer->AddWireframeDebugSphereToRenderer(CurrentState.mPosition.x,CurrentState.mPosition.y,CurrentState.mPosition.z,
            
    RAD2DEG(CurrentState.mEuler_Orientation.x),RAD2DEG(CurrentState.mEuler_Orientation.y),RAD2DEG(CurrentState.mEuler_Orientation.z),1,0,0,mRadius);
        }

    And here is the GameCycle() function that ties everything together, note that everything here is simply for debugging my latest project , but it shows how everything culminates in

    A - Updating objects ( Update() )
    B - Calling gpGLRenderer->RenderScene() rendering the stuff sent to it in each object's Update() function.

    PHP Code:

    void    MainGameCycle
    ()
    {
            
    double    currenttime gpFPS->FPS_GETTIME();
            
            
    //It just so happens that the last time we swapped the color buffer was the last time we ran
            //any of the physics frames
            
    double    dt currenttime gpFPS->LastTimeBufferSwapped;
            
            if(
    dt    >=    gpPhysicsManager->mSecondsPerFrame)    //this works        
            
    {
                
    gpTextManager->PushLineOfText(" Teletubbies deserve it  ",1,1,1,1);
                
    Export.mKeys    =    Export.CheckKeyBoard();

                
    gpFPS->UpdateFPS();    
                
                
    gpPhysicsManager->mTimeBuffer    +=    dt;    
                
    float    discrepancy                =    currenttime    -    (gpFPS->LastTimeBufferSwapped+gpPhysicsManager->mSecondsPerFrame);
                
                
    //this way we don't lose ANY time
                
    if(discrepancy>0)
                {
                    
    gpPhysicsManager->mTimeBuffer    +=    discrepancy;
                }
                
                
    Export.mpPlayer.mpCam->MatrixCheckMouse();
                
                
    ptest_hovertank->Update();
                
    ptest_hovertank1->Update();
                
    #define    DRAW_CLOSEST_NODE    1
    #if    DRAW_CLOSEST_NODE
                
    int    closest_node_dist    999999999.0f;
                
    int    closest_node_index  0;
                for(
    int c 0waypoint_nodes.size(); c++)
                {
                    
    Vector3 node_pos = *waypoint_nodes[c];
                    
    float    closest_dist_sq = (node_pos Export.mpPlayer.CurrentState.mPosition).BasicLength();
                    if(
    closest_dist_sq closest_node_dist)
                    {
                        
    closest_node_dist  closest_dist_sq;
                        
    closest_node_index c;
                    }
                }

                
    Vector3    &closest_node_pos = *waypoint_nodes[closest_node_index];
                
    gpGLRenderer->AddDebugLineToRenderer(closest_node_pos.x,closest_node_pos.y,closest_node_pos.z,closest_node_pos.x,closest_node_pos.100.0f,closest_node_pos.z,1.0f,1.0f,1.0f,5.0f);
                
    DebugPrintInt("closest_node_index",closest_node_index,gpTextManager);
    #endif
                //indexes into std::vector<vector3*> waypoint_nodes
                
    static    int    PATH_TEST_START 6;
                static    
    int    PATH_TEST_END   11;
                static    
    std::stack<int>    StackIntPath;
                static    
    std::vector<int>    VectorIntPath;
                static    
    HighResSecondsTimer    PathTestTimer;
                
    PathTestTimer.Update();
                if(
    Export.IsStateTrue(RMOUSEDOWN) && PathTestTimer.mTimeBuffer >= .5f)
                {
                    
    PathTestTimer.ResetTimeBuffer();
                    
    //Find a random start and end
                    
    int    random_start=0,random_end=0;
                    do
                    {
                        
    random_start rand()%waypoint_nodes.size();
                        
    random_end   rand()%waypoint_nodes.size();
                    }while    (
    random_start == random_end);
                    
    PATH_TEST_START random_start;
                    
    PATH_TEST_END    random_end;

                    
    //Fill the stack of index values
                    //For debugging, find the entire path immediately so we can draw it
    FindPath(PathMatrices,NUM_PATH_MATRICES,random_start,random_end,StackIntPath);
                    
                    
    VectorIntPath.clear();
                    
                    while(
    StackIntPath.size())
                    {
                        
    VectorIntPath.push_back(StackIntPath.top());
                        
    StackIntPath.pop();
                    }
                }
                
                
    int    num_moves GetNumMoves(PathMatrices,NUM_PATH_MATRICES,PATH_TEST_START,PATH_TEST_END);
                
    DebugPrintInt("#moves",num_moves,gpTextManager);
                
    DebugPrintInt("Path start",PATH_TEST_START,gpTextManager);
                
    DebugPrintInt("Path end",PATH_TEST_END,gpTextManager);
                
    gpTextManager->PushLineOfText(" ",-1024,1,1,1);
                
    gpTextManager->PushLineOfText("Printing path: " ,-1024,1,1,1);
                for(
    int i 0VectorIntPath.size(); i++)
                {
                    
    int    this_index VectorIntPath[i];
                    
    DebugPrintInt("Node#",this_index,gpTextManager);
                }
                
                if(
    waypoint_nodes.size())
                {
                    
    Vector3        path_start = *waypoint_nodes[PATH_TEST_START];
                    
    Vector3        path_end = *waypoint_nodes[PATH_TEST_END];

                    
    gpGLRenderer->AddWireframeDebugSphereToRenderer(path_start.x,path_start.y,path_start.z,0,0,0,0,0,0,70.0f);
                    
    gpGLRenderer->AddWireframeDebugSphereToRenderer(path_end.x,path_end.y,path_end.z,0,0,0,1,1,1,70.0f);
                    
                    
    int    num_lines_draw 0;
                    for(
    int i 0< (int)VectorIntPath.size(); i++)
                    {
                        
    Vector3    pos = *waypoint_nodes[VectorIntPath[i]];
                        
    gpGLRenderer->AddWireframeDebugSphereToRenderer(pos.x,pos.y,pos.z,0,0,0,0.0f,1.0f,0.0f,50.0f);
                    }
            
                
    //    DebugPrintInt("#lines drawn",num_lines_draw,gpTextManager);

                    
                    
    for(0< (int)VectorIntPath.size()-1i++)
                    {
                        
    Vector3    pos = *waypoint_nodes[VectorIntPath[i]];
                        
    Vector3    pos1 = *waypoint_nodes[VectorIntPath[i+1]];
                    
    //    gpGLRenderer->AddWireframeDebugSphereToRenderer(pos.x,pos.y,pos.z,0,0,0,0.0f,1.0f,0.0f,50.0f);
                        
    gpGLRenderer->AddDebugLineToRenderer(pos.x,pos.y,pos.z,pos1.x,pos1.y,pos1.z,1.0f,1.0f,1.0f,5.0f);
                    }
                    
                }

                
    std::vector<Vector3*>::iterator    waypoint_ptr;
                for(
    waypoint_ptr waypoint_nodes.begin(); waypoint_ptr != waypoint_nodes.end(); waypoint_ptr++)
                {
                    
    Vector3    ref = **waypoint_ptr;
                    
    gpGLRenderer->AddWireframeDebugSphereToRenderer(ref.x,ref.y,ref.z,0.0f,0.0f,0.0f,1.0f,1.0f,0.0f,15.0);
                }
                
                if(
    GetKeyState('E') & 0x80)
                {
                    
    ptest_hovertank->CurrentState.mPosition    =    Vector3(METER(3),1000,0);
                
                    
    ptest_hovertank->CurrentState.mLinearMomentum ptest_hovertank->CurrentState.mLinearVelocity Vector3(0,0,0);
                    
    ptest_hovertank->CurrentState.mAngularMomentum ptest_hovertank->CurrentState.mAngularVelocity Vector3(0,0,0);
                    
    ptest_hovertank->CurrentState.mQuat_Orientation.InitFromRadians(0,0,0);
                    
    ptest_hovertank->CurrentState.mMat_Orientation.MakeIdentity();
                    
    ptest_hovertank->CurrentState.mEuler_Orientation Vector3(0,0,0);
                }

                if(
    GetKeyState('T') & 0x80)
                {
                    if(
    player_starts.size())
                    {
                        
    Export.mpPlayer.CurrentState.mPosition = *player_starts[1];
                        
    ptest_hovertank->CurrentState.mPosition    =    *player_starts[0];
                    }
                    else
                    {
                        
    ptest_hovertank->CurrentState.mPosition    =    Vector3(METER(3),4000,METER(1));
                        
    Export.mpPlayer.CurrentState.mPosition    =    Vector3(METER(5),4000,METER(2));
                    }

                    
    ptest_hovertank->CurrentState.mLinearMomentum ptest_hovertank->CurrentState.mLinearVelocity Vector3(0,0,0);
                    
    ptest_hovertank->CurrentState.mAngularMomentum ptest_hovertank->CurrentState.mAngularVelocity Vector3(0,0,0);
                    
    ptest_hovertank->CurrentState.mQuat_Orientation.InitFromRadians(0,DEG2RAD(179),0);
                    
    ptest_hovertank->CurrentState.mMat_Orientation    =    ptest_hovertank->CurrentState.mQuat_Orientation.ToMatrix();
                    
    Vector3    *euler = &ptest_hovertank->CurrentState.mEuler_Orientation;
                    
    ptest_hovertank->CurrentState.mMat_Orientation.ToRadians(&euler->x,&euler->y,&euler->z);


                    
                    
    ptest_hovertank1->CurrentState.mPosition    =    Vector3(METER(3),2000,METER(1));
                    
    ptest_hovertank1->CurrentState.mLinearMomentum ptest_hovertank->CurrentState.mLinearVelocity Vector3(0,0,0);
                    
    ptest_hovertank1->CurrentState.mAngularMomentum ptest_hovertank->CurrentState.mAngularVelocity Vector3(0,0,0);
                    
    ptest_hovertank1->CurrentState.mQuat_Orientation.InitFromRadians(0,DEG2RAD(179),0);
                    
    ptest_hovertank1->CurrentState.mMat_Orientation    =    ptest_hovertank->CurrentState.mQuat_Orientation.ToMatrix();
                    
    euler = &ptest_hovertank1->CurrentState.mEuler_Orientation;
                    
    ptest_hovertank1->CurrentState.mMat_Orientation.ToRadians(&euler->x,&euler->y,&euler->z);
                }

                if(
    GetKeyState('Y') & 0x80)
                {
                    
    ptest_hovertank->CurrentState.mLinearMomentum ptest_hovertank->CurrentState.mLinearVelocity Vector3(0,0,0);
                }

                if(
    GetKeyState('R') & 0x80)
                {
                    
    ptest_hovertank->CurrentState.mLinearMomentum ptest_hovertank->CurrentState.mLinearVelocity Vector3(0,0,0);
                    
    ptest_hovertank->CurrentState.mAngularMomentum ptest_hovertank->CurrentState.mAngularVelocity Vector3(0,0,0);
                    
                    
    ptest_hovertank->CurrentState.mQuat_Orientation.InitFromRadians(0,0,0);
                    
    ptest_hovertank->CurrentState.mMat_Orientation.MakeIdentity();
                    
    ptest_hovertank->CurrentState.mEuler_Orientation Vector3(0,0,0);

                    
                    
    ptest_hovertank1->CurrentState.mLinearMomentum ptest_hovertank->CurrentState.mLinearVelocity Vector3(0,0,0);
                    
    ptest_hovertank1->CurrentState.mAngularMomentum ptest_hovertank->CurrentState.mAngularVelocity Vector3(0,0,0);
                    
                    
    ptest_hovertank1->CurrentState.mQuat_Orientation.InitFromRadians(0,0,0);
                    
    ptest_hovertank1->CurrentState.mMat_Orientation.MakeIdentity();
                    
    ptest_hovertank1->CurrentState.mEuler_Orientation Vector3(0,0,0);
                }
                
                
    float    force 100;
                if(
    GetKeyState(VK_LSHIFT) & 0x80)
                    
    force force 10;
                
                if(
    Export.mKeys    &    FORWARD)
                {
                    
    Export.mpPlayer.CurrentState.mLinearMomentum    +=    Export.mpPlayer.mpCam->mView*force;    
                }
                if(
    Export.mKeys    &    BACKWARD)
                {
                    
    Export.mpPlayer.CurrentState.mLinearMomentum    -=    Export.mpPlayer.mpCam->mView*force;
                }
                if(
    Export.mKeys    &    SIDESTEPRIGHT)
                {
                    
    Export.mpPlayer.CurrentState.mLinearMomentum    +=    Export.mpPlayer.mpCam->mStrafe*force;
                }
                if(
    Export.mKeys    &    SIDESTEPLEFT)
                {
                    
    Export.mpPlayer.CurrentState.mLinearMomentum    -=    Export.mpPlayer.mpCam->mStrafe*force;
                }
                if(
    GetKeyState(VK_SPACE) & 0x80)
                {
                    
    Export.mpPlayer.CurrentState.mLinearMomentum += Export.mpPlayer.mpCam->mUp force;
                }

                if(
    GetKeyState('G')    &    0x80)
                {
                    
    Export.mpPlayer.CurrentState.mLinearMomentum    *=    1-(10*gpFPS->mTimeFrac);
                }
                
                
    gpPhysicsManager->PHYSICS_RUNFRAMES();
                
                
    Export.mpPlayer.mpCam->mPosition=Export.mpPlayer.CurrentState.mPosition;

                
    Export.pRenderScene();

                
    gpFPS->LastTimeBufferSwapped currenttime;
            }
            
            else    
    //Yield processor time for your ........ing aol 
            
    {
                
    double    next_frame_time    =    gpFPS->LastTimeBufferSwapped gpPhysicsManager->mSecondsPerFrame;
                
                
    //FIXME: Have to ........ around with this, have to change the magical fraction to 
                // determine the right percent that will still allow me to get the exact framerate I want
                
    double    sleep_seconds    =    (next_frame_time currenttime);
                
    double    sleep_ms        =    SECONDS_TO_MS(sleep_seconds) * .1f;
                
    Sleep((int)sleep_ms);
            }

    Last edited by BobMcGee123; 06-15-2006 at 02:54 PM.
    I'm not immature, I'm refined in the opposite direction.

  3. #3
    Yah. Morgul's Avatar
    Join Date
    Feb 2005
    Posts
    109
    You're probably going to have to go through every vertex and add its normal, texture, and position data into the arrays. I'm not quite sure what the best method is for finding indices you can use to reference arms, legs, etc unless they are all separate subsets, in which case you just record the index to the first entry when you come to a new subset.

    As to the arrays themselves, it's up to you as to what data types they use, what type of array structure you use to contain it all, etc.
    Sic vis pacum para bellum. If you want peace, prepare for war.

  4. #4
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    Surely Bubba, I could implement that, but MS3D Models draw very strangely, look at the loading/drawing code much more closely, you'll see that the MS3D Models don't repeat vertices, and they don't repeat normals. This is fine if you're using a modelling program, but not a game where we need lighting and such.

    I also want to use vertex arrays because they will be faster in the end and alot neater. I do greatly appreciate the time and effort you put into your response. The only real difference between what you are doing and what I am doing is that I'm trying to make it so I put the MS3D Models into vertex arrays, which is proving to be very complicated given ms3d's nature.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  5. #5

    Join Date
    May 2005
    Posts
    1,042
    I'm not Bubba.

    You can do lighting with the setup I've shown, I've implemented basic vertex lighting, both flat and goraud shaded, and subsequently you could implement any lighting model with a shader.

    As for speed, a VA would be faster, but I'm pretty sure you don't need to change the layout in order to accomlish that.
    I'm not immature, I'm refined in the opposite direction.

  6. #6
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    oops, my bad bob, didn't really check who wrote it

    Thanks alot bob!

    But anyways...

    From other people I keep hearing that if you use vertex arrays you need to unshare the vertices because of some indexing issue and normals that aren't there, which results in faulty lighting
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  7. #7

    Join Date
    May 2005
    Posts
    1,042
    That may be a legit issue. Where did you hear that from? I'd like to look into it, I'm honestly not really sure (implementing realtime lighting has not been my forte'...I use VAs, but not for MS3D models, and in the constructs that I do use VAs I don't implement realtime lighting constructs at all).
    I'm not immature, I'm refined in the opposite direction.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Hello, I AM Bubba, but I'm not Bob.

    We have said in the past that the best implementation would be to use a central renderer and only switch textures when needed. However unless you are looking for like 200 FPS or Quake 4/Unreal 2 performance, you will do just as well by having each object draw itself in a render function. It certainly is a more viable alternative than just quitting 3D because you can't get what you want to work. Not all games implement rendering the same way and not all of them need to.

    Ever wonder why Novalogic Joint Ops eeks out a mere 70 to 90 FPS on top of the line cards, when the Quake's, Unreal's, and Half Life's can muster up an insane 150 to 190 FPS? It's all about implementation. Fact is, 200 FPS or 100 FPS does not matter. As long as you stay above 30 FPS you are ok. Also some of my games I still only get 30 to 40 FPS. Even with my GeForce 3 on modern games I was getting 18 FPS which is enough to be relatively smooth and interactive. Below or at 15 FPS you start getting into delayed inputs, delayed reactions, heavy jerkiness, etc, etc. But notice that not even then best of them can maintain a flat FPS in every single area of the game. At times even the mega-engines can be brought to their knees just by special effects and tons of objects.

    So my advice is stop trying to make it the uber-elite engine and just go with what you know and what works for you. Bob's setup is very similar to mine and I like his design because it's simple, robust, and efficient.

    So if you have to, flowchart your rendering loop to see what needs to be done and in what order. I recommend having a separate Update() loop so that Render() does 1 thing - renders the scene from memory based on game state. Update() does 1 thing. It updates all the objects that need it.

    You would be also wise to implement some type of document/view architecture. This has been the source of many a programmer's nightmare and headache but it's fairly simple.

    Document - maintains the data for the app/engine
    View - renders the scene based on what's in the document, or the data.
    App - responds to input from user, etc, etc.


    If you do this you can change 'document's' on the fly as well as views. A view doesn't care what's in the document, it just displays it's contents in a meaningful fashion. A document doesn't care about presenting the data to the user but cares about maintaining it, updating it, saving it, loading it, etc.

    So it's not the coding you are having a problem with. It's the design. Coding is much easier when you have a design. Coding without a design is like shooting an arrow without a target. You hit something, but you never know what it is.

    For a simple renderer I will show you 1 possible solution.

    View
    - CView
    -- CPlayerView
    -- CAIView
    -- CObjectView

    Document
    - CGameData
    -- CTextureData
    -- CSoundData
    -- CMusicData
    -- CAIData
    -- CScriptData

    The view will have to render objects so put a pointer to your render object in the base class and when you derive from it, the derived class will inherit it.

    As for your resource managers you can use the template idea, which I'm not fond of, or you can take advantage of C++ polymorphism which I like much better.

    All manager classes have one thing in common. They manage objects. Why not derive the Objects from a base class as well as the managers.

    Code:
    class CResource
    {
    };
    
    class CTexResource:public CResource {};
    class CScriptResource:public CResource {};
    class CMemResource:public CResource {};
    
    class CResourceMgr
    {
      std::vector<CResource *> m_vResources;
      public:
       ...
       CResourceMgr() { };
       virtual ~CResourceMgr(void)
       {
          for (DWORD i=0;i<static_cast<DWORD>(m_vResources.size());i++)
          {
             delete m_vResources[i];
          }
    
          m_vResources.clear();
       }
    
       DWORD Add(CResource *pResource)
       {
          m_vResources.push_back(pResource);
          return m_vResources.size()-1;
       }
    
       bool Remove(DWORD dwItem)
       {
          if (dwItem>m_vResources.size()) return true;
    
         std::vector<CResource *>::iterator it=m_vResources.begin;
         it+=dwItem;
         m_vResources.erase(it);
         return false;
       }
          
       CResource *GetObject(DWORD dwItem)
       {
         if (dwItem>m_vResources.size()) return NULL;
    
         return m_vResources[dwItem];
       }
    
    };
    
    class CTexMgr:public CResourceMgr {};
    class CScriptMgr:public CResourceMgr {};
    Now if you need a texture resource from the texture manager:

    Code:
    CTexResource *pRes=(CTexResource *)m_pTexMgr->GetObject(0);
    You can also use dynamic casting and other C++ cast's to get the job done.

    Maybe this will help.

  9. #9
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    I got me a resource manager, it works good too ...

    Code:
    #ifndef RESOURCE_MANAGER_H
    #define RESOURCE_MANAGER_H
    #pragma warning(disable: 4786)
    #pragma warning(disable: 64)
    #include <vector>
    #include <map>
    #include <string>
    #include <boost\shared_ptr.hpp>
    #include <boost\weak_ptr.hpp>
    
    template< typename T_ >
    class Resource_Manager
    {  
    
    public:
    
    	typedef T_ value_type; // std library convention 
    
    	typedef boost::shared_ptr<T_> Resource_Ptr;
    	typedef boost::weak_ptr<T_> Resource_Observer;
    	typedef std::map< std::string, Resource_Ptr > Resource_Map;
    
    	Resource_Manager<T_>() {};
    	~Resource_Manager<T_>() {};
    
    	Resource_Observer Request_Resource(const std::string & name)
    	{
    		Resource_Map::iterator  it = mResources.find(name);
    
    		if (it == mResources.end())
    		{
    			Resource_Ptr Raw_Resource(new T_);
    			Raw_Resource->Load(name);
    			mResources.insert(std::make_pair(name, Raw_Resource));
    			Resource_Observer Resource(Raw_Resource);
    			return Resource;
    		}
    		else
    		{
    			return Resource_Observer(it->second);
    		}
    	}
    
    	void Request_Resource_Removal(const std::string & name)
    	{
    		Resource_Map::iterator it = mResources.find(name);
    
    		if (it != mResources.end())
    		{
    			mResources.erase(it);
    		}
    	}
    
    private:
    	Resource_Map  mResources;
    };
    
    #endif
    There might be 1 or 2 things wrong with it, but I think it is pretty swell. This one I wrote completely myself, with research and such, no copied code at all.

    The only issue I'm having is not design, the thought I've put into design is why I only have 4000 lines of working code though, ironic huh? I design more than I write, but I don't write until I design. That is the way this has been going. The only issue I'm having really is these blasted ms3d models! I need to get them into vertex arrays otherwise I can't centralize the renderer!

    I don't want objects to animate and draw themselves! It seems logically invalid in my eyes (Not that it doesn't work, I do understand that it works!!!) It just isn't the way I want to do it.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  10. #10
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    So does anyone maybe like, know a friend of a friend of a friends brother's uncle that has ever thrown ms3d models into vertex arrays?
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  11. #11
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    Deleted
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  12. #12
    ---
    Join Date
    May 2004
    Posts
    1,379
    Quote Originally Posted by Shamino
    So does anyone maybe like, know a friend of a friend of a friends brother's uncle that has ever thrown ms3d models into vertex arrays?
    Yeah but he moved to Iceland.

    Seriously though, No.

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You should only need to send the model data to the hardware in some type of meaningful fashion in order to use vertex arrays. A vertex array is nothing more than a normal C buffer containing vertex data.

    I'm not sure what the problem is here?

  14. #14
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    The problem.

    To pinpoint it for you.

    Is that when you simple "throw the data into a c buffer" it isn't enough to perform proper lighting.

    That is with MS3D Models.

    MS3D Models don't repeat vertices/normals/texcoords or ANYTHING.

    And so therefore, you need to unshare all this crap, which is where the problem lies.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Then generate them when you load the file.

    Code:
    void CModel::GenerateNormals(void)
    {
      for (DWORD i=0;i<m_iNumTriangles;i+=3) 
      {
         //Get 3 vertices
         Vector3 a=m_pVerts[i].Pos;
         Vector3 b=m_pVerts[i+1].Pos;
         Vector3 c=m_pVerts[i+2].Pos;
    
         //Compute edge vectors
         Vector3 edge1=b-a;
         Vector3 edge2=c-b;
        
         //Compute cross product of edges
         Vector3 cross=Vector3Cross(&edge1,&edge2);
       
         //Normalize result
         Vector3Normalize(&cross,&cross);
    
         //Store as normal
         m_pVerts.Normal=cross;
       }
    }
    


    This is assuming you have a robust Vector3 class and vector3 functions to perform basic operations on vectors.

    This is a per triangle normal calculation function. A better way is to find all vertices that share edges and then take the average of the normals for those vertices.

    You could also have a class for managing vertex arrays, and a manager class for managing collections of vertex arrays.

    Our setup in the tile engine is as follows:

    CD3DApp
    The base application object - holds pointers to other classes which encapsulate portions of DirectX and Direct3D

    Some planned and current D3D/DX classes:
    CD3DTexture
    CD3DState
    CD3DEffect
    CDXSound
    CDXInput
    CDXKeyboard
    CDXController
    CDXMouse
    CDXShow
    CDXShowWnd


    Engine specific classes which use the above:

    CAnimFrame
    CAnimSeq
    CSprite
    CCutScene
    CMovie
    CGUISys
    CGUIWindow
    - CGUIButton
    - CGUIListbox
    - CGUIEditbox
    - CGUIScrollBar
    CScript
    CTimer
    CInventoryItem
    CInventoryCont
    CGameObject
    - CPlayer
    - CAI



    And many, many more.

    CGameData
    Essentially the game document. This class handles all of the relevant game data.

    CGameView
    This class is responsible for rendering the view based on the data.
    We derive from this to create different types of views.

    CResource
    A dummy class that acts as a core base class from which all resource classes are derived. Allows us to use a vector of CResource objects inside of CResMgr, and yet still be able to place CResource-derived objects in the vector.

    Current resource classes:
    CMemResource
    CTextureResource
    CAnimResource
    CFileResource
    CScriptResource
    CSoundFXResource
    CMusicResource
    CNetworkResource
    CThreadResource


    CResMgr
    Manages a collection of CResource objects

    CGameResMgr
    Manages all of the game's resources. Essentially a collection of CResMgr objects, which are in turn collections of CResources.
    This centralizes access to all of our game resources and also protects them since you must use this class to gain access to them.


    This is an early setup and some of these classes are not yet implemented. I'm sure many many more are on the way.

    Maybe this will help you with your design.
    Last edited by VirtualAce; 06-28-2006 at 05:32 AM.

Popular pages Recent additions subscribe to a feed