Archive

Posts Tagged ‘Ogre’

Ogre: Manually load .obj

April 23rd, 2009 admin No comments

Thought I’d post a bit more code. The following snippet shows you how to manually load data in to a temporary Ogre resource. Keep in mind that this method does not use a resource manager and, as such, lacks the ability to reuse loaded meshes.

Loading the data from an .obj file.

bool mesh::LoadObj(gcroot<System::String ^> fileName){
	Ogre::String name,shortName;
	char buffer[256];
 
	//Convert System::String to Ogre::String.  
	{
		System::IntPtr ip = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(fileName);
		const char *str = static_cast<const char*>(ip.ToPointer());
		name = str;
		System::Runtime::InteropServices::Marshal::FreeHGlobal(ip);
	}
 
	//Get shortname for entity creation 
	{
		int idx = name.find_last_of("\\");
		sprintf(buffer,"%s",name.c_str());
		shortName = name.substr(idx+1,name.size());
 
	}
	System::IO::FileInfo ^file = gcnew System::IO::FileInfo(fileName);
	System::IO::StreamReader ^read = gcnew System::IO::StreamReader(file->Open(System::IO::FileMode::Open,System::IO::FileAccess::Read));
	System::String ^line;
	array<System::Char> ^delims = {' ',','};
 
        //Vertex list
	System::Collections::Generic::List<System::Collections::Generic::List<float>^> ^vList = gcnew System::Collections::Generic::List<System::Collections::Generic::List<float>^>;
        //Face list      System::Collections::Generic::Dictionary<System::String^,System::Collections::Generic::List<face_group^>^> ^fList = gcnew System::Collections::Generic::Dictionary<System::String^,System::Collections::Generic::List<face_group^>^>;
	System::String ^group = gcnew System::String("999999");
	int group_id = -1;
 
	fList->Add("999999",gcnew System::Collections::Generic::List<face_group^>);
 
	int nFace=0, nVtx=0;
 
	//Read obj
	{
		while((line = read->ReadLine()) != nullptr){
			array<System::String^> ^tokens = line->Split(delims);
 
			if(line->Length > 0){
				if(line[0] == 'v'){
					try {
						System::Collections::Generic::List<float>^ tmp = gcnew System::Collections::Generic::List<float>;
						tmp->Add(System::Convert::ToDouble(tokens[1]));
						tmp->Add(System::Convert::ToDouble(tokens[2]));
						tmp->Add(System::Convert::ToDouble(tokens[3]));
 
						vList->Add(tmp);
						nVtx++;
					} catch(...){}
				} else if(line[0] == 'f'){
					face_group ^tmp = gcnew face_group;
					tmp->uid = group;
					tmp->group_id = group_id;
 
					tmp->idxList->Add(System::Convert::ToInt32(tokens[1]));
					tmp->idxList->Add(System::Convert::ToInt32(tokens[2]));
					tmp->idxList->Add(System::Convert::ToInt32(tokens[3]));
 
 
					fList[group]->Add(tmp);
					nFace++;
				} else if(line[0] == '#'){
					//Found new group
					if(tokens[1][0] == 'g'){
						if(!fList->ContainsKey(tokens[3])){
							fList->Add(tokens[3],gcnew System::Collections::Generic::List<face_group^>);
						}
						group = tokens[3];
						group_id = System::Convert::ToInt32(tokens[2]);
					}
				}
			}
		}
		read->Close();
	}//end read obj
 
	tdio_library::point3D_t *vtx = new tdio_library::point3D_t[nVtx];
	tdio_library::Face *face = new tdio_library::Face[nFace];
	_perFaceColor = new ncgColorNode[nFace];
 
	int i = 0;
	int k;
	//printf("Filling vertex array...");
 
	//Fill vertex array and get centroid
	for each(System::Collections::Generic::List<float>^ list in vList){
		k=0;
		for each(float val in list){
			vtx[i].cell[k] = val;
			k++;
		}
		i++;
	}
 
	//printf("Complete.\n");
 
	//printf("Filling Face array...");
	i=0;
	try {
		for each(System::Collections::Generic::KeyValuePair<System::String^,System::Collections::Generic::List<face_group^>^> ^kvp in fList){
 
			for each (face_group ^f in kvp->Value){
//				System::Drawing::Color ^color = Painter::HexColor::FromHex(f->uid);
 
				k=0;
				face[i].verts = new int[3];
				face[i].nverts = 3;
				face[i].intensity = -1;
 
                                //obj files lack color so set default to gray.
				_perFaceColor[i].color.r = 150;
				_perFaceColor[i].color.g = 150;
				_perFaceColor[i].color.b = 150;
 
				for each (int val in f->idxList){
					face[i].verts[k] = val;
					k++;
				}
				i++;
			}
		}
	} catch (System::Exception ^ex){
		//printf("Error: %s\n",ex->Message);
	}
	return BuildOgreMesh(shortName.c_str());
}

