Help those suffering in the Horn of Africa

ResModel Lib Source

Prev: Header Next: Requirements
 
/** ----------------------------------------------------------
 * \class VSResModelLib
 *
 * Lighthouse3D
 *
 * VSResModelLib - Very Simple Resource Model Library
 *
 * \version 0.2.0
 *		Added cubemap textures
 *		Added the possibility to reuse textures
 *		Loader gets tangent and bitangent attributes
 *
 * version 0.1.0
 *		Initial Release
 *
 * This lib provides an interface for Assimp to load and render 3D models
 *  and performs simple resource managment
 *
 * This lib requires the following classes from VSL:
 * (http://www.lighthouse3d.com/very-simple-libs)
 *
 * VSResourceLib
 * VSMathLib
 * VSLogLib
 * VSShaderLib
 *
 * and the following third party libs:
 *
 * GLEW (http://glew.sourceforge.net/),
 * Assimp (http://assimp.sourceforge.net/)
 *
 * Full documentation at
 * http://www.lighthouse3d.com/very-simple-libs
 *
 ---------------------------------------------------------------*/

#include "vsResModelLib.h"

VSResModelLib::VSResModelLib():pScene(0), pUseAdjacency(false)
{
	mMyMeshes.reserve(10);
	pMyMeshesAux.reserve(10);
}

VSResModelLib::~VSResModelLib() {

	for (unsigned int i = 0; i < mMyMeshes.size(); ++i) {

		glDeleteVertexArrays(1,&(mMyMeshes[i].vao));
		for (int t = 0; t < MAX_TEXTURES; ++t ) {
			glDeleteTextures(1,&(mMyMeshes[i].texUnits[t]));
		}
		glDeleteBuffers(1,&(mMyMeshes[i].uniformBlockIndex));
	}
	mMyMeshes.clear();
}

void
VSResModelLib::clone(VSResourceLib *res) {

	if (res == NULL)
		return;

	VSResModelLib *r = (VSResModelLib *)res;

	this->mMyMeshes = r->mMyMeshes;
}

bool
VSResModelLib::load(std::string filename) {

	Assimp::Importer importer;

	//check if file exists
	std::ifstream fin(filename.c_str());
	if(!fin.fail())
		fin.close();
	else {
		VSLOG(mLogError, "Unable to open file %s",
							filename.c_str());
		return false;
	}

	pScene = importer.ReadFile( filename,
					aiProcessPreset_TargetRealtime_Quality);

	// If the import failed, report it
	if( !pScene) {
		VSLOG(mLogError, "Failed to import %s",
					filename.c_str());
		return false;
	}
	// Get prefix for texture loading
	size_t index = filename.find_last_of("/\\");
	std::string prefix = filename.substr(0, index+1);
	pTextureIdMap.clear();
	bool result = loadTextures(pScene, prefix);

	genVAOsAndUniformBuffer(pScene);

	// determine bounding box
	struct aiVector3D min, max;
	min.x = min.y = min.z =  1e10f;
	max.x = max.y = max.z = -1e10f;

	mVSML->loadIdentity(VSMathLib::AUX0);
	get_bounding_box_for_node(pScene->mRootNode,&min,&max);

	float tmp;
	tmp = max.x - min.x;
	tmp = max.y - min.y > tmp? max.y - min.y:tmp;
	tmp = max.z - min.z > tmp? max.z - min.z:tmp;

	mScaleToUnitCube = 2.0f / tmp;

	mCenter[0] = min.x + (max.x - min.x) * 0.5f;
	mCenter[1] = min.y + (max.y - min.y) * 0.5f;
	mCenter[2] = min.z + (max.z - min.z) * 0.5f;

	mVSML->loadIdentity(VSMathLib::AUX0);
	mVSML->scale(VSMathLib::AUX0, mScaleToUnitCube, mScaleToUnitCube, mScaleToUnitCube);
	mVSML->translate(VSMathLib::AUX0, -mCenter[0], -mCenter[1], -mCenter[2]);

	for (unsigned int i = 0; i < mMyMeshes.size(); ++i) {

		mVSML->pushMatrix(mVSML->AUX0);

		mVSML->multMatrix(mVSML->AUX0, mMyMeshes[i].transform);
		memcpy(mMyMeshes[i].transform, mVSML->get(mVSML->AUX0), sizeof(float)*16);

		mVSML->popMatrix(mVSML->AUX0);
	}

	// clear texture map
	pTextureIdMap.clear();

	return result;
}

