コード例 #1
0
ShapePointer SceneLoader::readShape(const QDomElement &element) const {
  QString shapeType;
  if (!readAttributeAsString(element, "type", shapeType)) {
    return ShapePointer(NULL);
  }

  MaterialPointer shapeMaterial = readMaterial(element);
  if (shapeMaterial == NULL) {
    return ShapePointer(NULL);
  }

  if (shapeType == "plane") {
    return readPlane(element, shapeMaterial);
  }
  if (shapeType == "sphere") {
    return readSphere(element, shapeMaterial);
  }
  if (shapeType == "cylinder") {
    return readCylinder(element, shapeMaterial);
  }
  if (shapeType == "cone") {
    return readCone(element, shapeMaterial);
  }
  if (shapeType == "triangle") {
    return readTriangle(element, shapeMaterial);
  }
  if (shapeType == "box") {
    return readBox(element, shapeMaterial);
  }
  if (shapeType == "torus") {
    return readTorus(element, shapeMaterial);
  }
  if (shapeType == "model") {
    return readMeshModel(element, shapeMaterial);
  }

  std::cerr << "Scene parsing error: unknown shape type '" << shapeType.toUtf8().constData() << "'" << std::endl;
  return ShapePointer(NULL);
}
コード例 #2
0
ファイル: LoaderDFF.cpp プロジェクト: bisclavret/openrw
void LoaderDFF::readMaterialList(GeometryPtr &geom, const RWBStream &stream) {
    auto listStream = stream.getInnerStream();

    auto listStructID = listStream.getNextChunk();
    if (listStructID != CHUNK_STRUCT) {
        throw DFFLoaderException("MaterialList missing struct chunk");
    }

    unsigned int numMaterials = *(std::uint32_t *)listStream.getCursor();

    geom->materials.reserve(numMaterials);

    RWBStream::ChunkID chunkID;
    while ((chunkID = listStream.getNextChunk())) {
        switch (chunkID) {
            case CHUNK_MATERIAL:
                readMaterial(geom, listStream);
                break;
            default:
                break;
        }
    }
}
コード例 #3
0
void ModelBinaryLoader::loadBinaryFile(std::string p_FilePath)
{
	clearData();
	std::ifstream input(p_FilePath, std::istream::in | std::istream::binary);
	if(!input)
	{
		throw std::runtime_error("File could not be opened: " + p_FilePath);
	}
	m_FileHeader = readHeader(&input);
	m_Material = readMaterial(m_FileHeader.m_NumMaterial,&input);
	if(m_FileHeader.m_Animated)
	{
		m_AnimationVertexBuffer = readVertexBufferAnimation(m_FileHeader.m_NumVertex, &input);
		calculateBoundingVolume(m_AnimationVertexBuffer);
	}
	else
	{
		m_VertexBuffer = readVertexBuffer(m_FileHeader.m_NumVertex, &input);
		calculateBoundingVolume(m_VertexBuffer);
	}
	m_MaterialBuffer = readMaterialBuffer(m_FileHeader.m_NumMaterialBuffer, &input);
	
}
コード例 #4
0
ファイル: MyPlug.cpp プロジェクト: 50059021/wodegongjubao
bool readMaterial(iModelData * pModelData, const std::string& strFilename)
{
	IOReadBase* pRead = IOReadBase::autoOpen(strFilename);
	if (pRead==NULL)
	{
		return false;
	}
	std::string strCommand;
	std::vector<std::string> setWords;
	while (!pRead->IsEof())
	{
		strCommand = getLineCommand(pRead,setWords);
		if ("material"==strCommand)
		{
			if (setWords.size()>0)
			{
				CMaterial& material = pModelData->getMaterial(setWords[0].c_str());
				readMaterial(material,pRead,strFilename);
			}
		}
	}
	IOReadBase::autoClose(pRead);
	return true;
}
コード例 #5
0
int readMaterialList()
{
	printInt(7);
	printInt(2);

	//Placeholder for length
	printBytes("\xFF\xFF\xFF\xFF", 4);

	int beginningOffset = ftell(output);


	openBracket();

	int matCount = readInt("*MATERIAL_COUNT");

	printInt(matCount);

	int  i;
	for (i = 0; i < matCount; i++)
	{
		readMaterial();
	}

	closeBracket();

	int endOffset = ftell(output);
	int size = endOffset - beginningOffset;

	fseek(output, beginningOffset - 4, 0);
	
	printInt(size);

	fseek(output, endOffset, 0);

	return matCount;
}
コード例 #6
0
void ModelBinaryLoader::loadBinaryFromMemory(const char* p_Data, uint32_t p_DataLen)
{
	clearData();
	typedef boost::iostreams::basic_array_source<char> Device;
	boost::iostreams::stream_buffer<Device> buffer(p_Data, p_DataLen);
	std::istream input(&buffer);
	if(!input)
	{
		throw std::runtime_error("Memory stream could not be created");
	}
	m_FileHeader = readHeader(&input);
	m_Material = readMaterial(m_FileHeader.m_NumMaterial,&input);
	if(m_FileHeader.m_Animated)
	{
		m_AnimationVertexBuffer = readVertexBufferAnimation(m_FileHeader.m_NumVertex, &input);
		calculateBoundingVolume(m_AnimationVertexBuffer);
	}
	else
	{
		m_VertexBuffer = readVertexBuffer(m_FileHeader.m_NumVertex, &input);
		calculateBoundingVolume(m_VertexBuffer);
	}
	m_MaterialBuffer = readMaterialBuffer(m_FileHeader.m_NumMaterialBuffer, &input);
}
コード例 #7
0
ファイル: objLoader.cpp プロジェクト: saitej09/objviewer
void loadInput(Object *obj)
{
	// load the OBJ file here
	char buffer[MAX_BUFFER_SIZE];
	bool has_normal=false, has_texture = false,has_group = false;  
	string matlFileName;
	
	
	while(cin.getline(buffer, MAX_BUFFER_SIZE)!=0)
	{
		stringstream ss(buffer);
		std::string line = ss.str();
		
		// empty line stream
		if(line.empty()){
			continue; //skip blank lines
			//cout<<"this is empty"<<endl;
		}
		trimline(line);
		std::vector < std::string > values = splitline(line, ' ');
		Vector3f v;
		
		//skipping comments
		if(values[0] == "#"){
			continue;
		}
		
		//mtlib file name
		
		else if(values[0] == "mtllib"){
			matlFileName = values[1];
			readMaterial(matlFileName);
			continue;
		}
		
		//groups / objects
		else if(values[0] == "g"){
			has_group = true;
			obj = new Object;
			
			Model.push_back(obj);
			
			obj->obj_name = values[1];
			
			
		}
		//usemtl 
		
		//Reading Vertices
		
		else if(values[0] == "v"){
			//std::cout << "Reading Vertices :" << '\t' << values[1] << '\t'<< values[2]<< '\t' <<
		//values[3]
		//<< std::endl;
			v[0] = std::atof(values[1].c_str());
			v[1] = std::atof(values[2].c_str());
			v[2] = std::atof(values[3].c_str());
			
			if(v[0]<xmin)
				xmin=v[0];
			if(v[0]>xmax)
				xmax=v[0];

			if(v[1]<ymin)
				ymin=v[1];
			if(v[1]>ymax)
				ymax=v[1];

			if(v[2]<zmin)
				zmin=v[2];
			if(v[2]>zmax)
				zmax=v[2];
			//cout << v[0] << endl;
			vecv.push_back(v);
			
			//cout << obj->vecv[0] << endl;
		}
		
		//Reading texture
		else if(values[0] == "vt"){
			
			has_texture = true;
			if(values.size() == 4){
				v[0] = std::atof(values[1].c_str());
				v[1] = std::atof(values[2].c_str());
				v[2] = std::atof(values[3].c_str());
			}
			else if(values.size() == 3){
				v[0] = std::atof(values[1].c_str());
				v[1] = std::atof(values[2].c_str());
				v[2] = 0;
			}
			else{
				v[0] = std::atof(values[1].c_str());
				v[1] = 0;
				v[2] = 0;
			}
			
			
			vec_tex.push_back(v);
			
		}
		
		//Reading Normals
		else if(values[0] == "vn"){
			has_normal = true;
			if(values.size() == 4){
				v[0] = std::atof(values[1].c_str());
				v[1] = std::atof(values[2].c_str());
				v[2] = std::atof(values[3].c_str());
			//	std::cout << "Reading Vertex Normal :" << '\t'<< values[1] << '\t'<< values[2]<< '\t' <<
		//values[3]
		//<< std::endl;
			}
			// for less than two  need to add
			vecn.push_back(v);
		}
		
		//Reading Faces (f v/vt/vn v/vt/vn v/vt/vn v/vt/vn  a/b/c d/e/f g/h/i)
		else if(values[0] == "f"){
			//for triangles 
			if(!has_group){
				obj = new Object;
				Model.push_back(obj);
				obj->obj_name = "group_default";
				has_group = true;
			}
			vector<unsigned> face;
			//with both texture and normal variables
			if(has_normal == true && has_texture == true){
				vector<string> flds1 = splitline(values[1],'/');
				vector<string> flds2 = splitline(values[2],'/');
				vector<string> flds3 = splitline(values[3],'/');
				face.push_back(atoi(flds1[0].c_str()));face.push_back(atoi(flds1[1].c_str()));face.push_back(atoi(flds1[2].c_str()));
				face.push_back(atoi(flds2[0].c_str()));face.push_back(atoi(flds2[1].c_str()));face.push_back(atoi(flds2[2].c_str()));
				face.push_back(atoi(flds3[0].c_str()));face.push_back(atoi(flds3[1].c_str()));face.push_back(atoi(flds3[2].c_str()));
				
				
				obj->vecf.push_back(face);
				//cout << "1st loop" <<endl;
			}
			else if(has_normal == true && has_texture == false){    //b e and h are absent 
				//cout << "2nd loop" <<endl;
				vector<string> flds1 = splitline(values[1],'/');
				vector<string> flds2 = splitline(values[2],'/');
				vector<string> flds3 = splitline(values[3],'/');
				//cout << flds1.size() << endl;
				face.push_back(atoi(flds1[0].c_str()));face.push_back(0);face.push_back(atoi(flds1[1].c_str()));
				face.push_back(atoi(flds2[0].c_str()));face.push_back(0);face.push_back(atoi(flds2[1].c_str()));
				face.push_back(atoi(flds3[0].c_str()));face.push_back(0);face.push_back(atoi(flds3[1].c_str()));
				obj->vecf.push_back(face);
				
			}
			else if(has_normal == false && has_texture == true){    //c f and i are absent
				
				vector<string> flds1 = splitline(values[1],'/');
				vector<string> flds2 = splitline(values[2],'/');
				vector<string> flds3 = splitline(values[3],'/');
				face.push_back(atoi(flds1[0].c_str()));face.push_back(atoi(flds1[1].c_str()));face.push_back(0);
				face.push_back(atoi(flds2[0].c_str()));face.push_back(atoi(flds2[1].c_str()));face.push_back(0);
				face.push_back(atoi(flds3[0].c_str()));face.push_back(atoi(flds3[1].c_str()));face.push_back(0);
				obj->vecf.push_back(face);
				//cout << "3rd loop" <<endl;
			}
			
			else if(has_normal == false && has_texture == false){
				
				std::size_t found = values[1].find_first_of('/');
				if(found != std::string::npos){
					vector<string> flds1 = splitline(values[1],'/');
					vector<string> flds2 = splitline(values[2],'/');
					vector<string> flds3 = splitline(values[3],'/');
					face.push_back(atoi(flds1[0].c_str()));face.push_back(0);face.push_back(0);
					face.push_back(atoi(flds2[0].c_str()));face.push_back(0);face.push_back(0);
					face.push_back(atoi(flds3[0].c_str()));face.push_back(0);face.push_back(0);
					obj->vecf.push_back(face);
				}
				else{
					//cout << values[1] << '\t' << values[2] << '\t' << values[3] << endl;
					
					face.push_back(atoi(values[1].c_str()));face.push_back(atoi("0"));face.push_back(atoi("0"));
					face.push_back(atoi(values[2].c_str()));face.push_back(atoi("0"));face.push_back(atoi("0"));
					face.push_back(atoi(values[3].c_str()));face.push_back(atoi("0"));face.push_back(atoi("0"));
					obj->vecf.push_back(face);
				}
				
			}
			
			
			
			
		} //face loop ends
		
		
		
		
		
		

	}
	for(unsigned int i=0; i< Model.size(); i++){
		if(Model[i]->vecf.size() == 0)
			Model.erase(Model.begin()+i);
	}
	
		
	
	cout<<vecv.size()<<"\t"<<vecn.size() <<"\t"  << vec_tex.size()<<endl;
	cout <<"Model Size \t" << Model.size() << endl;
	//cout << "Model Name \t" << Model[0]->obj_name << endl;
	//cout << "Face Index \t" << Model[0]->vecf[0][3] << Model[0]->vecf[0][4] << Model[0]->vecf[0][5]  << endl;
	cout << "Vecf size\t" << Model[0]->vecf.size() << endl;
}
コード例 #8
0
/*
* private:
*/
ScenePointer SceneLoader::readScene(const QDomNode &rootNode) const {
  if (rootNode.toElement().tagName() != "scene") {
    std::cerr << "Scene parsing error: invalid root tag name, 'scene' expected" << std::endl;
    return ScenePointer(NULL);
  }

  ScenePointer scene = ScenePointer(new Scene());

  bool isCameraIntialized = false;
  bool isBackgroundMaterialInitialized = false;

  QDomElement element = rootNode.firstChildElement();
  while (!element.isNull()) {
    QString elementTagName = element.tagName();        
    
    if (elementTagName == "camera") {
      if (isCameraIntialized) {
        std::cerr << "Scene parsing error: 'camera' tag occurred twice, only one camera is allowed" << std::endl;
        return ScenePointer(NULL);
      }            
      
      CameraPointer camera = readCamera(element);           
      if (camera == NULL) {
        std::cerr << "Scene parsing error: failed camera parameters reading" << std::endl;
        return ScenePointer(NULL);
      }
      
      scene->setCamera(camera);
      isCameraIntialized = true;
    } else if (elementTagName == "light") {
      LightSourcePointer lightSource = readLightSource(element);
      if (lightSource == NULL) {
        std::cerr << "Scene parsing error: failed light source parameters reading" << std::endl;
        return ScenePointer(NULL);
      }
      scene->addLightSource(lightSource);
    } else if (elementTagName == "object") {
      ShapePointer shape = readShape(element);
      if (shape == NULL) {
        std::cerr << "Scene parsing error: failed shape parameters reading" << std::endl;
        return ScenePointer(NULL);
      }
      scene->addShape(shape);
    } else if (elementTagName == "csg") {
      CSGTreePointer csgTree = readCSGTree(element);
      if (csgTree == NULL) {
        std::cerr << "Scene parsing error: failed CSG tree parameters reading" << std::endl;
        return ScenePointer(NULL);
      }
      scene->addShape(csgTree);
    } else if (elementTagName == "background") {
      if (isBackgroundMaterialInitialized) {
        std::cerr << "Scene parsing error: 'background' tag occurred twice" << std::endl;
        return ScenePointer(NULL);
      }

      MaterialPointer material = readMaterial(element);
      if (material == NULL) {
        std::cerr << "Scene parsing error: failed background material parameters reading" << std::endl;
        return ScenePointer(NULL);
      }

      scene->setBackgroundMaterial(material);
      isBackgroundMaterialInitialized = true;
    } else {
      std::cerr << "Scene parsing error: unknown tag '" << elementTagName.toUtf8().constData() << "'" << std::endl;
      return ScenePointer(NULL);
    }

    element = element.nextSiblingElement();
  }
  
  if (!isCameraIntialized) {
    std::cerr << "Scene parsing error: camera parameters are not specified" << std::endl;
    return ScenePointer(NULL);
  }
  if (!isBackgroundMaterialInitialized) {
    std::cerr << "Scene parsing error: background material parameters are not specified" << std::endl;
    return ScenePointer(NULL);
  }

  return scene;
}
コード例 #9
0
ファイル: MyPlug.cpp プロジェクト: 50059021/wodegongjubao
bool CMyPlug::importData(iModelData * pModelData, const std::string& strFilename)
{
	assert(pModelData);
	// Loading the mesh.
	IOReadBase* pRead = IOReadBase::autoOpen(strFilename);
	if (!pRead)
	{
		return false;
	}
	// header
	readHeader(pRead);
	// mesh
	if (!pRead->IsEof())
	{
		unsigned short streamID;
		unsigned int uLength;
		pRead->Read(&streamID,sizeof(unsigned short));
		pRead->Read(&uLength,sizeof(unsigned int));

		switch (streamID)
		{
		case M_MESH:
			{
				readMesh(pRead,pModelData);
				break;
			}
		}
	}
	// close file
	IOReadBase::autoClose(pRead);

	// Loading the materials.
	if (!readMaterial(pModelData,ChangeExtension(strFilename,".material")))
	{
		std::string strMatFilename = GetParentPath(strFilename);
		strMatFilename=strMatFilename+GetFilename(strMatFilename)+".material";
		if (!readMaterial(pModelData,strMatFilename))
		{
			strMatFilename=ChangeExtension(strMatFilename,"_tileset.material");
			readMaterial(pModelData,strMatFilename);
		}
	}

	// mesh update
	pModelData->getMesh().update();

// 	//m_bbox = mesh.getBBox();
// 	std::string strMyPath ="Data\\"+GetFilename(GetParentPath(strFilename))+"\\";
// 	std::string strMatFilename = ChangeExtension(strFilename,".mat.csv");
// 	std::string strParFilename = ChangeExtension(strFilename,".par.csv");
// 	if (!IOReadBase::Exists(strMatFilename))
// 	{
// 		strMatFilename=strMyPath+ChangeExtension(GetFilename(strFilename),".mat.csv");
// 	}
// 	if (!IOReadBase::Exists(strParFilename))
// 	{
// 		strParFilename=strMyPath+ChangeExtension(GetFilename(strFilename),".par.csv");
// 	}
// 
// 	pModelData->loadMaterial(strMatFilename);
// 	pModelData->loadParticleEmitters(strParFilename);
	return true;
}
コード例 #10
0
bool WRL1BASE::ReadNode( WRLPROC& proc, WRL1NODE* aParent, WRL1NODE** aNode )
{
    // This function reads a node and stores a pointer to it in aNode.
    // A value 'true' is returned if a node is successfully read or,
    // if the node is not supported, successfully discarded. Callers
    // must always check the value of aNode when the function returns
    // 'true' since it will be NULL if the node type is not supported.

    if( NULL != aNode )
        *aNode = NULL;

    if( NULL == aParent )
    {
        #ifdef DEBUG_VRML1
        do {
            std::ostringstream ostr;
            ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
            ostr << " * [BUG] invalid parent pointer (NULL)";
            wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
        } while( 0 );
        #endif

        return false;
    }

    std::string glob;
    WRL1NODES ntype;

    if( !proc.ReadName( glob ) )
    {
        #if defined( DEBUG_VRML1 ) && ( DEBUG_VRML1 > 1 )
        if( !proc.eof() )
        {
            std::ostringstream ostr;
            ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
            ostr << proc.GetError();
            wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
        }
        #endif

        return false;
    }

    // Process node name:
    // the names encountered at this point should be one of the
    // built-in node names or one of:
    // DEF, USE
    if( !glob.compare( "USE" ) )
    {
        if( !implementUse( proc, aParent, aNode ) )
        {
            #if defined( DEBUG_VRML1 ) && ( DEBUG_VRML1 > 1 )
            do {
                std::ostringstream ostr;
                ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
                ostr << proc.GetError();
                wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
            } while( 0 );
            #endif

            return false;
        }

        return true;
    }

    if( !glob.compare( "DEF" ) )
    {
        if( !implementDef( proc, aParent, aNode ) )
        {
            #if defined( DEBUG_VRML1 ) && ( DEBUG_VRML1 > 1 )
            do {
                std::ostringstream ostr;
                ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
                ostr << proc.GetError();
                wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
            } while( 0 );
            #endif

            return false;
        }

        return true;
    }

    ntype = getNodeTypeID( glob );
    size_t line = 0;
    size_t column = 0;
    proc.GetFilePosData( line, column );

    #if defined( DEBUG_VRML1 ) && ( DEBUG_VRML1 > 2 )
    do {
        std::ostringstream ostr;
        ostr << " * [INFO] Processing node '" << glob << "' ID: " << ntype;
        wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
    } while( 0 );
    #endif

    switch( ntype )
    {
    case WRL1_GROUP:

        if( !readGroup( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_SEPARATOR:

        if( !readSeparator( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_SWITCH:

        if( !readSwitch( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_MATERIAL:

        if( !readMaterial( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_MATERIALBINDING:

        if( !readMatBinding( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_COORDINATE3:

        if( !readCoords( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_INDEXEDFACESET:

        if( !readFaceSet( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_TRANSFORM:
    case WRL1_TRANSLATION:
    case WRL1_ROTATION:
    case WRL1_SCALE:

        if( !readTransform( proc, aParent, aNode ) )
            return false;

        break;

    case WRL1_SHAPEHINTS:

        if( !readShapeHints( proc, aParent, aNode ) )
            return false;

        break;

    //
    // items not implemented or for optional future implementation:
    //
    default:

        proc.GetFilePosData( line, column );

        if( !proc.DiscardNode() )
        {
            #if defined( DEBUG_VRML1 ) && ( DEBUG_VRML1 > 1 )
            do {
                std::ostringstream ostr;
                ostr << proc.GetError() << "\n";
                ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
                ostr << " * [INFO] could not discard node at line " << line;
                ostr << ", column " << column;
                wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
            } while( 0 );
            #endif

            return false;
        }
        #if defined( DEBUG_VRML1 ) && ( DEBUG_VRML1 > 1 )
        else
        {
            std::ostringstream ostr;
            ostr << " * [INFO] discarded node '" << glob << "' at line ";
            ostr << line << ", col " << column << " (currently unsupported)";
            wxLogTrace( MASK_VRML, "%s\n", ostr.str().c_str() );
        }
        #endif

        break;
    }

    return true;
}
コード例 #11
0
    std::shared_ptr<gameplay::Model> OBJWriter::readModel(const boost::filesystem::path& path, const std::shared_ptr<gameplay::ShaderProgram>& shaderProgram, const glm::vec3& ambientColor) const
    {
        Assimp::Importer importer;
        const aiScene* scene = importer.ReadFile((m_basePath / path).string(), aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_ValidateDataStructure | aiProcess_FlipUVs);
        BOOST_ASSERT(scene != nullptr);

        auto renderModel = std::make_shared<gameplay::Model>();

        for( unsigned int mi = 0; mi < scene->mNumMeshes; ++mi )
        {
            BOOST_LOG_TRIVIAL(info) << "Converting mesh " << mi + 1 << " of " << scene->mNumMeshes << " from " << m_basePath / path;

            const aiMesh* mesh = scene->mMeshes[mi];
            if( mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE )
            BOOST_THROW_EXCEPTION(std::runtime_error("Mesh does not consist of triangles only"));
            if( !mesh->HasTextureCoords(0) )
            BOOST_THROW_EXCEPTION(std::runtime_error("Mesh does not have UV coordinates"));
            if( mesh->mNumUVComponents[0] != 2 )
            BOOST_THROW_EXCEPTION(std::runtime_error("Mesh does not have a 2D UV channel"));
            if( !mesh->HasFaces() )
            BOOST_THROW_EXCEPTION(std::runtime_error("Mesh does not have faces"));
            if( !mesh->HasPositions() )
            BOOST_THROW_EXCEPTION(std::runtime_error("Mesh does not have positions"));

            std::shared_ptr<gameplay::Mesh> renderMesh;

            if( mesh->HasNormals() )
            {
                std::vector<VDataNormal> vbuf(mesh->mNumVertices);
                for( unsigned int i = 0; i < mesh->mNumVertices; ++i )
                {
                    vbuf[i].position = glm::vec3{mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z} * static_cast<float>(SectorSize);
                    vbuf[i].normal = glm::vec3{mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z};
                    vbuf[i].uv = glm::vec2{mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y};
                    if( mesh->HasVertexColors(0) )
                        vbuf[i].color = glm::vec4(mesh->mColors[0][i].r, mesh->mColors[0][i].g, mesh->mColors[0][i].b, mesh->mColors[0][i].a);
                    else
                        vbuf[i].color = glm::vec4(ambientColor, 1);
                }

                renderMesh = std::make_shared<gameplay::Mesh>(VDataNormal::getFormat(), mesh->mNumVertices, false);
                renderMesh->rebuild(reinterpret_cast<const float*>(vbuf.data()), mesh->mNumVertices);
            }
            else
            {
                std::vector<VData> vbuf(mesh->mNumVertices);
                for( unsigned int i = 0; i < mesh->mNumVertices; ++i )
                {
                    vbuf[i].position = glm::vec3{mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z} * static_cast<float>(SectorSize);
                    vbuf[i].uv = glm::vec2{mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y};
                    if( mesh->HasVertexColors(0) )
                        vbuf[i].color = glm::vec4(mesh->mColors[0][i].r, mesh->mColors[0][i].g, mesh->mColors[0][i].b, mesh->mColors[0][i].a);
                    else
                        vbuf[i].color = glm::vec4(ambientColor, 1);
                }

                renderMesh = std::make_shared<gameplay::Mesh>(VData::getFormat(), mesh->mNumVertices, false);
                renderMesh->rebuild(reinterpret_cast<const float*>(vbuf.data()), mesh->mNumVertices);
            }

            std::vector<uint32_t> faces;
            for( const aiFace& face : gsl::span<aiFace>(mesh->mFaces, mesh->mNumFaces) )
            {
                BOOST_ASSERT(face.mNumIndices == 3);
                faces.push_back(face.mIndices[0]);
                faces.push_back(face.mIndices[1]);
                faces.push_back(face.mIndices[2]);
            }

            auto part = renderMesh->addPart(gameplay::Mesh::TRIANGLES, gameplay::Mesh::INDEX32, mesh->mNumFaces * 3, false);
            part->setIndexData(faces.data(), 0, faces.size());

            const aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
            aiString textureName;
            if( material->GetTexture(aiTextureType_DIFFUSE, 0, &textureName) != aiReturn_SUCCESS )
            BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get diffuse texture path from mesh"));

            part->setMaterial(readMaterial(textureName.C_Str(), shaderProgram));

            renderModel->addMesh(renderMesh);
        }

        return renderModel;
    }