void OrientConstRotation::GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method) { if (firstTimeFlag) { Point3 trans, scaleP; Quat quat; Matrix3 tempMat(1); // CAL-9/26/2002: in absolute mode the value could be un-initialized (random value). if (method == CTRL_RELATIVE) tempMat = *(Matrix3*)val; DecomposeMatrix(tempMat, trans, quat, scaleP); baseRotQuatWorld = baseRotQuatLocal * quat; baseRotQuatWorld.Normalize(); firstTimeFlag = 0; } if (!ivalid.InInterval(t)) { DbgAssert(val != NULL); Update(t); } valid &= ivalid; if (method==CTRL_RELATIVE) { Interval iv; if (IsLocal()){ // From Update, I'm getting target_local_TM in curRot Matrix3 *mat = (Matrix3*)val; // this is source_Parent_TM Quat q = curRot; // this is target_local_TM PreRotateMatrix(*mat, q); // source_world_TM = source_Parent_TM * target_local_TM } else { // i.e., WorldToWorld: from Update, I'm getting target_world_TM in curRot int ct = pblock->Count(orientation_target_list);; if (ct < 1){ Matrix3 *mat = (Matrix3*)val; Quat q = curRot; PreRotateMatrix(*mat, q); } else{ Matrix3 *mat = (Matrix3*)val; // this is source_Parent_TM // CAL-06/24/02: preserve all components and replace with the new orientation. AffineParts ap; decomp_affine( *mat, &ap ); ap.q = curRot; // target world rotation comp_affine( ap, *mat ); // CAL-06/24/02: this only preserve the translation component // Point3 tr; // tr = mat->GetTrans(); // mat->IdentityMatrix(); // Quat q = curRot; // this is target_world_TM // q.MakeMatrix(*mat); // mat->SetTrans(tr); } } } else { *((Quat*)val) = curRot; } // RedrawListbox(GetCOREInterface()->GetTime()); }
void convertToTransform(Matrix<4,4,F32> & mat, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale) { AffineParts parts; decomp_affine(mat,&parts); trans = parts.trans; rot = parts.rot; srot = parts.scaleRot; scale = parts.scale; }
//================================================================= // Methods for DumpFrameRotationsTEP // int DumpFrameRotationsTEP::callback(INode *pnode) { ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!"); if (::FNodeMarkedToSkip(pnode)) return TREE_CONTINUE; int iNode = ::GetIndexOfINode(pnode); TSTR strNodeName(pnode->GetName()); // The model's root is a child of the real "scene root" INode *pnodeParent = pnode->GetParentNode(); BOOL fNodeIsRoot = pnodeParent->IsRootNode( ); // Get Node's "Local" Transformation Matrix Matrix3 mat3NodeTM = pnode->GetNodeTM(m_tvToDump); Matrix3 mat3ParentTM = pnodeParent->GetNodeTM(m_tvToDump); mat3NodeTM.NoScale(); // Clear these out because they apparently mat3ParentTM.NoScale(); // screw up the following calculation. Matrix3 mat3NodeLocalTM = mat3NodeTM * Inverse(mat3ParentTM); Point3 rowTrans = mat3NodeLocalTM.GetTrans(); // check to see if the parent bone was mirrored. If so, mirror invert this bones position if (m_phec->m_rgmaxnode[iNode].imaxnodeParent >= 0 && m_phec->m_rgmaxnode[m_phec->m_rgmaxnode[iNode].imaxnodeParent].isMirrored) { rowTrans = rowTrans * -1.0f; } // Get the rotation (via decomposition into "affine parts", then quaternion-to-Euler) // Apparently the order of rotations returned by QuatToEuler() is X, then Y, then Z. AffineParts affparts; float rgflXYZRotations[3]; decomp_affine(mat3NodeLocalTM, &affparts); QuatToEuler(affparts.q, rgflXYZRotations); float xRot = rgflXYZRotations[0]; // in radians float yRot = rgflXYZRotations[1]; // in radians float zRot = rgflXYZRotations[2]; // in radians // Get rotations in the -2pi...2pi range xRot = ::FlReduceRotation(xRot); yRot = ::FlReduceRotation(yRot); zRot = ::FlReduceRotation(zRot); // Print rotations fprintf(m_pfile, "%3d %f %f %f %f %f %f\n", // Node:%-15s Rotation (x,y,z)\n", iNode, rowTrans.x, rowTrans.y, rowTrans.z, xRot, yRot, zRot); return TREE_CONTINUE; }
bool bgAnimMax::GetDecompAffine(TimeValue time, INode* pNode, AffineParts* pAP, Point3* pRotAxis, float* pRotAngle) { Matrix3 tm = pNode->GetNodeTM(time) * Inverse(pNode->GetParentTM(time)); decomp_affine(tm, pAP); Point3 vRotAxis; float fRotAngle; if (pRotAngle != NULL && pRotAngle != NULL) { AngAxisFromQ(pAP->q, pRotAngle, *pRotAxis); } return true; }
bool TbsAnimObj::GetDecompAffine( TimeValue t, INode* pNode, AffineParts* ap, Point3* rotAxis, float* rotAngle ) { Matrix3 tm = pNode->GetNodeTM(t) * Inverse(pNode->GetParentTM(t)); decomp_affine(tm, ap); Point3 vRotAxis; float fRotAngle; if( rotAngle != NULL && rotAngle != NULL ) { AngAxisFromQ(ap->q, rotAngle, *rotAxis); } return true; }
void zapScale(Matrix<4,4,F32> & mat) { AffineParts parts; decomp_affine(mat,&parts); // now put the matrix back together again without the scale: // mat = mat.rot * mat.pos Vector<F32,4> trans; trans[0] = parts.trans.x(); trans[1] = parts.trans.y(); trans[2] = parts.trans.z(); trans[3] = 1; mat = parts.rot.toMatrix(); mat.setCol(3,trans); #ifdef TEST_DTS_MATH { // A test...will get rid of once we know it works... Matrix<4,4,F32> mat2; decomp_affine(mat,&parts); trans[0] = parts.trans.x(); trans[1] = parts.trans.y(); trans[2] = parts.trans.z(); trans[3] = 1; mat2 = parts.rot.toMatrix(); mat2.setCol(3,trans); for (S32 i=0; i<4; i++) { for (S32 j=0; j<4; j++) { assert(isZero(mat[i][j]-mat2[i][j],0.01f)); } } } #endif }
//--------------------------------------------------------------------------- Matrix3 U2MaxSceneExport::UniformMatrix(Matrix3 orig_cur_mat) { AffineParts parts; Matrix3 mat; // Remove scaling from orig_cur_mat // 1) Decompose original and get decomposition info decomp_affine(orig_cur_mat, &parts); // 2) construct 3x3 rotation from quaternion parts.q parts.q.MakeMatrix(mat); // 3) construct position row from translation parts.t mat.SetRow(3, parts.t); return mat; }
// REAPPLYANIMATION // Now that we've reparented a node within the hierarchy, re-apply all its animation. void ReapplyAnimation(INode *node, plSampleVec *samples) { Control *controller = node->GetTMController(); Control *rotControl = NewDefaultRotationController(); // we set the default rotation controller type above in RemoveBiped() Control *posControl = NewDefaultPositionController(); // '' '' Control *scaleControl = NewDefaultScaleController(); // '' '' controller->SetRotationController(rotControl); controller->SetPositionController(posControl); controller->SetScaleController(scaleControl); for(int i = 0; i < samples->size(); i++) { nodeTMInfo *info = (*samples)[i]; Matrix3 m = info->fMat3; TimeValue t = info->fTime; #if 1 node->SetNodeTM(t, m); #else AffineParts parts; INode *parent = node->GetParentNode(); Matrix3 parentTM = parent->GetNodeTM(t); Matrix3 invParentTM = Inverse(parentTM); m *= invParentTM; decomp_affine(m, &parts); Quat q(parts.q.x, parts.q.y, parts.q.z, parts.q.w); Point3 p(parts.t.x, parts.t.y, parts.t.z); rotControl->SetValue(t, q); posControl->SetValue(t, p); #endif } IKeyControl *posKeyCont = GetKeyControlInterface(posControl); IKeyControl *scaleKeyCont = GetKeyControlInterface(scaleControl); ReduceKeys<ILinPoint3Key>(node, posKeyCont); EliminateScaleKeys(node, scaleKeyCont); // grrrr ReduceKeys<ILinScaleKey>(node, scaleKeyCont); }
static void AddStaticPropPoints(std::vector<PropPoint> &propPoints, const FMMatrix44& upAxisTransform, FCDSceneNode* node) { if (node->GetName().find("prop-") == 0 || node->GetName().find("prop_") == 0) { // Strip off the "prop-" from the name std::string propPointName (node->GetName().substr(5)); Log(LOG_INFO, "Adding prop point %s", propPointName.c_str()); // CalculateWorldTransform applies transformations recursively for all parents of this node // upAxisTransform transforms this node to right-handed Z_UP coordinates FMMatrix44 transform = upAxisTransform * node->CalculateWorldTransform(); HMatrix matrix; memcpy(matrix, transform.Transposed().m, sizeof(matrix)); AffineParts parts; decomp_affine(matrix, &parts); // Add prop point in game coordinates PropPoint p = { propPointName, // Flip translation across the x-axis by swapping y and z { parts.t.x, parts.t.z, parts.t.y }, // To convert the quaternions: imagine you're using the axis/angle // representation, then swap the y,z basis vectors and change the // direction of rotation by negating the angle ( => negating sin(angle) // => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w) // but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead) { parts.q.x, parts.q.z, parts.q.y, -parts.q.w }, 0xff }; propPoints.push_back(p); } // Search children for prop points for (size_t i = 0; i < node->GetChildrenCount(); ++i) AddStaticPropPoints(propPoints, upAxisTransform, node->GetChild(i)); }
void XsiExp::ExportNodeTM( INode * node, int indentLevel) { // dump the full matrix Matrix3 matrix = node->GetNodeTM(GetStaticFrame()); TSTR indent = GetIndent(indentLevel); fprintf(pStream,"%s\t%s {\n\n", indent.data(), "FrameTransformMatrix"); Object * obj = node->EvalWorldState(0).obj; BOOL isBone = obj && obj->ClassID() == Class_ID(BONE_CLASS_ID, 0) ? TRUE : FALSE; if (node->GetParentNode() && node->GetParentNode()->IsRootNode()) { // bone chains get grafted into the hierarchy tree // if (!isBone) { // root mesh oTopMatrix = matrix; AffineParts ap; decomp_affine( matrix, &ap); topMatrix.Set( Point3( ap.k.x,0.0f,0.0f), Point3( 0.0f,ap.k.z,0.0f), Point3(0.0f,0.0f,ap.k.y), Point3(0,0,0)); // root transform is controlled by the engine matrix.IdentityMatrix(); } } else { matrix = matrix * Inverse(node->GetParentTM(GetStaticFrame())); if (!isBone) { matrix.SetRow( 3, topMatrix * matrix.GetRow(3)); } } // write the matrix values DumpMatrix3( &matrix, indentLevel+2); // transform close brace fprintf(pStream,"%s\t}\n", indent.data()); }
void XsiExp::DumpMatrix3( Matrix3 * m, int indentLevel) { Point3 row; TSTR indent = GetIndent(indentLevel); // swap y and z; max to soft correction decomp_affine( *m, &affine); // translate float temp = affine.t.z; affine.t.z = -affine.t.y; affine.t.y = temp; // rotate AngAxis aa( affine.q); temp = aa.axis.z; aa.axis.z = -aa.axis.y; aa.axis.y = temp; affine.q.Set(aa); // scale aa.Set( affine.u); temp = aa.axis.z; aa.axis.z = -aa.axis.y; aa.axis.y = temp; affine.u.Set( aa); temp = affine.k.z; affine.k.z = affine.k.y; affine.k.y = temp; Matrix3 matrix(1); matrix.PreTranslate(affine.t); PreRotateMatrix(matrix, affine.q); row = matrix.GetRow(0); fprintf(pStream,"%s %.6f,%.6f,%.6f,0.000000,\n", indent.data(), row.x, row.y, row.z); row = matrix.GetRow(1); fprintf(pStream,"%s %.6f,%.6f,%.6f,0.000000,\n", indent.data(), row.x, row.y, row.z); row = matrix.GetRow(2); fprintf(pStream,"%s %.6f,%.6f,%.6f,0.000000,\n", indent.data(), row.x, row.y, row.z); row = matrix.GetRow(3); fprintf(pStream,"%s %.6f,%.6f,%.6f,1.000000;;\n", indent.data(), row.x, row.y, row.z); }
void AsciiExp::DumpRotSample(INode* node, int indentLevel) { TSTR indent = GetIndent(indentLevel); _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_ROT_TRACK); TimeValue start = ip->GetAnimRange().Start(); TimeValue end = ip->GetAnimRange().End(); TimeValue t; int delta = GetTicksPerFrame() * GetKeyFrameStep(); Matrix3 tm; AffineParts ap; Quat prevQ; prevQ.Identity(); for (t=start; t<=end; t+=delta) { tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); decomp_affine(tm, &ap); // Rotation keys should be relative, so we need to convert these // absolute samples to relative values. Quat q = ap.q / prevQ; prevQ = ap.q; if (q.IsIdentity()) { // No point in exporting null keys... continue; } // Output the sample _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), ID_ROT_SAMPLE, t, Format(q)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); }
/** * This method will check if there is any animation connected to the node. * It will run from start to end of animation range and if the position, * rotation or scale has been changed during this, it will return true. */ BOOL OSGExp::hasAnimation(INode* node){ TimeValue start = _ip->GetAnimRange().Start(); TimeValue end = _ip->GetAnimRange().End(); TimeValue t; int delta = GetTicksPerFrame(); Matrix3 tm; AffineParts ap; Point3 firstPos; float rotAngle, firstRotAngle; Point3 rotAxis, firstRotAxis; Point3 firstScaleFactor; //return TRUE; for (t=start; t<=end; t+=delta) { tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); decomp_affine(tm, &ap); AngAxisFromQ(ap.q, &rotAngle, rotAxis); // If any point, rotation or scale in the t'th frame has // changes from the start frame then we have an animation. if (t != start){ if(!Util::isPoint3Equal(ap.t, firstPos) || !Util::isPoint3Equal(rotAxis, firstRotAxis) || fabs(rotAngle - firstRotAngle) > ALMOST_ZERO || !Util::isPoint3Equal(ap.k, firstScaleFactor)){ return TRUE; } } else { firstPos = ap.t; firstRotAngle = rotAngle; firstRotAxis = rotAxis; firstScaleFactor = ap.k; } } return FALSE; }
/** * This method will sample the node at each frame to get any possible change * of position, rotation and scaling. The posible changes is inserted into an * OSG AnimationPath and returned. */ osg::AnimationPath* OSGExp::sampleNode(INode* node){ // Create the animation path. osg::AnimationPath* animationPath = new osg::AnimationPath(); animationPath->setLoopMode(osg::AnimationPath::LOOP); TimeValue start = _ip->GetAnimRange().Start(); TimeValue end = _ip->GetAnimRange().End(); int val; if (node->GetUserPropInt("startFrame",val)) start= val; if (node->GetUserPropInt("endFrame",val)) end= val; TimeValue t; int delta = GetTicksPerFrame(); Matrix3 tm; AffineParts ap; for (t=start; t<=end; t+=delta) { tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); decomp_affine(tm, &ap); Point3 pos = ap.t; Point3 sca = ap.k; // Note: OSG wants absolute rotations Quat rot = ap.q; // Convert from Max's left-handed rotation to right-handed float ang; Point3 axis; AngAxisFromQ(rot, &ang, axis); ang = -ang; rot = QFromAngAxis(ang, axis); // Insert the sample point into animation path addControlPoint(animationPath, (t/(float)TIME_TICKSPERSEC), pos, rot, sca); } return animationPath; }
void AsciiExp::DumpPosSample(INode* node, int indentLevel) { TSTR indent = GetIndent(indentLevel); _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_POS_TRACK); TimeValue start = ip->GetAnimRange().Start(); TimeValue end = ip->GetAnimRange().End(); TimeValue t; int delta = GetTicksPerFrame() * GetKeyFrameStep(); Matrix3 tm; AffineParts ap; Point3 prevPos; for (t=start; t<=end; t+=delta) { tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); decomp_affine(tm, &ap); Point3 pos = ap.t; if (t!= start && EqualPoint3(pos, prevPos)) { // Skip identical keys continue; } prevPos = pos; // Output the sample _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), ID_POS_SAMPLE, t, Format(pos)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); }
void CppOut::OutputObject(INode *node,TCHAR *fname) { ObjectState os = node->EvalWorldState(theCppOut.ip->GetTime()); assert(os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID); BOOL needDel; NullView nullView; Mesh *mesh = ((GeomObject*)os.obj)->GetRenderMesh(ip->GetTime(), node,nullView,needDel); if (!mesh) return; FILE *file = fopen(fname,_T("wt")); float maxLen = 0.0f, len; int i; Matrix3 tm = node->GetObjTMAfterWSM(theCppOut.ip->GetTime()); AffineParts parts; decomp_affine(tm, &parts); if (file) { Box3 bb = mesh->getBoundingBox() * tm; Point3 center = bb.Center(); for (i=0; i<mesh->getNumVerts(); i++) { Point3 v = tm * mesh->verts[i] - center; len = Length(v); if (len > maxLen) maxLen = len; } fprintf(file, " mesh.setNumVerts(%d);\n", mesh->getNumVerts()); fprintf(file, " mesh.setNumFaces(%d);\n", mesh->getNumFaces()); for (i=0; i<mesh->getNumVerts(); i++) { Point3 v = (tm * mesh->verts[i] - center) / maxLen; fprintf(file," mesh.setVert(%d, size * Point3(%f,%f,%f));\n", i, v.x, v.y, v.z); } for (i=0; i<mesh->getNumFaces(); i++) { if (parts.f < 0.0f) fprintf(file," mesh.faces[%d].setVerts(%d,%d,%d);\n", i, mesh->faces[i].v[1], mesh->faces[i].v[0], mesh->faces[i].v[2]); else fprintf(file," mesh.faces[%d].setVerts(%d,%d,%d);\n", i, mesh->faces[i].v[0], mesh->faces[i].v[1], mesh->faces[i].v[2]); if (parts.f < 0.0f) fprintf(file," mesh.faces[%d].setEdgeVisFlags(%d,%d,%d);\n", i, mesh->faces[i].getEdgeVis(0) ? 1 : 0, mesh->faces[i].getEdgeVis(2) ? 1 : 0, mesh->faces[i].getEdgeVis(1) ? 1 : 0); else fprintf(file," mesh.faces[%d].setEdgeVisFlags(%d,%d,%d);\n", i, mesh->faces[i].getEdgeVis(0) ? 1 : 0, mesh->faces[i].getEdgeVis(1) ? 1 : 0, mesh->faces[i].getEdgeVis(2) ? 1 : 0); fprintf(file," mesh.faces[%d].setSmGroup(%x);\n", i, mesh->faces[i].smGroup); } fclose(file); } if (needDel) delete mesh; }
void XsiExp::ExportMesh( INode * node, TimeValue t, int indentLevel) { ObjectState os = node->EvalWorldState(t); if (!os.obj || os.obj->SuperClassID() != GEOMOBJECT_CLASS_ID) { return; // Safety net. This shouldn't happen. } BOOL needDel; TriObject * tri = GetTriObjectFromNode(node, t, needDel); if (!tri) { // no tri object return; } // prepare mesh Mesh * mesh = &tri->GetMesh(); mesh->buildNormals(); // object offset matrix; apply to verts // swap y and z; max to soft correction Matrix3 matrix(1); // translate matrix.PreTranslate( Point3( node->GetObjOffsetPos().x, node->GetObjOffsetPos().z, -node->GetObjOffsetPos().y)); // rotate AngAxis aa( node->GetObjOffsetRot()); float temp = aa.axis.z; aa.axis.z = -aa.axis.y; aa.axis.y = temp; PreRotateMatrix(matrix, Quat( aa)); // scale ScaleValue scale = node->GetObjOffsetScale(); aa.Set( scale.q); temp = aa.axis.z; aa.axis.z = -aa.axis.y; aa.axis.y = temp; scale.q.Set( aa); temp = scale.s.z; scale.s.z = scale.s.y; scale.s.y = temp; ApplyScaling(matrix, scale); // apply root transform matrix = matrix * topMatrix; // only rotation for normals AffineParts ap; Matrix3 rotMatrix(1); decomp_affine( matrix, &ap); PreRotateMatrix( rotMatrix, ap.q); // set winding order int vx1 = 0, vx2 = 1, vx3 = 2; if (TMNegParity( node->GetNodeTM(GetStaticFrame())) != TMNegParity( matrix) ) { // negative scaling; invert winding order and normal rotation vx1 = 2; vx2 = 1; vx3 = 0; rotMatrix = rotMatrix * Matrix3( Point3(-1,0,0), Point3(0,-1,0), Point3(0,0,-1), Point3(0,0,0)); } // header TSTR indent = GetIndent(indentLevel+1); fprintf(pStream, "%s%s %s {\n",indent.data(), "Mesh", FixupName(node->GetName())); // write number of verts int numLoop = mesh->getNumVerts(); fprintf(pStream, "%s\t%d;\n",indent.data(), numLoop); // write verts for (int i = 0; i < numLoop; i++) { Point3 v = mesh->verts[i]; float temp = v.z; v.z = -v.y; v.y = temp; v = matrix * v; fprintf(pStream, "%s\t%.6f;%.6f;%.6f;%s\n", indent.data(), v.x, v.y, v.z, i == numLoop - 1 ? ";\n" : ","); } // write number of faces numLoop = mesh->getNumFaces(); fprintf(pStream, "%s\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream, "%s\t3;%d,%d,%d;%s\n", indent.data(), mesh->faces[i].v[vx1], mesh->faces[i].v[vx2], mesh->faces[i].v[vx3], i == numLoop - 1 ? ";\n" : ","); } // face materials Mtl * nodeMtl = node->GetMtl(); int numMtls = !nodeMtl || !nodeMtl->NumSubMtls() ? 1 : nodeMtl->NumSubMtls(); // write face material list header fprintf(pStream, "%s\tMeshMaterialList {\n", indent.data()); // write number of materials fprintf(pStream, "%s\t\t%d;\n", indent.data(), numMtls); // write number of faces fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write face material indices (1 for each face) for (i = 0; i < numLoop; i++) { int index = numMtls ? mesh->faces[i].getMatID() % numMtls : 0; fprintf(pStream,"%s\t\t%d%s\n", indent.data(), index, i == numLoop - 1 ? ";\n" : ","); } // write the materials ExportMaterial( node, indentLevel+2); // verts close brace fprintf(pStream, "%s\t}\n\n",indent.data()); // write normals header fprintf(pStream, "%s\t%s {\n", indent.data(), "SI_MeshNormals"); // write number of normals fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop * 3); // write normals (3 for each face) for (i = 0; i < numLoop; i++) { Face * f = &mesh->faces[i]; int vert = f->getVert(vx1); Point3 vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(vert)); float temp = vn.z; vn.z = -vn.y; vn.y = temp; vn = rotMatrix * vn; fprintf(pStream,"%s\t\t%.6f;%.6f;%.6f;,\n", indent.data(), vn.x, vn.y, vn.z); vert = f->getVert(vx2); vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(vert)); temp = vn.z; vn.z = -vn.y; vn.y = temp; vn = rotMatrix * vn; fprintf(pStream,"%s\t\t%.6f;%.6f;%.6f;,\n", indent.data(), vn.x, vn.y, vn.z); vert = f->getVert(vx3); vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(vert)); temp = vn.z; vn.z = -vn.y; vn.y = temp; vn = rotMatrix * vn; fprintf(pStream,"%s\t\t%.6f;%.6f;%.6f;%s\n", indent.data(), vn.x, vn.y, vn.z, i == numLoop - 1 ? ";\n" : ","); } // write number of faces fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream, "%s\t\t%d;3;%d,%d,%d;%s\n", indent.data(), i, i * 3 + vx1, i * 3 + vx2, i * 3 + vx3, i == numLoop - 1 ? ";\n" : ","); } // normals close brace fprintf(pStream, "%s\t}\n\n",indent.data()); // texcoords if (nodeMtl && mesh && (nodeMtl->Requirements(-1) & MTLREQ_FACEMAP)) { // facemapping numLoop = mesh->getNumFaces() * 3; // write texture coords header fprintf(pStream, "%s\tSI_MeshTextureCoords {\n", indent.data()); // write number of texture coords fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write texture coords for (int i = 0; i < numLoop; i++) { Point3 tv[3]; Face * f = &mesh->faces[i]; make_face_uv( f, tv); fprintf(pStream, "%s\t\t%.6f;%.6f;,\n", indent.data(), tv[0].x, tv[0].y); fprintf(pStream, "%s\t\t%.6f;%.6f;,\n", indent.data(), tv[1].x, tv[1].y); fprintf(pStream, "%s\t\t%.6f;%.6f;%s\n", indent.data(), tv[2].x, tv[2].y, i == numLoop - 1 ? ";\n" : ","); } // write number of faces numLoop = mesh->getNumFaces(); fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream,"%s\t\t%d;3;%d,%d,%d;%s\n", indent.data(), i, mesh->tvFace[i].t[vx1], mesh->tvFace[i].t[vx2], mesh->tvFace[i].t[vx3], i == numLoop - 1 ? ";\n" : ","); } // texture coords close brace fprintf(pStream, "%s\t}\n\n", indent.data()); } else { numLoop = mesh->getNumTVerts(); if (numLoop) { // write texture coords header fprintf(pStream, "%s\tSI_MeshTextureCoords {\n", indent.data()); // write number of texture coords fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write texture coords for (i = 0; i < numLoop; i++) { UVVert tv = mesh->tVerts[i]; fprintf(pStream, "%s\t\t%.6f;%.6f;%s\n", indent.data(), tv.x, tv.y, i == numLoop - 1 ? ";\n" : ","); } // write number of faces numLoop = mesh->getNumFaces(); fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream,"%s\t\t%d;3;%d,%d,%d;%s\n", indent.data(), i, mesh->tvFace[i].t[vx1], mesh->tvFace[i].t[vx2], mesh->tvFace[i].t[vx3], i == numLoop - 1 ? ";\n" : ","); } // texture coords close brace fprintf(pStream, "%s\t}\n\n", indent.data()); } } /* // Export color per vertex info if (GetIncludeVertexColors()) { int numCVx = mesh->numCVerts; fprintf(pStream, "%s\t%s %d\n",indent.data(), ID_MESH_NUMCVERTEX, numCVx); if (numCVx) { fprintf(pStream,"%s\t%s {\n",indent.data(), ID_MESH_CVERTLIST); for (i=0; i<numCVx; i++) { Point3 vc = mesh->vertCol[i]; fprintf(pStream, "%s\t\t%s %d\t%s\n",indent.data(), ID_MESH_VERTCOL, i, Format(vc)); } fprintf(pStream,"%s\t}\n",indent.data()); fprintf(pStream, "%s\t%s %d\n",indent.data(), ID_MESH_NUMCVFACES, mesh->getNumFaces()); fprintf(pStream, "%s\t%s {\n",indent.data(), ID_MESH_CFACELIST); for (i=0; i<mesh->getNumFaces(); i++) { fprintf(pStream,"%s\t\t%s %d\t%d\t%d\t%d\n", indent.data(), ID_MESH_CFACE, i, mesh->vcFace[i].t[vx1], mesh->vcFace[i].t[vx2], mesh->vcFace[i].t[vx3]); } fprintf(pStream, "%s\t}\n",indent.data()); } } */ // Mesh close brace fprintf(pStream, "%s}\n",indent.data()); // dispose of tri object if (needDel) { delete tri; } }
void XsiExp::DumpScaleKeys( INode * node, int indentLevel) { Control * cont = node->GetTMController()->GetScaleController(); IKeyControl * ikc = GetKeyControlInterface(cont); INode * parent = node->GetParentNode(); if (!cont || !parent || (parent && parent->IsRootNode()) || !ikc) { // no controller or root node return; } int numKeys = ikc->GetNumKeys(); if (numKeys <= 1) { return; } Object * obj = node->EvalWorldState(0).obj; BOOL isBone = obj && obj->ClassID() == Class_ID(BONE_CLASS_ID, 0) ? TRUE : FALSE; // anim keys header TSTR indent = GetIndent(indentLevel); fprintf(pStream,"%s\tSI_AnimationKey {\n", indent.data()); fprintf(pStream,"%s\t\t1;\n", indent.data()); // 1 means scale keys fprintf(pStream,"%s\t\t%d;\n", indent.data(), numKeys); int t, delta = GetTicksPerFrame(); Matrix3 matrix; AffineParts ap; for (int i = 0; i < numKeys; i++) { // get the key's time if (cont->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) { ITCBRotKey key; ikc->GetKey(i, &key); t = key.time; } else if (cont->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) { IBezQuatKey key; ikc->GetKey(i, &key); t = key.time; } else if (cont->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { ILinRotKey key; ikc->GetKey(i, &key); t = key.time; } // sample the node's matrix matrix = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); if (!isBone) { matrix = matrix * topMatrix; } decomp_affine(matrix, &ap); fprintf(pStream, "%s\t\t%d; 3; %.6f, %.6f, %.6f;;%s\n", indent.data(), t / delta, ap.k.x, ap.k.z, ap.k.y, i == numKeys - 1 ? ";\n" : ","); } // anim keys close fprintf(pStream,"%s\t}\n\n", indent.data()); }
void OrientConstRotation::Update(TimeValue t){ Interval iv = FOREVER; ivalid = FOREVER; int ct = pblock->Count(orientation_target_list); int ctf = pblock->Count(orientation_target_weight); float total_orient_target_weight_prev = 0.0f, total_orient_target_weight_current = 0.0f; float orient_targ_Wt_current = 0.0f; Quat quat_prev, quat_current, lCurRot; INode *orient_target_prev= NULL, *orient_target_current= NULL; Matrix3 targetTM(1); Point3 trans, scaleP; quat_prev.Identity(); quat_current.Identity(); lCurRot.Identity(); if (ct == 1){ pblock->GetValue(orientation_target_list, t, orient_target_prev, iv, 0); pblock->GetValue(orientation_target_weight, t, orient_targ_Wt_current, iv, 0); ivalid &= iv; if (orient_target_prev == NULL){ targetTM.IdentityMatrix(); } else { targetTM = orient_target_prev->GetNodeTM(t, &ivalid); } if (IsLocal() && orient_target_prev != NULL) { targetTM = targetTM * Inverse(orient_target_prev->GetParentTM(t)); } AffineParts comps; // Requires header decomp.h decomp_affine(targetTM, &comps); quat_current = comps.q; quat_current.Normalize(); quat_current.MakeClosest(quat_prev); lCurRot = quat_current; quat_prev = lCurRot; total_orient_target_weight_prev += orient_targ_Wt_current; // } } else if (ct > 1){ pblock->GetValue(orientation_target_list, t, orient_target_prev, iv, 0); pblock->GetValue(orientation_target_weight, t, orient_targ_Wt_current, iv, 0); // if (orient_target_prev != NULL) // { ivalid &= iv; if (orient_target_prev == NULL){ targetTM.IdentityMatrix(); } else { targetTM = orient_target_prev->GetNodeTM(t, &ivalid); } if (IsLocal() && orient_target_prev != NULL) { targetTM = targetTM * Inverse(orient_target_prev->GetParentTM(t)); } AffineParts comps; // Requires header decomp.h decomp_affine(targetTM, &comps); quat_current = comps.q; quat_current.Normalize(); quat_current.MakeClosest(quat_prev); lCurRot = quat_current; quat_prev = lCurRot; total_orient_target_weight_prev += orient_targ_Wt_current; // } for (int i = 0; i < ct -1; i++) { // ct = pblock->Count(orientation_target_list); pblock->GetValue(orientation_target_list, t, orient_target_current, iv, i+1); pblock->GetValue(orientation_target_weight, t, orient_targ_Wt_current, iv, i+1); // if (orient_target_current != NULL){ ivalid &= iv; if (orient_target_current == NULL){ targetTM.IdentityMatrix(); } else { targetTM = orient_target_current->GetNodeTM(t, &ivalid); } // Matrix3 targetTM = orient_target_current->GetNodeTM(t, &ivalid); if (IsLocal() && orient_target_current != NULL) { targetTM = targetTM * Inverse(orient_target_current->GetParentTM(t)); } AffineParts comps; // Requires header decomp.h decomp_affine(targetTM, &comps); quat_current = comps.q; quat_current.Normalize(); quat_current.MakeClosest(quat_prev); float slerp_wt = 0.0f; if ((total_orient_target_weight_prev + orient_targ_Wt_current) != 0.0){ slerp_wt = orient_targ_Wt_current / (total_orient_target_weight_prev + orient_targ_Wt_current); } lCurRot = Slerp(quat_prev, quat_current, slerp_wt); lCurRot.Normalize(); quat_prev = lCurRot; total_orient_target_weight_prev += orient_targ_Wt_current; // } } //for (int i = 0; i < ct -1; i++) } //else if (ct > 1) if (oldTargetNumber != ct) { InitialOrientQuat = lCurRot; } curRot = lCurRot; if (total_orient_target_weight_prev > 0.0){ if (Relative()){ if(IsLocal()){ curRot = baseRotQuatLocal * (lCurRot /InitialOrientQuat); } else{ curRot = baseRotQuatWorld * (lCurRot /InitialOrientQuat); } } } else { curRot = baseRotQuatLocal; } curRot.MakeClosest(IdentQuat()); curRot.Normalize(); oldTargetNumber = ct; // if (ivalid.Empty()) ivalid.SetInstant(t); }
//---------------------------------------------------------------------------------- bool DumpModel(IGameMesh *gM, m_model *pModel, IGameNode *pGameNode) { IGameSkin *skin = NULL; if (gM->InitializeData()) // prepare game data { GMatrix ObjectTM = pGameNode->GetObjectTM(ExporterMAX::GetExporter()->GetStaticFrame()); Matrix3 world_to_obj = Inverse(ObjectTM.ExtractMatrix3()); AffineParts PRS; decomp_affine(ObjectTM.ExtractMatrix3(), &PRS); //Matrix xform; // //xform.set_rot(Quaternion(-PRS.q.x, -PRS.q.z, PRS.q.y, PRS.q.w)); //xform.set_translation(Vector(-PRS.t.x, PRS.t.z, PRS.t.y)); const int numMod = gM->GetNumModifiers(); if (numMod > 0) { for (int i = 0; i < numMod; i++) // check for skin modifier { IGameModifier * pM = gM->GetIGameModifier(i); if (pM->IsSkin()) { skin = (IGameSkin*)pM; // skin modifier } } } mesh_opt *m_opt; TriMapType tri_map; MatFaceMapType matface_map; // int <-> material unsigned int max_face_idx = 0; unsigned int FaceNum = Helper_GetNumberOfFaces(gM, FaceNum, max_face_idx); for (size_t i = 0; i < FaceNum; ++i) { Helper_ProcessFace(gM, i, PRS, world_to_obj, matface_map, tri_map, max_face_idx); } Helper_ComputeNormals(gM, matface_map); for (size_t IndexAdd = 0; IndexAdd < tri_map.size(); ++IndexAdd) { pModel->meshes.push_back(new m_mesh()); } int count = 0; TriMapIt it = tri_map.begin(); while (it != tri_map.end()) { m_mesh &msh = *pModel->meshes[count]; msh.num_faces = (*it).second->size() / 3; msh.material_id = (*it).first; msh.faces_idx = new unsigned int[msh.num_faces * 3]; for (size_t i = 0; i < msh.num_faces * 3; i+=3) { int Idx0 = (*it).second->front(); (*it).second->pop_front(); int Idx1 = (*it).second->front(); (*it).second->pop_front(); int Idx2 = (*it).second->front(); (*it).second->pop_front(); msh.faces_idx[i+0] = Idx2; msh.faces_idx[i+1] = Idx1; msh.faces_idx[i+2] = Idx0; } MatFaceMapIt it_mapfacemap = matface_map.find((*it).first); assert(it_mapfacemap != matface_map.end()); m_opt = (*it_mapfacemap).second; msh.skin = skin ? true : false; msh.num_vertices = m_opt->face_map.size(); msh.vertices = new Vector[msh.num_vertices]; msh.normals = new Vector[msh.num_vertices]; msh.colors = new Vector4f[msh.num_vertices]; msh.weights = skin ? new Vector4f[msh.num_vertices] : NULL; msh.bone_idxs = skin ? new unsigned int[msh.num_vertices * 4] : NULL; unsigned int texdim = 0; bool * faceidx_cache = new bool[msh.num_vertices]; memset(faceidx_cache, 0, msh.num_vertices * sizeof(bool)); bool alloc_texture = false; for (size_t i = 0; i < msh.num_faces * 3; ++i) { unsigned int face_idx = msh.faces_idx[i]; FaceMapIt it_face_map = m_opt->face_map.find(face_idx); assert(it_face_map != m_opt->face_map.end()); vert_opt face = (*it_face_map).second; msh.faces_idx[i] = face.face_idx; if (faceidx_cache[face.face_idx] == false) { faceidx_cache[face.face_idx] = true; msh.vertices[face.face_idx] = Vector(face.v.x, face.v.y, face.v.z); msh.colors[face.face_idx].x = face.c.x; msh.colors[face.face_idx].y = face.c.y; msh.colors[face.face_idx].z = face.c.z; Vector V(face.n.x, face.n.y, face.n.z); V.normalize(); msh.normals[face.face_idx] = V; if (msh.skin) { msh.weights[face.face_idx].x = face.weights.x; msh.weights[face.face_idx].y = face.weights.y; msh.weights[face.face_idx].z = face.weights.z; msh.bone_idxs[face.face_idx * 4 + 0] = face.bones[0]; // already remapped idxs msh.bone_idxs[face.face_idx * 4 + 1] = face.bones[1]; msh.bone_idxs[face.face_idx * 4 + 2] = face.bones[2]; msh.bone_idxs[face.face_idx * 4 + 3] = face.bones[3]; } if (face.num_tmaps && alloc_texture == false) { alloc_texture = true; texdim = 2; msh.num_texcoord_sets = face.num_tmaps; msh.texcoord_sets = new m_texcoord_set[msh.num_texcoord_sets]; for (size_t j = 0; j < face.num_tmaps; ++j) { msh.texcoord_sets[j].dim = texdim; msh.texcoord_sets[j].texcoords = new float[msh.num_vertices * texdim]; } } for (size_t j = 0; j < face.num_tmaps; ++j) { msh.texcoord_sets[j].texcoords[face.face_idx * texdim] = face.tmaps[j].x; msh.texcoord_sets[j].texcoords[face.face_idx * texdim + 1] = face.tmaps[j].y; } Vector tmp = face.v; //transform_coord(face.v, xform); //mult_pos(tmp, xform, face.v); // aabb min... if (tmp.x < pModel->aabb_min.x) pModel->aabb_min.x = tmp.x; if (tmp.y < pModel->aabb_min.y) pModel->aabb_min.y = tmp.y; if (tmp.z < pModel->aabb_min.z) pModel->aabb_min.z = tmp.z; // aabb max... if (tmp.x > pModel->aabb_max.x) pModel->aabb_max.x = tmp.x; if (tmp.y > pModel->aabb_max.y) pModel->aabb_max.y = tmp.y; if (tmp.z > pModel->aabb_max.z) pModel->aabb_max.z = tmp.z; // mesh bounding box // aabb min... if (tmp.x < msh.aabb_min.x) msh.aabb_min.x = tmp.x; if (tmp.y < msh.aabb_min.y) msh.aabb_min.y = tmp.y; if (tmp.z < msh.aabb_min.z) msh.aabb_min.z = tmp.z; // aabb max... if (tmp.x > msh.aabb_max.x) msh.aabb_max.x = tmp.x; if (tmp.y > msh.aabb_max.y) msh.aabb_max.y = tmp.y; if (tmp.z > msh.aabb_max.z) msh.aabb_max.z = tmp.z; } } delete [] faceidx_cache; ++count; ++it; Helper_ComputeUV(msh); Helper_ComputeTBN(msh); } return true; } return false; // "BadObject"; }
Matrix<4,4,F32> & getLocalNodeMatrix(AppNode * node, AppNode * parent, const AppTime & time, Matrix<4,4,F32> & matrix, AffineParts & a10, AffineParts & a20) { // Here's the story: the default transforms have no scale. In order to account for scale, the // scale at the default time is folded into the object offset (which is multiplied into the points // before exporting). Because of this, the local transform at a given time must take into account // the scale of the parent and child node at time 0 in addition to the current time. In particular, // the world transform at a given time is WT(time) = T(time) * inverse(Tscale(0)) // in order to avoid recomputing matrix at default time over and over, we assume that the first request // for the matrix will be at the default time, and thereafter, we will pass that matrix in and reuse it... Matrix<4,4,F32> m1 = node->getNodeTransform(time); Matrix<4,4,F32> m2; if (parent) m2 = parent->getNodeTransform(time); else m2 = Matrix<4,4,F32>::identity(); if (time == AppTime::DefaultTime()) { decomp_affine(m1,&a10); decomp_affine(m2,&a20); } // build the inverse scale matrices Matrix<4,4,F32> stretchRot10,stretchRot20; Matrix<4,4,F32> scaleMat10,scaleMat20; Matrix<4,4,F32> invScale10, invScale20; Point3D sfactor10, sfactor20; stretchRot10 = a10.scaleRot.toMatrix(); stretchRot20 = a20.scaleRot.toMatrix(); sfactor10 = Point3D(a10.sign/a10.scale.x(),a10.sign/a10.scale.y(),a10.sign/a10.scale.z()); sfactor20 = Point3D(a20.sign/a20.scale.x(),a20.sign/a20.scale.y(),a20.sign/a20.scale.z()); scaleMat10 = Matrix<4,4,F32>::identity(); scaleMat10[0][0] = sfactor10.x(); scaleMat10[1][1] = sfactor10.y(); scaleMat10[2][2] = sfactor10.z(); scaleMat20 = Matrix<4,4,F32>::identity(); scaleMat20[0][0] = sfactor20.x(); scaleMat20[1][1] = sfactor20.y(); scaleMat20[2][2] = sfactor20.z(); invScale10 = stretchRot10 * scaleMat10 * stretchRot10.inverse(); invScale20 = stretchRot20 * scaleMat20 * stretchRot20.inverse(); // build world transforms m1 = m1 * invScale10; m2 = m2 * invScale20; // build local transform matrix = m2.inverse() * m1; #ifdef TEST_DTS_MATH { Matrix<4,4,F32> testMat; Matrix<4,4,F32> m2inv = m2.inverse(); testMat = m2inv * m2; { for (S32 i=0; i<4; i++) for (S32 j=0; j<4; j++) { F32 val = i==j ? 1.0f : 0.0f; assert(isEqual(testMat[i][j],val,0.01f) && "assertion failed"); } } testMat = m2 * m2inv; { for (S32 i=0; i<4; i++) for (S32 j=0; j<4; j++) { F32 val = i==j ? 1.0f : 0.0f; assert(isEqual(testMat[i][j],val,0.01f) && "assertion failed"); } } } #endif return matrix; }
BOOL AsciiExp::CheckForAnimation(INode* node, BOOL& bPos, BOOL& bRot, BOOL& bScale) { TimeValue start = ip->GetAnimRange().Start(); TimeValue end = ip->GetAnimRange().End(); TimeValue t; int delta = GetTicksPerFrame(); Matrix3 tm; AffineParts ap; Point3 firstPos; float rotAngle, firstRotAngle = 0.0f; Point3 rotAxis, firstRotAxis; Point3 firstScaleFactor; bPos = bRot = bScale = FALSE; for (t=start; t<=end; t+=delta) { tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); decomp_affine(tm, &ap); AngAxisFromQ(ap.q, &rotAngle, rotAxis); if (t != start) { if (!bPos) { if (!EqualPoint3(ap.t, firstPos)) { bPos = TRUE; } } // MAX 2.x: // We examine the rotation angle to see if the rotation component // has changed. // Although not entierly true, it should work. // It is rare that the rotation axis is animated without // the rotation angle being somewhat affected. // MAX 3.x: // The above did not work, I have a repro scene that doesn't export a rotation track // because of this. I fixed it to also compare the axis. if (!bRot) { if (fabs(rotAngle - firstRotAngle) > ALMOST_ZERO) { bRot = TRUE; } else if (!EqualPoint3(rotAxis, firstRotAxis)) { bRot = TRUE; } } if (!bScale) { if (!EqualPoint3(ap.k, firstScaleFactor)) { bScale = TRUE; } } } else { firstPos = ap.t; firstRotAngle = rotAngle; firstRotAxis = rotAxis; firstScaleFactor = ap.k; } // No need to continue looping if all components are animated if (bPos && bRot && bScale) break; } return bPos || bRot || bScale; }
int EditPatchMod::DoAttach(INode *node, PatchMesh *attPatch, RPatchMesh *rattPatch, bool & canUndo) { ModContextList mcList; INodeTab nodes; if (!ip) return 0; ip->GetModContexts(mcList, nodes); if (mcList.Count() != 1) { nodes.DisposeTemporary(); return 0; } EditPatchData *patchData =(EditPatchData*)mcList[0]->localData; if (!patchData) { nodes.DisposeTemporary(); return 0; } patchData->BeginEdit(ip->GetTime()); // If the mesh isn't yet cached, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); if (!patch) { nodes.DisposeTemporary(); return 0; } patchData->RecordTopologyTags(patch); RecordTopologyTags(); // Transform the shape for attachment: // If reorienting, just translate to align pivots // Otherwise, transform to match our transform Matrix3 attMat(1); if (attachReorient) { Matrix3 thisTM = nodes[0]->GetNodeTM(ip->GetTime()); Matrix3 thisOTMBWSM = nodes[0]->GetObjTMBeforeWSM(ip->GetTime()); Matrix3 thisPivTM = thisTM * Inverse(thisOTMBWSM); Matrix3 otherTM = node->GetNodeTM(ip->GetTime()); Matrix3 otherOTMBWSM = node->GetObjTMBeforeWSM(ip->GetTime()); Matrix3 otherPivTM = otherTM * Inverse(otherOTMBWSM); Point3 otherObjOffset = node->GetObjOffsetPos(); attMat = Inverse(otherPivTM) * thisPivTM; } else { attMat = node->GetObjectTM(ip->GetTime()) * Inverse(nodes[0]->GetObjectTM(ip->GetTime())); } // RB 3-17-96 : Check for mirroring AffineParts parts; decomp_affine(attMat, &parts); if (parts.f < 0.0f) { int v[8], ct, ct2, j; Point3 p[9]; for (int i = 0; i < attPatch->numPatches; i++) { // Re-order rpatch if (attPatch->patches[i].type == PATCH_QUAD) { UI_PATCH rpatch=rattPatch->getUIPatch (i); int ctU=rpatch.NbTilesU<<1; int ctV=rpatch.NbTilesV<<1; int nU; for (nU=0; nU<ctU; nU++) { for (int nV=0; nV<ctV; nV++) { rattPatch->getUIPatch (i).getTileDesc (nU+nV*ctU)=rpatch.getTileDesc (ctU-1-nU+(ctV-1-nV)*ctU); } } for (nU=0; nU<ctU+1; nU++) { for (int nV=0; nV<ctV+1; nV++) { rattPatch->getUIPatch (i).setColor (nU+nV*(ctU+1), rpatch.getColor (ctU-nU+(ctV-nV)*ctU)); } } } // Re-order vertices ct = attPatch->patches[i].type == PATCH_QUAD ? 4 : 3; for (j = 0; j < ct; j++) { v[j] = attPatch->patches[i].v[j]; } for (j = 0; j < ct; j++) { attPatch->patches[i].v[j] = v[ct - j - 1]; } // Re-order vecs ct = attPatch->patches[i].type == PATCH_QUAD ? 8 : 6; ct2 = attPatch->patches[i].type == PATCH_QUAD ? 5 : 3; for (j = 0; j < ct; j++) { v[j] = attPatch->patches[i].vec[j]; } for (j = 0; j < ct; j++, ct2--) { if (ct2 < 0) ct2 = ct - 1; attPatch->patches[i].vec[j] = v[ct2]; } // Re-order enteriors if (attPatch->patches[i].type == PATCH_QUAD) { ct = 4; for (j = 0; j < ct; j++) { v[j] = attPatch->patches[i].interior[j]; } for (j = 0; j < ct; j++) { attPatch->patches[i].interior[j] = v[ct - j - 1]; } } // Re-order aux if (attPatch->patches[i].type == PATCH_TRI) { ct = 9; for (j = 0; j < ct; j++) { p[j] = attPatch->patches[i].aux[j]; } for (j = 0; j < ct; j++) { attPatch->patches[i].aux[j] = p[ct - j - 1]; } } // Re-order TV faces if present for (int chan = 0; chan < patch->getNumMaps(); ++chan) { if (attPatch->tvPatches[chan]) { ct = 4; for (j = 0; j < ct; j++) { v[j] = attPatch->tvPatches[chan][i].tv[j]; } for (j = 0; j < ct; j++) { attPatch->tvPatches[chan][i].tv[j] = v[ct - j - 1]; } } } } } int i; for (i = 0; i < attPatch->numVerts; ++i) attPatch->verts[i].p = attPatch->verts[i].p * attMat; for (i = 0; i < attPatch->numVecs; ++i) attPatch->vecs[i].p = attPatch->vecs[i].p * attMat; attPatch->computeInteriors(); theHold.Begin(); // Combine the materials of the two nodes. int mat2Offset = 0; Mtl *m1 = nodes[0]->GetMtl(); Mtl *m2 = node->GetMtl(); bool condenseMe = FALSE; if (m1 && m2 &&(m1 != m2)) { if (attachMat == ATTACHMAT_IDTOMAT) { int ct = 1; if (m1->IsMultiMtl()) ct = m1->NumSubMtls(); for (int i = 0; i < patch->numPatches; ++i) { int mtid = patch->getPatchMtlIndex(i); if (mtid >= ct) patch->setPatchMtlIndex(i, mtid % ct); } FitPatchIDsToMaterial(*attPatch, m2); if (condenseMat) condenseMe = TRUE; } // the theHold calls here were a vain attempt to make this all undoable. // This should be revisited in the future so we don't have to use the SYSSET_CLEAR_UNDO. theHold.Suspend(); if (attachMat == ATTACHMAT_MATTOID) { m1 = FitMaterialToPatchIDs(*patch, m1); m2 = FitMaterialToPatchIDs(*attPatch, m2); } Mtl *multi = CombineMaterials(m1, m2, mat2Offset); if (attachMat == ATTACHMAT_NEITHER) mat2Offset = 0; theHold.Resume(); // We can't be in face subobject mode, else we screw up the materials: DWORD oldSL = patch->selLevel; DWORD roldSL = patch->selLevel; patch->selLevel = PATCH_OBJECT; rpatch->SetSelLevel (EP_OBJECT); nodes[0]->SetMtl(multi); patch->selLevel = oldSL; rpatch->SetSelLevel (roldSL); m1 = multi; canUndo = FALSE; // Absolutely cannot undo material combinations. } if (!m1 && m2) { // We can't be in face subobject mode, else we screw up the materials: DWORD oldSL = patch->selLevel; DWORD roldSL = rpatch->GetSelLevel(); patch->selLevel = PATCH_OBJECT; rpatch->SetSelLevel (EP_OBJECT); nodes[0]->SetMtl(m2); patch->selLevel = oldSL; rpatch->SetSelLevel (roldSL); m1 = m2; } // Start a restore object... if (theHold.Holding()) theHold.Put(new PatchRestore(patchData, this, patch, rpatch, "DoAttach")); // Do the attach patch->Attach(attPatch, mat2Offset); rpatch->Attach(rattPatch, *patch); patchData->UpdateChanges(patch, rpatch); patchData->TempData(this)->Invalidate(PART_TOPO | PART_GEOM); // Get rid of the original node ip->DeleteNode(node); ResolveTopoChanges(); theHold.Accept(GetString(IDS_TH_ATTACH)); if (m1 && condenseMe) { // Following clears undo stack. patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); m1 = CondenseMatAssignments(*patch, m1); } nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); NotifyDependents(FOREVER, PART_TOPO | PART_GEOM, REFMSG_CHANGE); ip->RedrawViews(ip->GetTime(), REDRAW_NORMAL); return 1; }
/** * Converts a COLLADA XML document into the PMD mesh format. * * @param input XML document to parse * @param output callback for writing the PMD data; called lots of times * with small strings * @param xmlErrors output - errors reported by the XML parser * @throws ColladaException on failure */ static void ColladaToPMD(const char* input, OutputCB& output, std::string& xmlErrors) { CommonConvert converter(input, xmlErrors); if (converter.GetInstance().GetEntity()->GetType() == FCDEntity::GEOMETRY) { Log(LOG_INFO, "Found static geometry"); FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)converter.GetInstance().GetEntity()); // Convert the geometry into a suitable form for the game ReindexGeometry(polys); std::vector<VertexBlend> boneWeights; // unused std::vector<BoneTransform> boneTransforms; // unused std::vector<PropPoint> propPoints; // Get the raw vertex data FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION); FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL); FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD); const uint32* indicesCombined = inputPosition->GetIndices(); size_t indicesCombinedCount = inputPosition->GetIndexCount(); // (ReindexGeometry guarantees position/normal/texcoord have the same indexes) FCDGeometrySource* sourcePosition = inputPosition->GetSource(); FCDGeometrySource* sourceNormal = inputNormal ->GetSource(); FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource(); float* dataPosition = sourcePosition->GetData(); float* dataNormal = sourceNormal ->GetData(); float* dataTexcoord = sourceTexcoord->GetData(); size_t vertexCount = sourcePosition->GetDataCount() / 3; assert(sourcePosition->GetDataCount() == vertexCount*3); assert(sourceNormal ->GetDataCount() == vertexCount*3); assert(sourceTexcoord->GetDataCount() == vertexCount*2); // Transform mesh coordinate system to game coordinates // (doesn't modify prop points) TransformStaticModel(dataPosition, dataNormal, vertexCount, converter.GetEntityTransform(), converter.IsYUp()); // Add static prop points // which are empty child nodes of the main parent // Default prop points are already given in game coordinates AddDefaultPropPoints(propPoints); // Calculate transform to convert from COLLADA-defined up_axis to Z-up because // it's relatively straightforward to convert that to game coordinates FMMatrix44 upAxisTransform = FMMatrix44_Identity; if (converter.IsYUp()) { // Prop points are rotated -90 degrees about the X-axis, reverse that rotation // (do this once now because it's easier than messing with quaternions later) upAxisTransform = FMMatrix44::XAxisRotationMatrix(1.57f); } AddStaticPropPoints(propPoints, upAxisTransform, converter.GetInstance().GetParent()); WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoord, vertexCount, boneWeights, boneTransforms, propPoints); } else if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER) { Log(LOG_INFO, "Found skinned geometry"); FCDControllerInstance& controllerInstance = static_cast<FCDControllerInstance&>(converter.GetInstance()); // (NB: GetType is deprecated and should be replaced with HasType, // except that has irritating linker errors when using a DLL, so don't // bother) assert(converter.GetInstance().GetEntity()->GetType() == FCDEntity::CONTROLLER); // assume this is always true? FCDController* controller = static_cast<FCDController*>(converter.GetInstance().GetEntity()); FCDSkinController* skin = controller->GetSkinController(); REQUIRE(skin != NULL, "is skin controller"); FixSkeletonRoots(controllerInstance); // Data for joints is stored in two places - avoid overflows by limiting // to the minimum of the two sizes, and warn if they're different (which // happens in practice for slightly-broken meshes) size_t jointCount = std::min(skin->GetJointCount(), controllerInstance.GetJointCount()); if (skin->GetJointCount() != controllerInstance.GetJointCount()) { Log(LOG_WARNING, "Mismatched bone counts (skin has %d, skeleton has %d)", skin->GetJointCount(), controllerInstance.GetJointCount()); for (size_t i = 0; i < skin->GetJointCount(); ++i) Log(LOG_INFO, "Skin joint %d: %s", i, skin->GetJoint(i)->GetId().c_str()); for (size_t i = 0; i < controllerInstance.GetJointCount(); ++i) Log(LOG_INFO, "Skeleton joint %d: %s", i, controllerInstance.GetJoint(i)->GetName().c_str()); } // Get the skinned mesh for this entity FCDGeometry* baseGeometry = controller->GetBaseGeometry(); REQUIRE(baseGeometry != NULL, "controller has base geometry"); FCDGeometryPolygons* polys = GetPolysFromGeometry(baseGeometry); // Make sure it doesn't use more bones per vertex than the game can handle SkinReduceInfluences(skin, maxInfluences, 0.001f); // Convert the geometry into a suitable form for the game ReindexGeometry(polys, skin); const Skeleton& skeleton = FindSkeleton(controllerInstance); // Convert the bone influences into VertexBlend structures for the PMD: bool hasComplainedAboutNonexistentJoints = false; // because we want to emit a warning only once std::vector<VertexBlend> boneWeights; // one per vertex const FCDSkinControllerVertex* vertexInfluences = skin->GetVertexInfluences(); for (size_t i = 0; i < skin->GetInfluenceCount(); ++i) { VertexBlend influences = defaultInfluences; assert(vertexInfluences[i].GetPairCount() <= maxInfluences); // guaranteed by ReduceInfluences; necessary for avoiding // out-of-bounds writes to the VertexBlend for (size_t j = 0; j < vertexInfluences[i].GetPairCount(); ++j) { uint32 jointIdx = vertexInfluences[i].GetPair(j)->jointIndex; REQUIRE(jointIdx <= 0xFF, "sensible number of joints (<256)"); // because we only have a u8 to store them in // Find the joint on the skeleton, after checking it really exists FCDSceneNode* joint = NULL; if (jointIdx < controllerInstance.GetJointCount()) joint = controllerInstance.GetJoint(jointIdx); // Complain on error if (! joint) { if (! hasComplainedAboutNonexistentJoints) { Log(LOG_WARNING, "Vertexes influenced by nonexistent joint"); hasComplainedAboutNonexistentJoints = true; } continue; } // Store into the VertexBlend int boneId = skeleton.GetBoneID(joint->GetName().c_str()); if (boneId < 0) { // The relevant joint does exist, but it's not a recognised // bone in our chosen skeleton structure Log(LOG_ERROR, "Vertex influenced by unrecognised bone '%s'", joint->GetName().c_str()); continue; } influences.bones[j] = (uint8)boneId; influences.weights[j] = vertexInfluences[i].GetPair(j)->weight; } boneWeights.push_back(influences); } // Convert the bind pose into BoneTransform structures for the PMD: BoneTransform boneDefault = { { 0, 0, 0 }, { 0, 0, 0, 1 } }; // identity transform std::vector<BoneTransform> boneTransforms (skeleton.GetBoneCount(), boneDefault); for (size_t i = 0; i < jointCount; ++i) { FCDSceneNode* joint = controllerInstance.GetJoint(i); int boneId = skeleton.GetRealBoneID(joint->GetName().c_str()); if (boneId < 0) { // unrecognised joint - it's probably just a prop point // or something, so ignore it continue; } FMMatrix44 bindPose = skin->GetJoint(i)->GetBindPoseInverse().Inverted(); HMatrix matrix; memcpy(matrix, bindPose.Transposed().m, sizeof(matrix)); // set matrix = bindPose^T, to match what decomp_affine wants AffineParts parts; decomp_affine(matrix, &parts); BoneTransform b = { { parts.t.x, parts.t.y, parts.t.z }, { parts.q.x, parts.q.y, parts.q.z, parts.q.w } }; boneTransforms[boneId] = b; } // Construct the list of prop points. // Currently takes all objects that are directly attached to a // standard bone, and whose name begins with "prop-" or "prop_". std::vector<PropPoint> propPoints; AddDefaultPropPoints(propPoints); for (size_t i = 0; i < jointCount; ++i) { FCDSceneNode* joint = controllerInstance.GetJoint(i); int boneId = skeleton.GetBoneID(joint->GetName().c_str()); if (boneId < 0) { // unrecognised joint name - ignore, same as before continue; } // Check all the objects attached to this bone for (size_t j = 0; j < joint->GetChildrenCount(); ++j) { FCDSceneNode* child = joint->GetChild(j); if (child->GetName().find("prop-") != 0 && child->GetName().find("prop_") != 0) { // doesn't begin with "prop-", so skip it continue; } // Strip off the "prop-" from the name std::string propPointName (child->GetName().substr(5)); Log(LOG_INFO, "Adding prop point %s", propPointName.c_str()); // Get translation and orientation of local transform FMMatrix44 localTransform = child->ToMatrix(); HMatrix matrix; memcpy(matrix, localTransform.Transposed().m, sizeof(matrix)); AffineParts parts; decomp_affine(matrix, &parts); // Add prop point to list PropPoint p = { propPointName, { parts.t.x, parts.t.y, parts.t.z }, { parts.q.x, parts.q.y, parts.q.z, parts.q.w }, (uint8)boneId }; propPoints.push_back(p); } } // Get the raw vertex data FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION); FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL); FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD); const uint32* indicesCombined = inputPosition->GetIndices(); size_t indicesCombinedCount = inputPosition->GetIndexCount(); // (ReindexGeometry guarantees position/normal/texcoord have the same indexes) FCDGeometrySource* sourcePosition = inputPosition->GetSource(); FCDGeometrySource* sourceNormal = inputNormal ->GetSource(); FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource(); float* dataPosition = sourcePosition->GetData(); float* dataNormal = sourceNormal ->GetData(); float* dataTexcoord = sourceTexcoord->GetData(); size_t vertexCount = sourcePosition->GetDataCount() / 3; assert(sourcePosition->GetDataCount() == vertexCount*3); assert(sourceNormal ->GetDataCount() == vertexCount*3); assert(sourceTexcoord->GetDataCount() == vertexCount*2); // Transform model coordinate system to game coordinates TransformSkinnedModel(dataPosition, dataNormal, vertexCount, boneTransforms, propPoints, converter.GetEntityTransform(), skin->GetBindShapeTransform(), converter.IsYUp(), converter.IsXSI()); WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoord, vertexCount, boneWeights, boneTransforms, propPoints); } else { throw ColladaException("Unrecognised object type"); } }
//---------------------------------------------------------------------------- PX2::Transform SceneBuilder::GetLocalTransform (INode *node, TimeValue time) { // 计算节点的本地变换。Max节点的变换方法提供的节点的世界变换,所以我们 // 必须做一些操纵去获得节点的本地变换。 Matrix3 maxLocal = node->GetObjTMAfterWSM(time) * Inverse(node->GetParentNode()->GetObjTMAfterWSM(time)); // 分解变换 AffineParts affParts; decomp_affine(maxLocal, &affParts); // Position bool isTranslationZero = fabsf(affParts.t.x) < MIN_DIFFERENCE && fabsf(affParts.t.y) < MIN_DIFFERENCE && fabsf(affParts.t.z) < MIN_DIFFERENCE; // Rotation float qSign = (affParts.q.w >= 0.0f ? 1.0f : -1.0f); bool isRotationIndentity = fabsf(qSign*affParts.q.w - 1.0f) < MIN_DIFFERENCE && fabsf(affParts.q.x) < MIN_DIFFERENCE && fabsf(affParts.q.y) < MIN_DIFFERENCE && fabsf(affParts.q.z) < MIN_DIFFERENCE; // Reflect bool hasReflection = (affParts.f < 0.0f); // Uniform scale bool isScaleUniform = (fabsf(affParts.k.x - affParts.k.y)<MIN_DIFFERENCE && fabsf(affParts.k.y - affParts.k.z)<MIN_DIFFERENCE); // Unity scale bool isScaleUnity = isScaleUniform && fabsf(affParts.k.x - 1.0f) < MIN_DIFFERENCE; // Scale orientation is identity? float uSign = (affParts.u.w >= 0.0f ? 1.0f : -1.0f); bool isOrientIndentity = isScaleUniform || ( fabsf(uSign*affParts.u.w - 1.0f) < MIN_DIFFERENCE && fabsf(affParts.u.x) < MIN_DIFFERENCE && fabsf(affParts.u.y) < MIN_DIFFERENCE && fabsf(affParts.u.z) < MIN_DIFFERENCE); // 计算Phoenix2等价变换 PX2::Transform local; if (!isTranslationZero) { local.SetTranslate(PX2::APoint(affParts.t.x, affParts.t.y, affParts.t.z)); } if (hasReflection) { affParts.k *= -1.0f; } if (isScaleUniform) { // 矩阵的形式为R*(s*I),s是统一缩放矩阵。 if (!isRotationIndentity) { PX2::HMatrix rot; PX2::HQuaternion(affParts.q.w, -affParts.q.x, -affParts.q.y, -affParts.q.z).ToRotationMatrix(rot); local.SetRotate(rot); } if (!isScaleUnity) { local.SetUniformScale(affParts.k.x); } } else if (isOrientIndentity) { if (!isRotationIndentity) { PX2::HMatrix rot; PX2::HQuaternion(affParts.q.w, -affParts.q.x, -affParts.q.y, -affParts.q.z).ToRotationMatrix(rot); local.SetRotate(rot); } local.SetScale(PX2::APoint(affParts.k.x, affParts.k.y, affParts.k.z)); } else { PX2::Matrix3f mat( maxLocal.GetAddr()[0][0], maxLocal.GetAddr()[1][0], maxLocal.GetAddr()[2][0], maxLocal.GetAddr()[0][1], maxLocal.GetAddr()[1][1], maxLocal.GetAddr()[2][1], maxLocal.GetAddr()[0][2], maxLocal.GetAddr()[1][2], maxLocal.GetAddr()[2][2]); local.SetMatrix(PX2::HMatrix(mat)); } return local; }
void ISCExport::saveISC(const TCHAR *name,ExpInterface *ei,Interface *i) { SceneEnumerator enumerator(ei,i); // No triobjects to export -> quit if (enumerator.numTriobjects()==0) return; size_t objnum=0; unsigned long chunksize,namessize=0; FILE *outfile=fopen(name,"wb"); if (!outfile) return; for (objnum=0;objnum<enumerator.numTriobjects();++objnum) namessize+=(unsigned long)(enumerator.triobjectname(objnum).length())+1; fwrite("isc0",1,4,outfile); fwrite("objt",1,4,outfile); chunksize=(unsigned long)(enumerator.numTriobjects()*(3*sizeof(float)+4*sizeof(float)+4))+namessize; fwrite(&chunksize,1,4,outfile); { DWORD numobjs=(DWORD)(enumerator.numTriobjects()); fwrite(&numobjs,1,4,outfile); } for (objnum=0;objnum<enumerator.numTriobjects();++objnum) { TriObject *pTriobj=enumerator.triobject(objnum); std::string objname=enumerator.triobjectname(objnum); Matrix3 *pTM=enumerator.triobjecttransform(objnum); /*Point3 trans=pTM->GetTrans(); Quat rot(*pTM);*/ AffineParts parts; decomp_affine(*pTM,&parts); Point3 trans=parts.t; Quat rot=parts.q; //float ang[3]; //QuatToEuler(rot,ang); /*rot.GetEuler(&ang[0],&ang[1],&ang[2]); //{ float f=ang[1]; ang[1]=ang[2]; ang[2]=f; } rot.SetEuler(ang[0],ang[2],ang[1]);*/ trans.x=trans.y=trans.z=0; rot.Identity(); fwrite(objname.c_str(),1,objname.length()+1,outfile); //trans.x*=-1; fwrite(&(trans.x),1,sizeof(float),outfile); fwrite(&(trans.y),1,sizeof(float),outfile); fwrite(&(trans.z),1,sizeof(float),outfile); fwrite(&(rot.x),1,sizeof(float),outfile); fwrite(&(rot.y),1,sizeof(float),outfile); fwrite(&(rot.z),1,sizeof(float),outfile); fwrite(&(rot.w),1,sizeof(float),outfile); fwrite(&objnum,1,4,outfile); } fwrite("mesh",1,4,outfile); chunksize=(unsigned long)(enumerator.numTriobjects()*(2*4 + 3*4 + 4 + (3+3+2+3+3)*sizeof(float)))+namessize; fwrite(&chunksize,1,4,outfile); { DWORD numobjs=(DWORD)(enumerator.numTriobjects()); fwrite(&numobjs,1,4,outfile); } for (objnum=0;objnum<enumerator.numTriobjects();++objnum) { Matrix3 *pTM=enumerator.triobjecttransform(objnum); TriObject *pTriobj=enumerator.triobject(objnum); std::string objname=enumerator.triobjectname(objnum); fwrite(objname.c_str(),1,objname.length()+1,outfile); saveMesh(outfile,pTriobj,pTM,enumerator.triobjectmaterial(objnum),enumerator); } fwrite("mtrl",1,4,outfile); chunksize=0; fwrite(&chunksize,1,4,outfile); { DWORD nummtrls=(DWORD)(enumerator.numMaterials()); fwrite(&nummtrls,1,4,outfile); } for (size_t mtrlnum=0;mtrlnum<enumerator.numMaterials();++mtrlnum) { Mtl* pMtl=enumerator.material(mtrlnum); saveMaterial(outfile,pMtl); } fclose(outfile); }