void
VSResModelLib::render () {

	// we are only going to use texture unit 0

	mVSML->pushMatrix(VSMathLib::MODEL);
	//mVSML->scale(mScaleToUnitCube, mScaleToUnitCube, mScaleToUnitCube);
	//mVSML->translate(-mCenter[0], -mCenter[1], -mCenter[2]);
	for (unsigned int i = 0; i < mMyMeshes.size(); ++i) {

		mVSML->pushMatrix(VSMathLib::MODEL);
		mVSML->multMatrix(VSMathLib::MODEL,
								mMyMeshes[i].transform);
		// send matrices to shaders
		mVSML->matricesToGL();

		// set material
		setMaterial(mMyMeshes[i].mat);

		// bind texture
		for (unsigned int j = 0; j < VSResourceLib::MAX_TEXTURES; ++j) {
			if (mMyMeshes[i].texUnits[j] != 0) {
				glActiveTexture(GL_TEXTURE0 + j);
				glBindTexture(mMyMeshes[i].texTypes[j],
						mMyMeshes[i].texUnits[j]);
			}
		}
		// bind VAO
		glBindVertexArray(mMyMeshes[i].vao);
		glDrawElements(mMyMeshes[i].type,
			mMyMeshes[i].numIndices*2, GL_UNSIGNED_INT, 0);

		for (unsigned int j = 0; j < VSResourceLib::MAX_TEXTURES; ++j) {
			if (mMyMeshes[i].texUnits[j] != 0) {
				glActiveTexture(GL_TEXTURE0 + j);
				glBindTexture(mMyMeshes[i].texTypes[j], 0);
			}
		}

		mVSML->popMatrix(VSMathLib::MODEL);
	}
	glBindVertexArray(0);
	mVSML->popMatrix(VSMathLib::MODEL);
}

// Load model textures
bool
VSResModelLib::loadTextures(const aiScene* scene,
							std::string prefix)
{
	VSLOG(mLogInfo, "Loading Textures from %s",
						prefix.c_str());

	/* scan scene's materials for textures */
	for (unsigned int m=0; mmNumMaterials; ++m)
	{
		int texIndex = 0;
		aiString path;	// filename

		aiReturn texFound =
			scene->mMaterials[m]->
			GetTexture(aiTextureType_DIFFUSE, texIndex, &path);

		while (texFound == AI_SUCCESS) {
			//fill map with textures, OpenGL image ids set to 0
			pTextureIdMap[path.data] = 0;
			// more textures?
			texIndex++;
			texFound =
				scene->mMaterials[m]->
				GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
		}
	}

	int numTextures = pTextureIdMap.size();

	/* get iterator */
	std::map::iterator itr =
							pTextureIdMap.begin();

	for (int i= 0; itr != pTextureIdMap.end(); ++i, ++itr)
	{
		// get filename
		std::string filename = (*itr).first;
		filename = prefix + filename;
		// save texture id for filename in map
		(*itr).second = loadRGBATexture(filename, true,true);
		VSLOG(mLogInfo, "Texture %s loaded with name %d",
			filename.c_str(), (int)(*itr).second);
	}

	return true;
}