Building the Ogre mesh:

bool ncgMesh::BuildOgreMesh(const char *meshName){
	Ogre::String name = meshName;
	tdio_library::point3D_t *vtx = _ply->GetVertices();
	tdio_library::Face *face = _ply->GetFaces();
	int nVtx = _ply->GetNumVertices();
	int nFace = _ply->GetNumFaces();
 
	map<int,Vector3> mNormals = GetNormals(vtx,nVtx,face,nFace);
	MeshPtr mesh = MeshManager::getSingleton().createManual(name + "_manual_mesh","Custom");
	SubMesh *submesh = mesh->createSubMesh(name + "_manual_sub");
 
	//Create OgreMesh
	try
	{
		//printf("Building manual OGRE mesh\n");
 
		submesh->useSharedVertices = false;
		submesh->vertexData = new VertexData();
		submesh->vertexData->vertexStart = 0;
		submesh->vertexData->vertexCount = nVtx + 1;
		VertexDeclaration * dec = submesh->vertexData->vertexDeclaration;
		static const unsigned short source = 0;
		static const unsigned short csource = 1;
		size_t offset = 0;
 
		offset += dec->addElement(source,offset,VET_FLOAT3,VES_POSITION).getSize();
		offset += dec->addElement(source,offset,VET_FLOAT3,VES_NORMAL).getSize();
 
		//printf("  - Assigning Vertex Buffer: ");
		HardwareVertexBufferSharedPtr vbuffer = HardwareBufferManager::getSingleton().createVertexBuffer(dec->getVertexSize(source),\
																										 submesh->vertexData->vertexCount,\
																										 HardwareBuffer::HBU_STATIC_WRITE_ONLY);
 
		//printf("1...");
		float *vdata = static_cast<float*>(vbuffer->lock(HardwareBuffer::HBL_DISCARD));
		AxisAlignedBox aabox;
 
		map<int,Vector3>::const_iterator mIter;
 
		_min = _max = Vector3(vtx[0].x,vtx[0].y,vtx[0].z);
		_centroid = Vector3::ZERO;
 
		//printf("2...");
		for(int i = 0; i < nVtx; i++){
			Vector3 pos(vtx[i].x,vtx[i].y,vtx[i].z);
 
			if(_max.x < vtx[i].x)_max.x = vtx[i].x;
			if(_min.x > vtx[i].x)_min.x = vtx[i].x;
 
			if(_max.y < vtx[i].y)_max.y = vtx[i].y;
			if(_min.y > vtx[i].y)_min.y = vtx[i].y;	
 
			if(_max.z < vtx[i].z)_max.z = vtx[i].z;
			if(_min.z > vtx[i].z)_min.z = vtx[i].z;
 
			_centroid+=pos;
		}
		_centroid /= nVtx;
		//printf("3....");
 
		tdio_library::FaceMapNode *faceMap = _ply->GetFaceMap();
 
		for(int i = 0; i < nVtx; i++){
			Vector3 pos(vtx[i].x,vtx[i].y,vtx[i].z);
 
			*vdata++ = pos.x;
			*vdata++ = pos.y;
			*vdata++ = pos.z;
 
			Vector3 normal = Vector3::ZERO;
			int *ind = faceMap[i].ind;
			int nInd = faceMap[i].n_ind;
			int ct = 0;
			for(int k = 0; k < nInd; k++){
				for(int j = 0; j < 3; j++){
					if(_ply->GetFaces()[ind[k]].verts[j] != i){
						mIter = mNormals.find(_ply->GetFaces()[ind[k]].verts[j]);
						if(mIter != mNormals.end()){
							normal += mNormals[_ply->GetFaces()[ind[k]].verts[j]];
							ct++;
						} else {
							normal += Vector3(1,0,0);
							ct++;
						}
					}
				}
			}
			normal/=ct;
 
 
			*vdata++ = normal.x;
			*vdata++ = normal.y;
			*vdata++ = normal.z;
			aabox.merge(pos);
		}
 
		vbuffer->unlock();
 
		//printf("Complete.\n");
 
		//printf("  - Assigning Face buffer");
		submesh->vertexData->vertexBufferBinding->setBinding(source,vbuffer);
		submesh->indexData->indexStart = 0;
		submesh->indexData->indexCount = nFace * 3;
		submesh->indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(HardwareIndexBuffer::IT_32BIT,submesh->indexData->indexCount,HardwareBuffer::HBU_STATIC_WRITE_ONLY);
 
		//printf("1...");
		uint32 *idata = static_cast<uint32*>(submesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
 
		for(int i = 0; i < nFace; i++){
			*idata++ = face[i].verts[0];
			*idata++ = face[i].verts[1];
			*idata++ = face[i].verts[2];
		}
 
		submesh->indexData->indexBuffer->unlock();
		//printf("Complete.\n");
 
		if(_perFaceColor != NULL){
			//printf("  - Assigning Color buffer");
			offset = 0;
			offset += dec->addElement(csource,offset,VET_COLOUR,VES_DIFFUSE).getSize();
			vbuffer = HardwareBufferManager::getSingleton().createVertexBuffer(offset,\
																			   submesh->vertexData->vertexCount,\
																			   HardwareBuffer::HBU_STATIC_WRITE_ONLY);
			//printf("1...");
 
			RenderSystem *rs = Root::getSingleton().getRenderSystem();
			RGBA *cdata = static_cast<RGBA*>(vbuffer->lock(HardwareBuffer::HBL_DISCARD));
			for(int i = 0; i < nFace; i++){
				int *idx = face[i].verts;
				rs->convertColourValue(ColourValue(_perFaceColor[i].color.r/255.0,_perFaceColor[i].color.g/255.0,_perFaceColor[i].color.b/255.0),(cdata+idx[0]));
				rs->convertColourValue(ColourValue(_perFaceColor[i].color.r/255.0,_perFaceColor[i].color.g/255.0,_perFaceColor[i].color.b/255.0),(cdata+idx[1]));
				rs->convertColourValue(ColourValue(_perFaceColor[i].color.r/255.0,_perFaceColor[i].color.g/255.0,_perFaceColor[i].color.b/255.0),(cdata+idx[2]));
			}
			vbuffer->unlock();
			submesh->vertexData->vertexBufferBinding->setBinding(csource,vbuffer);
			//printf("Complete.\n");
			Ogre::LogManager::getSingleton().logMessage("  - Color buffer loaded");
		} else {
			Ogre::LogManager::getSingleton().logMessage("  - No color buffer to load");
		}
 
		//printf("  - Create mesh boundaries...");
		mesh->_setBounds(aabox);
		mesh->_setBoundingSphereRadius( (aabox.getMaximum()-aabox.getMinimum()).length() / 2.0);
		//printf("Complete.\n");
 
                //Create a customcolour in your own material scripts.  Mine sets ambient and diffuse
                //colors to use whatever was entered manually.
		submesh->setMaterialName("CustomColour");
		//printf("  - Loading finalized mesh...");
		mesh->load();
		//printf("Complete.\n");
 
		//printf("  - Attaching completed mesh to node...");
		Entity *ent = NULL;
		Ogre::SceneManager *_sceneMgr = Ogre::Root::getSingleton ().getSceneManager ("ExampleSMInstance"); 
		try {
			ent = _sceneMgr->getEntity(name + "_manual_ent");
		} catch (...) {
			ent = _sceneMgr->createEntity(name + "_manual_ent",name + "_manual_mesh");
		}
 
		try {
			ent->setCastShadows(false);
			_viewNode->attachObject(ent);
		} catch (Ogre::Exception &ex) {
			//printf("Error[%s at %d]: %s\n",__FILE__,__LINE__,ex.getDescription().c_str());
		}
		_sId = name;
		_bEmpty = false;
		//printf("Complete.\n");
 
		//printf("Reticulating splines.... Complete!\n"); // :) 
	}catch (Ogre::Exception& e){
		//printf("Problem creating ogre mesh: %s\n",e.getDescription().c_str());
		return false;	
	}
	return true;
}

Ogre Custom Color Script (CustomColour.material):

material CustomColour
{
          technique
          {
                    pass
                    {
                         ambient vertexcolour
                         diffuse vertexcolour
                         cull_hardware none
                         shading phong
                     }
           }
}

Excuse my code blocks, the script I use to bound them in to a smaller viewing area is borked atm. I’ll fix it when I get a free minute.

There are a couple of custom data types in there like point3D_t and face. Point3D contains 3 floats: x,y,z. Face contains an array of indices, in this case we assume only 3 as we area loading triangles. There are plenty of libraries out there for handling this type of data, the main focus of my post was the BuildOgreMesh function.

I’ve included an example that also loads color on a per-face basis. In order to make use of this color I had to create a CustomColour material script for ogre. My basic .obj loader doesn’t handle color from a material file, I’ll leave that to the reader. My loader assigns a generic gray color to all the faces.

Categories: Coding Tags: