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; } }
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); } }
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; }
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); } }
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 }
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); } }