void
VSResModelLib::genVAOsAndUniformBuffer(const struct aiScene *sc) {

	struct MyMesh aMesh;
	struct Material aMat;
	GLuint buffer;
	int totalTris = 0;
	unsigned int *adjFaceArray;

	VSLOG(mLogInfo, "Number of Meshes: %d",sc->mNumMeshes);
	// For each mesh
	for (unsigned int n = 0; n < sc->mNumMeshes; ++n)
	{
		const struct aiMesh* mesh = sc->mMeshes[n];

		if (mesh->mPrimitiveTypes != 4) {
			aMesh.numIndices = 0;
			pMyMeshesAux.push_back(aMesh);
			continue;
		}

		VSLOG(mLogInfo, "Mesh[%d] Triangles %d",n,
							mesh->mNumFaces);
		totalTris += mesh->mNumFaces;
		// create array with faces
		// have to convert from Assimp format to array
		unsigned int *faceArray;
		faceArray = (unsigned int *)malloc(
				sizeof(unsigned int) * mesh->mNumFaces * 3);
		unsigned int faceIndex = 0;

		for (unsigned int t = 0; t < mesh->mNumFaces; ++t) {
			const struct aiFace* face = &mesh->mFaces[t];

			memcpy(&faceArray[faceIndex], face->mIndices,
								3 * sizeof(unsigned int));
			faceIndex += 3;
		}

		if (pUseAdjacency) {
			// Create the half edge structure
			std::map, struct HalfEdge *> myEdges;
			struct HalfEdge *edge;

			// fill it up with edges. twin info will be added latter
			edge = (struct HalfEdge *)malloc(sizeof(struct HalfEdge) * mesh->mNumFaces * 3);
			for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {

				edge[i*3].vertex = faceArray[i*3+1];
				edge[i*3+1].vertex = faceArray[i*3+2];
				edge[i*3+2].vertex = faceArray[i*3];

				edge[i*3].next = &edge[i*3+1];
				edge[i*3+1].next = &edge[i*3+2];
				edge[i*3+2].next = &edge[i*3];

				myEdges[std::pair(faceArray[i*3+2],faceArray[i*3])] = &edge[i*3];
				myEdges[std::pair(faceArray[i*3],faceArray[i*3+1])] = &edge[i*3+1];
				myEdges[std::pair(faceArray[i*3+1],faceArray[i*3+2])] = &edge[i*3+2];
			}

			// add twin info
			std::map, struct HalfEdge *>::iterator iter;
			std::pair edgeIndex, twinIndex;

			iter = myEdges.begin();

			for (; iter != myEdges.end(); ++iter) {

				edgeIndex = iter->first;
				twinIndex = std::pair(edgeIndex.second, edgeIndex.first);

				if (myEdges.count(twinIndex))
					iter->second->twin = myEdges[twinIndex];
				else
					iter->second->twin = NULL;
			}

			adjFaceArray = (unsigned int *)malloc(sizeof(unsigned int) * mesh->mNumFaces * 6);
			for (unsigned int i = 0; i < mesh->mNumFaces; i++) {

				// NOTE: twin may be null
				adjFaceArray[i*6]   = edge[3*i + 0].next->vertex;
				adjFaceArray[i*6+1] = edge[3*i + 0].twin?edge[3*i + 0].twin->vertex:edge[3*i + 0].next->vertex;

				adjFaceArray[i*6+2] = edge[3*i + 1].next->vertex;
				adjFaceArray[i*6+3] = edge[3*i + 1].twin?edge[3*i + 1].twin->vertex:edge[3*i + 1].next->vertex;

				adjFaceArray[i*6+4] = edge[3*i + 2].next->vertex;
				adjFaceArray[i*6+5] = edge[3*i + 2].twin?edge[3*i + 2].twin->vertex:edge[3*i + 2].next->vertex;

			}
		}
		//printf("\n");
		//for (int i = 0; i < mesh->mNumFaces * 3; ++i)
		//	printf("%d ", faceArray[i]);
		//printf("\n");
		//for (int i = 0; i < mesh->mNumFaces * 6; ++i)
		//	printf("%d ", adjFaceArray[i]);
		//printf("\n");

		aMesh.numIndices = sc->mMeshes[n]->mNumFaces * 3;

		// generate Vertex Array for mesh
		glGenVertexArrays(1,&(aMesh.vao));
		glBindVertexArray(aMesh.vao);

		// buffer for faces
		glGenBuffers(1, &buffer);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);

		if (pUseAdjacency) {
			glBufferData(GL_ELEMENT_ARRAY_BUFFER,
						sizeof(unsigned int) * mesh->mNumFaces * 6,
						adjFaceArray, GL_STATIC_DRAW);
			free(adjFaceArray);
		}
		else
			glBufferData(GL_ELEMENT_ARRAY_BUFFER,
						sizeof(unsigned int) * mesh->mNumFaces * 3,
						faceArray, GL_STATIC_DRAW);

		free(faceArray);

		// buffer for vertex positions
		if (mesh->HasPositions()) {
			glGenBuffers(1, &buffer);
			glBindBuffer(GL_ARRAY_BUFFER, buffer);
			glBufferData(GL_ARRAY_BUFFER,
				sizeof(float)*3*mesh->mNumVertices,
				mesh->mVertices, GL_STATIC_DRAW);
			glEnableVertexAttribArray(
					VSShaderLib::VERTEX_COORD_ATTRIB);
			glVertexAttribPointer(VSShaderLib::VERTEX_COORD_ATTRIB,
					3, GL_FLOAT, 0, 0, 0);
		}

		// buffer for vertex normals
		if (mesh->HasNormals()) {
			glGenBuffers(1, &buffer);
			glBindBuffer(GL_ARRAY_BUFFER, buffer);
			glBufferData(GL_ARRAY_BUFFER,
				sizeof(float)*3*mesh->mNumVertices, mesh->mNormals,
				GL_STATIC_DRAW);
			glEnableVertexAttribArray(VSShaderLib::NORMAL_ATTRIB);
			glVertexAttribPointer(VSShaderLib::NORMAL_ATTRIB,
				3, GL_FLOAT, 0, 0, 0);
		}

		// buffer for vertex normals
		if (mesh->HasTangentsAndBitangents()) {
			glGenBuffers(1, &buffer);
			glBindBuffer(GL_ARRAY_BUFFER, buffer);
			glBufferData(GL_ARRAY_BUFFER,
				sizeof(float)*3*mesh->mNumVertices, mesh->mTangents,
				GL_STATIC_DRAW);
			glEnableVertexAttribArray(VSShaderLib::TANGENT_ATTRIB);
			glVertexAttribPointer(VSShaderLib::TANGENT_ATTRIB,
				3, GL_FLOAT, 0, 0, 0);
		}

		// buffer for vertex normals
		if (mesh->HasTangentsAndBitangents()) {
			glGenBuffers(1, &buffer);
			glBindBuffer(GL_ARRAY_BUFFER, buffer);
			glBufferData(GL_ARRAY_BUFFER,
				sizeof(float)*3*mesh->mNumVertices, mesh->mBitangents,
				GL_STATIC_DRAW);
			glEnableVertexAttribArray(VSShaderLib::BITANGENT_ATTRIB);
			glVertexAttribPointer(VSShaderLib::BITANGENT_ATTRIB,
				3, GL_FLOAT, 0, 0, 0);
		}

		// buffer for vertex texture coordinates
		if (mesh->HasTextureCoords(0)) {
			float *texCoords = (float *)malloc(
						sizeof(float)*2*mesh->mNumVertices);
			for (unsigned int k = 0; k < mesh->mNumVertices; ++k) {

				texCoords[k*2]   = mesh->mTextureCoords[0][k].x;
				texCoords[k*2+1] = mesh->mTextureCoords[0][k].y; 

			}
			glGenBuffers(1, &buffer);
			glBindBuffer(GL_ARRAY_BUFFER, buffer);
			glBufferData(GL_ARRAY_BUFFER,
				sizeof(float)*2*mesh->mNumVertices, texCoords,
				GL_STATIC_DRAW);
			glEnableVertexAttribArray(
				VSShaderLib::TEXTURE_COORD_ATTRIB);
			glVertexAttribPointer(
				VSShaderLib::TEXTURE_COORD_ATTRIB, 2,
				GL_FLOAT, 0, 0, 0);
			free(texCoords);
		}

		// unbind buffers
		glBindVertexArray(0);
		glBindBuffer(GL_ARRAY_BUFFER,0);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);

		// create material uniform buffer
		struct aiMaterial *mtl =
			sc->mMaterials[mesh->mMaterialIndex];

		aiString texPath;	//contains filename of texture

		for (int j = 0; j < VSResourceLib::MAX_TEXTURES; ++j)
			aMesh.texUnits[j] = 0;

		if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE,
											0, &texPath)){
				//bind texture
				aMesh.texUnits[0] =
								pTextureIdMap[texPath.data];
				aMesh.texTypes[0] = GL_TEXTURE_2D;
				aMat.texCount = 1;
			}
		else {
			aMesh.texUnits[0] = 0;
			aMat.texCount = 0;
		}

		float c[4];
		set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
		aiColor4D diffuse;
		if(AI_SUCCESS == aiGetMaterialColor(mtl,
							AI_MATKEY_COLOR_DIFFUSE, &diffuse))
			color4_to_float4(&diffuse, c);
		memcpy(aMat.diffuse, c, sizeof(c));

		set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
		aiColor4D ambient;
		if(AI_SUCCESS == aiGetMaterialColor(mtl,
							AI_MATKEY_COLOR_AMBIENT, &ambient))
			color4_to_float4(&ambient, c);
		memcpy(aMat.ambient, c, sizeof(c));

		set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
		aiColor4D specular;
		if(AI_SUCCESS == aiGetMaterialColor(mtl,
							AI_MATKEY_COLOR_SPECULAR, &specular))
			color4_to_float4(&specular, c);
		memcpy(aMat.specular, c, sizeof(c));

		set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
		aiColor4D emission;
		if(AI_SUCCESS == aiGetMaterialColor(mtl,
							AI_MATKEY_COLOR_EMISSIVE, &emission))
			color4_to_float4(&emission, c);
		memcpy(aMat.emissive, c, sizeof(c));

		float shininess = 0.0;
		unsigned int max;
		aiGetMaterialFloatArray(mtl,
							AI_MATKEY_SHININESS, &shininess, &max);
		aMat.shininess = shininess;

		aMesh.mat = aMat;

		pMyMeshesAux.push_back(aMesh);
	}

	mVSML->loadIdentity(VSMathLib::AUX0);
	recursive_walk_for_matrices(sc, sc->mRootNode);

	pMyMeshesAux.clear();

	VSLOG(mLogInfo, "Total Meshes: %d  | Faces: %d",
					sc->mNumMeshes, totalTris);
}

