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; }
void RenderMesh::ConvertFaces(Mesh *Mesh, int MatIndex, Tab<Vert3> &Verts, Tab<Face3> &Faces, bool NegScale) { Face3 TmpFace; Vert3 TmpVert; BitArray Written; int i,j,k,NumFace; int NumUV,UVCount,Index; int NumVert,Count,VIndex; Face *aFace; Tab<BasisVert> FNormals; Tab<VNormal> Normals; UVVert *UVVert; TVFace *UVFace; Point3 S,T,SxT; unsigned long Sg; bool useMeshNorms = false; if(NegScale) { gVIndex[0] = 2; gVIndex[1] = 1; gVIndex[2] = 0; } else { gVIndex[0] = 0; gVIndex[1] = 1; gVIndex[2] = 2; } // Do we have an EditNormal modifier present - if so we use those normals instead. // We only use this if they have been applied on a face with smoothing groups, otherwise // it messes up the tangent space calculation. Probably not the most obtmized route, but it // works... MeshNormalSpec * meshNorm = Mesh->GetSpecifiedNormals(); if(meshNorm && meshNorm->GetNumNormals()) useMeshNorms = true; NumFace = 0; for(i=0; i < Mesh->getNumFaces(); i++) { if(!Mesh->faces[i].Hidden()) { Index = Mesh->getFaceMtlIndex(i) + 1; if(Index == MatIndex || MatIndex == 0) { NumFace++; } } } NumVert = Mesh->getNumVerts(); Verts.SetCount(NumVert); Faces.SetCount(NumFace); if(NumVert == 0 || NumFace == 0) { return; } ComputeVertexNormals(Mesh,FNormals,Normals,NegScale); Written.SetSize(Mesh->getNumVerts()); Written.ClearAll(); NumUV = Mesh->getNumMaps(); if(NumUV) { Count = 0; if(NumUV > MAX_TMUS + 1) { NumUV = MAX_TMUS + 1; } for(i=0; i < Mesh->getNumFaces(); i++) { aFace = &Mesh->faces[i]; TmpFace.m_Num[0] = aFace->v[gVIndex[0]]; TmpFace.m_Num[1] = aFace->v[gVIndex[1]]; TmpFace.m_Num[2] = aFace->v[gVIndex[2]]; Sg = aFace->smGroup; for(j=0; j < 3; j++) { VIndex = aFace->v[gVIndex[j]]; TmpVert.m_Pos = Mesh->verts[VIndex]; if(Sg) { if(useMeshNorms) { int normID = meshNorm->Face(i).GetNormalID(gVIndex[j]); TmpVert.m_Normal = meshNorm->Normal(normID).Normalize(); Normals[VIndex].GetNormal(Sg,S,T,SxT); } else TmpVert.m_Normal = Normals[VIndex].GetNormal(Sg,S,T,SxT); TmpVert.m_S = S; TmpVert.m_T = T; TmpVert.m_SxT = SxT; } else { TmpVert.m_Normal = FNormals[i].m_Normal; TmpVert.m_S = FNormals[i].m_S; TmpVert.m_T = FNormals[i].m_T; TmpVert.m_SxT = FNormals[i].m_SxT; } UVCount = 0; TmpVert.m_Sg = Sg; for(k=0;k<m_MapChannels.Count();k++) { int index = m_MapChannels[k]; if(Mesh->getNumMapVerts(index)) { UVVert = Mesh->mapVerts(index); UVFace = Mesh->mapFaces(index); TmpVert.m_UV[k].x = UVVert[UVFace[i].t[gVIndex[j]]].x; TmpVert.m_UV[k].y = UVVert[UVFace[i].t[gVIndex[j]]].y; } else { TmpVert.m_UV[k].x = 0.0f; TmpVert.m_UV[k].y = 0.0f; } } if(Written[VIndex]) { if((Sg == 0) || (Verts[VIndex].m_Sg != TmpVert.m_Sg) || (!UVVertEqual(Verts[VIndex].m_UV[0],TmpVert.m_UV[0]))) { TmpFace.m_Num[j] = Verts.Count(); Verts.Append(1,&TmpVert,10); } } else { Verts[VIndex] = TmpVert; Written.Set(VIndex); } } if(!Mesh->faces[i].Hidden()) { Index = Mesh->getFaceMtlIndex(i) + 1; if(Index == MatIndex || MatIndex == 0) { Faces[Count++] = TmpFace; } } } } else { for(i=0; i < Mesh->getNumFaces(); i++) { aFace = &Mesh->faces[i]; Faces[i].m_Num[0] = aFace->v[gVIndex[0]]; Faces[i].m_Num[1] = aFace->v[gVIndex[1]]; Faces[i].m_Num[2] = aFace->v[gVIndex[2]]; for(j=0; j < 3; j++) { VIndex = aFace->v[gVIndex[j]]; Verts[VIndex].m_Pos = Mesh->verts[VIndex]; Verts[VIndex].m_Normal = Normals[VIndex].GetNormal(aFace->smGroup,S,T,SxT); Verts[VIndex].m_S = Point3(0.0f,0.0f,0.0f); Verts[VIndex].m_T = Point3(0.0f,0.0f,0.0f); Verts[VIndex].m_SxT = Point3(0.0f,0.0f,0.0f); for(k=0; k < MAX_TMUS; k++) { Verts[VIndex].m_UV[k].x = 0.0f; Verts[VIndex].m_UV[k].y = 0.0f; } } } } Verts.Shrink(); }
/* * Get vertex normal using smooth group with normal method.(using RVertex information) * FaceVertexIdx is between 0 and 2 local to a triangle */ Point3 SGP_MaxInterface::GetVertexNormal( Mesh* pMesh, int faceId, int vertexId, int FaceVertexIdx ) { ////////////////////////////////////////////////////////////////////////// // Below is the way if someone has used "edit normals modifier" to change vertex normal MeshNormalSpec *pNormalSpec = pMesh->GetSpecifiedNormals(); if( pNormalSpec ) { const int NumFaces = pNormalSpec->GetNumFaces(); const int NumNormals = pNormalSpec->GetNumNormals(); if( NumFaces != 0 && NumNormals != 0 ) { const int NormalID = pNormalSpec->Face(faceId).GetNormalID(FaceVertexIdx); return pNormalSpec->Normal(NormalID).Normalize(); } } ////////////////////////////////////////////////////////////////////////// RVertex *pRVertex; pRVertex = pMesh->getRVertPtr(vertexId); // get the face Face *pFace; pFace = &pMesh->faces[faceId]; // get the smoothing group of the face DWORD smGroup; smGroup = pFace->smGroup; // get the number of normals int normalCount; normalCount = pRVertex->rFlags & NORCT_MASK; // check if the normal is specified ... if(pRVertex->rFlags & SPECIFIED_NORMAL) { return pRVertex->rn.getNormal(); } // ... otherwise, check for a smoothing group else if((normalCount > 0) && (smGroup != 0)) { // If there is only one vertex is found in the rn member. if(normalCount == 1) { return pRVertex->rn.getNormal(); } else { int normalId; Point3 n(0,0,0); for(normalId = 0; normalId < normalCount; normalId++) { if(pRVertex->ern[normalId].getSmGroup() & smGroup) { n = n+pRVertex->ern[normalId].getNormal(); } } n = n.Normalize(); return n; } } // if all fails, return the face normal return pMesh->getFaceNormal(faceId); }
void SymmetryMod::ModifyTriObject (TimeValue t, ModContext &mc, TriObject *tobj, INode *inode) { Mesh &mesh = tobj->GetMesh(); Interval iv = FOREVER; int axis, slice, weld, flip; float threshold; mp_pblock->GetValue (kSymAxis, t, axis, iv); mp_pblock->GetValue (kSymFlip, t, flip, iv); mp_pblock->GetValue (kSymSlice, t, slice, iv); mp_pblock->GetValue (kSymWeld, t, weld, iv); mp_pblock->GetValue (kSymThreshold, t, threshold, iv); if (threshold<0) threshold=0; // Get transform from mirror controller: Matrix3 tm = CompMatrix (t, NULL, &mc, &iv); Matrix3 itm = Inverse (tm); // Get DotProd(N,x)=offset plane definition from transform Point3 Axis(0,0,0); Axis[axis] = flip ? -1.0f : 1.0f; Point3 origin = tm.GetTrans(); Point3 N = Normalize(tm*Axis - origin); float offset = DotProd (N, origin); // Slice operation does not handle NormalSpecs, but it handles mapping channels. // move our mesh normal data to a map channel MeshNormalSpec *pNormals = mesh.GetSpecifiedNormals (); int normalMapChannel = INVALID_NORMALMAPCHANNEL; if (pNormals && pNormals->GetNumFaces()) { pNormals->SetParent(&mesh); //find an empty map channel for (int mp = 0; mp < mesh.getNumMaps(); mp++) { if (!mesh.mapSupport(mp)) { normalMapChannel = mp; mesh.setMapSupport(normalMapChannel,TRUE); MeshMap& map = mesh.Map(normalMapChannel); for (int i = 0; i < map.fnum; i++) { for (int j = 0; j < 3; j++) { unsigned int newID = pNormals->Face(i).GetNormalID(j); map.tf[i].t[j] = newID; } } map.setNumVerts(pNormals->GetNumNormals()); for (int i = 0; i < map.vnum; i++) { map.tv[i] = pNormals->Normal(i); } // make sure nothing is done with MeshNormalSpec (until data is copied back) pNormals->Clear(); break; } } } // Slice off everything below the plane. if (slice) SliceTriObject (mesh, N, offset); MirrorTriObject (mesh, axis, tm, itm,normalMapChannel); if (weld) WeldTriObject (mesh, N, offset, threshold); //now move the normals back if (pNormals && normalMapChannel != -1) { MeshMap& map = mesh.Map(normalMapChannel); pNormals->SetNumFaces(map.fnum); pNormals->SetNumNormals(map.vnum); pNormals->SetAllExplicit(true); BitArray temp; temp.SetSize(map.vnum); temp.SetAll(); pNormals->SpecifyNormals(TRUE,&temp); for (int i = 0; i < map.vnum; i++) { pNormals->GetNormalArray()[i] = map.tv[i]; pNormals->SetNormalExplicit(i,true); } for (int i = 0; i < map.fnum; i++) { for (int j = 0; j < 3; j++) { pNormals->SetNormalIndex(i,j,map.tf[i].t[j]); MeshNormalFace& face = pNormals->Face(i); face.SpecifyAll(true); } } pNormals->SetFlag(MESH_NORMAL_MODIFIER_SUPPORT); for (int i = 0; i < pNormals->GetNumFaces(); i++) { for (int j = 0; j < 3; j++) { int id = pNormals->GetNormalIndex(i,j); } } pNormals->CheckNormals(); pNormals->SetParent(NULL); // Free the map channel mesh.setMapSupport(normalMapChannel,FALSE); } tobj->UpdateValidity (GEOM_CHAN_NUM, iv); tobj->UpdateValidity (TOPO_CHAN_NUM, iv); tobj->UpdateValidity (VERT_COLOR_CHAN_NUM, iv); tobj->UpdateValidity (TEXMAP_CHAN_NUM, iv); tobj->UpdateValidity (SELECT_CHAN_NUM, iv); }
BOOL GetVertexNormalUsingSmoothGroup(Point3& VN, Mesh& mesh, int faceId, int globalvertexId, int _FaceVertexIdx) { //_FaceVertexIdx is between 0 and 2 local to a triangle //THIS IS WHAT YOU NEED IF SOMEONE HAS USED THE EDIT NORMAL MODIFIER MeshNormalSpec * normalspec = mesh.GetSpecifiedNormals(); if (normalspec) { const int NumFaces = normalspec->GetNumFaces (); const int NumNormals = normalspec->GetNumNormals (); if (NumFaces && NumNormals) { const int normID = normalspec->Face(faceId).GetNormalID(_FaceVertexIdx); VN = normalspec->Normal(normID).Normalize(); return TRUE; } } // get the "rendered" vertex RVertex *pRVertex = mesh.getRVertPtr(globalvertexId); if(! pRVertex)return FALSE; // get the face const Face& Face = mesh.faces[faceId]; // get the smoothing group of the face const DWORD smGroup = Face.smGroup; // get the number of normals const int normalCount = pRVertex->rFlags & NORCT_MASK; // check if the normal is specified ... if(pRVertex->rFlags & SPECIFIED_NORMAL) { VN = pRVertex->rn.getNormal(); return TRUE; } // ... otherwise, check for a smoothing group else if((normalCount > 0) && (smGroup != 0)) { // If there is only one vertex is found in the rn member. if(normalCount == 1) { VN = pRVertex->rn.getNormal(); return TRUE; } else { for(int normalId = 0; normalId < normalCount; normalId++) { if(pRVertex->ern[normalId].getSmGroup() & smGroup) { VN = pRVertex->ern[normalId].getNormal(); return TRUE; } } } } // if all failed, return the face normal VN = mesh.getFaceNormal(faceId); return TRUE; }