void MaxAWDExporter::ExportTriGeom(AWDTriGeom *awdGeom, Object *obj, INode *node, ISkin *skin, IGameMesh * igame_mesh) { if (awdGeom != NULL) { if (awdGeom->get_is_created()) return; else { awdGeom->set_is_created(true); // Extract skinning information (returns number of joints per vertex), // and writes the weights and joints array according to the jpv int jpv=0; awd_float64 *weights=NULL; awd_uint32 *joints=NULL; jpv = ExportSkin(node, skin, &weights, &joints); // Calculate offset matrix from the object TM (which includes geometry offset) // this will be used to transform all vertices into node space. int time=maxInterface->GetTime(); // get the neutral pose time from the vertex (using a dedcated cache for this) bool hasVertexAnim=vetexAnimNeutralPosecache->hasKey(node); if(hasVertexAnim) time=vetexAnimNeutralPosecache->Get(node)*GetTicksPerFrame(); else{ // get the neutral pose time from the skeleton, if any is used if (opts->ExportSkin() && skin && jpv>0) { SkeletonCacheItem *skel = skeletonCache->GetFromBone(skin->GetBone(0)); time=skel->awdSkel->get_neutralPose(); } } Matrix3 offsMtx = node->GetObjectTM(time) * Inverse(node->GetNodeTM(time)); bool isInSkinPoseMode=false; double *mtxData = (double *)malloc(12*sizeof(double)); SerializeMatrix3(offsMtx, mtxData); /*if (skin && jpv>0) { // if the mesh is not in "Skin Pose Mode", // we add the bind-matrix to the offset matrix (so that all points are moved accoridingly) // and later we set the transform matrix of the mesh to the identity-matrix (no transform) ISkinPose * skinPose; skinPose=skinPose->GetISkinPose(*node); //isInSkinPoseMode=skinPose->SkinPoseMode(); Matrix3 mtx; mtx.IdentityMatrix(); if (!isInSkinPoseMode){ Matrix3 bm; bm.IdentityMatrix(); //skin->GetSkinInitTM(node, bm, true); //offsMtx *= bm; //SerializeMatrix3(mtx, mtxData); } skinPose=NULL; }*/ ObjectState os; // Flatten entire modifier stack os = node->EvalWorldState(time); obj = os.obj; // its allready been taken care that the correct obj is submitted to this function, so we can directly convert to TriObject (?) TriObject *triObject = (TriObject*)obj->ConvertToType(time, Class_ID(TRIOBJ_CLASS_ID, 0)); Mesh mesh = triObject->mesh; int numTris = mesh.getNumFaces(); int numVerts = mesh.getNumVerts(); awdGeom->set_originalPointCnt(numVerts); // This could happen for example with splines (No!, this should never happen, because we check for this earlier (?)) if (numTris==0) return; /* //TODO: optional reorder exported faces, so that quads are stored first //we than can store one uint32 as offset into the triangle-list, to reconstruct quads on import int * quadDic=(int *)malloc(sizeof(int) * numTris); int * isExportedDic=(int *)malloc(sizeof(int) * numTris); int cnt1=0; for(cnt1=0;cnt1<numTris;cnt1++){ quadDic[cnt1]=0; isExportedDic[cnt1]=0; } PolyObject *polyObject = (PolyObject*)obj->ConvertToType(time, Class_ID(POLYOBJ_CLASS_ID, 0)); if (polyObject!=NULL){ MNMesh mnMesh = polyObject->GetMesh(); int numFaces = mnMesh.FNum(); int numtri = mnMesh.TriNum(); int idxCnt=0; for(idxCnt=0;idxCnt<numFaces;idxCnt++){ MNFace thisface=mnMesh.f[idxCnt]; if(thisface.TriNum()==2){ Tab<int> intList; thisface.GetTriangles(intList); //quadDic[intList[0]]=intList[1]; //quadDic[intList[1]]=intList[0]; } } //if (polyObject!=obj) // polyObject->DeleteMe(); } */ bool force_split=opts->SplitByMatID();// if true, the GeomUtils will create a SubGeo for each MaterialID used by a face, no matter if they share materials or not bool useUV=opts->ExportUVs();// TODO: check if uvs exists bool useSecUVs=useUV; // TODO: check if second UVs exists (and are requested) bool useNormals=opts->ExportNormals(); // ATTENTION: // we have collected all meshintsances that are using this geometry. // but some material-settings ( UV / SecondUV / explode) can force us to create multiple geometries... // the IGAmeMesh gives acces to some handy functions for exporting meshes // we still need to use the mesh from the standart api, to have access to the correct UV (?) MeshNormalSpec *specificNormals = NULL; if(igame_mesh!=NULL){ int numTrisGameMesh = igame_mesh->GetNumberOfFaces(); if (numTrisGameMesh!=numTris){ return; //ERROR: faceCount of game-mesh is not facecount of api-mesh - should not happen } AWDBlockList * meshInstanceList = awdGeom->get_mesh_instance_list(); if (meshInstanceList==NULL){ return; //ERROR: faceCount of game-mesh is not facecount of api-mesh - should not happen } int numMeshInstances=meshInstanceList->get_num_blocks(); if ((meshInstanceList!=NULL)&&(numMeshInstances==0)){ return; //ERROR: faceCount of game-mesh is not facecount of api-mesh - should not happen } // check if the first UVChannel is available for this mesh ( numTVFaces must be equal to numTris) if (useUV) { try{ if (mesh.mapSupport(1)){ MeshMap * mesh_map; mesh_map = &(mesh.Map(1)); int numTrisMap = mesh_map->getNumFaces(); if (numTrisMap!=numTris){ useUV=false; useSecUVs=false; } } else{ useUV=false; useSecUVs=false; } } catch(...){ useUV=false; useSecUVs=false; } } // check if any normals are available for the mesh if (useNormals) { mesh.SpecifyNormals(); specificNormals = mesh.GetSpecifiedNormals(); int specificNormalCount = specificNormals->GetNumNormals();// for me, this is allways been 0 if (specificNormalCount==0){ specificNormals=NULL; int numNorms = igame_mesh->GetNumberOfNormals(); if (numNorms==0){ useNormals=false; } } } AWD_field_type precision_geo=AWD_FIELD_FLOAT32; if (opts->StorageGeometry()==1) precision_geo=AWD_FIELD_FLOAT64; AWDGeomUtil * geomUtil=new AWDGeomUtil(awdGeom->get_split_faces(), force_split, useUV, useSecUVs, useNormals, 0.0, jpv, precision_geo); // create a list of GUGeom for each Mesh instance. // before collecting the actual geom-data, // we will reduce the number of GUGeoms to the minimum needed to display all mesh-instances correctly geomUtil->createPreGeometries(meshInstanceList); Tab<int> MatIDList=igame_mesh->GetActiveMatIDs(); int matIDCnt; Tab<FaceEx *> facelist; // for each submesh do: for (matIDCnt=0; matIDCnt< MatIDList.Count(); matIDCnt++){ facelist=igame_mesh->GetFacesFromMatID(MatIDList[matIDCnt]); int idx; int faceCnt=facelist.Count(); if (faceCnt>0){ // if the submesh will be used (if the matID is used by any face): AWDBlockList * subMaterialList = new AWDBlockList(); int meshInstCnt=0; // for each mesh instance, apply the material for (meshInstCnt=0;meshInstCnt<numMeshInstances; meshInstCnt++){ AWDMeshInst * awdMesh=(AWDMeshInst *)meshInstanceList->getByIndex(meshInstCnt); if (awdMesh==NULL){ return; } else{ AWDBlockList * preMaterials = awdMesh->get_pre_materials(); if (preMaterials==NULL){ return; } else{ bool createDefault = false; AWDMaterial * thisMatBlock = (AWDMaterial *)preMaterials->getByIndex(MatIDList[matIDCnt]); if (thisMatBlock==NULL){ thisMatBlock=(AWDMaterial *)awdMesh->get_defaultMat(); } if (useUV){ if (thisMatBlock->get_mappingChannel()>0) thisMatBlock->set_mappingChannel(checkIfUVMapExists(mesh, numTris, thisMatBlock->get_mappingChannel())); } if (useSecUVs){ if (thisMatBlock->get_secondMappingChannel()>0) thisMatBlock->set_secondMappingChannel(checkIfUVMapExists(mesh, numTris, thisMatBlock->get_secondMappingChannel())); } subMaterialList->force_append(thisMatBlock); } } } geomUtil->add_new_sub_geo_to_preGUgeoms(subMaterialList, MatIDList[matIDCnt]); delete subMaterialList; } } geomUtil->createGeometries(); for (matIDCnt=0; matIDCnt<MatIDList.Count(); matIDCnt++){ facelist=igame_mesh->GetFacesFromMatID(MatIDList[matIDCnt]); int idx; int faceCnt=facelist.Count(); if (faceCnt>0){ int numGeoms=geomUtil->get_geoList()->get_num_blocks(); int geoCnt; for(geoCnt=0; geoCnt<numGeoms; geoCnt++){ GUGeo * thisGUGeo = geomUtil->get_geoList()->get_by_idx(geoCnt); // get the uvs using the index stored in the geomUtil // get the secondUV using the index stored in the geomUtil // get the explode (normals) using the index stored in the geomUtil // ATTENTION: its not a Subgeo but a SubgeoGroup, so it contain several Subgeos if the face or vert lists are to big for one subgeo int thisSubGeoIdx=geomUtil->getSubGeoIdxForMatIdx(MatIDList[matIDCnt]); GUSubGeoGroup *thisSubGeoGroup = thisGUGeo->get_subGeomList()->get_by_idx(thisSubGeoIdx); if (thisSubGeoGroup==NULL){ return; } AWDMaterial * thisAWDMat=(AWDMaterial *)thisSubGeoGroup->materials->getByIndex(0); bool explode=thisAWDMat->get_is_faceted(); MeshMap * mainUVMeshMap=NULL; MeshMap * secondUVMeshMap=NULL; if (thisSubGeoGroup->include_uv){ if (thisAWDMat->get_mappingChannel()>0) mainUVMeshMap = &(mesh.Map(thisAWDMat->get_mappingChannel())); } if (thisSubGeoGroup->include_suv){ if (thisAWDMat->get_secondMappingChannel()>0) secondUVMeshMap = &(mesh.Map(thisAWDMat->get_secondMappingChannel())); } // for each face in the list do: bool hasMultipleUV=false; for (idx=0;idx<faceCnt;idx++){ /*if(isExportedDic[idx]==0){ isExportedDic[idx]=1; if(quadDic[idx]>0){ int yes=0; }*/ // this will create a new SubGeo inside the SubgeoGroup, if the limits are reached thisSubGeoGroup->check_limits(); // create a new vert FaceEx * f=facelist[idx]; int apiFaceIdx=f->meshFaceIndex; TVFace tvface; TVFace tvFaceSecond; Face face = mesh.faces[apiFaceIdx]; DWORD *inds = face.getAllVerts(); if (thisSubGeoGroup->include_uv){ if (mainUVMeshMap!=NULL) tvface = mainUVMeshMap->tf[apiFaceIdx]; else tvface = mesh.tvFace[apiFaceIdx]; } if (thisSubGeoGroup->include_suv){ if (secondUVMeshMap!=NULL) tvFaceSecond = secondUVMeshMap->tf[apiFaceIdx]; else tvFaceSecond = mesh.tvFace[apiFaceIdx]; } Point3 faceNormal; if (geomUtil->include_normals) { // if we want to export normals, but no normals was read, than we need to calculate ourself, // using the face-normal for the angle-calulation if ((igame_mesh==NULL && specificNormals==NULL)||(explode)){ // faceNormal = mesh.getFaceNormal(t); // this crashes 3dsmax (why?), so calulate the facenormal manually: Point3 v0, v1, v2; Tab<Point3> fnorms; v0 = mesh.getVert(face.getVert(0)); v1 = mesh.getVert(face.getVert(1)); v2 = mesh.getVert(face.getVert(2)); faceNormal = (v1-v0)^(v2-v1); faceNormal = Normalize(faceNormal); } } int v; for (v=2; v>=0; v--) { int vIdx = face.getVert(v); Point3 vtx = offsMtx * mesh.getVert(vIdx); vdata *vd = (vdata *)malloc(sizeof(vdata)); vd->orig_idx = vIdx; vd->x = vtx.x; vd->y = vtx.z; vd->z = vtx.y; // Might not have UV coords if (geomUtil->include_uv) { int tvIdx; Point3 tvtx; Point3 stvtx; if (mainUVMeshMap!=NULL) tvtx=mainUVMeshMap->tv[tvface.t[v]]; else{ tvIdx = tvface.getTVert(v); tvtx = mesh.getTVert(tvIdx); } if (secondUVMeshMap!=NULL) stvtx=secondUVMeshMap->tv[tvFaceSecond.t[v]]; else{ tvIdx = tvface.getTVert(v); stvtx = mesh.getTVert(tvIdx); } vd->u = tvtx.x; vd->v = 1.0-tvtx.y; vd->su = stvtx.x; vd->sv = 1.0-stvtx.y; } if (geomUtil->include_normals) { Point3 normal; // if specific vertex-normals was found, we use it, if the subgeo is not set to explode if ((specificNormals!=NULL) && (!explode)){ normal = specificNormals->GetNormal(apiFaceIdx, v); } // else if a (not specific) vertex-normals was found (on the igame-mesh), we use it, if the subgeo is not set to explode else if ((igame_mesh!=NULL) && (!explode)){ igame_mesh->GetNormal(f->norm[v], normal, true); } // else: since we still want normals exported, we use the face-normal (using facenormal with threshold of 0 will explode the mesh else{ // i dont think this should really get executed anymore (since the igame-object allways should give access to the normals) normal=faceNormal; } // if the object is skinned, we get the global normals if (jpv>0){ if (normal) normal=offsMtx*normal; } vd->nx = normal.x; vd->ny = normal.z; vd->nz = normal.y; } // If there is skinning information, copy it from the weight // and joint index arrays returned by ExportSkin() above. vd->num_bindings = jpv; if (jpv > 0) { vd->weights = (awd_float64*)malloc(jpv*sizeof(awd_float64)); vd->joints = (awd_uint32*)malloc(jpv*sizeof(awd_uint32)); int memoffs = jpv*vIdx; memcpy(vd->weights, weights+memoffs, jpv*sizeof(awd_float64)); memcpy(vd->joints, joints+memoffs, jpv*sizeof(awd_uint32)); } vd->force_hard = false; // add the new vertex to the subgeo thisSubGeoGroup->append_vdata(vd); } } } } } AWDBlockList * returned_geoms = geomUtil->build_geom(awdGeom); AWDBlockIterator * it=NULL; AWDMeshInst * block; int maxCount=0; int geomCnt=0; for (geomCnt=0; geomCnt<returned_geoms->get_num_blocks();geomCnt++){ AWDTriGeom * thisAWDGeom=(AWDTriGeom *)returned_geoms->getByIndex(geomCnt); it = new AWDBlockIterator(thisAWDGeom->get_mesh_instance_list()); AWDSubGeom *sub; sub = thisAWDGeom->get_first_sub(); while (sub) { AWDBlockList *subGeoGroupMatList=sub->get_materials(); int thisIdx=0; it->reset(); while ((block = (AWDMeshInst*)it->next()) != NULL) { block->set_geom(thisAWDGeom); //if (!isInSkinPoseMode){ // block->set_transform(mtxData); //} AWDMaterial * thisMat=(AWDMaterial *)subGeoGroupMatList->getByIndex(thisIdx); if (thisMat==NULL){ int test=0; //ERROR - this should never happen } else{ AWDLightPicker * lightPicker=(AWDLightPicker *)block->get_lightPicker(); AWDBlock * thisAnimator=(AWDBlock *)block->get_animator(); if ((lightPicker!=NULL)||(thisAnimator!=NULL)){ thisMat=thisMat->get_unique_material(lightPicker, thisAnimator, NULL); if(lightPicker!=NULL){ if(opts->SetMultiPass()){ // multipass using the number of lights, that the lightpicker uses if (lightPicker->get_lights()->get_num_blocks()>4){ thisMat->set_multiPass(true); } else{ thisMat->set_multiPass(false); } } if ((lightPicker->get_lights()->get_num_blocks()>4)&&(!thisMat->get_multiPass())){ AWDMessageBlock * newWarning = new AWDMessageBlock(thisMat->get_name(), "AWDMaterial has more than 4 lights assigned, but is set to singlepass. this will cause problems on render."); awd->get_message_blocks()->append(newWarning); } if(opts->IncludeShadows()){ if(thisMat->get_shadowMethod()!=NULL){ bool shadowOK=lightPicker->check_shadowMethod((AWDShadowMethod *)(thisMat->get_shadowMethod())); if(!shadowOK){ AWDMessageBlock * newWarning = new AWDMessageBlock(thisMat->get_name(), "Could not find the ShadowMethod thats applied to the material on one of the lights that it assigned to the material."); awd->get_message_blocks()->append(newWarning); thisMat->set_shadowMethod(NULL); } } if(thisMat->get_shadowMethod()==NULL){ thisMat->set_shadowMethod(lightPicker->get_shadowMethod()); } } } } block->add_material((AWDMaterial*)thisMat); } thisIdx++; } sub = sub->next; } delete it; } delete returned_geoms; // If conversion created a new object, dispose it if (triObject != obj) triObject->DeleteMe(); delete geomUtil; } else{ } //free(quadDic); //free(isExportedDic); free(mtxData); if (weights!=NULL) free(weights); if (joints!=NULL) free(joints); } } return; }
Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue t) { ObjectState os = node->EvalWorldState(t); bool local = !mFlattenHierarchy; TriObject *tri = (TriObject *)os.obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0)); if (!tri) return Skip; Mesh *copymesh = NULL; Mesh *mesh = &tri->GetMesh(); Matrix3 mtx(true), rtx(true); if (Exporter::mCollapseTransforms) { mtx = GetNodeLocalTM(node, t); mtx.NoTrans(); Quat q(mtx); q.MakeMatrix(rtx); mesh = copymesh = new Mesh(*mesh); { int n = mesh->getNumVerts(); for ( unsigned int i = 0; i < n; ++i ) { Point3& vert = mesh->getVert(i); vert = mtx * vert; } mesh->checkNormals(TRUE); #if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 6+ MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals (); if (NULL != specNorms) { specNorms->CheckNormals(); for ( unsigned int i = 0; i < specNorms->GetNumNormals(); ++i ) { Point3& norm = specNorms->Normal(i); norm = (rtx * norm).Normalize(); } } #endif } } // Note that calling setVCDisplayData will clear things like normals so we set this up first vector<Color4> vertColors; if (mVertexColors) { bool hasvc = false; if (mesh->mapSupport(MAP_ALPHA)) { mesh->setVCDisplayData(MAP_ALPHA); int n = mesh->getNumVertCol(); if (n > vertColors.size()) vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f)); VertColor *vertCol = mesh->vertColArray; if (vertCol) { for (int i=0; i<n; ++i) { VertColor c = vertCol[ i ]; float a = (c.x + c.y + c.z) / 3.0f; vertColors[i].a = a; hasvc |= (a != 1.0f); } } } if (mesh->mapSupport(0)) { mesh->setVCDisplayData(0); VertColor *vertCol = mesh->vertColArray; int n = mesh->getNumVertCol(); if (n > vertColors.size()) vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f)); if (vertCol) { for (int i=0; i<n; ++i) { VertColor col = vertCol[ i ]; vertColors[i] = Color4(col.x, col.y, col.z, vertColors[i].a); hasvc |= (col.x != 1.0f || col.y != 1.0f || col.z != 1.0f); } } } if (!hasvc) vertColors.clear(); } #if VERSION_3DSMAX <= ((5000<<16)+(15<<8)+0) // Version 5 mesh->checkNormals(TRUE); #else MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals (); if (NULL != specNorms) { specNorms->CheckNormals(); if (specNorms->GetNumNormals() == 0) mesh->checkNormals(TRUE); } else { mesh->checkNormals(TRUE); } #endif Result result = Ok; Modifier* geomMorpherMod = GetMorpherModifier(node); bool noSplit = FALSE; // bool noSplit = (NULL != geomMorpherMod); while (1) { FaceGroups grps; if (!splitMesh(node, *mesh, grps, t, vertColors, noSplit)) { result = Error; break; } bool exportStrips = mTriStrips && (Exporter::mNifVersionInt > VER_4_2_2_0); Matrix44 tm = Matrix44::IDENTITY; if ( mExportExtraNodes || (mExportType != NIF_WO_ANIM && isNodeKeyed(node) ) ) { tm = TOMATRIX4(getObjectTransform(node, t, false) * Inverse(getNodeTransform(node, t, false))); } else { Matrix33 rot; Vector3 trans; objectTransform(rot, trans, node, t, local); tm = Matrix44(trans, rot, 1.0f); } tm = TOMATRIX4(Inverse(mtx)) * tm; TSTR basename = node->NodeName(); TSTR format = (!basename.isNull() && grps.size() > 1) ? "%s:%d" : "%s"; int i=1; FaceGroups::iterator grp; for (grp=grps.begin(); grp!=grps.end(); ++grp, ++i) { string name = FormatString(format, basename.data(), i); NiTriBasedGeomRef shape = makeMesh(ninode, getMaterial(node, grp->first), grp->second, exportStrips); if (shape == NULL) { result = Error; break; } if (node->IsHidden()) shape->SetVisibility(false); shape->SetName(name); shape->SetLocalTransform(tm); if (Exporter::mZeroTransforms) { shape->ApplyTransforms(); } makeSkin(shape, node, grp->second, t); if (geomMorpherMod) { vector<Vector3> verts = shape->GetData()->GetVertices(); exportGeomMorpherControl(geomMorpherMod, verts, shape->GetData()->GetVertexIndices(), shape); shape->GetData()->SetConsistencyFlags(CT_VOLATILE); } } break; } if (tri != os.obj) tri->DeleteMe(); if (copymesh) delete copymesh; return result; }
/* Transfer the max Node's mesh to PhysX unit and store it. */ void ccMaxNode::SyncFromMaxMesh() { //MaxMsgBox(NULL, _T("SyncFromMaxMesh"), _T("Error"), MB_OK); ShapeType = NX_SHAPE_MESH; const TimeValue t = ccMaxWorld::MaxTime(); Matrix3 tm = MaxINode->GetNodeTM(t); NodePosInPhysics = tm.GetRow(3); NodePosInPhysics = ccMaxWorld::ChangeToPhysXUnit(NodePosInPhysics); ScaleValue sv = MaxINode->GetObjOffsetScale(); bool objectIsScaled = PivotScaled(sv); //if(objectIsScaled) // RemovePivotScale(MaxINode); //------------------------ MaxNodeTM = MaxINode->GetNodeTM(t); // get pivot TM MaxPivotTM.IdentityMatrix(); MaxPivotTM.SetTrans(MaxINode->GetObjOffsetPos()); PreRotateMatrix(MaxPivotTM, MaxINode->GetObjOffsetRot()); ApplyScaling(MaxPivotTM, MaxINode->GetObjOffsetScale()); // char blaat[1024]; // sprintf(blaat,"MaxPivotTM.MaxPivotTM.GetTrans()=(%f,%f,%f)",MaxPivotTM.GetTrans().x,MaxPivotTM.GetTrans().y,MaxPivotTM.GetTrans().z); // MaxMsgBox(NULL, _T(blaat), _T("Error"), MB_OK); // GetObjectTM() = MaxPivotTM * MaxNodeTM MaxNodeObjectTM = MaxINode->GetObjectTM(t); //----------------------------------------------------------------------------------------------- // object might be modified by world space modifiers. We need a solution in future for this case. bool isWorldSpace = MaxINode->GetObjTMAfterWSM(t).IsIdentity(); //----------------------------------------------------------------------------------------------- Matrix3 PhysicsNodeTM = ccMaxWorld::ChangeToPhysXUnit(MaxNodeObjectTM); // get scale TM // PhysicsNodeTM == PhysicsNodeScaleTM * PhysicsNodePoseTM Point3 maxNodeScale = ccMaxWorld::ParseScale(PhysicsNodeTM, PhysicsNodeScaleTM, PhysicsNodePoseTM); //Matrix3 right; //ParseMatrix(PhysicsNodePoseTM, right); //PhysicsNodePoseTM = right; //PhysicsNodeScaleTM = PhysicsNodeTM * Inverse(PhysicsNodePoseTM); //if(IsScaled(PhysicsNodeScaleTM)) // objectIsScaled = true; // check whether the Node's mesh is scaled equally at x/y/z ScaledIsUnified = (fabs((maxNodeScale.x - maxNodeScale.z)/maxNodeScale.z) < gTolerenceEpsilon) && (fabs((maxNodeScale.y - maxNodeScale.z)/maxNodeScale.z) < gTolerenceEpsilon); ScaledIsUnified = (! objectIsScaled) && ScaledIsUnified; //if(! ScaledIsUnified) { // PhysicsNodePoseTM.IdentityMatrix(); // PhysicsNodePoseTM.SetRow(3, NodePosInPhysics); // PhysicsNodeScaleTM = PhysicsNodeTM * Inverse(PhysicsNodePoseTM); //} PhysicsNodePoseTMInv = Inverse(PhysicsNodePoseTM); //gCurrentstream->printf("\nNxScaleTM = "); MxUtils::PrintMatrix3(PhysicsNodeScaleTM); //gCurrentstream->printf("\nNxPoseTM = "); MxUtils::PrintMatrix3(PhysicsNodePoseTM); //gCurrentstream->printf("\n"); //Matrix3 t1 = PhysicsNodeScaleTM * PhysicsNodePoseTM; //bool e1 = (t1 == MaxNodeObjectTM); Object* obj = MaxINode->EvalWorldState(t).obj; if (obj != NULL) { SimpleObject* so = (SimpleObject*)obj; Class_ID id = obj->ClassID(); if (id == Class_ID(SPHERE_CLASS_ID, 0)) { ShapeType = NX_SHAPE_SPHERE; so->pblock->GetValue(SPHERE_RADIUS, 0, PrimaryShapePara.Radius, FOREVER); PrimaryShapePara.Radius *= maxNodeScale.x * ccMaxWorld::GetUnitChange(); // x/y/z is scaled with a same value } else if (id == Class_ID(BOXOBJ_CLASS_ID, 0)) { ShapeType = NX_SHAPE_BOX; so->pblock->GetValue(BOXOBJ_WIDTH , 0, PrimaryShapePara.BoxDimension[0], FOREVER); so->pblock->GetValue(BOXOBJ_LENGTH, 0, PrimaryShapePara.BoxDimension[1], FOREVER); so->pblock->GetValue(BOXOBJ_HEIGHT, 0, PrimaryShapePara.BoxDimension[2], FOREVER); PrimaryShapePara.BoxDimension *= (0.5f * maxNodeScale.x * ccMaxWorld::GetUnitChange()); // x/y/z is scaled with a same value // Physics box is half the size } else if (id == CAPS_CLASS_ID) { ShapeType = NX_SHAPE_CAPSULE; int centersflag = 0; so->pblock->GetValue(CAPS_RADIUS , 0, PrimaryShapePara.Radius, FOREVER); so->pblock->GetValue(CAPS_HEIGHT , 0, PrimaryShapePara.Height, FOREVER); so->pblock->GetValue(CAPS_CENTERS, 0, centersflag, FOREVER); if(!centersflag) //there are some different ways in which you can specify a capsule in 3ds max, adjust length if "center" mode is not used PrimaryShapePara.Height -= PrimaryShapePara.Radius * 2.0f; PrimaryShapePara.Radius *= maxNodeScale.x * ccMaxWorld::GetUnitChange(); // x/y/z is scaled with a same value PrimaryShapePara.Height *= maxNodeScale.x * ccMaxWorld::GetUnitChange(); // x/y/z is scaled with a same value } } if(! ScaledIsUnified) ShapeType = NX_SHAPE_MESH; // Disable backface culling for cloth //MaxINode->BackCull(FALSE); // get mesh BOOL needDel = FALSE; TriObject* tri = MxUtils::GetTriObjectFromNode(MaxINode, t, needDel); if (tri == NULL) return; Mesh& mesh = tri->GetMesh(); SimpleMesh.alloc(mesh.getNumVerts(), mesh.getNumFaces()); //Matrix3 change = PhysicsNodeTM * Inverse(PhysicsNodePoseTM); for(NxU32 i = 0; i < SimpleMesh.numPoints; i++) { Point3 tmp = mesh.verts[i] * ccMaxWorld::GetUnitChange() * PhysicsNodeScaleTM; ((Point3*)SimpleMesh.points)[i] = tmp; // systemTM is unit change TM. } for(NxU32 i = 0; i < SimpleMesh.numFaces; i++) { for(NxU32 j = 0; j < 3; j++) { SimpleMesh.faces[i*3+j] = mesh.faces[i].v[j]; } } if (needDel) tri->DeleteMe(); }