bool Exporter::makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t) { if (!mExportSkin) return false; if (grp.verts.empty()) return false; //get the skin modifier Modifier *mod = GetSkin(node); if (!mod) return false; ISkin *skin = (ISkin *) mod->GetInterface(I_SKIN); if (!skin) return false; ISkinContextData *skinData = skin->GetContextInterface(node); if (!skinData) return false; if (grp.strips.empty()) strippify(grp); // Create new call back to finish export SkinInstance* si = new SkinInstance(this); mPostExportCallbacks.push_back(si); skin->GetSkinInitTM(node, si->bone_init_tm, false); skin->GetSkinInitTM(node, si->node_init_tm, true); si->shape = shape; // Get bone references (may not actually exist in proper structure at this time) int totalBones = skin->GetNumBones(); si->boneWeights.resize(totalBones); si->boneList.resize(totalBones); for (int i=0; i<totalBones; ++i) { si->boneList[i] = getNode(skin->GetBone(i)); } vector<int>& vidx = grp.vidx; int nv = vidx.size(); for (int i=0; i<nv; ++i) { int vi = vidx[i]; int nbones = skinData->GetNumAssignedBones(vi); for (int j=0; j<nbones; ++j) { SkinWeight sw; sw.index = i; sw.weight = skinData->GetBoneWeight(vi,j); int boneIndex = skinData->GetAssignedBone(vi,j); SkinInstance::SkinWeightList& weights = si->boneWeights[boneIndex]; weights.push_back(sw); } } // remove unused bones vector<NiNodeRef>::iterator bitr = si->boneList.begin(); SkinInstance::BoneWeightList::iterator switr = si->boneWeights.begin(); for (int i=0; i<totalBones; ++i) { vector<SkinWeight> &weights = (*switr); if (weights.empty()) { bitr = si->boneList.erase(bitr); switr = si->boneWeights.erase(switr); } else { ++bitr, ++switr; } } // Check for dismemberment if (IsFallout3() || IsSkyrim()) { Modifier *dismemberSkinMod = GetBSDismemberSkin(node); if (dismemberSkinMod) { if (IBSDismemberSkinModifier *disSkin = (IBSDismemberSkinModifier *) dismemberSkinMod->GetInterface(I_BSDISMEMBERSKINMODIFIER)){ Tab<IBSDismemberSkinModifierData*> modData = disSkin->GetModifierData(); if (modData.Count() >= 1) { IBSDismemberSkinModifierData* bsdsmd = modData[0]; si->SkinInstConstructor = BSDismemberSkinInstance::Create; Tab<BSDSPartitionData> &flags = bsdsmd->GetPartitionFlags(); GenericNamedSelSetList &fselSet = bsdsmd->GetFaceSelList(); FaceMap fmap; NiTriBasedGeomDataRef data = DynamicCast<NiTriBasedGeomData>(shape->GetData()); vector<Triangle> tris = data->GetTriangles(); for (int i=0; i<tris.size(); ++i) { Triangle tri = tris[i]; fmap[ rotate(tri) ] = i; } // Build up list of partitions and face to partition map si->partitions.resize(flags.Count()); si->facePartList.resize( grp.faces.size(), -1 ); for (int i=0; i<flags.Count(); ++i) { BodyPartList& bp = si->partitions[i]; bp.bodyPart = (BSDismemberBodyPartType)flags[i].bodyPart; bp.partFlag = (BSPartFlag)(flags[i].partFlag | PF_START_NET_BONESET); BitArray& fSelect = fselSet[i]; for (int j=0; j<fSelect.GetSize(); ++j){ if ( fSelect[j] ) { Triangle tri = grp.faces[grp.fidx[j]]; FaceMap::iterator fitr = fmap.find( rotate(tri) ); if (fitr != fmap.end()) si->facePartList[ (*fitr).second ] = i; } } } } } } } return true; }
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; }