Example #1
0
FbxNode* ILDLMesh::CreateMesh( FbxScene* pScene, const char* pName )
{
    int nVerts  = (int)m_vertex_list.size();

    FbxMesh* lMesh = FbxMesh::Create(pScene,pName);
    lMesh->InitControlPoints( nVerts );
    FbxVector4* lControlPoints = lMesh->GetControlPoints();
    FbxGeometryElementNormal* lGeometryElementNormal= lMesh->CreateElementNormal();
    lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);
    lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);


    int vi = 0, glidx = 0;
    for( ILDLVertexIter i = m_vertex_list.begin();
        i !=  m_vertex_list.end(); ++i, vi++ )
    {
        ILDLVertexPtr vp = (*i);
        vp->m_glIdx = glidx;
        vec3 coords = vp->getCoords();
        lControlPoints[vi] = FbxVector4( coords[0], coords[1], coords[2] );
        vec3 normal = vp->getNormal();
        lGeometryElementNormal->GetDirectArray().Add( FbxVector4( normal[0], normal[1], normal[2] ) );
        glidx++;
    }

    for( ILDLFaceIter i = m_face_list.begin();
        i != m_face_list.end(); ++i )
    {
        ILDLFacePtr fp = *i;
        ILDLFaceVertexPtr fvp = fp->front();

        lMesh->BeginPolygon(-1, -1, -1, false);
        lMesh->AddPolygon( fvp->getVertexPtr()->m_glIdx );
        fvp = fvp->next();
        lMesh->AddPolygon( fvp->getVertexPtr()->m_glIdx );
        fvp = fvp->next();
        lMesh->AddPolygon( fvp->getVertexPtr()->m_glIdx );
        lMesh->EndPolygon ();
    }


    FbxNode* lNode = FbxNode::Create(pScene,pName);
    lNode->SetNodeAttribute(lMesh);

    return lNode;
}
Example #2
0
// Converts a CC mesh to an FBX mesh
static FbxNode* ToFbxMesh(ccGenericMesh* mesh, FbxScene* pScene)
{
	if (!mesh)
		return 0;

    FbxMesh* lMesh = FbxMesh::Create(pScene, qPrintable(mesh->getName()));

	ccGenericPointCloud* cloud = mesh->getAssociatedCloud();
	if (!cloud)
		return 0;
	unsigned vertCount = cloud->size();
	unsigned faceCount = mesh->size();

    // Create control points.
	{
		lMesh->InitControlPoints(vertCount);
		FbxVector4* lControlPoints = lMesh->GetControlPoints();

		for (unsigned i=0; i<vertCount; ++i)
		{
			const CCVector3* P = cloud->getPoint(i);
			lControlPoints[i] = FbxVector4(P->x,P->y,P->z);
		}
	}

	ccMesh* asCCMesh = 0;
	if (mesh->isA(CC_MESH))
		asCCMesh = static_cast<ccMesh*>(mesh);

    // normals
	if (mesh->hasNormals())
	{
		FbxGeometryElementNormal* lGeometryElementNormal = lMesh->CreateElementNormal();
		if (mesh->hasTriNormals())
		{
			// We want to have one normal per vertex of each polygon,
			// so we set the mapping mode to eByPolygonVertex.
			lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByPolygonVertex);
			lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eIndexToDirect);
			lGeometryElementNormal->GetIndexArray().SetCount(faceCount*3);
			
			if (asCCMesh)
			{
				NormsIndexesTableType* triNorms = asCCMesh->getTriNormsTable();
				assert(triNorms);
				for (unsigned i=0; i<triNorms->currentSize(); ++i)
				{
					const PointCoordinateType* N = ccNormalVectors::GetNormal(triNorms->getValue(i));
					FbxVector4 Nfbx(N[0],N[1],N[2]);
					lGeometryElementNormal->GetDirectArray().Add(Nfbx);
				}
				for (unsigned j=0; j<faceCount; ++j)
				{
					int i1,i2,i3;
					asCCMesh->getTriangleNormalIndexes(j,i1,i2,i3);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+0, i1);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+1, i2);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+2, i3);
				}
			}
			else
			{
				for (unsigned j=0; j<faceCount; ++j)
				{
					//we can't use the 'NormsIndexesTable' so we save all the normals of all the vertices
					CCVector3 Na,Nb,Nc;
					lGeometryElementNormal->GetDirectArray().Add(FbxVector4(Na.x,Na.y,Na.z));
					lGeometryElementNormal->GetDirectArray().Add(FbxVector4(Nb.x,Nb.y,Nb.z));
					lGeometryElementNormal->GetDirectArray().Add(FbxVector4(Nc.x,Nc.y,Nc.z));
					
					mesh->getTriangleNormals(j,Na,Nb,Nc);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+0, static_cast<int>(j)*3+0);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+1, static_cast<int>(j)*3+1);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+2, static_cast<int>(j)*3+2);
				}
			}
		}
		else
		{
			// We want to have one normal for each vertex (or control point),
			// so we set the mapping mode to eByControlPoint.
			lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);
			// The first method is to set the actual normal value
			// for every control point.
			lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);
			for (unsigned i=0; i<vertCount; ++i)
			{
				const PointCoordinateType* N = cloud->getPointNormal(i);
				FbxVector4 Nfbx(N[0],N[1],N[2]);
				lGeometryElementNormal->GetDirectArray().Add(Nfbx);
			}
		}
	}
	else
	{
		ccLog::Warning("[FBX] Mesh has no normal! You can manually compute them (select it then call \"Edit > Normals > Compute\")");
	}

    // colors
	if (cloud->hasColors())
	{
		FbxGeometryElementVertexColor* lGeometryElementVertexColor = lMesh->CreateElementVertexColor();
		lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint);
		lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect);
		for (unsigned i=0; i<vertCount; ++i)
		{
			const colorType* C = cloud->getPointColor(i);
			FbxColor col( FbxDouble3(	static_cast<double>(C[0])/MAX_COLOR_COMP,
										static_cast<double>(C[1])/MAX_COLOR_COMP,
										static_cast<double>(C[2])/MAX_COLOR_COMP ) );
			lGeometryElementVertexColor->GetDirectArray().Add(col);
		}
	}

	// Set material mapping.
    //FbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
    //lMaterialElement->SetMappingMode(FbxGeometryElement::eByPolygon);
    //lMaterialElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

    // Create polygons. Assign material indices.
	{
		for (unsigned j=0; j<faceCount; ++j)
		{
			const CCLib::TriangleSummitsIndexes* tsi = mesh->getTriangleIndexes(j);

			lMesh->BeginPolygon(static_cast<int>(j));
			lMesh->AddPolygon(tsi->i1);
			lMesh->AddPolygon(tsi->i2);
			lMesh->AddPolygon(tsi->i3);
			lMesh->EndPolygon();
		}
	}

    FbxNode* lNode = FbxNode::Create(pScene,qPrintable(mesh->getName()));

    lNode->SetNodeAttribute(lMesh);

    //CreateMaterials(pScene, lMesh);

    return lNode;
}
Example #3
0
// Converts a CC mesh to an FBX mesh
static FbxNode* ToFbxMesh(ccGenericMesh* mesh, FbxScene* pScene, QString filename, size_t meshIndex)
{
	if (!mesh)
		return 0;

	FbxNode* lNode = FbxNode::Create(pScene,qPrintable(mesh->getName()));
	FbxMesh* lMesh = FbxMesh::Create(pScene, qPrintable(mesh->getName()));
	lNode->SetNodeAttribute(lMesh);


	ccGenericPointCloud* cloud = mesh->getAssociatedCloud();
	if (!cloud)
		return 0;
	unsigned vertCount = cloud->size();
	unsigned faceCount = mesh->size();

	// Create control points.
	{
		lMesh->InitControlPoints(vertCount);
		FbxVector4* lControlPoints = lMesh->GetControlPoints();

		for (unsigned i=0; i<vertCount; ++i)
		{
			const CCVector3* P = cloud->getPoint(i);
			lControlPoints[i] = FbxVector4(P->x,P->y,P->z);
			//lControlPoints[i] = FbxVector4(P->x,P->z,-P->y); //DGM: see loadFile (Y and Z are inverted)
		}
	}

	ccMesh* asCCMesh = 0;
	if (mesh->isA(CC_TYPES::MESH))
		asCCMesh = static_cast<ccMesh*>(mesh);

	// normals
	if (mesh->hasNormals())
	{
		FbxGeometryElementNormal* lGeometryElementNormal = lMesh->CreateElementNormal();
		if (mesh->hasTriNormals())
		{
			// We want to have one normal per vertex of each polygon,
			// so we set the mapping mode to eByPolygonVertex.
			lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByPolygonVertex);
			lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eIndexToDirect);
			lGeometryElementNormal->GetIndexArray().SetCount(faceCount*3);
			
			if (asCCMesh)
			{
				NormsIndexesTableType* triNorms = asCCMesh->getTriNormsTable();
				assert(triNorms);
				for (unsigned i=0; i<triNorms->currentSize(); ++i)
				{
					const CCVector3& N = ccNormalVectors::GetNormal(triNorms->getValue(i));
					FbxVector4 Nfbx(N.x,N.y,N.z);
					lGeometryElementNormal->GetDirectArray().Add(Nfbx);
				}
				for (unsigned j=0; j<faceCount; ++j)
				{
					int i1,i2,i3;
					asCCMesh->getTriangleNormalIndexes(j,i1,i2,i3);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+0, i1);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+1, i2);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+2, i3);
				}
			}
			else
			{
				for (unsigned j=0; j<faceCount; ++j)
				{
					//we can't use the 'NormsIndexesTable' so we save all the normals of all the vertices
					CCVector3 Na,Nb,Nc;
					lGeometryElementNormal->GetDirectArray().Add(FbxVector4(Na.x,Na.y,Na.z));
					lGeometryElementNormal->GetDirectArray().Add(FbxVector4(Nb.x,Nb.y,Nb.z));
					lGeometryElementNormal->GetDirectArray().Add(FbxVector4(Nc.x,Nc.y,Nc.z));
					
					mesh->getTriangleNormals(j,Na,Nb,Nc);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+0, static_cast<int>(j)*3+0);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+1, static_cast<int>(j)*3+1);
					lGeometryElementNormal->GetIndexArray().SetAt(static_cast<int>(j)*3+2, static_cast<int>(j)*3+2);
				}
			}
		}
		else
		{
			// We want to have one normal for each vertex (or control point),
			// so we set the mapping mode to eByControlPoint.
			lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);
			// The first method is to set the actual normal value
			// for every control point.
			lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);
			for (unsigned i=0; i<vertCount; ++i)
			{
				const CCVector3& N = cloud->getPointNormal(i);
				FbxVector4 Nfbx(N.x,N.y,N.z);
				lGeometryElementNormal->GetDirectArray().Add(Nfbx);
			}
		}
	}
	else
	{
		ccLog::Warning("[FBX] Mesh has no normal! You can manually compute them (select it then call \"Edit > Normals > Compute\")");
	}

	// Set material mapping.
	bool hasMaterial = false;
	if (asCCMesh && asCCMesh->hasMaterials())
	{
		const ccMaterialSet* matSet = asCCMesh->getMaterialSet();
		size_t matCount = matSet->size();

		//check if we have textures
		bool hasTextures = asCCMesh->hasTextures();
		if (hasTextures)
		{
			//check that we actually have materials with textures as well!
			hasTextures = false;
			for (size_t i=0; i<matCount; ++i)
			{
				ccMaterial::CShared mat = matSet->at(i);
				if (mat->hasTexture())
				{
					hasTextures = true;
					break;
				}
			}
		}

		static const char gDiffuseElementName[] = "DiffuseUV";

		// Create UV for Diffuse channel
		if (hasTextures)
		{
			FbxGeometryElementUV* lUVDiffuseElement = lMesh->CreateElementUV(gDiffuseElementName);
			assert(lUVDiffuseElement != 0);
			lUVDiffuseElement->SetMappingMode(FbxGeometryElement::eByPolygonVertex);
			lUVDiffuseElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);

			//fill Direct Array
			const TextureCoordsContainer* texCoords = asCCMesh->getTexCoordinatesTable();
			assert(texCoords);
			if (texCoords)
			{
				unsigned count = texCoords->currentSize();
				lUVDiffuseElement->GetDirectArray().SetCount(static_cast<int>(count));
				for (unsigned i=0; i<count; ++i)
				{
					const float* uv = texCoords->getValue(i);
					lUVDiffuseElement->GetDirectArray().SetAt(i,FbxVector2(uv[0],uv[1]));
				}
			}

			//fill Indexes Array
			assert(asCCMesh->hasPerTriangleTexCoordIndexes());
			if (asCCMesh->hasPerTriangleTexCoordIndexes())
			{
				unsigned triCount = asCCMesh->size();
				lUVDiffuseElement->GetIndexArray().SetCount(static_cast<int>(3*triCount));
				for (unsigned j=0; j<triCount; ++j)
				{
					int t1=0, t2=0, t3=0;
					asCCMesh->getTriangleTexCoordinatesIndexes(j, t1, t2, t3);

					lUVDiffuseElement->GetIndexArray().SetAt(j*3+0,t1);
					lUVDiffuseElement->GetIndexArray().SetAt(j*3+1,t2);
					lUVDiffuseElement->GetIndexArray().SetAt(j*3+2,t3);
				}
			}
		}

		//Textures used in this file
		QMap<QString,QString> texFilenames;
		//directory to save textures (if any)
		QFileInfo info(filename);
		QString textDirName = info.baseName() + QString(".fbm");
		QDir baseDir = info.absoluteDir();
		QDir texDir = QDir(baseDir.absolutePath() + QString("/") + textDirName);

		for (size_t i=0; i<matCount; ++i)
		{
			ccMaterial::CShared mat = matSet->at(i);
			FbxSurfacePhong *lMaterial = FbxSurfacePhong::Create(pScene, qPrintable(mat->getName()));

			const ccColor::Rgbaf& emission = mat->getEmission();
			const ccColor::Rgbaf& ambient = mat->getAmbient();
			const ccColor::Rgbaf& diffuse = mat->getDiffuseFront();
			const ccColor::Rgbaf& specular = mat->getDiffuseFront();
			lMaterial->Emissive.Set(FbxDouble3(emission.r,emission.g,emission.b));
			lMaterial->Ambient .Set(FbxDouble3( ambient.r, ambient.g, ambient.b));
			lMaterial->Diffuse .Set(FbxDouble3( diffuse.r, diffuse.g, diffuse.b));
			lMaterial->Specular.Set(FbxDouble3(specular.r,specular.g,specular.b));
			lMaterial->Shininess = mat->getShininessFront();
			lMaterial->ShadingModel.Set("Phong");

			if (hasTextures && mat->hasTexture())
			{
				QString texFilename = mat->getTextureFilename();
				
				//texture has not already been processed
				if (!texFilenames.contains(texFilename))
				{
					//if necessary, we (try to) create a subfolder to store textures
					if (!texDir.exists())
					{
						texDir = baseDir;
						if (texDir.mkdir(textDirName))
						{
							texDir.cd(textDirName);
						}
						else
						{
							textDirName = QString();
							ccLog::Warning("[FBX] Failed to create subfolder '%1' to store texture files (files will be stored next to the .fbx file)");
						}
					}

					QFileInfo fileInfo(texFilename);
					QString baseTexName = fileInfo.fileName();
					//add extension
					QString extension = QFileInfo(texFilename).suffix();
					if (fileInfo.suffix().isEmpty())
						baseTexName += QString(".png");

					QString absoluteFilename = texDir.absolutePath() + QString("/") + baseTexName;
					ccLog::PrintDebug(QString("[FBX] Material '%1' texture: %2").arg(mat->getName()).arg(absoluteFilename));

					texFilenames[texFilename] = absoluteFilename;
				}
				//mat.texture.save(absoluteFilename);

				// Set texture properties.
				FbxFileTexture* lTexture = FbxFileTexture::Create(pScene,"DiffuseTexture");
				assert(!texFilenames[texFilename].isEmpty());
				lTexture->SetFileName(qPrintable(texFilenames[texFilename]));
				lTexture->SetTextureUse(FbxTexture::eStandard);
				lTexture->SetMappingType(FbxTexture::eUV);
				lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial);
				lTexture->SetSwapUV(false);
				lTexture->SetTranslation(0.0, 0.0);
				lTexture->SetScale(1.0, 1.0);
				lTexture->SetRotation(0.0, 0.0);
				lTexture->UVSet.Set(FbxString(gDiffuseElementName)); // Connect texture to the proper UV

				// don't forget to connect the texture to the corresponding property of the material
				lMaterial->Diffuse.ConnectSrcObject(lTexture);
			}

			int matIndex = lNode->AddMaterial(lMaterial);
			assert(matIndex  == static_cast<int>(i));
		}

		//don't forget to save the texture files
		{
			for (QMap<QString,QString>::ConstIterator it = texFilenames.begin(); it != texFilenames.end(); ++it)
			{
				const QImage image = ccMaterial::GetTexture(it.key());
				image.mirrored().save(it.value());
			}
			
			texFilenames.clear(); //don't need this anymore!
		}

		// Create 'triangle to material index' mapping
		{
			FbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
			lMaterialElement->SetMappingMode(FbxGeometryElement::eByPolygon);
			lMaterialElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect);
		}

		hasMaterial = true;
	}

	// colors
	if (cloud->hasColors())
	{
		FbxGeometryElementVertexColor* lGeometryElementVertexColor = lMesh->CreateElementVertexColor();
		lGeometryElementVertexColor->SetMappingMode(FbxGeometryElement::eByControlPoint);
		lGeometryElementVertexColor->SetReferenceMode(FbxGeometryElement::eDirect);
		lGeometryElementVertexColor->GetDirectArray().SetCount(vertCount);
		for (unsigned i=0; i<vertCount; ++i)
		{
			const colorType* C = cloud->getPointColor(i);
			FbxColor col(	static_cast<double>(C[0])/ccColor::MAX,
							static_cast<double>(C[1])/ccColor::MAX,
							static_cast<double>(C[2])/ccColor::MAX );
			lGeometryElementVertexColor->GetDirectArray().SetAt(i,col);
		}

		if (!hasMaterial)
		{
			//it seems that we have to create a fake material in order for the colors to be displayed (in Unity and FBX Review at least)!
			FbxSurfacePhong *lMaterial = FbxSurfacePhong::Create(pScene, "ColorMaterial");

			lMaterial->Emissive.Set(FbxDouble3(0,0,0));
			lMaterial->Ambient.Set(FbxDouble3(0,0,0));
			lMaterial->Diffuse.Set(FbxDouble3(1,1,1));
			lMaterial->Specular.Set(FbxDouble3(0,0,0));
			lMaterial->Shininess = 0;
			lMaterial->ShadingModel.Set("Phong");

			FbxGeometryElementMaterial* lMaterialElement = lMesh->CreateElementMaterial();
			lMaterialElement->SetMappingMode(FbxGeometryElement::eAllSame);
			lMaterialElement->SetReferenceMode(FbxGeometryElement::eDirect);
			lNode->AddMaterial(lMaterial);
		}
	}

	// Create polygons
	{
		for (unsigned j=0; j<faceCount; ++j)
		{
			const CCLib::TriangleSummitsIndexes* tsi = mesh->getTriangleIndexes(j);

			int matIndex = hasMaterial ? asCCMesh->getTriangleMtlIndex(j) : -1;
			lMesh->BeginPolygon(matIndex);
			lMesh->AddPolygon(tsi->i1);
			lMesh->AddPolygon(tsi->i2);
			lMesh->AddPolygon(tsi->i3);
			lMesh->EndPolygon();
		}
	}

	return lNode;
}
Example #4
0
void ExportFbxMesh(const Value& obj)
{
	string name = obj["Name"].GetString();

	FbxNode* pNode = FbxNode::Create(pManager, name.c_str());
	FbxMesh* pMesh = FbxMesh::Create(pManager, name.c_str());
	pNode->AddNodeAttribute(pMesh);
	pScene->GetRootNode()->AddChild(pNode);

	int numVertex = obj["NumVertex"].GetInt();
	
	{
		pMesh->InitControlPoints(numVertex);
		FbxVector4* lControlPoints = pMesh->GetControlPoints();
		const Value& pos = obj["Position"];
		for (int i = 0; i < numVertex; i++)
		{
			double x = pos[i * 3 + 0].GetDouble();
			x = -x;
			double y = pos[i * 3 + 1].GetDouble();
			double z = pos[i * 3 + 2].GetDouble();
			lControlPoints[i] = FbxVector4(x, y, z);
		}
	}

	{
		FbxGeometryElementNormal* lGeometryElementNormal = pMesh->CreateElementNormal();
		lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);
		lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);
		FbxLayerElementArrayTemplate<FbxVector4>& array = lGeometryElementNormal->GetDirectArray();

		const Value& normal = obj["Normal"];
		for (int i = 0; i < numVertex; i++)
		{
			double x = normal[i * 3 + 0].GetDouble();
			x = -x;
			double y = normal[i * 3 + 1].GetDouble();
			double z = normal[i * 3 + 2].GetDouble();
			array.Add(FbxVector4(x, y, z));
		}
	}

	{
		FbxGeometryElementUV* lUVDiffuseElement = pMesh->CreateElementUV("DiffuseUV");
		FBX_ASSERT(lUVDiffuseElement != NULL);
		lUVDiffuseElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
		lUVDiffuseElement->SetReferenceMode(FbxGeometryElement::eDirect);
		FbxLayerElementArrayTemplate<FbxVector2>& array = lUVDiffuseElement->GetDirectArray();

		const Value& v = obj["UV0"];
		for (int i = 0; i < numVertex; i++)
		{
			double x = v[i * 2 + 0].GetDouble();
			double y = v[i * 2 + 1].GetDouble();
			array.Add(FbxVector2(x, y));
		}
	}

	{
		const Value& color = obj["Color"];
		if (!color.IsNull())
		{
			FbxGeometryElementVertexColor* pColorElement = pMesh->CreateElementVertexColor();
			FBX_ASSERT(pColorElement != NULL);
			pColorElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
			pColorElement->SetReferenceMode(FbxGeometryElement::eDirect);
			FbxLayerElementArrayTemplate<FbxColor>& array = pColorElement->GetDirectArray();

			for (int i = 0; i < numVertex; i++)
			{
				double r = color[i * 4 + 0].GetDouble();
				double g = color[i * 4 + 1].GetDouble();
				double b = color[i * 4 + 2].GetDouble();
				double a = color[i * 4 + 3].GetDouble();
				array.Add(FbxColor(r, g, b, a));
			}
		}
	}

	{
		const Value& Indeices = obj["Indeices"];

		for (uint32_t subMesh = 0; subMesh < Indeices.Size(); subMesh++)
		{
			const Value& index0 = Indeices[subMesh];
			int numIndex = index0.Size();
			printf("index %d\n", numIndex);
			for (int i = 0; i < numIndex / 3; i++)
			{
				pMesh->BeginPolygon(-1, -1, subMesh);
				int index[3] = {
					index0[i * 3 + 0].GetInt(),
					index0[i * 3 + 1].GetInt(),
					index0[i * 3 + 2].GetInt(),
				};

				pMesh->AddPolygon(index[0]);
				pMesh->AddPolygon(index[2]);
				pMesh->AddPolygon(index[1]);
				pMesh->EndPolygon();
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////

	// export skin
	const Value& boneIndex = obj["BoneIndex"];

	if (!boneIndex.IsNull())
	{
		if (fbxBones.empty())
		{
			printf("no bones, can not export skin");
			return;
		}

		const Value& boneWeight = obj["BoneWeight"];
		vector<FbxCluster*> clusters(fbxBones.size(), NULL);
		for (uint32_t i = 0; i < fbxBones.size(); i++) {
			FbxCluster* pCluster = FbxCluster::Create(pScene, "");
			pCluster->SetLink(fbxBones[i]);
			pCluster->SetLinkMode(FbxCluster::eTotalOne);
			clusters[i] = pCluster;
		}

		for (int i = 0; i < numVertex; i++) {
			for (int j = 0; j < 4; j++) {
				int bone = boneIndex[i * 4 + j].GetInt();
				double weight = boneWeight[i * 4 + j].GetDouble();
				clusters[bone]->AddControlPointIndex(i, weight);
			}
		}

		FbxSkin* lSkin = FbxSkin::Create(pScene, "");

		FbxScene* p = pNode->GetScene();
		FbxAMatrix modelMatrix = pNode->EvaluateGlobalTransform();
		for (uint32_t i = 0; i < clusters.size(); i++) {
			clusters[i]->SetTransformMatrix(modelMatrix);
			FbxAMatrix boneMatrix = fbxBones[i]->EvaluateGlobalTransform();
			clusters[i]->SetTransformLinkMatrix(boneMatrix);
			lSkin->AddCluster(clusters[i]);
		}

		pMesh->AddDeformer(lSkin);
	}

}