#define aisgl_min(x,y) (xx?y:x)

void
VSResModelLib::get_bounding_box_for_node (const struct aiNode* nd,
	struct aiVector3D* min,
	struct aiVector3D* max)

{
	unsigned int n = 0;

	mVSML->pushMatrix(VSMathLib::AUX0);

	if (nd->mNumMeshes) {

		// Get node transformation matrix
		struct aiMatrix4x4 m = nd->mTransformation;

		// OpenGL matrices are column major
		m.Transpose();

		// apply node transformation
		float aux[16];
		memcpy(aux,&m,sizeof(float) * 16);
		mVSML->multMatrix(VSMathLib::AUX0, aux);

		for (; n < nd->mNumMeshes; ++n) {
			const struct aiMesh* mesh =
							pScene->mMeshes[nd->mMeshes[n]];
			for (unsigned int t = 0; t < mesh->mNumVertices; ++t) {

				struct aiVector3D tmp = mesh->mVertices[t];
				float a[4], res[4];

				a[0] = tmp.x;
				a[1] = tmp.y;
				a[2] = tmp.z;
				a[3] = 1.0f;

				mVSML->multMatrixPoint(VSMathLib::AUX0, a, res);

				min->x = aisgl_min(min->x,res[0]);
				min->y = aisgl_min(min->y,res[1]);
				min->z = aisgl_min(min->z,res[2]);

				max->x = aisgl_max(max->x,res[0]);
				max->y = aisgl_max(max->y,res[1]);
				max->z = aisgl_max(max->z,res[2]);
			}
		}
	}

