bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom, int v_start/*=0*/) { bool ok = true; NiSkinInstanceRef nifSkin = triGeom->GetSkinInstance(); if (!nifSkin) return false; INode *tnode = node->GetINode(); NiSkinDataRef data = nifSkin->GetSkinData(); NiSkinPartitionRef part = nifSkin->GetSkinPartition(); vector<NiNodeRef> nifBones = nifSkin->GetBones(); //create a skin modifier and add it Modifier *skinMod = GetOrCreateSkin(tnode); TriObject *triObject = GetTriObject(tnode->GetObjectRef()); Mesh& m = triObject->GetMesh(); //get the skin interface if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){ ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA); // Set the num weights to 4. Yes its in the nif but Shon doesn't like to expose those values // and the value always seems to be 4 anyway. I'd also this be more dynamic than hard coded numbers // but I cant figure out the correct values to pass the scripting engine from here so I'm giving up. int numWeightsPerVertex = 4; #if VERSION_3DSMAX >= ((5000<<16)+(15<<8)+0) // Version 5+ IParamBlock2 *params = skinMod->GetParamBlockByID(2/*advanced*/); params->SetValue(0x7/*bone_Limit*/, 0, numWeightsPerVertex); #endif // Can get some truly bizarre animations without this in MAX with Civ4 Leaderheads #if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 6+ BOOL ignore = TRUE; params->SetValue(0xE/*ignoreBoneScale*/, 0, ignore); #endif //RefTargetHandle advanced = skinMod->GetReference(3); //setMAXScriptValue(advanced, "bone_Limit", 0, numWeightsPerVertex); Matrix3 geom = TOMATRIX3(triGeom->GetLocalTransform()); Matrix3 m3 = TOMATRIX3(data->GetOverallTransform()); Matrix3 im3 = Inverse(m3); Matrix3 nm3 = im3 * geom; iskinImport->SetSkinTm(tnode, nm3, nm3); // ??? // Create Bone List Tab<INode*> bones; for (size_t i=0; i<nifBones.size(); ++i){ NiNodeRef bone = nifBones[i]; if (INode *boneRef = FindNode(bone)) { bones.Append(1, &boneRef); iskinImport->AddBoneEx(boneRef, TRUE); //// Set Bone Transform Matrix3 b3 = TOMATRIX3(data->GetBoneTransform(i)); Matrix3 ib3 = Inverse(b3); ib3 *= geom; iskinImport->SetBoneTm(boneRef, ib3, ib3); } } if (bones.Count() != data->GetBoneCount()) return false; ObjectState os = tnode->EvalWorldState(0); // Need to get a list of bones and weights for each vertex. vector<VertexHolder> vertexHolders; vertexHolders.resize(m.numVerts); for (int i=0, n=data->GetBoneCount();i<n; ++i){ if (INode *boneRef = bones[i]){ vector<SkinWeight> weights = data->GetBoneWeights(i); for (vector<SkinWeight>::iterator itr=weights.begin(), end=weights.end(); itr != end; ++itr){ VertexHolder& h = vertexHolders[itr->index]; h.vertIndex = itr->index; ++h.count; h.weights.Append(1, &itr->weight); h.boneNodeList.Append(1, &boneRef); } } } tnode->EvalWorldState(0); skinMod->DisableModInViews(); skinMod->EnableModInViews(); #if VERSION_3DSMAX < ((5000<<16)+(15<<8)+0) // Version 4 gi->SetCommandPanelTaskMode(TASK_MODE_MODIFY); gi->SelectNode(tnode); #endif // Assign the weights for (vector<VertexHolder>::iterator itr=vertexHolders.begin(), end=vertexHolders.end(); itr != end; ++itr){ VertexHolder& h = (*itr); if (h.count){ float sum = 0.0f; for (int i = 0; i < h.count; ++i) sum += h.weights[i]; ASSERT(fabs(sum-1.0f) < 0.001f); BOOL add = iskinImport->AddWeights(tnode, h.vertIndex, h.boneNodeList, h.weights); add = add; // What was the purpose of this? } } // This is a kludge to get skin transforms to update and avoid jumping around after modifying the transforms. // Initially they show up incorrectly but magically fix up if you go to the modifier roll up. // There is still an outstanding issue with skeleton and GetObjectTMBeforeWSM. skinMod->DisableModInViews(); skinMod->EnableModInViews(); // If BSDismembermentSkinInstance, ... if ( BSDismemberSkinInstanceRef bsdsi = DynamicCast<BSDismemberSkinInstance>(nifSkin) ) { Modifier *dismemberSkinMod = GetOrCreateBSDismemberSkin(tnode); if (IBSDismemberSkinModifier *disSkin = (IBSDismemberSkinModifier *) dismemberSkinMod->GetInterface(I_BSDISMEMBERSKINMODIFIER)){ // Evaluate node ensure the modifier data is created // ObjectState os = tnode->EvalWorldState(0); FaceMap faceMap; int nfaces = m.getNumFaces(); for ( int i=0; i<nfaces; ++i ){ Face f = m.faces[i]; faceMap[ rotate(f) ] = i; } Tab<IBSDismemberSkinModifierData*> modData = disSkin->GetModifierData(); for (int i=0; i<modData.Count(); ++i) { IBSDismemberSkinModifierData* bsdsmd = modData[i]; Tab<BSDSPartitionData> &flags = bsdsmd->GetPartitionFlags(); vector<BodyPartList> partitions = bsdsi->GetPartitions(); if (partitions.empty()) continue; //bsdsmd->SetActivePartition( partitions.size() - 1 ); // Old Code for (unsigned int j = 0; j < (partitions.size() - 1); ++j){ bsdsmd->AddPartition(); } for (unsigned int j=0; j < partitions.size(); ++j ) { flags[j].bodyPart = (DismemberBodyPartType)partitions[j].bodyPart; flags[j].partFlag = partitions[j].partFlag; } for (int j=0; j < part->GetNumPartitions(); ++j) { bsdsmd->SetActivePartition( j ); dismemberSkinMod->SelectAll(3); // ensures bitarrays are properly synced to mesh dismemberSkinMod->ClearSelection(3); vector<Triangle> triangles = part->GetTriangles(j); vector<unsigned short> map = part->GetVertexMap(j); GenericNamedSelSetList& fselSet = bsdsmd->GetFaceSelList(); if ( BitArray* fsel = fselSet.GetSetByIndex(j) ) { for (vector<Triangle>::iterator itrtri = triangles.begin(); itrtri != triangles.end(); ++itrtri) { Face f; f.setVerts( map[(*itrtri).v1], map[(*itrtri).v2], map[(*itrtri).v3] ); FaceMap::iterator fitr = faceMap.find( rotate(f) ); if (fitr != faceMap.end()) fsel->Set((*fitr).second); } } } bsdsmd->SetActivePartition( 0 ); disSkin->LocalDataChanged(); } } } } return ok; }
bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, vector<Triangle>& tris) { Mesh& mesh = o->GetMesh(); INode *tnode = node->GetINode(); Matrix44 baseTM = (importBones) ? triGeom->GetLocalTransform() : triGeom->GetWorldTransform(); node->SetTransform(0,TOMATRIX3(baseTM)); // Vertex info { int nVertices = triGeomData->GetVertexCount(); vector<Vector3> vertices = triGeomData->GetVertices(); mesh.setNumVerts(nVertices); for (int i=0; i < nVertices; ++i){ Vector3 &v = vertices[i]; mesh.verts[i].Set(v.x, v.y, v.z); } } // uv texture info { int nUVSet = triGeomData->GetUVSetCount(); mesh.setNumMaps(nUVSet + 1, TRUE); int n = 0, j = 0; for (int j=0; j<nUVSet; j++){ vector<TexCoord> texCoords = triGeomData->GetUVSet(j); n = texCoords.size(); if (j == 0) { mesh.setNumTVerts(n, TRUE); for (int i=0; i<n; ++i) { TexCoord& texCoord = texCoords[i]; mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0); } } mesh.setMapSupport (j + 1, TRUE); mesh.setNumMapVerts(j + 1, n, TRUE); if ( UVVert *tVerts = mesh.mapVerts(j+1) ) { for (int i=0; i<n; ++i) { TexCoord& texCoord = texCoords[i]; tVerts[i].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0); } } } } // Triangles and texture vertices SetTriangles(mesh, tris); SetNormals(mesh, tris, triGeomData->GetNormals() ); vector<Color4> cv = triGeomData->GetColors(); ImportVertexColor(tnode, o, tris, cv, 0); if (Mtl* m = ImportMaterialAndTextures(node, triGeom)) { gi->GetMaterialLibrary().Add(m); node->GetINode()->SetMtl(m); } if (removeDegenerateFaces) mesh.RemoveDegenerateFaces(); if (removeIllegalFaces) mesh.RemoveIllegalFaces(); if (enableSkinSupport) ImportSkin(node, triGeom); if (weldVertices) WeldVertices(mesh); i->AddNodeToScene(node); INode *inode = node->GetINode(); inode->EvalWorldState(0); // attach child if (INode *parent = GetNode(triGeom->GetParent())) parent->AttachChild(inode, 1); inode->Hide(triGeom->GetVisibility() ? FALSE : TRUE); if (enableAutoSmooth){ mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); } RegisterNode(triGeom, inode); return true; }
/*---------------------------------------------------------------------------*/ unsigned int NifCollisionUtility::getGeometryFromShapeData(vector<Vector3>& vertices, vector<Triangle>& triangles, NiTriBasedGeomRef pShape, vector<hkGeometry>& geometryMap, vector<Matrix44>& transformAry) { hkGeometry tmpGeo; hkArray<hkVector4>& vertAry (tmpGeo.m_vertices); hkArray<hkGeometry::Triangle>& triAry (tmpGeo.m_triangles); Vector3 tVector; float factor (0.0143f); unsigned int material(_defaultMaterial); //@TODO: modify factor depending on _nifVersion if necessary // add local transformation to list transformAry.push_back(pShape->GetLocalTransform()); // clear arrays vertAry.clear(); triAry.clear(); // get vertices for (unsigned int idx(0); idx < vertices.size(); ++idx) { // get vertex tVector = vertices[idx]; // transform vertex to global coordinates for (int t((int) (transformAry.size())-1); t >= 0; --t) { tVector = transformAry[t] * tVector; } // scale final vertex tVector *= factor; // add vertex to tmp. array vertAry.pushBack(hkVector4(tVector.x, tVector.y, tVector.z)); } // for (unsigned int idx(0); idx < vertices.size(); ++idx) // map material to geometry/triangles switch (_mtHandling) { case NCU_MT_SINGLE: // one material for all { material = _mtMapping[-1]; break; } case NCU_MT_MATMAP: // material defined by node id { material = _mtMapping[pShape->internal_block_number]; break; } case NCU_MT_NITRISHAPE_NAME: // material defined by node name { material = _materialList.getMaterialCode(pShape->GetName()); break; } } // get triangles for (unsigned int idx(0); idx < triangles.size(); ++idx) { hkGeometry::Triangle tTri; tTri.set(triangles[idx].v1, triangles[idx].v2, triangles[idx].v3, material); triAry.pushBack(tTri); } // add geometry to result array geometryMap.push_back(tmpGeo); // remove local transformation from array transformAry.pop_back(); return geometryMap.size(); }