示例#1
0
		void GameEngine::FbxLoader::FbxLoader::ProcessAnimCurveR(FbxAnimCurve* curve[3], AnimTransformCurve& animCurve, FbxAMatrix & preRotation)
		{
			if(!curve) return;

			auto& curve3 = animCurve.rotation;

			int xKeys = curve[0]->KeyGetCount();
			int yKeys = curve[1]->KeyGetCount();
			int zKeys = curve[2]->KeyGetCount();

			// check key sync
			if(xKeys != yKeys || xKeys != zKeys)
				return;

			int nKeys = xKeys;

			curve3.curves[0].keyframes.resize(nKeys);
			curve3.curves[1].keyframes.resize(nKeys);
			curve3.curves[2].keyframes.resize(nKeys);

			animCurve.end = (float)curve[0]->KeyGetTime(nKeys - 1).GetSecondDouble();

			for(int ki = 0; ki < nKeys; ++ki) {
				auto& xkey = curve3.curves[0].keyframes[ki];
				auto& ykey = curve3.curves[1].keyframes[ki];
				auto& zkey = curve3.curves[2].keyframes[ki];

				curve[0]->KeySetTangentMode(ki, FbxAnimCurveDef::eTangentAuto);
				curve[1]->KeySetTangentMode(ki, FbxAnimCurveDef::eTangentAuto);
				curve[2]->KeySetTangentMode(ki, FbxAnimCurveDef::eTangentAuto);
				auto xvalue = curve[0]->KeyGetValue(ki);
				auto yvalue = curve[1]->KeyGetValue(ki);
				auto zvalue = curve[2]->KeyGetValue(ki);

				auto xlt = curve[0]->KeyGetLeftAuto(ki);
				auto xrt = curve[0]->KeyGetRightAuto(ki);
				auto ylt = curve[1]->KeyGetLeftAuto(ki);
				auto yrt = curve[1]->KeyGetRightAuto(ki);
				auto zlt = curve[2]->KeyGetLeftAuto(ki);
				auto zrt = curve[2]->KeyGetRightAuto(ki);

				FbxAMatrix temp;
				temp.SetR(FbxVector4(xvalue, yvalue, zvalue));
				auto R = (preRotation* temp).GetR();
				xvalue = (float)R[0]; yvalue = (float)R[1]; zvalue = (float)R[2];

				float t = (float)curve[0]->KeyGetTime(ki).GetSecondDouble();

				if(axismode == eLeftHanded) {
					yvalue *= -1; ylt *= -1; yrt *= -1;
					zvalue *= -1; zlt *= -1; zrt *= -1;
				}

				xkey.time = t; ykey.time = t; zkey.time = t;
				xkey.value = xvalue; ykey.value = yvalue; zkey.value = zvalue;
				xkey.leftTangent = xlt; xkey.rightTangent = xrt;
				ykey.leftTangent = ylt; ykey.rightTangent = yrt;
				zkey.leftTangent = zlt; zkey.rightTangent = zrt;
			}
		}
示例#2
0
		void FbxLoader::ComputeNodeMatrix(FbxNode* node, Node& meshNode, bool local)
		{
			if(!node)
				return;
			FbxAnimEvaluator* evaluator = scene->GetAnimationEvaluator();
			FbxAMatrix global;
			global.SetIdentity();

			FbxTime time;
			time.SetSecondDouble(0.0);
			
			if(node != scene->GetRootNode()) {
				if(local) {
					global = evaluator->GetNodeLocalTransform(node, time);
				}
				else {
					global = evaluator->GetNodeGlobalTransform(node, time);
				}
			}
			auto T = global.GetT() * factor;
			
			if(axismode == eLeftHanded) {
				auto R = global.GetR();
				R[1] *= -1; R[2] *= -1;
				T[0] *= -1;
				global.SetR(R);
			}
			global.SetT(T);

			meshNode.matrix = Matrix(
				(float)global[0][0], (float)global[0][1], (float)global[0][2], (float)global[0][3],
				(float)global[1][0], (float)global[1][1], (float)global[1][2], (float)global[1][3],
				(float)global[2][0], (float)global[2][1], (float)global[2][2], (float)global[2][3],
				(float)global[3][0], (float)global[3][1], (float)global[3][2], (float)global[3][3]);
		}