	for (n = 0; n < nd->mNumChildren; ++n) {
		get_bounding_box_for_node(nd->mChildren[n],min,max);
	}

	mVSML->popMatrix(VSMathLib::AUX0);
}

void
VSResModelLib::recursive_walk_for_matrices(
			const struct aiScene *sc,
			const struct aiNode* nd) {

	mVSML->pushMatrix(VSMathLib::AUX0);
	if (nd->mNumMeshes)
	{
		// Get node transformation matrix
		struct aiMatrix4x4 m = nd->mTransformation;
		// OpenGL matrices are column major
		m.Transpose();
		// save model matrix and apply node transformation
		float aux[16];
		memcpy(aux,&m,sizeof(float) * 16);
		mVSML->multMatrix(VSMathLib::AUX0, aux);

		// get matrices for all meshes assigned to this node
		for (unsigned int n = 0; n < nd->mNumMeshes; ++n) {

			if(sc->mMeshes[nd->mMeshes[n]]->mPrimitiveTypes == 4) {

				struct MyMesh aMesh;
				memcpy(&aMesh, &(pMyMeshesAux[nd->mMeshes[n]]),
											sizeof (aMesh));
				memcpy(aMesh.transform,mVSML->get(VSMathLib::AUX0),
											sizeof(float)*16);
				aMesh.type = GL_TRIANGLES;
				mMyMeshes.push_back(aMesh);
			}
		}
	}
	// recurse for all children
	for (unsigned int n=0; n < nd->mNumChildren; ++n){
		recursive_walk_for_matrices(sc, nd->mChildren[n]);
	}
	mVSML->popMatrix(VSMathLib::AUX0);
}

