Exporter::Result SkinInstance::execute() { shape->BindSkinWith(boneList, SkinInstConstructor); unsigned int bone = 0; for (BoneWeightList::iterator bitr = boneWeights.begin(); bitr != boneWeights.end(); ++bitr, ++bone) { shape->SetBoneWeights(bone, (*bitr)); } int* faceMap = NULL; if (partitions.size() > 0) { BSDismemberSkinInstanceRef dismem = DynamicCast<BSDismemberSkinInstance>(shape->GetSkinInstance()); if (dismem != NULL) dismem->SetPartitions(partitions); faceMap = &facePartList[0]; } if (Exporter::mNifVersionInt > VER_4_0_0_2) { if (Exporter::mMultiplePartitions) shape->GenHardwareSkinInfo(Exporter::mBonesPerPartition, Exporter::mBonesPerVertex, Exporter::mTriPartStrips, faceMap); else shape->GenHardwareSkinInfo(0, 0, Exporter::mTriPartStrips); } return Exporter::Ok; }
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; }
NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp, bool exportStrips) { NiTriBasedGeomRef shape; NiTriBasedGeomDataRef data; //if (Exporter::mFixNormals) { // FixNormals(grp.faces, grp.verts, grp.vnorms); //} if (exportStrips) { shape = new NiTriStrips(); data = new NiTriStripsData(grp.faces, !mUseAlternateStripper); } else { shape = new NiTriShape(); data = new NiTriShapeData(grp.faces); } if ( IsFallout3() || IsSkyrim() ) shape->SetFlags( 14 ); data->SetVertices(grp.verts); data->SetNormals(grp.vnorms); data->SetVertexIndices(grp.vidx); data->SetUVSetMap(grp.uvMapping); int nUVs = grp.uvs.size(); if ( IsFallout3() || IsSkyrim() ) nUVs = min(1, nUVs); data->SetUVSetCount(nUVs); for (int i =0;i<nUVs; ++i) { data->SetUVSet(i, grp.uvs[i]); } //if (IsSkyrim() && grp.vcolors.size() == 0) // grp.vcolors.resize(grp.verts.size(), Color4(1.0f,1.0f,1.0f,1.0f)); if (mVertexColors && grp.vcolors.size() > 0) { bool allWhite = true; Color4 white(1.0f, 1.0f, 1.0f, 1.0f); for (int i=0,n=grp.vcolors.size();i<n; ++i) { if (white != grp.vcolors[i]) { allWhite = false; break; } } if (!allWhite) data->SetVertexColors(grp.vcolors); } data->SetConsistencyFlags(CT_STATIC); shape->SetData(data); if (Exporter::mTangentAndBinormalExtraData && (Exporter::mNifVersionInt > VER_4_2_2_0)) { // enable traditional tangents and binormals for non-oblivion meshes if ( !IsOblivion() && (Exporter::mNifVersionInt >= VER_10_0_1_0) ) data->SetTspaceFlag( 0x01 ); shape->UpdateTangentSpace(Exporter::mTangentAndBinormalMethod); } parent->AddChild(DynamicCast<NiAVObject>(shape)); NiAVObjectRef av(DynamicCast<NiAVObject>(shape)); makeMaterial(av, mtl); shape->SetActiveMaterial(0); return shape; }
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; }
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; }
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; }
/*---------------------------------------------------------------------------*/ 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(); }