Example #1
0
void ccGenericMesh::applyGLTransformation(const ccGLMatrix& trans)
{
    //vertices should be handled another way!

    //we must take care of the triangle normals!
    if (m_triNormals && (!getParent() || !getParent()->isKindOf(CC_MESH)))
    {
        bool recoded = false;

        //if there is more triangle normals than the size of the compressed
        //normals array, we recompress the array instead of recompressing each normal
        unsigned i,numTriNormals = m_triNormals->currentSize();
        if (numTriNormals>ccNormalVectors::GetNumberOfVectors())
        {
            NormsIndexesTableType* newNorms = new NormsIndexesTableType;
            if (newNorms->reserve(ccNormalVectors::GetNumberOfVectors()))
            {
                for (i=0; i<ccNormalVectors::GetNumberOfVectors(); i++)
                {
                    CCVector3 new_n(ccNormalVectors::GetNormal(i));
                    trans.applyRotation(new_n);
                    normsType newNormIndex = ccNormalVectors::GetNormIndex(new_n.u);
                    newNorms->addElement(newNormIndex);
                }

                m_triNormals->placeIteratorAtBegining();
                for (i=0; i<numTriNormals; i++)
                {
                    m_triNormals->setValue(i,newNorms->getValue(m_triNormals->getCurrentValue()));
                    m_triNormals->forwardIterator();
                }
                recoded=true;
            }
            newNorms->clear();
            newNorms->release();
            newNorms=0;
        }

        //if there is less triangle normals than the compressed normals array size
        //(or if there is not enough memory to instantiate the temporary array),
        //we recompress each normal ...
        if (!recoded)
        {
            //on recode direct chaque normale
            m_triNormals->placeIteratorAtBegining();
            for (i=0; i<numTriNormals; i++)
            {
                normsType* _theNormIndex = m_triNormals->getCurrentValuePtr();
                CCVector3 new_n(ccNormalVectors::GetNormal(*_theNormIndex));
                trans.applyRotation(new_n.u);
                *_theNormIndex = ccNormalVectors::GetNormIndex(new_n.u);
                m_triNormals->forwardIterator();
            }
        }
    }
    else
    {
        //TODO: process failed!
    }
}
Example #2
0
//converts a FBX mesh to a CC mesh
static ccMesh* FromFbxMesh(FbxMesh* fbxMesh, bool alwaysDisplayLoadDialog/*=true*/, bool* coordinatesShiftEnabled/*=0*/, CCVector3d* coordinatesShift/*=0*/)
{
	if (!fbxMesh)
		return 0;

	int polyCount = fbxMesh->GetPolygonCount();
	//fbxMesh->GetLayer(
	unsigned triCount = 0;
	unsigned polyVertCount = 0; //different from vertCount (vertices can be counted multiple times here!)
	//as we can't load all polygons (yet ;) we already look if we can load any!
	{
		unsigned skipped = 0;
		for (int i=0; i<polyCount; ++i)
		{
			int pSize = fbxMesh->GetPolygonSize(i);

			if (pSize == 3)
			{
				++triCount;
				polyVertCount += 3;
			}
			else if (pSize == 4)
			{
				triCount += 2;
				polyVertCount += 4;
			}
			else
			{
				++skipped;
			}
		}

		if (triCount == 0)
		{
			ccLog::Warning(QString("[FBX] No triangle or quad found in mesh '%1'! (polygons with more than 4 vertices are not supported for the moment)").arg(fbxMesh->GetName()));
			return 0;
		}
		else if (skipped != 0)
		{
			ccLog::Warning(QString("[FBX] Some polygons in mesh '%1' were ignored (%2): polygons with more than 4 vertices are not supported for the moment)").arg(fbxMesh->GetName()).arg(skipped));
			return 0;
		}
	}

	int vertCount = fbxMesh->GetControlPointsCount();
	if (vertCount <= 0)
	{
		ccLog::Warning(QString("[FBX] Mesh '%1' has no vetex or no polygon?!").arg(fbxMesh->GetName()));
		return 0;
	}

	ccPointCloud* vertices = new ccPointCloud("vertices");
	ccMesh* mesh = new ccMesh(vertices);
	mesh->setName(fbxMesh->GetName());
	mesh->addChild(vertices);
	vertices->setEnabled(false);
	
	if (!mesh->reserve(static_cast<unsigned>(triCount)) || !vertices->reserve(vertCount))
	{
		ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1'!").arg(fbxMesh->GetName()));
		delete mesh;
		return 0;
	}

	//colors
	{
		for (int l=0; l<fbxMesh->GetElementVertexColorCount(); l++)
		{
			FbxGeometryElementVertexColor* vertColor = fbxMesh->GetElementVertexColor(l);
			//CC can only handle per-vertex colors
			if (vertColor->GetMappingMode() == FbxGeometryElement::eByControlPoint)
			{
				if (vertColor->GetReferenceMode() == FbxGeometryElement::eDirect
					|| vertColor->GetReferenceMode() == FbxGeometryElement::eIndexToDirect)
				{
					if (vertices->reserveTheRGBTable())
					{
						switch (vertColor->GetReferenceMode())
						{
						case FbxGeometryElement::eDirect:
							{
								for (int i=0; i<vertCount; ++i)
								{
									FbxColor c = vertColor->GetDirectArray().GetAt(i);
									vertices->addRGBColor(	static_cast<colorType>(c.mRed	* MAX_COLOR_COMP),
															static_cast<colorType>(c.mGreen	* MAX_COLOR_COMP),
															static_cast<colorType>(c.mBlue	* MAX_COLOR_COMP) );
								}
							}
							break;
						case FbxGeometryElement::eIndexToDirect:
							{
								for (int i=0; i<vertCount; ++i)
								{
									int id = vertColor->GetIndexArray().GetAt(i);
									FbxColor c = vertColor->GetDirectArray().GetAt(id);
									vertices->addRGBColor(	static_cast<colorType>(c.mRed	* MAX_COLOR_COMP),
															static_cast<colorType>(c.mGreen	* MAX_COLOR_COMP),
															static_cast<colorType>(c.mBlue	* MAX_COLOR_COMP) );
								}
							}
							break;
						default:
							assert(false);
							break;
						}

						vertices->showColors(true);
						mesh->showColors(true);
						break; //no need to look for other color fields (we won't be able to handle them!
					}
					else
					{
						ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' colors!").arg(fbxMesh->GetName()));
					}
				}
				else
				{
					ccLog::Warning(QString("[FBX] Color field #%i of mesh '%1' will be ignored (unhandled type)").arg(l).arg(fbxMesh->GetName()));
				}
			}
			else
			{
				ccLog::Warning(QString("[FBX] Color field #%i of mesh '%1' will be ignored (unhandled type)").arg(l).arg(fbxMesh->GetName()));
			}
		}
	}


	//normals can be per vertices or per-triangle
	int perPointNormals = -1;
	int perVertexNormals = -1;
	int perPolygonNormals = -1;
	{
        for (int j=0; j<fbxMesh->GetElementNormalCount(); j++)
        {
			FbxGeometryElementNormal* leNormals = fbxMesh->GetElementNormal(j);
			switch(leNormals->GetMappingMode())
			{
			case FbxGeometryElement::eByControlPoint:
				perPointNormals = j;
				break;
			case FbxGeometryElement::eByPolygonVertex:
				perVertexNormals = j;
				break;
			case FbxGeometryElement::eByPolygon:
				perPolygonNormals = j;
				break;
			default:
				//not handled
				break;
			}
		}
	}

	//per-point normals
	if (perPointNormals >= 0)
	{
		FbxGeometryElementNormal* leNormals = fbxMesh->GetElementNormal(perPointNormals);
		FbxLayerElement::EReferenceMode refMode = leNormals->GetReferenceMode();
		const FbxLayerElementArrayTemplate<FbxVector4>& normals = leNormals->GetDirectArray();
		assert(normals.GetCount() == vertCount);
		if (normals.GetCount() != vertCount)
		{
			ccLog::Warning(QString("[FBX] Wrong number of normals on mesh '%1'!").arg(fbxMesh->GetName()));
			perPointNormals = -1;
		}
		else if (!vertices->reserveTheNormsTable())
		{
			ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' normals!").arg(fbxMesh->GetName()));
			perPointNormals = -1;
		}
		else
		{
			//import normals
			for (int i=0; i<vertCount; ++i)
			{
				int id = refMode != FbxGeometryElement::eDirect ? leNormals->GetIndexArray().GetAt(i) : i;
				FbxVector4 N = normals.GetAt(id);
				//convert to CC-structure
				CCVector3 Npc(	static_cast<PointCoordinateType>(N.Buffer()[0]),
								static_cast<PointCoordinateType>(N.Buffer()[1]),
								static_cast<PointCoordinateType>(N.Buffer()[2]) );
				vertices->addNorm(Npc.u);
			}
			vertices->showNormals(true);
			mesh->showNormals(true);
			//no need to import the other normals (if any)
			perVertexNormals = -1;
			perPolygonNormals = -1;
		}
	}

	//per-triangle normals
	NormsIndexesTableType* normsTable = 0;
	if (perVertexNormals >= 0 || perPolygonNormals >= 0)
	{
		normsTable = new NormsIndexesTableType();
		if (!normsTable->reserve(polyVertCount) || !mesh->reservePerTriangleNormalIndexes())
		{
			ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' normals!").arg(fbxMesh->GetName()));
			normsTable->release();
			normsTable = 0;
		}
		else
		{
			mesh->setTriNormsTable(normsTable);
			mesh->addChild(normsTable);
			vertices->showNormals(true);
			mesh->showNormals(true);
		}
	}

	//import textures UV
	int perVertexUV = -1;
	bool hasTexUV = false;
	{
		for (int l=0; l<fbxMesh->GetElementUVCount(); ++l)
		{
			FbxGeometryElementUV* leUV = fbxMesh->GetElementUV(l);
			//per-point UV coordinates
			if (leUV->GetMappingMode() == FbxGeometryElement::eByControlPoint)
			{
				TextureCoordsContainer* vertTexUVTable = new TextureCoordsContainer();
				if (!vertTexUVTable->reserve(vertCount) || !mesh->reservePerTriangleTexCoordIndexes())
				{
					vertTexUVTable->release();
					ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' UV coordinates!").arg(fbxMesh->GetName()));
				}
				else
				{
					FbxLayerElement::EReferenceMode refMode = leUV->GetReferenceMode();
					for (int i=0; i<vertCount; ++i)
					{
						int id = refMode != FbxGeometryElement::eDirect ? leUV->GetIndexArray().GetAt(i) : i;
						FbxVector2 uv = leUV->GetDirectArray().GetAt(id);
						//convert to CC-structure
						float uvf[2] = {static_cast<float>(uv.Buffer()[0]),
										static_cast<float>(uv.Buffer()[1])};
						vertTexUVTable->addElement(uvf);
					}
					mesh->addChild(vertTexUVTable);
					hasTexUV = true;
				}
				perVertexUV = -1;
				break; //no need to look to the other UV fields (can't handle them!)
			}
			else if (leUV->GetMappingMode() == FbxGeometryElement::eByPolygonVertex)
			{
				//per-vertex UV coordinates
				perVertexUV = l;
			}
		}
	}

	//per-vertex UV coordinates
	TextureCoordsContainer* texUVTable = 0;
	if (perVertexUV >= 0)
	{
		texUVTable = new TextureCoordsContainer();
		if (!texUVTable->reserve(polyVertCount) || !mesh->reservePerTriangleTexCoordIndexes())
		{
			texUVTable->release();
			ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' UV coordinates!").arg(fbxMesh->GetName()));
		}
		else
		{
			mesh->addChild(texUVTable);
			hasTexUV = true;
		}
	}

	//import polygons
	{
		for (int i=0; i<polyCount; ++i)
		{
			int pSize = fbxMesh->GetPolygonSize(i);

			if (pSize > 4)
			{
				//not handled for the moment
				continue;
			}
			//we split quads into two triangles

			//vertex indices
			int i1 = fbxMesh->GetPolygonVertex(i, 0);
			int i2 = fbxMesh->GetPolygonVertex(i, 1);
			int i3 = fbxMesh->GetPolygonVertex(i, 2);
			mesh->addTriangle(i1,i2,i3);

			int i4 = -1;
			if (pSize == 4)
			{
				i4 = fbxMesh->GetPolygonVertex(i, 3);
				mesh->addTriangle(i1,i3,i4);
			}

			if (hasTexUV)
			{
				if (texUVTable)
				{
					assert(perVertexUV >= 0);

					int uvIndex = static_cast<int>(texUVTable->currentSize());
					for (int j=0; j<pSize; ++j)
					{
						int lTextureUVIndex = fbxMesh->GetTextureUVIndex(i, j);
						FbxGeometryElementUV* leUV = fbxMesh->GetElementUV(perVertexUV);
						FbxVector2 uv = leUV->GetDirectArray().GetAt(lTextureUVIndex);
						//convert to CC-structure
						float uvf[2] = {static_cast<float>(uv.Buffer()[0]),
										static_cast<float>(uv.Buffer()[1])};
						texUVTable->addElement(uvf);
					}
					mesh->addTriangleTexCoordIndexes(uvIndex,uvIndex+1,uvIndex+2);
					if (pSize == 4)
						mesh->addTriangleTexCoordIndexes(uvIndex,uvIndex+2,uvIndex+3);
				}
				else
				{
					mesh->addTriangleTexCoordIndexes(i1,i2,i3);
					if (pSize == 4)
						mesh->addTriangleTexCoordIndexes(i1,i3,i4);
				}
			}

			//per-triangle normals
			if (normsTable)
			{
				int nIndex = static_cast<int>(normsTable->currentSize());
				for (int j=0; j<pSize; ++j)
				{
					FbxVector4 N;
					fbxMesh->GetPolygonVertexNormal(i, j, N);
					CCVector3 Npc(	static_cast<PointCoordinateType>(N.Buffer()[0]),
									static_cast<PointCoordinateType>(N.Buffer()[1]),
									static_cast<PointCoordinateType>(N.Buffer()[2]) );
					normsTable->addElement(ccNormalVectors::GetNormIndex(Npc.u));
				}

				mesh->addTriangleNormalIndexes(nIndex,nIndex+1,nIndex+2);
				if (pSize == 4)
					mesh->addTriangleNormalIndexes(nIndex,nIndex+2,nIndex+3);
			}
		}
		
		if (mesh->size() == 0)
		{
			ccLog::Warning(QString("[FBX] No triangle found in mesh '%1'! (only triangles are supported for the moment)").arg(fbxMesh->GetName()));
			delete mesh;
			return 0;
		}
	}

	//import vertices
	{
		const FbxVector4* fbxVertices = fbxMesh->GetControlPoints();
		assert(vertices && fbxVertices);
		CCVector3d Pshift(0,0,0);
		for (int i=0; i<vertCount; ++i, ++fbxVertices)
		{
			const double* P = fbxVertices->Buffer();
			assert(P[3] == 0);

			//coordinate shift management
			if (i == 0)
			{
				bool shiftAlreadyEnabled = (coordinatesShiftEnabled && *coordinatesShiftEnabled && coordinatesShift);
				if (shiftAlreadyEnabled)
					Pshift = *coordinatesShift;
				bool applyAll = false;
				if (	sizeof(PointCoordinateType) < 8
					&&	ccCoordinatesShiftManager::Handle(P,0,alwaysDisplayLoadDialog,shiftAlreadyEnabled,Pshift,0,applyAll))
				{
					vertices->setGlobalShift(Pshift);
					ccLog::Warning("[FBX] Mesh has been recentered! Translation: (%.2f,%.2f,%.2f)",Pshift.x,Pshift.y,Pshift.z);

					//we save coordinates shift information
					if (applyAll && coordinatesShiftEnabled && coordinatesShift)
					{
						*coordinatesShiftEnabled = true;
						*coordinatesShift = Pshift;
					}
				}
			}

			CCVector3 PV(	static_cast<PointCoordinateType>(P[0] + Pshift.x),
							static_cast<PointCoordinateType>(P[1] + Pshift.y),
							static_cast<PointCoordinateType>(P[2] + Pshift.z) );

			vertices->addPoint(PV);
		}
	}

	//import textures
	{
		//TODO
	}

	return mesh;
}
Example #3
0
CC_FILE_ERROR ObjFilter::loadFile(QString filename, ccHObject& container, LoadParameters& parameters)
{
	ccLog::Print(QString("[OBJ] ") + filename);

	//open file
	QFile file(filename);
	if (!file.open(QFile::ReadOnly))
		return CC_FERR_READING;
	QTextStream stream(&file);

	//current vertex shift
	CCVector3d Pshift(0,0,0);

	//vertices
	ccPointCloud* vertices = new ccPointCloud("vertices");
	int pointsRead = 0;

	//facets
	unsigned int facesRead = 0;
	unsigned int totalFacesRead = 0;
	int maxVertexIndex = -1;

	//base mesh
	ccMesh* baseMesh = new ccMesh(vertices);
	baseMesh->setName(QFileInfo(filename).baseName());
	//we need some space already reserved!
	if (!baseMesh->reserve(128))
	{
		ccLog::Error("Not engouh memory!");
		return CC_FERR_NOT_ENOUGH_MEMORY;
	}

	//groups (starting index + name)
	std::vector<std::pair<unsigned,QString> > groups;

	//materials
	ccMaterialSet* materials = 0;
	bool hasMaterial = false;
	int currentMaterial = -1;
	bool currentMaterialDefined = false;
	bool materialsLoadFailed = true;

	//texture coordinates
	TextureCoordsContainer* texCoords = 0;
	bool hasTexCoords = false;
	int texCoordsRead = 0;
	int maxTexCoordIndex = -1;

	//normals
	NormsIndexesTableType* normals = 0;
	int normsRead = 0;
	bool normalsPerFacet = false;
	int maxTriNormIndex = -1;

	//progress dialog
	ccProgressDialog pDlg(true);
	pDlg.setMethodTitle("OBJ file");
	pDlg.setInfo("Loading in progress...");
	pDlg.setRange(0,static_cast<int>(file.size()));
	pDlg.show();
	QApplication::processEvents();

	//common warnings that can appear multiple time (we avoid to send too many messages to the console!)
	enum OBJ_WARNINGS {	INVALID_NORMALS		= 0,
						INVALID_INDEX		= 1,
						NOT_ENOUGH_MEMORY	= 2,
						INVALID_LINE		= 3,
						CANCELLED_BY_USER	= 4,
	};
	bool objWarnings[5] = { false, false, false, false, false };
	bool error = false;

	try
	{
		unsigned lineCount = 0;
		unsigned polyCount = 0;
		QString currentLine = stream.readLine();
		while (!currentLine.isNull())
		{
			if ((++lineCount % 2048) == 0)
			{
				if (pDlg.wasCanceled())
				{
					error = true;
					objWarnings[CANCELLED_BY_USER] = true;
					break;
				}
				pDlg.setValue(static_cast<int>(file.pos()));
				QApplication::processEvents();
			}

			QStringList tokens = QString(currentLine).split(QRegExp("\\s+"),QString::SkipEmptyParts);

			//skip comments & empty lines
			if( tokens.empty() || tokens.front().startsWith('/',Qt::CaseInsensitive) || tokens.front().startsWith('#',Qt::CaseInsensitive) )
			{
				currentLine = stream.readLine();
				continue;
			}

			/*** new vertex ***/
			if (tokens.front() == "v")
			{
				//reserve more memory if necessary
				if (vertices->size() == vertices->capacity())
				{
					if (!vertices->reserve(vertices->capacity()+MAX_NUMBER_OF_ELEMENTS_PER_CHUNK))
					{
						objWarnings[NOT_ENOUGH_MEMORY] = true;
						error = true;
						break;
					}
				}

				//malformed line?
				if (tokens.size() < 4)
				{
					objWarnings[INVALID_LINE] = true;
					error = true;
					break;
				}

				CCVector3d Pd( tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble() );

				//first point: check for 'big' coordinates
				if (pointsRead == 0)
				{
					if (HandleGlobalShift(Pd,Pshift,parameters))
					{
						vertices->setGlobalShift(Pshift);
						ccLog::Warning("[OBJ] Cloud has been recentered! Translation: (%.2f,%.2f,%.2f)",Pshift.x,Pshift.y,Pshift.z);
					}
				}

				//shifted point
				CCVector3 P = CCVector3::fromArray((Pd + Pshift).u);
				vertices->addPoint(P);
				++pointsRead;
			}
			/*** new vertex texture coordinates ***/
			else if (tokens.front() == "vt")
			{
				//create and reserve memory for tex. coords container if necessary
				if (!texCoords)
				{
					texCoords = new TextureCoordsContainer();
					texCoords->link();
				}
				if (texCoords->currentSize() == texCoords->capacity())
				{
					if (!texCoords->reserve(texCoords->capacity() + MAX_NUMBER_OF_ELEMENTS_PER_CHUNK))
					{
						objWarnings[NOT_ENOUGH_MEMORY] = true;
						error = true;
						break;
					}
				}

				//malformed line?
				if (tokens.size() < 2)
				{
					objWarnings[INVALID_LINE] = true;
					error = true;
					break;
				}

				float T[2] = { T[0] = tokens[1].toFloat(), 0 };

				if (tokens.size() > 2) //OBJ specification allows for only one value!!!
				{
					T[1] = tokens[2].toFloat();
				}

				texCoords->addElement(T);
				++texCoordsRead;
			}
			/*** new vertex normal ***/
			else if (tokens.front() == "vn") //--> in fact it can also be a facet normal!!!
			{
				//create and reserve memory for normals container if necessary
				if (!normals)
				{
					normals = new NormsIndexesTableType;
					normals->link();
				}
				if (normals->currentSize() == normals->capacity())
				{
					if (!normals->reserve(normals->capacity() + MAX_NUMBER_OF_ELEMENTS_PER_CHUNK))
					{
						objWarnings[NOT_ENOUGH_MEMORY] = true;
						error = true;
						break;
					}
				}

				//malformed line?
				if (tokens.size() < 4)
				{
					objWarnings[INVALID_LINE] = true;
					error = true;
					break;
				}

				CCVector3 N(static_cast<PointCoordinateType>(tokens[1].toDouble()),
							static_cast<PointCoordinateType>(tokens[2].toDouble()),
							static_cast<PointCoordinateType>(tokens[3].toDouble()));

				if (fabs(N.norm2() - 1.0) > 0.005)
				{
					objWarnings[INVALID_NORMALS] = true;
					N.normalize();
				}
				CompressedNormType nIndex = ccNormalVectors::GetNormIndex(N.u);

				normals->addElement(nIndex); //we don't know yet if it's per-vertex or per-triangle normal...
				++normsRead;
			}
			/*** new group ***/
			else if (tokens.front() == "g" || tokens.front() == "o")
			{
				//update new group index
				facesRead = 0;
				//get the group name
				QString groupName = (tokens.size() > 1 && !tokens[1].isEmpty() ? tokens[1] : "default");
				for (int i=2; i<tokens.size(); ++i) //multiple parts?
					groupName.append(QString(" ")+tokens[i]);
				//push previous group descriptor (if none was pushed)
				if (groups.empty() && totalFacesRead > 0)
					groups.push_back(std::pair<unsigned,QString>(0,"default"));
				//push new group descriptor
				if (!groups.empty() && groups.back().first == totalFacesRead)
					groups.back().second = groupName; //simply replace the group name if the previous group was empty!
				else
					groups.push_back(std::pair<unsigned,QString>(totalFacesRead,groupName));
				polyCount = 0; //restart polyline count at 0!
			}
			/*** new face ***/
			else if (tokens.front().startsWith('f'))
			{
				//malformed line?
				if (tokens.size() < 4)
				{
					objWarnings[INVALID_LINE] = true;
					currentLine = stream.readLine();
					continue;
					//error = true;
					//break;
				}

				//read the face elements (singleton, pair or triplet)
				std::vector<facetElement> currentFace;
				{
					for (int i=1; i<tokens.size(); ++i)
					{
						QStringList vertexTokens = tokens[i].split('/');
						if (vertexTokens.size() == 0 || vertexTokens[0].isEmpty())
						{
							objWarnings[INVALID_LINE] = true;
							error = true;
							break;
						}
						else
						{
							//new vertex
							facetElement fe; //(0,0,0) by default
							
							fe.vIndex = vertexTokens[0].toInt();
							if (vertexTokens.size() > 1 && !vertexTokens[1].isEmpty())
								fe.tcIndex = vertexTokens[1].toInt();
							if (vertexTokens.size() > 2 && !vertexTokens[2].isEmpty())
								fe.nIndex = vertexTokens[2].toInt();
						
							currentFace.push_back(fe);
						}
					}
				}

				if (error)
					break;

				if (currentFace.size() < 3)
				{
					ccLog::Warning("[OBJ] Malformed file: polygon on line %1 has less than 3 vertices!",lineCount);
					error = true;
					break;
				}

				//first vertex
				std::vector<facetElement>::iterator A = currentFace.begin();

				//the very first vertex of the group tells us about the whole sequence
				if (facesRead == 0)
				{
					//we have a tex. coord index as second vertex element!
					if (!hasTexCoords && A->tcIndex != 0 && !materialsLoadFailed)
					{
						if (!baseMesh->reservePerTriangleTexCoordIndexes())
						{
							objWarnings[NOT_ENOUGH_MEMORY] = true;
							error = true;
							break;
						}
						for (unsigned int i=0; i<totalFacesRead; ++i)
							baseMesh->addTriangleTexCoordIndexes(-1, -1, -1);

						hasTexCoords = true;
					}

					//we have a normal index as third vertex element!
					if (!normalsPerFacet && A->nIndex != 0)
					{
						//so the normals are 'per-facet'
						if (!baseMesh->reservePerTriangleNormalIndexes())
						{
							objWarnings[NOT_ENOUGH_MEMORY] = true;
							error = true;
							break;
						}
						for (unsigned int i=0; i<totalFacesRead; ++i)
							baseMesh->addTriangleNormalIndexes(-1, -1, -1);
						normalsPerFacet = true;
					}
				}

				//we process all vertices accordingly
				for (std::vector<facetElement>::iterator it = currentFace.begin() ; it!=currentFace.end(); ++it)
				{
					facetElement& vertex = *it;

					//vertex index
					{
						if (!vertex.updatePointIndex(pointsRead))
						{
							objWarnings[INVALID_INDEX] = true;
							error = true;
							break;
						}
						if (vertex.vIndex > maxVertexIndex)
							maxVertexIndex = vertex.vIndex;
					}
					//should we have a tex. coord index as second vertex element?
					if (hasTexCoords && currentMaterialDefined)
					{
						if (!vertex.updateTexCoordIndex(texCoordsRead))
						{
							objWarnings[INVALID_INDEX] = true;
							error = true;
							break;
						}
						if (vertex.tcIndex > maxTexCoordIndex)
							maxTexCoordIndex = vertex.tcIndex;
					}

					//should we have a normal index as third vertex element?
					if (normalsPerFacet)
					{
						if (!vertex.updateNormalIndex(normsRead))
						{
							objWarnings[INVALID_INDEX] = true;
							error = true;
							break;
						}
						if (vertex.nIndex > maxTriNormIndex)
							maxTriNormIndex = vertex.nIndex;
					}
				}

				//don't forget material (common for all vertices)
				if (currentMaterialDefined && !materialsLoadFailed)
				{
					if (!hasMaterial)
					{
						if (!baseMesh->reservePerTriangleMtlIndexes())
						{
							objWarnings[NOT_ENOUGH_MEMORY] = true;
							error = true;
							break;
						}
						for (unsigned int i=0; i<totalFacesRead; ++i)
							baseMesh->addTriangleMtlIndex(-1);

						hasMaterial = true;
					}
				}

				if (error)
					break;

				//Now, let's tesselate the whole polygon
				//FIXME: yeah, we do very ulgy tesselation here!
				std::vector<facetElement>::const_iterator B = A+1;
				std::vector<facetElement>::const_iterator C = B+1;
				for ( ; C != currentFace.end(); ++B,++C)
				{
					//need more space?
					if (baseMesh->size() == baseMesh->capacity())
					{
						if (!baseMesh->reserve(baseMesh->size()+128))
						{
							objWarnings[NOT_ENOUGH_MEMORY] = true;
							error = true;
							break;
						}
					}

					//push new triangle
					baseMesh->addTriangle(A->vIndex, B->vIndex, C->vIndex);
					++facesRead;
					++totalFacesRead;

					if (hasMaterial)
						baseMesh->addTriangleMtlIndex(currentMaterial);

					if (hasTexCoords)
						baseMesh->addTriangleTexCoordIndexes(A->tcIndex, B->tcIndex, C->tcIndex);

					if (normalsPerFacet)
						baseMesh->addTriangleNormalIndexes(A->nIndex, B->nIndex, C->nIndex);
				}
			}
			/*** polyline ***/
			else if (tokens.front().startsWith('l'))
			{
				//malformed line?
				if (tokens.size() < 3)
				{
					objWarnings[INVALID_LINE] = true;
					currentLine = stream.readLine();
					continue;
				}

				//read the face elements (singleton, pair or triplet)
				ccPolyline* polyline = new ccPolyline(vertices);
				if (!polyline->reserve(static_cast<unsigned>(tokens.size()-1)))
				{
					//not enough memory
					objWarnings[NOT_ENOUGH_MEMORY] = true;
					delete polyline;
					polyline = 0;
					currentLine = stream.readLine();
					continue;
				}

				for (int i=1; i<tokens.size(); ++i)
				{
					//get next polyline's vertex index
					QStringList vertexTokens = tokens[i].split('/');
					if (vertexTokens.size() == 0 || vertexTokens[0].isEmpty())
					{
						objWarnings[INVALID_LINE] = true;
						error = true;
						break;
					}
					else
					{
						int index = vertexTokens[0].toInt(); //we ignore normal index (if any!)
						if (!UpdatePointIndex(index,pointsRead))
						{
							objWarnings[INVALID_INDEX] = true;
							error = true;
							break;
						}

						polyline->addPointIndex(index);
					}
				}

				if (error)
				{
					delete polyline;
					polyline = 0;
					break;
				}
			
				polyline->setVisible(true);
				QString name = groups.empty() ? QString("Line") : groups.back().second+QString(".line");
				polyline->setName(QString("%1 %2").arg(name).arg(++polyCount));
				vertices->addChild(polyline);

			}
			/*** material ***/
			else if (tokens.front() == "usemtl") //see 'MTL file' below
			{
				if (materials) //otherwise we have failed to load MTL file!!!
				{
					QString mtlName = currentLine.mid(7).trimmed();
					//DGM: in case there's space characters in the material name, we must read it again from the original line buffer
					//QString mtlName = (tokens.size() > 1 && !tokens[1].isEmpty() ? tokens[1] : "");
					currentMaterial = (!mtlName.isEmpty() ? materials->findMaterialByName(mtlName) : -1);
					currentMaterialDefined = true;
				}
			}
			/*** material file (MTL) ***/
			else if (tokens.front() == "mtllib")
			{
				//malformed line?
				if (tokens.size() < 2 || tokens[1].isEmpty())
				{
					objWarnings[INVALID_LINE] = true;
				}
				else
				{
					//we build the whole MTL filename + path
					//DGM: in case there's space characters in the filename, we must read it again from the original line buffer
					//QString mtlFilename = tokens[1];
					QString mtlFilename = currentLine.mid(7).trimmed();
					ccLog::Print(QString("[OBJ] Material file: ")+mtlFilename);
					QString mtlPath = QFileInfo(filename).canonicalPath();
					//we try to load it
					if (!materials)
					{
						materials = new ccMaterialSet("materials");
						materials->link();
					}

					size_t oldSize = materials->size();
					QStringList errors;
					if (ccMaterialSet::ParseMTL(mtlPath,mtlFilename,*materials,errors))
					{
						ccLog::Print("[OBJ] %i materials loaded",materials->size()-oldSize);
						materialsLoadFailed = false;
					}
					else
					{
						ccLog::Error(QString("[OBJ] Failed to load material file! (should be in '%1')").arg(mtlPath+'/'+QString(mtlFilename)));
						materialsLoadFailed = true;
					}

					if (!errors.empty())
					{
						for (int i=0; i<errors.size(); ++i)
							ccLog::Warning(QString("[OBJ::Load::MTL parser] ")+errors[i]);
					}
					if (materials->empty())
					{
						materials->release();
						materials=0;
						materialsLoadFailed = true;
					}
				}
			}
			///*** shading group ***/
			//else if (tokens.front() == "s")
			//{
			//	//ignored!
			//}

			if (error)
				break;

			currentLine = stream.readLine();
		}
	}
	catch (const std::bad_alloc&)
	{
		//not enough memory
		objWarnings[NOT_ENOUGH_MEMORY] = true;
		error = true;
	}

	file.close();

	//1st check
	if (!error && pointsRead == 0)
	{
		//of course if there's no vertex, that's the end of the story ...
		ccLog::Warning("[OBJ] Malformed file: no vertex in file!");
		error = true;
	}

	if (!error)
	{
		ccLog::Print("[OBJ] %i points, %u faces",pointsRead,totalFacesRead);
		if (texCoordsRead > 0 || normsRead > 0)
			ccLog::Print("[OBJ] %i tex. coords, %i normals",texCoordsRead,normsRead);

		//do some cleaning
		vertices->shrinkToFit();
		if (normals)
			normals->shrinkToFit();
		if (texCoords)
			texCoords->shrinkToFit();
		if (baseMesh->size() == 0)
		{
			delete baseMesh;
			baseMesh = 0;
		}
		else
		{
			baseMesh->shrinkToFit();
		}

		if (	maxVertexIndex >= pointsRead
			||	maxTexCoordIndex >= texCoordsRead
			||	maxTriNormIndex >= normsRead)
		{
			//hum, we've got a problem here
			ccLog::Warning("[OBJ] Malformed file: indexes go higher than the number of elements! (v=%i/tc=%i/n=%i)",maxVertexIndex,maxTexCoordIndex,maxTriNormIndex);
			if (maxVertexIndex >= pointsRead)
			{
				error = true;
			}
			else
			{
				objWarnings[INVALID_INDEX] = true;
				if (maxTexCoordIndex >= texCoordsRead)
				{
					texCoords->release();
					texCoords = 0;
					materials->release();
					materials = 0;
				}
				if (maxTriNormIndex >= normsRead)
				{
					normals->release();
					normals = 0;
				}
			}
		}
		
		if (!error && baseMesh)
		{
			if (normals && normalsPerFacet)
			{
				baseMesh->setTriNormsTable(normals);
				baseMesh->showTriNorms(true);
			}
			if (materials)
			{
				baseMesh->setMaterialSet(materials);
				baseMesh->showMaterials(true);
			}
			if (texCoords)
			{
				if (materials)
				{
					baseMesh->setTexCoordinatesTable(texCoords);
				}
				else
				{
					ccLog::Warning("[OBJ] Texture coordinates were defined but no material could be loaded!");
				}
			}

			//normals: if the obj file doesn't provide any, should we compute them?
			if (!normals)
			{
				//DGM: normals can be per-vertex or per-triangle so it's better to let the user do it himself later
				//Moreover it's not always good idea if the user doesn't want normals (especially in ccViewer!)
				//if (!materials && !baseMesh->hasColors()) //yes if no material is available!
				//{
				//	ccLog::Print("[OBJ] Mesh has no normal! We will compute them automatically");
				//	baseMesh->computeNormals();
				//	baseMesh->showNormals(true);
				//}
				//else
				{
					ccLog::Warning("[OBJ] Mesh has no normal! You can manually compute them (select it then call \"Edit > Normals > Compute\")");
				}
			}

			//create sub-meshes if necessary
			ccLog::Print("[OBJ] 1 mesh loaded - %i group(s)", groups.size());
			if (groups.size() > 1)
			{
				for (size_t i=0; i<groups.size(); ++i)
				{
					const QString& groupName = groups[i].second;
					unsigned startIndex = groups[i].first;
					unsigned endIndex = (i+1 == groups.size() ? baseMesh->size() : groups[i+1].first);

					if (startIndex == endIndex)
					{
						continue;
					}

					ccSubMesh* subTri = new ccSubMesh(baseMesh);
					if (subTri->reserve(endIndex-startIndex))
					{
						subTri->addTriangleIndex(startIndex,endIndex);
						subTri->setName(groupName);
						subTri->showMaterials(baseMesh->materialsShown());
						subTri->showNormals(baseMesh->normalsShown());
						subTri->showTriNorms(baseMesh->triNormsShown());
						//subTri->showColors(baseMesh->colorsShown());
						//subTri->showWired(baseMesh->isShownAsWire());
						baseMesh->addChild(subTri);
					}
					else
					{
						delete subTri;
						subTri = 0;
						objWarnings[NOT_ENOUGH_MEMORY] = true;
					}
				}
				baseMesh->setVisible(false);
				vertices->setLocked(true);
			}

			baseMesh->addChild(vertices);
			//DGM: we can't deactive the vertices if it has children! (such as polyline)
			if (vertices->getChildrenNumber() != 0)
				vertices->setVisible(false);
			else
				vertices->setEnabled(false);

			container.addChild(baseMesh);
		}

		if (!baseMesh && vertices->size() != 0)
		{
			//no (valid) mesh!
			container.addChild(vertices);
			//we hide the vertices if the entity has children (probably polylines!)
			if (vertices->getChildrenNumber() != 0)
			{
				vertices->setVisible(false);
			}
		}

		//special case: normals held by cloud!
		if (normals && !normalsPerFacet)
		{
			if (normsRead == pointsRead) //must be 'per-vertex' normals
			{
				vertices->setNormsTable(normals);
				if (baseMesh)
					baseMesh->showNormals(true);
			}
			else
			{
				ccLog::Warning("File contains normals which seem to be neither per-vertex nor per-face!!! We had to ignore them...");
			}
		}
	}

	if (error)
	{
		if (baseMesh)
			delete baseMesh;
		if (vertices)
			delete vertices;
	}

	//release shared structures
	if (normals)
	{
		normals->release();
		normals = 0;
	}
	if (texCoords)
	{
		texCoords->release();
		texCoords = 0;
	}
	if (materials)
	{
		materials->release();
		materials = 0;
	}

	pDlg.close();

	//potential warnings
	if (objWarnings[INVALID_NORMALS])
		ccLog::Warning("[OBJ] Some normals in file were invalid. You should re-compute them (select entity, then \"Edit > Normals > Compute\")");
	if (objWarnings[INVALID_INDEX])
		ccLog::Warning("[OBJ] File is malformed! Check indexes...");
	if (objWarnings[NOT_ENOUGH_MEMORY])
		ccLog::Warning("[OBJ] Not enough memory!");
	if (objWarnings[INVALID_LINE])
		ccLog::Warning("[OBJ] File is malformed! Missing data.");

	if (error)
	{
		if (objWarnings[NOT_ENOUGH_MEMORY])
			return CC_FERR_NOT_ENOUGH_MEMORY;
		else if (objWarnings[CANCELLED_BY_USER])
			return CC_FERR_CANCELED_BY_USER;
		else 
			return CC_FERR_MALFORMED_FILE;
	}
	else
	{
		return CC_FERR_NO_ERROR;
	}
}
Example #4
0
//converts a FBX mesh to a CC mesh
static ccMesh* FromFbxMesh(FbxMesh* fbxMesh, FileIOFilter::LoadParameters& parameters)
{
	if (!fbxMesh)
		return 0;

	int polyCount = fbxMesh->GetPolygonCount();
	//fbxMesh->GetLayer(
	unsigned triCount = 0;
	unsigned polyVertCount = 0; //different from vertCount (vertices can be counted multiple times here!)
	//as we can't load all polygons (yet ;) we already look if we can load any!
	{
		unsigned skipped = 0;
		for (int i=0; i<polyCount; ++i)
		{
			int pSize = fbxMesh->GetPolygonSize(i);

			if (pSize == 3)
			{
				++triCount;
				polyVertCount += 3;
			}
			else if (pSize == 4)
			{
				triCount += 2;
				polyVertCount += 4;
			}
			else
			{
				++skipped;
			}
		}

		if (triCount == 0)
		{
			ccLog::Warning(QString("[FBX] No triangle or quad found in mesh '%1'! (polygons with more than 4 vertices are not supported for the moment)").arg(fbxMesh->GetName()));
			return 0;
		}
		else if (skipped != 0)
		{
			ccLog::Warning(QString("[FBX] Some polygons in mesh '%1' were ignored (%2): polygons with more than 4 vertices are not supported for the moment)").arg(fbxMesh->GetName()).arg(skipped));
			return 0;
		}
	}

	int vertCount = fbxMesh->GetControlPointsCount();
	if (vertCount <= 0)
	{
		ccLog::Warning(QString("[FBX] Mesh '%1' has no vetex or no polygon?!").arg(fbxMesh->GetName()));
		return 0;
	}

	ccPointCloud* vertices = new ccPointCloud("vertices");
	ccMesh* mesh = new ccMesh(vertices);
	mesh->setName(fbxMesh->GetName());
	mesh->addChild(vertices);
	vertices->setEnabled(false);
	
	if (!mesh->reserve(static_cast<unsigned>(triCount)) || !vertices->reserve(vertCount))
	{
		ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1'!").arg(fbxMesh->GetName()));
		delete mesh;
		return 0;
	}

	//colors
	{
		for (int l=0; l<fbxMesh->GetElementVertexColorCount(); l++)
		{
			FbxGeometryElementVertexColor* vertColor = fbxMesh->GetElementVertexColor(l);
			//CC can only handle per-vertex colors
			if (vertColor->GetMappingMode() == FbxGeometryElement::eByControlPoint)
			{
				if (vertColor->GetReferenceMode() == FbxGeometryElement::eDirect
					|| vertColor->GetReferenceMode() == FbxGeometryElement::eIndexToDirect)
				{
					if (vertices->reserveTheRGBTable())
					{
						switch (vertColor->GetReferenceMode())
						{
						case FbxGeometryElement::eDirect:
							{
								for (int i=0; i<vertCount; ++i)
								{
									FbxColor c = vertColor->GetDirectArray().GetAt(i);
									vertices->addRGBColor(	static_cast<colorType>(c.mRed	* ccColor::MAX),
															static_cast<colorType>(c.mGreen	* ccColor::MAX),
															static_cast<colorType>(c.mBlue	* ccColor::MAX) );
								}
							}
							break;
						case FbxGeometryElement::eIndexToDirect:
							{
								for (int i=0; i<vertCount; ++i)
								{
									int id = vertColor->GetIndexArray().GetAt(i);
									FbxColor c = vertColor->GetDirectArray().GetAt(id);
									vertices->addRGBColor(	static_cast<colorType>(c.mRed	* ccColor::MAX),
															static_cast<colorType>(c.mGreen	* ccColor::MAX),
															static_cast<colorType>(c.mBlue	* ccColor::MAX) );
								}
							}
							break;
						default:
							assert(false);
							break;
						}

						vertices->showColors(true);
						mesh->showColors(true);
						break; //no need to look for other color fields (we won't be able to handle them!
					}
					else
					{
						ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' colors!").arg(fbxMesh->GetName()));
					}
				}
				else
				{
					ccLog::Warning(QString("[FBX] Color field #%i of mesh '%1' will be ignored (unhandled type)").arg(l).arg(fbxMesh->GetName()));
				}
			}
			else
			{
				ccLog::Warning(QString("[FBX] Color field #%i of mesh '%1' will be ignored (unhandled type)").arg(l).arg(fbxMesh->GetName()));
			}
		}
	}

	//normals can be per vertices or per-triangle
	int perPointNormals = -1;
	int perVertexNormals = -1;
	int perPolygonNormals = -1;
	{
		for (int j=0; j<fbxMesh->GetElementNormalCount(); j++)
		{
			FbxGeometryElementNormal* leNormals = fbxMesh->GetElementNormal(j);
			switch(leNormals->GetMappingMode())
			{
			case FbxGeometryElement::eByControlPoint:
				perPointNormals = j;
				break;
			case FbxGeometryElement::eByPolygonVertex:
				perVertexNormals = j;
				break;
			case FbxGeometryElement::eByPolygon:
				perPolygonNormals = j;
				break;
			default:
				//not handled
				break;
			}
		}
	}

	//per-point normals
	if (perPointNormals >= 0)
	{
		FbxGeometryElementNormal* leNormals = fbxMesh->GetElementNormal(perPointNormals);
		FbxLayerElement::EReferenceMode refMode = leNormals->GetReferenceMode();
		const FbxLayerElementArrayTemplate<FbxVector4>& normals = leNormals->GetDirectArray();
		assert(normals.GetCount() == vertCount);
		if (normals.GetCount() != vertCount)
		{
			ccLog::Warning(QString("[FBX] Wrong number of normals on mesh '%1'!").arg(fbxMesh->GetName()));
			perPointNormals = -1;
		}
		else if (!vertices->reserveTheNormsTable())
		{
			ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' normals!").arg(fbxMesh->GetName()));
			perPointNormals = -1;
		}
		else
		{
			//import normals
			for (int i=0; i<vertCount; ++i)
			{
				int id = refMode != FbxGeometryElement::eDirect ? leNormals->GetIndexArray().GetAt(i) : i;
				FbxVector4 N = normals.GetAt(id);
				//convert to CC-structure
				CCVector3 Npc(	static_cast<PointCoordinateType>(N.Buffer()[0]),
								static_cast<PointCoordinateType>(N.Buffer()[1]),
								static_cast<PointCoordinateType>(N.Buffer()[2]) );
				vertices->addNorm(Npc);
			}
			vertices->showNormals(true);
			mesh->showNormals(true);
			//no need to import the other normals (if any)
			perVertexNormals = -1;
			perPolygonNormals = -1;
		}
	}

	//per-triangle normals
	NormsIndexesTableType* normsTable = 0;
	if (perVertexNormals >= 0 || perPolygonNormals >= 0)
	{
		normsTable = new NormsIndexesTableType();
		if (!normsTable->reserve(polyVertCount) || !mesh->reservePerTriangleNormalIndexes())
		{
			ccLog::Warning(QString("[FBX] Not enough memory to load mesh '%1' normals!").arg(fbxMesh->GetName()));
			normsTable->release();
			normsTable = 0;
		}
		else
		{
			mesh->setTriNormsTable(normsTable);
			vertices->showNormals(true);
			mesh->showNormals(true);
		}
	}

	//materials
	ccMaterialSet* materials = 0;
	{
		FbxNode* lNode = fbxMesh->GetNode();
		int lMaterialCount = lNode ? lNode->GetMaterialCount() : 0;
		for (int i=0; i<lMaterialCount; i++)
		{
			FbxSurfaceMaterial *lBaseMaterial = lNode->GetMaterial(i);

			bool isLambert = lBaseMaterial->GetClassId().Is(FbxSurfaceLambert::ClassId);
			bool isPhong = lBaseMaterial->GetClassId().Is(FbxSurfacePhong::ClassId);
			if (isLambert || isPhong)
			{
				ccMaterial::Shared mat(new ccMaterial(lBaseMaterial->GetName()));

				FbxSurfaceLambert* lLambertMat = static_cast<FbxSurfaceLambert*>(lBaseMaterial);
			
				float ambient[4];
				float diffuse[4];
				float emission[4];
				float specular[4];

				FbxSurfacePhong* lPhongMat = isPhong ? static_cast<FbxSurfacePhong*>(lBaseMaterial) : 0;

				for (int k=0; k<3; ++k)
				{
					ambient[k]  = static_cast<float>(lLambertMat->Ambient.Get()[k]);
					diffuse[k]  = static_cast<float>(lLambertMat->Diffuse.Get()[k]);
					emission[k] = static_cast<float>(lLambertMat->Emissive.Get()[k]);

					if (lPhongMat)
					{
						specular[k]		= static_cast<float>(lPhongMat->Specular.Get()[k]);
					}
				}

				mat->setAmbient(ambient);
				mat->setDiffuse(diffuse);
				mat->setEmission(emission);
				if (isPhong)
				{
					mat->setSpecular(specular);
					assert(lPhongMat);
					mat->setShininess(static_cast<float>(lPhongMat->Shininess));
				}

				//import associated texture (if any)
				{
					int lTextureIndex;
					FBXSDK_FOR_EACH_TEXTURE(lTextureIndex)
					{
						FbxProperty lProperty = lBaseMaterial->FindProperty(FbxLayerElement::sTextureChannelNames[lTextureIndex]);
						if( lProperty.IsValid() )
						{
							int lTextureCount = lProperty.GetSrcObjectCount<FbxTexture>();
							FbxTexture* texture = 0; //we can handle only one texture per material! We'll take the non layered one by default (if any)
							for (int j = 0; j < lTextureCount; ++j)
							{
								//Here we have to check if it's layeredtextures, or just textures:
								FbxLayeredTexture *lLayeredTexture = lProperty.GetSrcObject<FbxLayeredTexture>(j);
								if (lLayeredTexture)
								{
									//we don't handle layered textures!
									/*int lNbTextures = lLayeredTexture->GetSrcObjectCount<FbxTexture>();
									for (int k=0; k<lNbTextures; ++k)
									{
										FbxTexture* lTexture = lLayeredTexture->GetSrcObject<FbxTexture>(k);
										if(lTexture)
										{
										}
									}
									//*/
								}
								else
								{
									//non-layered texture
									FbxTexture* lTexture = lProperty.GetSrcObject<FbxTexture>(j);
									if(lTexture)
									{
										//we take the first non layered texture by default
										texture = lTexture;
										break;
									}
								}
							}

							if (texture)
							{
								FbxFileTexture *lFileTexture = FbxCast<FbxFileTexture>(texture);
								if (lFileTexture)
								{
									const char* texAbsoluteFilename = lFileTexture->GetFileName();
									ccLog::PrintDebug(QString("[FBX] Texture absolue filename: %1").arg(texAbsoluteFilename));
									if (texAbsoluteFilename != 0 && texAbsoluteFilename[0] != 0)
									{
										if (!mat->loadAndSetTexture(texAbsoluteFilename))
										{
											ccLog::Warning(QString("[FBX] Failed to load texture file: %1").arg(texAbsoluteFilename));
										}
									}
								}
							}
						}
					}
				}

				if (!materials)
				{
					materials = new ccMaterialSet("materials");
					mesh->addChild(materials);
				}
				materials->addMaterial(mat);
			}
			else
			{
				ccLog::Warning(QString("[FBX] Material '%1' has an unhandled type").arg(lBaseMaterial->GetName()));
			}
		}