void FBXSceneEncoder::transformNode(FbxNode* fbxNode, Node* node)
{
    FbxAMatrix matrix;
    float m[16];
    
    if (fbxNode->GetCamera() || fbxNode->GetLight())
    {
        FbxAMatrix rotateAdjust;
        
        if(fbxNode->GetLight())
        {
            /*
             * according to the fbx-documentation the light's forward vector
             * points along a node's negative Y axis.
             * so we have to rotate it by 90° around the X-axis to correct it.
             */
            if(fbxNode->RotationActive.Get())
            {
                const FbxVector4& postRotation = fbxNode->PostRotation.Get();
                fbxNode->SetPostRotation(FbxNode::eSourcePivot, FbxVector4(postRotation.mData[0] + 90.0,
                                                                           postRotation.mData[1],
                                                                           postRotation.mData[2])
                                         );
            }
            else
            {
                // if the rotation is deactivated we have to rotate it anyway to get the correct transformation in the end
                rotateAdjust.SetR(FbxVector4(-90.0, 0.0, 0.0));
            }
            
            matrix = fbxNode->EvaluateLocalTransform() * rotateAdjust;
        }
        else if(fbxNode->GetCamera())
        {
            // TODO: use the EvaluateLocalTransform() function for the transformations for the camera
            /*
             * the current implementation ignores pre- and postrotation among others (usually happens with fbx-export from blender)
             *
             * Some info for a future implementation:
             * according to the fbx-documentation the camera's forward vector
             * points along a node's positive X axis.
             * so we have to correct it if we use the EvaluateLocalTransform-function
             * just rotating it by 90° around the Y axis (similar to above) doesn't work
             */
            matrix.SetTRS(fbxNode->LclTranslation.Get(), fbxNode->LclRotation.Get(), fbxNode->LclScaling.Get());
        }
        
        copyMatrix(matrix, m);
        node->setTransformMatrix(m);
    }
    else
    {
        matrix = fbxNode->EvaluateLocalTransform();
        copyMatrix(matrix, m);
        node->setTransformMatrix(m);
    }
}
示例#4
0
MeshData* FBXImporter::GetMeshInfo()
{
	mMeshData = new MeshData();

	int indicesIndexOffset = 0;		// 记录当前mesh在整个ib中的索引位移。
	int verticesIndexOffset = 0;	// 记录当前mesh在整个vb中的顶点位移。

	for (int meshIndex = 0; meshIndex < mFBXMeshDatas.size(); meshIndex++)
	{
		FbxMesh* mesh = mFBXMeshDatas[meshIndex]->mMesh;
		FBXMeshData* fbxMeshData = mFBXMeshDatas[meshIndex];
		fbxMeshData->mVerticesCount = mesh->GetControlPointsCount();
		fbxMeshData->mIndicesCount = mesh->GetPolygonVertexCount();
		fbxMeshData->mTrianglesCount = mesh->GetPolygonCount();

		// 获取3dsmax中的全局变换矩阵,稍后可以在DX中还原。
		FbxMatrix gloableTransform = mesh->GetNode()->EvaluateGlobalTransform();

		FbxAMatrix matrixGeo;
		matrixGeo.SetIdentity();

		const FbxVector4 lT = mesh->GetNode()->GetGeometricTranslation(FbxNode::eSourcePivot);
		const FbxVector4 lR = mesh->GetNode()->GetGeometricRotation(FbxNode::eSourcePivot);
		const FbxVector4 lS = mesh->GetNode()->GetGeometricScaling(FbxNode::eSourcePivot);

		matrixGeo.SetT(lT);
		matrixGeo.SetR(lR);
		matrixGeo.SetS(lS);

		FbxAMatrix matrixL2W;
		matrixL2W.SetIdentity();

		matrixL2W = mesh->GetNode()->EvaluateGlobalTransform();

		matrixL2W *= matrixGeo;

		XMMATRIX globalTransform = XMLoadFloat4x4(&fbxMeshData->globalTransform);
		FbxMatrixToXMMATRIX(globalTransform, matrixL2W);
		XMStoreFloat4x4(&fbxMeshData->globalTransform, globalTransform);

		// 读取顶点。
		ReadVertices(fbxMeshData);
		// 读取索引。
		ReadIndices(fbxMeshData);

		// 先读取网格对应的材质索引信息,以便优化稍后纹理读取。
		// 一个网格可能只对应一个materialId,也可能对应多个materialId(3dsmax里的Multi/Sub-Object材质)。
		// 如果只对应一个材质,简单的读取就行,不过普遍情况可能是为了优化渲染合并mesh从而拥有多材质。
		// 这个函数调用完毕我们会得到materialId和拥有这个materialId的三角形列表(三角形编号列表),保存在vector<MaterialIdOffset>的容器中。
		//struct Material
		//{
		//	Material() {}
		//	Material(int id, string diffuse, string normalMap)
		//		: materialId(id),
		//		diffuseTextureFile(diffuse),
		//		normalMapTextureFile(normalMap)
		//	{}
		//
		//	int materialId;
		//	string diffuseTextureFile;
		//	string normalMapTextureFile;
		//};
		// struct MaterialIdOffset
		//{
		//	MaterialIdOffset()
		//		: polygonCount(0)
		//	{}
		//	int polygonCount;
		//	Material material;
		//};
		ConnectMaterialsToMesh(mesh, fbxMeshData->mTrianglesCount);

		// 根据ConnectMaterialsToMesh得到的信息读取材质纹理信息,同样存入vector<MaterialIdOffset>容器。
		LoadMaterials(fbxMeshData);

		int triangleCount = mesh->GetPolygonCount();
		int controlPointIndex = 0;
		int normalIndex = 0;
		fbxMeshData->mUVs.resize(fbxMeshData->mIndicesCount, XMFLOAT2(-1.0f, -1.0f));

		// Extract normals and uvs from FbxMesh.
		for (int i = 0; i < triangleCount; i++)
		{
			int polygonSize = mesh->GetPolygonSize(i);

			for (int j = 0; j < polygonSize; j++)
			{ 
				controlPointIndex = mesh->GetPolygonVertex(i, j);

				ReadNormals(fbxMeshData, controlPointIndex, normalIndex);

				// 有纹理我们才读取uv,tangent以及binormal。
				if (fbxMeshData->hasDiffuseTexture())
				{
					ReadUVs(fbxMeshData, controlPointIndex, normalIndex, mesh->GetTextureUVIndex(i, j), 0);
					ReadTangents(fbxMeshData, controlPointIndex, normalIndex);
					ReadBinormals(fbxMeshData, controlPointIndex, normalIndex);
				}

				normalIndex++;
			}
		}

		SplitVertexByNormal(fbxMeshData);

		if (fbxMeshData->hasDiffuseTexture())
		{
			SplitVertexByUV(fbxMeshData);
		}
		else
		{
			fbxMeshData->mUVs.resize(fbxMeshData->mVerticesCount);
		}

		if (fbxMeshData->hasNormalMapTexture())
		{
			SplitVertexByTangent(fbxMeshData);
			SplitVertexByBinormal(fbxMeshData);
		}
		else
		{
			fbxMeshData->mTangents.resize(fbxMeshData->mVerticesCount);
			fbxMeshData->mBinormals.resize(fbxMeshData->mVerticesCount);
		}

		// 如果.fbx包含一个以上的mesh,需要计算当前FBXMeshData的索引在全局索引中的位置。
		for (int i = 0; i < fbxMeshData->mIndicesCount; i++)
		{
			fbxMeshData->mIndices[i] = fbxMeshData->mIndices[i] + verticesIndexOffset;
		}

		mMeshData->verticesCount += fbxMeshData->mVerticesCount;
		mMeshData->indicesCount += fbxMeshData->mIndicesCount;
		mMeshData->meshesCount++;

		// 多材质的情况。
		// 根据之前填充的materialIdOffsets容器保存的materialId和三角形的对应关系,
		// 计算每个RenderPackage渲染所需的索引数量和索引起始位置(偏移)。
		if (isByPolygon && fbxMeshData->hasDiffuseTexture())
		{
			vector<MaterialIdOffset> materialIdOffsets = mMeshData->materialIdOffsets;

			for (int i = 0; i < materialIdOffsets.size(); i++)
			{
				RenderPackage renderPacakge;
				renderPacakge.globalTransform = fbxMeshData->globalTransform;
				renderPacakge.indicesCount = materialIdOffsets[i].polygonCount * 3;
	
				if (i == 0)
				{
					renderPacakge.indicesOffset = indicesIndexOffset;
				}
				else
				{
					renderPacakge.indicesOffset += indicesIndexOffset;
				}

				renderPacakge.material = materialIdOffsets[i].material;

				mMeshData->renderPackages.push_back(renderPacakge);

				indicesIndexOffset += renderPacakge.indicesCount;
			}
		}
		else
		// 单一材质的情况。
		{
			RenderPackage renderPackage;
			renderPackage.indicesCount = fbxMeshData->mIndicesCount;
			renderPackage.indicesOffset = indicesIndexOffset;
			renderPackage.material = fbxMeshData->mMaterial;
			renderPackage.globalTransform = fbxMeshData->globalTransform;

			mMeshData->renderPackages.push_back(renderPackage);

			indicesIndexOffset += fbxMeshData->mIndices.size();
		}

		verticesIndexOffset += fbxMeshData->mVertices.size();

		// 将当前mesh的数据追加到全局数据容器。
		Merge(mMeshData->vertices, fbxMeshData->mVertices);
		Merge(mMeshData->indices, fbxMeshData->mIndices);
		Merge(mMeshData->normals, fbxMeshData->mNormals);
		Merge(mMeshData->uvs, fbxMeshData->mUVs);
		Merge(mMeshData->tangents, fbxMeshData->mTangents);
		Merge(mMeshData->binormals, fbxMeshData->mBinormals);

		mMeshData->materialIdOffsets.clear();
	}

	clear();

	return mMeshData;
}
示例#5
0
void FBXSceneEncoder::transformNode(FbxNode* fbxNode, Node* node)
{
    FbxAMatrix matrix;
    float m[16];
    
    if (fbxNode->GetCamera() || fbxNode->GetLight())
    {
        FbxAMatrix rotateAdjust;
        
        if(fbxNode->GetLight())
        {
            /*
             * according to the fbx-documentation the light's forward vector
             * points along a node's negative Y axis.
             * so we have to rotate it by 90° around the X-axis to correct it.
             */
            if(fbxNode->RotationActive.Get())
            {
                const FbxVector4& postRotation = fbxNode->PostRotation.Get();
                fbxNode->SetPostRotation(FbxNode::eSourcePivot, FbxVector4(postRotation.mData[0] + 90.0,
                                                                           postRotation.mData[1],
                                                                           postRotation.mData[2])
                                         );
            }
            else
            {
                // if the rotation is deactivated we have to rotate it anyway to get the correct transformation in the end
                rotateAdjust.SetR(FbxVector4(-90.0, 0.0, 0.0));
            }
            
            matrix = fbxNode->EvaluateLocalTransform() * rotateAdjust;
        }
        else if(fbxNode->GetCamera())
        {
			/*
			* according to the fbx-documentation the camera's forward vector
			* points along a node's positive X axis.
			* so we have to rotate it by 90 around the Y-axis to correct it.
			*/
			if (fbxNode->RotationActive.Get())
			{
				const FbxVector4& postRotation = fbxNode->PostRotation.Get();
				fbxNode->SetPostRotation(FbxNode::eSourcePivot, FbxVector4(postRotation.mData[0],
					postRotation.mData[1] + 90.0,
					postRotation.mData[2]));
			}
			else
			{
				// usually for the fbx-exporter in Blender 2.75
				// if rotation is deactivated we have to rotate the local transform in 90°
				rotateAdjust.SetR(FbxVector4(0, 90.0, 0.0));
			}

			matrix = fbxNode->EvaluateLocalTransform() * rotateAdjust;
        }
        
        copyMatrix(matrix, m);
        node->setTransformMatrix(m);
    }
    else
    {
        matrix = fbxNode->EvaluateLocalTransform();
        copyMatrix(matrix, m);
        node->setTransformMatrix(m);
    }
}
示例#6
0
		void FbxLoader::ProcessBoneAndAnimation(FbxNode* node, Node& meshNode)
		{
			auto currMesh = node->GetMesh();
			if(!currMesh) return;

			FbxVector4 lT = node->GetGeometricTranslation(FbxNode::eSourcePivot);
			FbxVector4 lR = node->GetGeometricRotation(FbxNode::eSourcePivot);
			FbxVector4 lS = node->GetGeometricScaling(FbxNode::eSourcePivot);

			FbxAMatrix geometryTransform = FbxAMatrix(lT, lR, lS);

			FbxSkin* skin = nullptr;
			const int deformerCnt = currMesh->GetDeformerCount();
			for(int deformerIndex = 0; deformerIndex < deformerCnt; ++deformerIndex) {
				skin = (FbxSkin*)(currMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
				if(skin) break;
			}
			if(!skin) return;

			meshNode.useSkinnedMesh = true;
			const size_t nClusters = skin->GetClusterCount();
			if(nClusters < 1) return;

			for(auto& clip : animationClips)
				clip.second->transformCurves.resize(nClusters);

			meshNode.bones.resize(nClusters);
			const int animCount = scene->GetSrcObjectCount<FbxAnimStack>();

			float time = 0;
			for(int ci = 0; ci < nClusters; ++ci) {
				FbxCluster* cluster = skin->GetCluster(ci);
				auto elink = cluster->GetLinkMode();
				std::string boneName = cluster->GetLink()->GetName();

				FbxAMatrix transformMatrix, transformLinkMatrix, globalBindposeInverse;
				cluster->GetTransformMatrix(transformMatrix);
				cluster->GetTransformLinkMatrix(transformLinkMatrix);
				globalBindposeInverse = transformLinkMatrix.Inverse() * geometryTransform * transformMatrix;

				FbxNode* boneNode = cluster->GetLink();
				Bone& bone = meshNode.bones[ci];
				bone.name = boneName;
				bone.index = ci;

				auto T = globalBindposeInverse.GetT() * factor;
				
				if(axismode == eLeftHanded) {
					auto R = globalBindposeInverse.GetR();
					T[0] *= -1; R[1] *= -1; R[2] *= -1;
					globalBindposeInverse.SetR(R);
				}
				globalBindposeInverse.SetT(T);

				ConvertMatrix(bone.bindPoseInverse, globalBindposeInverse);

				const int nCtrl = cluster->GetControlPointIndicesCount();

				for(int ctrlIndex = 0; ctrlIndex < nCtrl; ++ctrlIndex) {
					BlendWeightPair pair;
					pair.boneIndex = ci;
					pair.weight = (float)cluster->GetControlPointWeights()[ctrlIndex];
					meshNode.controlPoints[cluster->GetControlPointIndices()[ctrlIndex]].blendWeigths.push_back(pair);
				}

				FbxAMatrix preRot;
				auto preR = boneNode->GetPreRotation(FbxNode::eSourcePivot);
				preRot.SetR(preR);

				for(int ai = 0; ai < animCount; ++ai) {
					auto animStack = scene->GetSrcObject<FbxAnimStack>(ai);
					scene->SetCurrentAnimationStack(animStack);
					std::string animName = animStack->GetName();
					auto& clip = animationClips[animName];
					auto& transformCurve = clip->transformCurves[ci];
					transformCurve.boneName = boneName;
					transformCurve.begin = 0;

					auto animLayer = animStack->GetMember<FbxAnimLayer>(0);
					FbxAnimCurve* fbxTCurves[3];
					FbxAnimCurve* fbxRCurves[3];
					FbxAnimCurve* fbxSCurves[3];

					fbxTCurves[0] = boneNode->LclTranslation.GetCurve(animLayer, "X");

					if(!fbxTCurves[0])
						continue;
					
					fbxTCurves[1] = boneNode->LclTranslation.GetCurve(animLayer, "Y");
					fbxTCurves[2] = boneNode->LclTranslation.GetCurve(animLayer, "Z");

					fbxRCurves[0] = boneNode->LclRotation.GetCurve(animLayer, "X");
					fbxRCurves[1] = boneNode->LclRotation.GetCurve(animLayer, "Y");
					fbxRCurves[2] = boneNode->LclRotation.GetCurve(animLayer, "Z");

					fbxSCurves[0] = boneNode->LclScaling.GetCurve(animLayer, "X");
					fbxSCurves[1] = boneNode->LclScaling.GetCurve(animLayer, "Y");
					fbxSCurves[2] = boneNode->LclScaling.GetCurve(animLayer, "Z");

					// set & apply filter
					FbxAnimCurveFilterKeyReducer keyReducer;
					keyReducer.SetKeySync(false);
					keyReducer.Apply(fbxTCurves, 3);
					keyReducer.Apply(fbxSCurves, 3);
					keyReducer.SetKeySync(true);
					keyReducer.Apply(fbxRCurves, 3);

					FbxAnimCurveFilterUnroll unroll;
					unroll.SetForceAutoTangents(true);
					unroll.Apply(fbxRCurves, 3);

					FbxAnimCurveFilterTSS tss;
					FbxTime tt;
					tt.SetSecondDouble(-fbxTCurves[0]->KeyGetTime(0).GetSecondDouble());
					tss.SetShift(tt);
					tss.Apply(fbxTCurves, 3);
					tss.Apply(fbxRCurves, 3);
					tss.Apply(fbxSCurves, 3);
					//

					// process curves
					if(fbxTCurves[0]->KeyGetCount() > 0) {
						ProcessAnimCurveT(fbxTCurves, transformCurve);
						ProcessAnimCurveS(fbxSCurves, transformCurve);
						ProcessAnimCurveR(fbxRCurves, transformCurve, preRot);

						//clamping by reduced keyframes
						clip->startTime = 0;
						clip->lengthInSeconds = transformCurve.end;
						clip->lengthInFrames = (int)(1.5f + (transformCurve.end / (1 / 30.0f)));
					}
				} // animations loop
			} // cluster loop
		}
示例#7
0
void ParseMesh( FbxNode* pNode, FbxMesh* pFbxMesh, ExportFrame* pParentFrame, bool bSubDProcess, const CHAR* strSuffix )
{
    if( !g_pScene->Settings().bExportMeshes )
        return;

    if( !pNode || !pFbxMesh )
        return;

    const CHAR* strName = pFbxMesh->GetName();
    if( !strName || strName[0] == '\0' )
        strName = pParentFrame->GetName().SafeString();

    if( !strSuffix )
    {
        strSuffix = "";
    }
    CHAR strDecoratedName[512];
    sprintf_s( strDecoratedName, "%s_%s%s", g_pScene->Settings().strMeshNameDecoration, strName, strSuffix );
    ExportMesh* pMesh = new ExportMesh( strDecoratedName );
    pMesh->SetDCCObject( pFbxMesh );

    bool bSmoothMesh = false;

    auto Smoothness = pFbxMesh->GetMeshSmoothness();
    if( Smoothness != FbxMesh::eHull && g_pScene->Settings().bConvertMeshesToSubD )
    {
        bSubDProcess = true;
        bSmoothMesh = true;
    }

    ExportLog::LogMsg( 2, "Parsing %s mesh \"%s\", renamed to \"%s\"", bSmoothMesh ? "smooth" : "poly", strName, strDecoratedName );

    SkinData skindata;
    bool bSkinnedMesh = ParseMeshSkinning( pFbxMesh, &skindata );
    if( bSkinnedMesh )
    {
        DWORD dwBoneCount = skindata.GetBoneCount();
        for( DWORD i = 0; i < dwBoneCount; ++i )
        {
            pMesh->AddInfluence( skindata.InfluenceNodes[i]->GetName() );
        }
    }

    bool bExportColors = g_pScene->Settings().bExportColors;
    pMesh->SetVertexColorCount( 0 );

    // Vertex normals and tangent spaces
    if( !g_pScene->Settings().bExportNormals )
    {
        pMesh->SetVertexNormalCount( 0 );
    }
    else if( g_pScene->Settings().bComputeVertexTangentSpace )
    {
        if( g_pScene->Settings().bExportBinormal )
            pMesh->SetVertexNormalCount( 3 );
        else
            pMesh->SetVertexNormalCount( 2 );
    }
    else
    {
        pMesh->SetVertexNormalCount( 1 );
    }

    DWORD dwLayerCount = pFbxMesh->GetLayerCount();
    ExportLog::LogMsg( 4, "%u layers in FBX mesh", dwLayerCount );

    if (!dwLayerCount || !pFbxMesh->GetLayer(0)->GetNormals())
    {
        ExportLog::LogMsg( 4, "Generating normals..." );
        pFbxMesh->InitNormals();
#if (FBXSDK_VERSION_MAJOR >= 2015)
        pFbxMesh->GenerateNormals();
#else
        pFbxMesh->ComputeVertexNormals();
#endif
    }

    DWORD dwVertexColorCount = 0;
    FbxLayerElementVertexColor* pVertexColorSet = nullptr;
    DWORD dwUVSetCount = 0;
    FbxLayerElementMaterial* pMaterialSet = nullptr;
    std::vector<FbxLayerElementUV*> VertexUVSets;
    for( DWORD dwLayerIndex = 0; dwLayerIndex < dwLayerCount; ++dwLayerIndex )
    {
        if( pFbxMesh->GetLayer(dwLayerIndex)->GetVertexColors() && bExportColors )
        {
            if( dwVertexColorCount == 0 )
            {
                dwVertexColorCount++;
                pVertexColorSet = pFbxMesh->GetLayer(dwLayerIndex)->GetVertexColors();
            }
            else
            {
                ExportLog::LogWarning( "Only one vertex color set is allowed; ignoring additional vertex color sets." );
            }
        }
        if( pFbxMesh->GetLayer(dwLayerIndex)->GetUVs() )
        {
            dwUVSetCount++;
            VertexUVSets.push_back( pFbxMesh->GetLayer(dwLayerIndex)->GetUVs() );
        }
        if( pFbxMesh->GetLayer(dwLayerIndex)->GetMaterials() )
        {
            if( pMaterialSet )
            {
                ExportLog::LogWarning( "Multiple material layers detected on mesh %s.  Some will be ignored.", pMesh->GetName().SafeString() );
            }
            pMaterialSet = pFbxMesh->GetLayer(dwLayerIndex)->GetMaterials();
        }
    }

    std::vector<ExportMaterial*> MaterialList;
    for( int dwMaterial = 0; dwMaterial < pNode->GetMaterialCount(); ++dwMaterial )
    {
        auto pMat = pNode->GetMaterial( dwMaterial );
        if ( !pMat )
            continue;

        auto pMaterial = ParseMaterial( pMat );
        MaterialList.push_back( pMaterial );
    }

    ExportLog::LogMsg( 4, "Found %u UV sets", dwUVSetCount );
    dwUVSetCount = std::min<DWORD>( dwUVSetCount, g_pScene->Settings().iMaxUVSetCount );
    ExportLog::LogMsg( 4, "Using %u UV sets", dwUVSetCount );

    pMesh->SetVertexColorCount( dwVertexColorCount );
    pMesh->SetVertexUVCount( dwUVSetCount );
    // TODO: Does FBX only support 2D texture coordinates?
    pMesh->SetVertexUVDimension( 2 );

    DWORD dwMeshOptimizationFlags = 0;
    if( g_pScene->Settings().bCompressVertexData )
        dwMeshOptimizationFlags |= ExportMesh::COMPRESS_VERTEX_DATA;

    DWORD dwPolyCount = pFbxMesh->GetPolygonCount();
    // Assume that polys are usually quads.
    g_MeshTriangleAllocator.SetSizeHint( dwPolyCount * 2 );

    DWORD dwVertexCount = pFbxMesh->GetControlPointsCount();
    auto pVertexPositions = pFbxMesh->GetControlPoints();

    if( bSkinnedMesh )
    {
        assert( skindata.dwVertexCount == dwVertexCount );
    }
    
    ExportLog::LogMsg( 4, "%u vertices, %u polygons", dwVertexCount, dwPolyCount );

    DWORD dwNonConformingSubDPolys = 0;

    // Compute total transformation
    FbxAMatrix vertMatrix;
    FbxAMatrix normMatrix;
    {
        auto trans = pNode->GetGeometricTranslation( FbxNode::eSourcePivot );
        auto rot = pNode->GetGeometricRotation( FbxNode::eSourcePivot );
        auto scale = pNode->GetGeometricScaling( FbxNode::eSourcePivot );

        FbxAMatrix geom;
        geom.SetT( trans );
        geom.SetR( rot );
        geom.SetS( scale );

        if ( g_pScene->Settings().bExportAnimations || !g_pScene->Settings().bApplyGlobalTrans )
        {
            vertMatrix = geom;
        }
        else
        {
            auto global = pNode->EvaluateGlobalTransform();
            vertMatrix = global * geom;
        }

        // Calculate the normal transform matrix (inverse-transpose)
        normMatrix = vertMatrix;
        normMatrix = normMatrix.Inverse();
        normMatrix = normMatrix.Transpose();
    }

    const bool bInvertTexVCoord = g_pScene->Settings().bInvertTexVCoord;
    
    // Loop over polygons.
    DWORD basePolyIndex = 0;
    for( DWORD dwPolyIndex = 0; dwPolyIndex < dwPolyCount; ++dwPolyIndex )
    {
        // Triangulate each polygon into one or more triangles.
        DWORD dwPolySize = pFbxMesh->GetPolygonSize( dwPolyIndex );
        assert( dwPolySize >= 3 );
        DWORD dwTriangleCount = dwPolySize - 2;
        assert( dwTriangleCount > 0 );

        if( dwPolySize > 4 )
        {
            ++dwNonConformingSubDPolys;
        }

        DWORD dwMaterialIndex = 0;
        if( pMaterialSet )
        {
            switch( pMaterialSet->GetMappingMode() )
            {
            case FbxLayerElement::eByPolygon:
                switch( pMaterialSet->GetReferenceMode() )
                {
                case FbxLayerElement::eDirect:
                    dwMaterialIndex = dwPolyIndex;
                    break;
                case FbxLayerElement::eIndex:
                case FbxLayerElement::eIndexToDirect:
                    dwMaterialIndex = pMaterialSet->GetIndexArray().GetAt( dwPolyIndex );
                    break;
                }
            }
        }

        DWORD dwCornerIndices[3];
        // Loop over triangles in the polygon.
        for( DWORD dwTriangleIndex = 0; dwTriangleIndex < dwTriangleCount; ++dwTriangleIndex )
        {
            dwCornerIndices[0] = pFbxMesh->GetPolygonVertex( dwPolyIndex, 0 );
            dwCornerIndices[1] = pFbxMesh->GetPolygonVertex( dwPolyIndex, dwTriangleIndex + 1 );
            dwCornerIndices[2] = pFbxMesh->GetPolygonVertex( dwPolyIndex, dwTriangleIndex + 2 );

            //ExportLog::LogMsg( 4, "Poly %d Triangle %d: %d %d %d", dwPolyIndex, dwTriangleIndex, dwCornerIndices[0], dwCornerIndices[1], dwCornerIndices[2] );

            FbxVector4 vNormals[3];
            ZeroMemory( vNormals, 3 * sizeof(FbxVector4) );
            INT iPolyIndex = static_cast<INT>( dwPolyIndex );
            INT iVertIndex[3] = { 0, static_cast<INT>( dwTriangleIndex + 1 ), static_cast<INT>( dwTriangleIndex + 2 ) };
            pFbxMesh->GetPolygonVertexNormal( iPolyIndex, iVertIndex[0], vNormals[0] );
            pFbxMesh->GetPolygonVertexNormal( iPolyIndex, iVertIndex[1], vNormals[1] );
            pFbxMesh->GetPolygonVertexNormal( iPolyIndex, iVertIndex[2], vNormals[2] );

            // Build the raw triangle.
            auto pTriangle = g_MeshTriangleAllocator.GetNewTriangle();

            // Store polygon index
            pTriangle->PolygonIndex = static_cast<INT>( dwPolyIndex );

            // Store material subset index
            pTriangle->SubsetIndex = dwMaterialIndex;

            for( DWORD dwCornerIndex = 0; dwCornerIndex < 3; ++dwCornerIndex )
            {
                const DWORD& dwDCCIndex = dwCornerIndices[dwCornerIndex];
                // Store DCC vertex index (this helps the mesh reduction/VB generation code)
                pTriangle->Vertex[dwCornerIndex].DCCVertexIndex = dwDCCIndex;

                // Store vertex position
                auto finalPos = vertMatrix.MultT( pVertexPositions[dwDCCIndex] );

                pTriangle->Vertex[dwCornerIndex].Position.x = (float)finalPos.mData[0];
                pTriangle->Vertex[dwCornerIndex].Position.y = (float)finalPos.mData[1];
                pTriangle->Vertex[dwCornerIndex].Position.z = (float)finalPos.mData[2];

                // Store vertex normal
                auto finalNorm = vNormals[dwCornerIndex];
                finalNorm.mData[3] = 0.0;
                finalNorm = normMatrix.MultT( finalNorm );
                finalNorm.Normalize();

                pTriangle->Vertex[dwCornerIndex].Normal.x = (float)finalNorm.mData[0];
                pTriangle->Vertex[dwCornerIndex].Normal.y = (float)finalNorm.mData[1];
                pTriangle->Vertex[dwCornerIndex].Normal.z = (float)finalNorm.mData[2];

                // Store UV sets
                for( DWORD dwUVIndex = 0; dwUVIndex < dwUVSetCount; ++dwUVIndex )
                {
                    // Crack apart the FBX dereferencing system for UV coordinates
                    FbxLayerElementUV* pUVSet = VertexUVSets[dwUVIndex];
                    FbxVector2 Value( 0, 0 );
                    switch( pUVSet->GetMappingMode() )
                    {
                    case FbxLayerElement::eByControlPoint:
                        switch (pUVSet->GetReferenceMode())
                        {
                        case FbxLayerElement::eDirect:
                            Value = pUVSet->GetDirectArray().GetAt(dwDCCIndex);
                            break;

                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iUVIndex = pUVSet->GetIndexArray().GetAt(dwDCCIndex);
                                Value = pUVSet->GetDirectArray().GetAt(iUVIndex);
                            }
                            break;
                        }
                        break;

                    case FbxLayerElement::eByPolygonVertex:
                        switch (pUVSet->GetReferenceMode())
                        {
                        case FbxLayerElement::eDirect:
                            Value = pUVSet->GetDirectArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
                            break;

                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iUVIndex = pUVSet->GetIndexArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
#ifdef _DEBUG
                                if (!dwUVIndex)
                                {
                                    // Warning: pFbxMesh->GetTextureUVIndex only works for the first layer of the mesh
                                    int iUVIndex2 = pFbxMesh->GetTextureUVIndex(iPolyIndex, iVertIndex[dwCornerIndex]);
                                    assert(iUVIndex == iUVIndex2);
                                }
#endif
                                Value = pUVSet->GetDirectArray().GetAt( iUVIndex );
                            }
                            break;
                        }
                        break;
                    }

                    // Store a single UV set
                    pTriangle->Vertex[dwCornerIndex].TexCoords[dwUVIndex].x = (float)Value.mData[0];
                    if( bInvertTexVCoord )
                    {
                        pTriangle->Vertex[dwCornerIndex].TexCoords[dwUVIndex].y = 1.0f - (float) Value.mData[1];
                    }
                    else
                    {
                        pTriangle->Vertex[dwCornerIndex].TexCoords[dwUVIndex].y = (float)Value.mData[1];
                    }
                }

                // Store vertex color set
                if( dwVertexColorCount > 0 && pVertexColorSet )
                {
                    // Crack apart the FBX dereferencing system for Color coordinates
                    FbxColor Value( 1, 1, 1, 1 );
                    switch( pVertexColorSet->GetMappingMode() )
                    {
                    case FbxLayerElement::eByControlPoint:
                        switch( pVertexColorSet->GetReferenceMode() )
                        {
                        case FbxLayerElement::eDirect:
                            Value = pVertexColorSet->GetDirectArray().GetAt( dwDCCIndex );
                            break;
                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iColorIndex = pVertexColorSet->GetIndexArray().GetAt(dwDCCIndex);
                                Value = pVertexColorSet->GetDirectArray().GetAt(iColorIndex);
                            }
                            break;
                        }
                        break;

                    case FbxLayerElement::eByPolygonVertex:
                        switch( pVertexColorSet->GetReferenceMode() )
                        {
                        case FbxLayerElement::eDirect:
                            Value = pVertexColorSet->GetDirectArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
                            break;
                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iColorIndex = pVertexColorSet->GetIndexArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
                                Value = pVertexColorSet->GetDirectArray().GetAt(iColorIndex);
                            }
                            break;
                        }
                        break;
                    }

                    // Store a single vertex color set
                    pTriangle->Vertex[dwCornerIndex].Color.x = (float)Value.mRed;
                    pTriangle->Vertex[dwCornerIndex].Color.y = (float)Value.mGreen;
                    pTriangle->Vertex[dwCornerIndex].Color.z = (float)Value.mBlue;
                    pTriangle->Vertex[dwCornerIndex].Color.w = (float)Value.mAlpha;
                }

                // Store skin weights
                if( bSkinnedMesh )
                {
                    memcpy( &pTriangle->Vertex[dwCornerIndex].BoneIndices, skindata.GetIndices( dwDCCIndex ), sizeof(PackedVector::XMUBYTE4) );
                    memcpy( &pTriangle->Vertex[dwCornerIndex].BoneWeights, skindata.GetWeights( dwDCCIndex ), sizeof(XMFLOAT4) );
                }
            }

            // Add raw triangle to the mesh.
            pMesh->AddRawTriangle( pTriangle );
        }

        basePolyIndex += dwPolySize;
    }

    if( bSubDProcess )
    {
        dwMeshOptimizationFlags |= ExportMesh::FORCE_SUBD_CONVERSION;
    }

    if ( g_pScene->Settings().bCleanMeshes )
    {
        dwMeshOptimizationFlags |= ExportMesh::CLEAN_MESHES;
    }

    if ( g_pScene->Settings().bOptimizeVCache )
    {
        dwMeshOptimizationFlags |= ExportMesh::CLEAN_MESHES | ExportMesh::VCACHE_OPT;
    }

    pMesh->Optimize( dwMeshOptimizationFlags );

    ExportModel* pModel = new ExportModel( pMesh );
    size_t dwMaterialCount = MaterialList.size();
    if( !pMesh->GetSubDMesh() )
    {
        for( size_t dwSubset = 0; dwSubset < dwMaterialCount; ++dwSubset )
        {
            auto pMaterial = MaterialList[dwSubset];
            auto pSubset = pMesh->GetSubset( dwSubset );
            CHAR strUniqueSubsetName[100];
            sprintf_s( strUniqueSubsetName, "subset%Iu_%s", dwSubset, pMaterial->GetName().SafeString() );
            pSubset->SetName( strUniqueSubsetName );
            pModel->SetSubsetBinding( pSubset->GetName(), pMaterial );
        }
    }
    else
    {
        auto pSubDMesh = pMesh->GetSubDMesh();
        size_t dwSubsetCount = pSubDMesh->GetSubsetCount();
        for( size_t dwSubset = 0; dwSubset < dwSubsetCount; ++dwSubset )
        {
            auto pSubset = pSubDMesh->GetSubset( dwSubset );
            assert( pSubset != nullptr );
            assert( pSubset->iOriginalMeshSubset < static_cast<INT>( dwMaterialCount ) );
            auto pMaterial = MaterialList[pSubset->iOriginalMeshSubset];
            CHAR strUniqueSubsetName[100];
            sprintf_s( strUniqueSubsetName, "subset%Iu_%s", dwSubset, pMaterial->GetName().SafeString() );
            pSubset->Name = strUniqueSubsetName;
            pModel->SetSubsetBinding( pSubset->Name, pMaterial, true );
        }
    }

    if( bSubDProcess && ( dwNonConformingSubDPolys > 0 ) )
    {
        ExportLog::LogWarning( "Encountered %u polygons with 5 or more sides in mesh \"%s\", which were subdivided into quad and triangle patches.  Mesh appearance may have been affected.", dwNonConformingSubDPolys, pMesh->GetName().SafeString() );
    }

    // update statistics
    if( pMesh->GetSubDMesh() )
    {
        g_pScene->Statistics().SubDMeshesProcessed++;
        g_pScene->Statistics().SubDQuadsProcessed += pMesh->GetSubDMesh()->GetQuadPatchCount();
        g_pScene->Statistics().SubDTrisProcessed += pMesh->GetSubDMesh()->GetTrianglePatchCount();
    }
    else
    {
        g_pScene->Statistics().TrisExported += pMesh->GetIB()->GetIndexCount() / 3;
        g_pScene->Statistics().VertsExported += pMesh->GetVB()->GetVertexCount();
        g_pScene->Statistics().MeshesExported++;
    }

    pParentFrame->AddModel( pModel );
    g_pScene->AddMesh( pMesh );
}
void CameraOrbit(FbxScene* pScene, FbxVector4 lOrigCamPos, double OrigRoll, int dX, int dY)
{
    // Orbit the camera horizontally dX degrees, vertically dY degrees.
    FbxCamera* lCamera = GetCurrentCamera(pScene);
    if (!lCamera) return;
    FbxGlobalCameraSettings& lGlobalCameraSettings = pScene->GlobalCameraSettings();
    if (lCamera != lGlobalCameraSettings.GetCameraProducerPerspective()) return;
    if (lCamera->LockMode.Get()) return;
    if (dX == 0 && dY == 0) return;

    FbxVector4 lRotationVector, lNewPosition, lCurPosition;
    FbxAMatrix lRotation;
    FbxVector4 lCenter = lCamera->InterestPosition.Get();

    // current position
    FbxVector4 lPosition = lCamera->Position.Get();
    lCurPosition = lPosition-lCenter;

    // translate
    lNewPosition = lOrigCamPos-lCenter;

    int rotX;
    if (lNewPosition[2] == 0) {
        rotX = 90;
    } else {
        rotX = (int) (atan((double)lNewPosition[0]/(double)lNewPosition[2]) * FBXSDK_180_DIV_PI);
    }
    bool bRoll = (((int)OrigRoll % 360) != 0);
    if (   (lNewPosition[2] < 0 && !bRoll)
        || (lNewPosition[2] > 0 && bRoll) ) {
            dY = -dY;
    }
    if (bRoll) dX = -dX;

    // Center on the X axis (push)
    lRotationVector[1] = -rotX;
    lRotation.SetR(lRotationVector);
    lNewPosition = lRotation.MultT(lNewPosition);
    // Rotation for the vertical movement: around the X axis
    lRotationVector[1] = 0;
    lRotationVector[0] = dY;
    lRotation.SetR(lRotationVector);
    lNewPosition = lRotation.MultT(lNewPosition);
    // Back from the X axis (pop)
    lRotationVector[0] = 0;
    lRotationVector[1] = rotX;
    lRotation.SetR(lRotationVector);
    lNewPosition = lRotation.MultT(lNewPosition);
    // Rotation for the horizontal movement
    lRotationVector[1] = -dX;
    lRotation.SetR(lRotationVector);
    lNewPosition = lRotation.MultT(lNewPosition);

    // Detect camera flip
    if (   lNewPosition[0]*lCurPosition[0] < 0 
        && lNewPosition[2]*lCurPosition[2] < 0) {
            // flip -> roll 180.
            double lRoll = lCamera->Roll.Get();
            lRoll = 180.0-lRoll;
            lCamera->Roll.Set(lRoll);
    }

    // Back from center
    lNewPosition = lNewPosition + lCenter;

    lCamera->Position.Set(lNewPosition);

}
void CameraZoom(FbxScene* pScene, int pZoomDepth, int pZoomMode)
{
    FbxCamera* lCamera = GetCurrentCamera(pScene);
    if( lCamera == NULL)
        return;
    if( pZoomMode == SceneContext::ZOOM_FOCAL_LENGTH)
    {
        if (lCamera->ProjectionType.Get() == FbxCamera::ePerspective)
        {
            double lTransform = 0 - pZoomDepth / 100.0;

            double lApertureW = lCamera->GetApertureWidth();
            lApertureW = TransformAperture( lApertureW, lTransform);

            double lApertureH = lCamera->GetApertureHeight();
            lApertureH = TransformAperture( lApertureH, lTransform);

            UpdatePerspCameraAttributes( lCamera, lApertureW, lApertureH);


        }
        else
        {
            if( pZoomDepth > 0)
                gsOrthoCameraScale *= 0.8;
            else
                gsOrthoCameraScale *= 1.25;
        }
    }
    else
    {
        FbxNode*   lCameraNode = lCamera ? lCamera->GetNode() : NULL;

        // Compute the camera position and direction.
        FbxVector4 lEye(0,0,1);
        FbxVector4 lCenter(0,0,0);
        FbxVector4 lForward(0,0,0);

        if (lCamera)
        {
            lEye = lCamera->Position.Get();
        }

        if (lCameraNode && lCameraNode->GetTarget())
        {
            lCenter = lCameraNode->GetTarget()->LclTranslation.Get();
            lForward = lCenter - lEye;
        }
        else
        {
            if (!lCameraNode || IsProducerCamera(pScene, lCamera))
            {
                if (lCamera)
                {
                    lCenter = lCamera->InterestPosition.Get();
                    lForward = lCenter - lEye;
                }
            }
            else
            {
                // Get the direction
                FbxAMatrix lGlobalRotation;
                FbxVector4 lRotationVector( lCameraNode->LclRotation.Get());
                lGlobalRotation.SetR(lRotationVector);

                // Set the center.
                // A camera with rotation = {0,0,0} points to the X direction. So create a
                // vector in the X direction, rotate that vector by the global rotation amount
                // and then position the center by scaling and translating the resulting vector
                lRotationVector = FbxVector4(1.0,0,0);
                lForward = lGlobalRotation.MultT(lRotationVector);
            }
        }
        lForward.Normalize();
        lEye += lForward * pZoomDepth;
        FbxDouble3 lPosition(lEye[0], lEye[1], lEye[2]);
        lCamera->Position.Set(lPosition);
        
    }
}
// Set the view to the current camera settings.
void SetCamera(FbxScene* pScene, 
               FbxTime& pTime, 
               FbxAnimLayer* pAnimLayer,
               const FbxArray<FbxNode*>& pCameraArray,
               int pWindowWidth, int pWindowHeight)
{
    // Find the current camera at the given time.
    FbxCamera* lCamera = GetCurrentCamera(pScene, pTime, pAnimLayer, pCameraArray);
    if( lCamera == NULL)
        return;
    FbxNode*   lCameraNode = lCamera ? lCamera->GetNode() : NULL;

    // Compute the camera position and direction.
    FbxVector4 lEye(0,0,1);
    FbxVector4 lCenter(0,0,0);
    FbxVector4 lUp(0,1,0);
    FbxVector4 lForward, lRight;

    if (lCamera)
    {
        lEye = lCamera->Position.Get();
        lUp = lCamera->UpVector.Get();
    }

    if (lCameraNode && lCameraNode->GetTarget())
    {
        lCenter = GetGlobalPosition(lCameraNode->GetTarget(), pTime).GetT();
    }
    else
    {
        if (!lCameraNode || IsProducerCamera(pScene, lCamera))
        {
            if (lCamera)
                lCenter = lCamera->InterestPosition.Get();
        }
        else
        {
            // Get the direction
            FbxAMatrix lGlobalRotation;
            FbxVector4 lRotationVector(GetGlobalPosition(lCameraNode, pTime).GetR());
            lGlobalRotation.SetR(lRotationVector);

            // Get the length
            FbxVector4 lInterestPosition(lCamera->InterestPosition.Get());
            FbxVector4 lCameraGlobalPosition(GetGlobalPosition(lCameraNode, pTime).GetT());
            double      lLength = (FbxVector4(lInterestPosition - lCameraGlobalPosition).Length());

            // Set the center.
            // A camera with rotation = {0,0,0} points to the X direction. So create a
            // vector in the X direction, rotate that vector by the global rotation amount
            // and then position the center by scaling and translating the resulting vector
            lRotationVector = FbxVector4(1.0,0,0);
            lCenter = lGlobalRotation.MultT(lRotationVector);
            lCenter *= lLength;
            lCenter += lEye;

            // Update the default up vector with the camera rotation.
            lRotationVector = FbxVector4(0,1.0,0);
            lUp = lGlobalRotation.MultT(lRotationVector);
        }
    }

    // Align the up vector.
    lForward = lCenter - lEye;
    lForward.Normalize();
    lRight = lForward.CrossProduct(lUp);
    lRight.Normalize();
    lUp = lRight.CrossProduct(lForward);
    lUp.Normalize();

    // Rotate the up vector with the roll value.
    double lRadians = 0;

    if (lCamera)
        lRadians = lCamera->Roll.Get() * FBXSDK_PI_DIV_180;
    lUp = lUp * cos( lRadians) + lRight * sin(lRadians);

    
    double lNearPlane = 0.01;
    if (lCamera)
        lNearPlane = lCamera->GetNearPlane();    
    double lFarPlane = 4000.0;
    if (lCamera)
        lFarPlane = lCamera->GetFarPlane();

    //Get global scaling.
    FbxVector4 lCameraScaling = GetGlobalPosition(lCameraNode, pTime).GetS();
    static const int  FORWARD_SCALE = 2;
    
    //scaling near plane and far plane
    lNearPlane *= lCameraScaling[ FORWARD_SCALE];
    lFarPlane *= lCameraScaling[ FORWARD_SCALE];






    // Get the relevant camera settings for a perspective view.
    if (lCamera && lCamera->ProjectionType.Get() == FbxCamera::ePerspective)
    {
        //get the aspect ratio
        FbxCamera::EAspectRatioMode lCamAspectRatioMode = lCamera->GetAspectRatioMode();
        double lAspectX = lCamera->AspectWidth.Get(); //ºñÀ²
        double lAspectY = lCamera->AspectHeight.Get();
        double lAspectRatio = 1.333333;
        switch( lCamAspectRatioMode)
        {
        case FbxCamera::eWindowSize:
            lAspectRatio = lAspectX / lAspectY;
            break;
        case FbxCamera::eFixedRatio:
            lAspectRatio = lAspectX;

            break;
        case FbxCamera::eFixedResolution:
            lAspectRatio = lAspectX / lAspectY * lCamera->GetPixelRatio();
            break;
        case FbxCamera::eFixedWidth:
            lAspectRatio = lCamera->GetPixelRatio() / lAspectY;
            break;
        case FbxCamera::eFixedHeight:
            lAspectRatio = lCamera->GetPixelRatio() * lAspectX;
            break;
        default:
            break;

        }

        //get the aperture ratio
        double lFilmHeight = lCamera->GetApertureHeight();
        double lFilmWidth = lCamera->GetApertureWidth() * lCamera->GetSqueezeRatio();
        //here we use Height : Width
        double lApertureRatio = lFilmHeight / lFilmWidth;


        //change the aspect ratio to Height : Width
        lAspectRatio = 1 / lAspectRatio;
        //revise the aspect ratio and aperture ratio
        FbxCamera::EGateFit lCameraGateFit = lCamera->GateFit.Get();
        switch( lCameraGateFit )
        {

        case FbxCamera::eFitFill:
            if( lApertureRatio > lAspectRatio)  // the same as eHORIZONTAL_FIT
            {
                lFilmHeight = lFilmWidth * lAspectRatio;
                lCamera->SetApertureHeight( lFilmHeight);
                lApertureRatio = lFilmHeight / lFilmWidth;
            }
            else if( lApertureRatio < lAspectRatio) //the same as eVERTICAL_FIT
            {
                lFilmWidth = lFilmHeight / lAspectRatio;
                lCamera->SetApertureWidth( lFilmWidth);
                lApertureRatio = lFilmHeight / lFilmWidth;
            }
            break;
        case FbxCamera::eFitVertical:
            lFilmWidth = lFilmHeight / lAspectRatio;
            lCamera->SetApertureWidth( lFilmWidth);
            lApertureRatio = lFilmHeight / lFilmWidth;
            break;
        case FbxCamera::eFitHorizontal:
            lFilmHeight = lFilmWidth * lAspectRatio;
            lCamera->SetApertureHeight( lFilmHeight);
            lApertureRatio = lFilmHeight / lFilmWidth;
            break;
        case FbxCamera::eFitStretch:
            lAspectRatio = lApertureRatio;
            break;
        case FbxCamera::eFitOverscan:
            if( lFilmWidth > lFilmHeight)
            {
                lFilmHeight = lFilmWidth * lAspectRatio;
            }
            else
            {
                lFilmWidth = lFilmHeight / lAspectRatio;
            }
            lApertureRatio = lFilmHeight / lFilmWidth;
            break;
        case FbxCamera::eFitNone:
        default:
            break;
        }
        //change the aspect ratio to Width : Height
        lAspectRatio = 1 / lAspectRatio;

        double lFieldOfViewX = 0.0;
        double lFieldOfViewY = 0.0;
        if ( lCamera->GetApertureMode() == FbxCamera::eVertical)
        {
                lFieldOfViewY = lCamera->FieldOfView.Get();
                lFieldOfViewX = VFOV2HFOV( lFieldOfViewY, 1 / lApertureRatio);
        }
        else if (lCamera->GetApertureMode() == FbxCamera::eHorizontal)
        {
            lFieldOfViewX = lCamera->FieldOfView.Get(); //get HFOV
            lFieldOfViewY = HFOV2VFOV( lFieldOfViewX, lApertureRatio);
        }
        else if (lCamera->GetApertureMode() == FbxCamera::eFocalLength)
        {
            lFieldOfViewX = lCamera->ComputeFieldOfView(lCamera->FocalLength.Get());    //get HFOV
            lFieldOfViewY = HFOV2VFOV( lFieldOfViewX, lApertureRatio);
        }
        else if (lCamera->GetApertureMode() == FbxCamera::eHorizAndVert) {
            lFieldOfViewX = lCamera->FieldOfViewX.Get();
            lFieldOfViewY = lCamera->FieldOfViewY.Get();
        }



        double lRealScreenRatio = (double)pWindowWidth / (double)pWindowHeight;
        int  lViewPortPosX = 0, 
            lViewPortPosY = 0, 
            lViewPortSizeX = pWindowWidth, 
            lViewPortSizeY = pWindowHeight;
        //compute the view port
        if( lRealScreenRatio > lAspectRatio)
        {
            lViewPortSizeY = pWindowHeight;
            lViewPortSizeX = (int)( lViewPortSizeY * lAspectRatio);
            lViewPortPosY = 0;
            lViewPortPosX = (int)((pWindowWidth - lViewPortSizeX) * 0.5);
        }
        else
        {
            lViewPortSizeX = pWindowWidth;
            lViewPortSizeY = (int)(lViewPortSizeX / lAspectRatio);
            lViewPortPosX = 0;
            lViewPortPosY = (int)((pWindowHeight - lViewPortSizeY) * 0.5);
        }

        //revise the Perspective since we have film offset
        double lFilmOffsetX = lCamera->FilmOffsetX.Get();
        double lFilmOffsetY = lCamera->FilmOffsetY.Get();
        lFilmOffsetX = 0 - lFilmOffsetX / lFilmWidth * 2.0;
        lFilmOffsetY = 0 - lFilmOffsetY / lFilmHeight * 2.0;

        GlSetCameraPerspective( lFieldOfViewY, lAspectRatio, lNearPlane, lFarPlane, lEye, lCenter, lUp, lFilmOffsetX, lFilmOffsetY);



    //glMatrixMode(GL_PROJECTION);
    //double lTestPerpMatrix[ 16];
    //glGetDoublev( GL_PROJECTION_MATRIX, lTestPerpMatrix);

    //lTestPerpMatrix[ 8] -= lFilmOffsetX;
    //lTestPerpMatrix[ 9] -= lFilmOffsetY;
    //
    //glLoadMatrixd( lTestPerpMatrix);
    //glMatrixMode(GL_MODELVIEW);
        

        glViewport( lViewPortPosX, lViewPortPosY, lViewPortSizeX, lViewPortSizeY);
        

    }
    // Get the relevant camera settings for an orthogonal view.
    else
    {
        double lPixelRatio = 1.0;
        if (lCamera)
            lPixelRatio = lCamera->GetPixelRatio();  

        double lLeftPlane, lRightPlane, lBottomPlane, lTopPlane;

        if(pWindowWidth < pWindowHeight) 
        {   
            lLeftPlane   = -gsOrthoCameraScale * lPixelRatio;
            lRightPlane  =  gsOrthoCameraScale * lPixelRatio;
            lBottomPlane = -gsOrthoCameraScale * pWindowHeight / pWindowWidth;
            lTopPlane    =  gsOrthoCameraScale * pWindowHeight / pWindowWidth;
        } 
        else 
        {
            pWindowWidth *= (int) lPixelRatio;
            lLeftPlane   = -gsOrthoCameraScale * pWindowWidth / pWindowHeight;
            lRightPlane  =  gsOrthoCameraScale * pWindowWidth / pWindowHeight;
            lBottomPlane = -gsOrthoCameraScale;
            lTopPlane    =  gsOrthoCameraScale;
        }

        GlSetCameraOrthogonal(lLeftPlane,
            lRightPlane,
            lBottomPlane,
            lTopPlane,
            lNearPlane,
            lFarPlane,
            lEye,
            lCenter,
            lUp);
    }
}