void
VSResModelLib::setTexture(unsigned int unit, unsigned int textureID, GLenum textureType) {

	for (unsigned int i = 0; i < mMyMeshes.size(); ++i) {
		mMyMeshes[i].texUnits[unit] = textureID;
		mMyMeshes[i].texTypes[unit] = textureType;
		mMyMeshes[i].mat.texCount = 1;
	}
}

void
VSResModelLib::addTexture(unsigned int unit, std::string filename) {

	int textID = loadRGBATexture(filename, true);
	for (unsigned int i = 0; i < mMyMeshes.size(); ++i) {
		mMyMeshes[i].texUnits[unit] = textID;
		mMyMeshes[i].texTypes[unit] = GL_TEXTURE_2D;
		mMyMeshes[i].mat.texCount = 1;
	}
}

void
VSResModelLib::addCubeMapTexture(unsigned int unit, std::string posX, std::string negX,
									std::string posY, std::string negY,
									std::string posZ, std::string negZ) {

	int textID = loadCubeMapTexture(posX, negX, posY, negY, posZ, negZ);
	for (unsigned int i = 0; i < mMyMeshes.size(); ++i) {
		mMyMeshes[i].texUnits[unit] = textID;
		mMyMeshes[i].texTypes[unit] = GL_TEXTURE_CUBE_MAP;
		mMyMeshes[i].mat.texCount = 1;
	}
}

// Auxiliary functions to convert Assimp data to float arays
void
VSResModelLib::set_float4(float f[4],
					float a, float b, float c, float d)
{
	f[0] = a;
	f[1] = b;
	f[2] = c;
	f[3] = d;
}

// Auxiliary functions to convert Assimp data to float arays
void
VSResModelLib::color4_to_float4(const struct aiColor4D *c,
					float f[4])
{
	f[0] = c->r;
	f[1] = c->g;
	f[2] = c->b;
	f[3] = c->a;
}

 

Prev: Header Next: Requirements
 

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2012 Lighthouse3d.com Suffusion theme by Sayontan Sinha