void bhkProxyObject::BuildColConvex() { proxyMesh.FreeAll(); MeshDelta md(proxyMesh); for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) { INode *tnode = NULL; pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); if (tnode) { ObjectState os = tnode->EvalWorldState(0); Matrix3 wm = tnode->GetNodeTM(0); TriObject *tri = (TriObject *)os.obj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0)); if (tri) { Mesh& mesh = tri->GetMesh(); MeshDelta tmd (mesh); md.AttachMesh(proxyMesh, mesh, wm, 0); md.Apply(proxyMesh); } } } compute_convex_hull(proxyMesh, proxyMesh); BuildOptimize(proxyMesh); proxyPos = Point3::Origin; forceRedraw = true; }
void bhkProxyObject::BuildColCapsule() { proxyMesh.FreeAll(); MeshDelta md(proxyMesh); for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) { INode *tnode = NULL; pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); if (tnode) { ObjectState os = tnode->EvalWorldState(0); Matrix3 wm = tnode->GetNodeTM(0); TriObject *tri = (TriObject *)os.obj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0)); if (tri) { Mesh& mesh = tri->GetMesh(); MeshDelta tmd (mesh); md.AttachMesh(proxyMesh, mesh, wm, 0); md.Apply(proxyMesh); } } } Point3 pt1 = Point3::Origin; Point3 pt2 = Point3::Origin; float r1 = 0.0; float r2 = 0.0; if (proxyMesh.getNumVerts() > 3) // Doesn't guarantee that the mesh is not a plane. { CalcCapsule(proxyMesh, pt1, pt2, r1, r2); BuildCapsule(proxyMesh, pt1, pt2, r1, r2); } proxyPos = Point3::Origin; forceRedraw = true; }
Mesh* SGP_MaxInterface::GetMesh( INode* pNode ) { if( !IsMesh( pNode ) ) return NULL; TimeValue time = 0; // get max mesh instance ObjectState os; os = pNode->EvalWorldState(time); Object* obj = os.obj; if( !os.obj ) { assert( false ); return NULL; } TriObject* triObj = (TriObject *)obj->ConvertToType( time, triObjectClassID ); if( !triObj ) { assert( false ); return NULL; } Mesh* pMesh = &triObj->GetMesh(); return pMesh; }
void bhkProxyObject::BuildColBox() { Box3 box; box.Init(); for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) { INode *tnode = NULL; pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); if (tnode) { ObjectState os = tnode->EvalWorldState(0); Matrix3 wm = tnode->GetNodeTM(0); TriObject *tri = (TriObject *)os.obj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0)); if (tri) { Box3 box2; box2.Init(); Mesh& mesh = tri->GetMesh(); CalcAxisAlignedBox(mesh, box2, &wm); box += box2; } } } BuildBox(proxyMesh, box.Max().y-box.Min().y, box.Max().x-box.Min().x, box.Max().z-box.Min().z); MNMesh mn(proxyMesh); Matrix3 tm(true); tm.SetTranslate(box.Center()); mn.Transform(tm); mn.OutToTri(proxyMesh); //proxyPos = box.Center(); proxyPos = Point3::Origin; forceRedraw = true; }
bool CollisionImport::ImportTriStripsShape(INode *rbody, bhkRigidBodyRef body, bhkNiTriStripsShapeRef shape, INode *parent, Matrix3& tm) { if (shape->GetNumStripsData() != 1) return NULL; if ( ImpNode *node = ni.i->CreateNode() ) { TriObject *triObject = CreateNewTriObject(); node->Reference(triObject); INode *inode = node->GetINode(); // Texture Mesh& mesh = triObject->GetMesh(); NiTriStripsDataRef triShapeData = shape->GetStripsData(0); if (triShapeData == NULL) return false; // Temporary shape NiTriStripsRef triShape = new NiTriStrips(); vector<Triangle> tris = triShapeData->GetTriangles(); ni.ImportMesh(node, triObject, triShape, triShapeData, tris); CreatebhkCollisionModifier(inode, bv_type_shapes, shape->GetMaterial(), OL_UNIDENTIFIED, 0); ImportBase(body, shape, parent, inode, tm); AddShape(rbody, inode); return true; } return false; }
void CVDModifier::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node) { if (!os->obj->IsSubClassOf(triObjectClassID)) return; // Get a mesh from input object TriObject *tobj = (TriObject*)os->obj; Mesh* mesh = &tobj->GetMesh(); int numVert = mesh->getNumVerts(); // Get parameters from pblock float sparam = 0.0f; Interval valid = FOREVER; pblock->GetValue(cvd_codev, t, sparam, valid); // Take over the channel, realloc with size == number of verts mesh->setVDataSupport(MY_CHANNEL,TRUE); // Get a pointer back to the floating point array float *vdata = mesh->vertexFloat(MY_CHANNEL); if(vdata) { // loop through all verticies // Ask the random number generator for a value bound to the // paramblock value // and encode it into the vertex. for(int i=0;i<numVert;i++) { vdata[i] = randomGen.getf(sparam); } } }
bool BakeRadiosity::CreateNewMesh (INode *orgNode, Mesh *orgMesh, Matrix3 orgMtx) { if((orgNode == NULL)||(orgMesh == NULL)){ DebugPrint(_T("Mesh error\n")); return false; } // Creates an instance of a registered class. Object *newObj = (Object*)(ip->CreateInstance( GEOMOBJECT_CLASS_ID, Class_ID(TRIOBJ_CLASS_ID, 0))); if(newObj == NULL){ DebugPrint(_T("CreateInstance error\n")); return false; } // Creates a new node in the scene with the given object. INode *newNode = ip->CreateObjectNode(newObj); if(newNode == NULL){ DebugPrint(_T("CreateObjectNode error\n")); return false; } // Sets the name of the node. if(keepOrgFlag != true){ newNode->SetName(orgNode->GetName()); } else { TSTR newName; newName.printf(_T("%s_BAKED"), orgNode->GetName()); newNode->SetName(newName); } // Sets the renderer material used by the node. newNode->SetMtl(orgNode->GetMtl()); // Returns a reference to the mesh data member of new TriObject. TriObject *newTriObj = (TriObject *)newObj; Mesh &newMesh = newTriObj->GetMesh(); // Returns the number of vertices from original mesh. int nbVert = orgMesh->getNumVerts(); // Sets the number of geometric vertices in the new mesh. newMesh.setNumVerts(nbVert); // The loop will continue until handling all vertices... for(int i=0; i<nbVert; i++) { newMesh.verts[i] = orgMtx * orgMesh->verts[i];//Set new vertices } // Returns the number of faces in the original mesh. int nbFace = orgMesh->getNumFaces(); // Sets the number of faces in the new mesh // and previous faces are discarded. newMesh.setNumFaces(nbFace, FALSE); // The loop will continue until handling all faces... for(int i=0; i<nbFace; i++){ // Set new faces and Material id newMesh.faces[i] = orgMesh->faces[i]; newMesh.faces[i].setMatID(orgMesh->faces[i].getMatID()); } // Makes a complete copy of the specified channels // of the original Mesh object into new Mesh. newMesh.DeepCopy(orgMesh, CNVERT_CHANNELS); return true; }
void UnwrapMod::GetFaceSelectionFromMesh(ObjectState *os, ModContext &mc, TimeValue t) { TriObject *tobj = (TriObject*)os->obj; MeshTopoData *d = (MeshTopoData*)mc.localData; if (d) { d->SetFaceSel(tobj->GetMesh().faceSel, this, t); UpdateFaceSelection(d->faceSel); } }
Object* bhkProxyObject::ConvertToType(TimeValue t, Class_ID obtype) { if (obtype == triObjectClassID) { int bvType = 0; pblock2->GetValue(PB_BOUND_TYPE, 0, bvType, FOREVER, 0); if (bvType != 0) { TriObject *ob = CreateNewTriObject(); #if VERSION_3DSMAX >= ((5000<<16)+(15<<8)+0) // Version 5+ ob->GetMesh().CopyBasics(proxyMesh); #else ob->GetMesh() = proxyMesh; #endif ob->SetChannelValidity(TOPO_CHAN_NUM,ObjectValidity(t)); ob->SetChannelValidity(GEOM_CHAN_NUM,ObjectValidity(t)); return ob; } } return 0; }
Object* SimpleObject::ConvertToType(TimeValue t, Class_ID obtype) { if (obtype==defObjectClassID||obtype==triObjectClassID||obtype==mapObjectClassID) { TriObject *triob; UpdateMesh(t); triob = CreateNewTriObject(); triob->GetMesh() = mesh; triob->SetChannelValidity(TOPO_CHAN_NUM,ObjectValidity(t)); triob->SetChannelValidity(GEOM_CHAN_NUM,ObjectValidity(t)); return triob; } #ifndef NO_PATCHES if (obtype == patchObjectClassID) { UpdateMesh(t); PatchObject *patchob = new PatchObject(); patchob->patch = mesh; // Handy Mesh->PatchMesh conversion patchob->SetChannelValidity(TOPO_CHAN_NUM,ObjectValidity(t)); patchob->SetChannelValidity(GEOM_CHAN_NUM,ObjectValidity(t)); return patchob; } #endif if (Object::CanConvertToType (obtype)) { UpdateMesh (t); return Object::ConvertToType(t,obtype); } if (CanConvertTriObject(obtype)) { UpdateMesh (t); TriObject *triob = CreateNewTriObject (); triob->GetMesh() = mesh; triob->SetChannelValidity(TOPO_CHAN_NUM,ObjectValidity(t)); triob->SetChannelValidity(GEOM_CHAN_NUM,ObjectValidity(t)); Object *ob = triob->ConvertToType (t, obtype); if (ob != triob) triob->DeleteThis (); // (ob should never = tob.) return ob; } return NULL; }
void AFRMod::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node) { Interval iv = FOREVER; float f, p, b; int backface; Point3 pt1, pt2; pblock->GetValue(PB_FALLOFF,t,f,iv); pblock->GetValue(PB_PINCH,t,p,iv); pblock->GetValue(PB_BUBBLE,t,b,iv); pblock->GetValue(PB_BACKFACE,t,backface,iv); p1->GetValue(t,&pt1,iv,CTRL_ABSOLUTE); p2->GetValue(t,&pt2,iv,CTRL_ABSOLUTE); if (f==0.0) { os->obj->UpdateValidity(GEOM_CHAN_NUM,iv); return; } Tab<Point3> normals; if (backface) { // Need to get vertex normals. if (os->obj->IsSubClassOf(triObjectClassID)) { TriObject *tobj = (TriObject*)os->obj; AverageVertexNormals (tobj->GetMesh(), normals); } else if (os->obj->IsSubClassOf (polyObjectClassID)) { PolyObject *pobj = (PolyObject *) os->obj; MNMesh &mesh = pobj->GetMesh(); normals.SetCount (mesh.numv); Point3 *vn = normals.Addr(0); for (int i=0; i<mesh.numv; i++) { if (mesh.v[i].GetFlag (MN_DEAD)) vn[i]=Point3(0,0,0); else vn[i] = mesh.GetVertexNormal (i); } #ifndef NO_PATCHES } else if (os->obj->IsSubClassOf (patchObjectClassID)) { PatchObject *pobj = (PatchObject *) os->obj; normals.SetCount (pobj->NumPoints ()); Point3 *vn = normals.Addr(0); for (int i=0; i<pobj->NumPoints(); i++) vn[i] = pobj->VertexNormal (i); #endif } } if (normals.Count()) { AFRDeformer deformer(mc,f,p,b,pt1,pt2,&normals); os->obj->Deform(&deformer, TRUE); } else { AFRDeformer deformer(mc,f,p,b,pt1,pt2); os->obj->Deform(&deformer, TRUE); } os->obj->UpdateValidity(GEOM_CHAN_NUM,iv); }
INode *CollisionImport::ImportCollisionMesh( const vector<Vector3>& verts, const vector<Triangle>& tris, const vector<Vector3>& norms, Matrix3& tm, INode *parent ) { INode *returnNode = NULL; if ( ImpNode *node = ni.i->CreateNode() ) { TriObject *triObject = CreateNewTriObject(); node->Reference(triObject); Mesh& mesh = triObject->GetMesh(); INode *tnode = node->GetINode(); // Vertex info { int nVertices = verts.size(); mesh.setNumVerts(nVertices); for (int i=0; i < nVertices; ++i){ Vector3 v = verts[i] * ni.bhkScaleFactor; mesh.verts[i].Set(v.x, v.y, v.z); } } // Triangles and texture vertices ni.SetTriangles(mesh, tris); //ni.SetNormals(mesh, tris, norms); MNMesh mn(mesh); mn.Transform(tm); mn.OutToTri(mesh); mesh.checkNormals(TRUE); ni.i->AddNodeToScene(node); returnNode = node->GetINode(); returnNode->EvalWorldState(0); if (parent != NULL) parent->AttachChild(tnode, 1); } return returnNode; }
void bhkProxyObject::BuildColOBB() { proxyMesh.FreeAll(); MeshDelta md(proxyMesh); for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) { INode *tnode = NULL; pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i); if (tnode) { ObjectState os = tnode->EvalWorldState(0); Matrix3 wm = tnode->GetNodeTM(0); TriObject *tri = (TriObject *)os.obj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0)); if (tri) { Mesh& mesh = tri->GetMesh(); MeshDelta tmd (mesh); md.AttachMesh(proxyMesh, mesh, wm, 0); md.Apply(proxyMesh); } } } Matrix3 rtm(true); Point3 center = Point3::Origin;; float udim = 0.0f, vdim = 0.0f, ndim = 0.0f; if (proxyMesh.getNumVerts() > 3) // Doesn't guarantee that the mesh is not a plane. { // First build a convex mesh to put the box around; // the method acts oddly if extra vertices are present. BuildColConvex(); CalcOrientedBox(proxyMesh, udim, vdim, ndim, center, rtm); BuildBox(proxyMesh, vdim, udim, ndim); } MNMesh mn(proxyMesh); mn.Transform(rtm); mn.OutToTri(proxyMesh); proxyPos = Point3::Origin; forceRedraw = true; }
BOOL FaceDataExport::nodeEnum(INode* node,Interface *ip) { if(!exportSelected || node->Selected()) { ObjectState os = node->EvalWorldState(ip->GetTime()); IFaceDataMgr *pFDMgr = NULL; if (os.obj->IsSubClassOf(triObjectClassID)) { TriObject *tobj = (TriObject *)os.obj; Mesh* mesh = &tobj->GetMesh(); pFDMgr = static_cast<IFaceDataMgr*>(mesh->GetInterface( FACEDATAMGR_INTERFACE )); } else if (os.obj->IsSubClassOf (polyObjectClassID)) { PolyObject *pobj = (PolyObject *)os.obj; MNMesh *mesh = &pobj->GetMesh(); pFDMgr = static_cast<IFaceDataMgr*>(mesh->GetInterface( FACEDATAMGR_INTERFACE )); } if (pFDMgr == NULL) return FALSE; SampleFaceData* SampleDataChan = NULL; IFaceDataChannel* fdc = pFDMgr->GetFaceDataChan( FACE_MAXSAMPLEUSE_CLSID ); if ( fdc != NULL ) SampleDataChan = dynamic_cast<SampleFaceData*>(fdc); if ( SampleDataChan == NULL) { fileStream.Printf(_T("Node %s does not have our Face Data\n"),node->GetName()); return false; } //OK so We have Face data lets dump it out.. fileStream.Printf(_T("\nNode %s has %d faces with FaceFloats\n"),node->GetName(), SampleDataChan->Count()); for(ULONG i=0;i<SampleDataChan->Count();i++) { float data = SampleDataChan->data[i]; fileStream.Printf(_T("Face %d, float %f\n"),i,data); } } // Recurse through this node's children, if any for (int c = 0; c < node->NumberOfChildren(); c++) { if (!nodeEnum(node->GetChildNode(c), ip)) return FALSE; } return TRUE; }
int OBJImport::DoImport(const TCHAR *filename,ImpInterface *i,Interface *gi, BOOL suppressPrompts) { TriObject *object = CreateNewTriObject(); if(!object) return 0; if(objFileRead(filename, &object->GetMesh())) { ImpNode *node = i->CreateNode(); if(!node) { delete object; return 0; } Matrix3 tm; tm.IdentityMatrix(); node->Reference(object); node->SetTransform(0,tm); i->AddNodeToScene(node); node->SetName(GetString(IDS_TH_WAVE_OBJ_NAME)); i->RedrawViews(); return 1; } return 0; }
Object* SimpleObject::ConvertToType(TimeValue t, Class_ID obtype) { if (obtype==defObjectClassID||obtype==triObjectClassID||obtype==mapObjectClassID) { TriObject *triob; UpdateMesh(t); triob = CreateNewTriObject(); triob->GetMesh() = mesh; triob->SetChannelValidity(TOPO_CHAN_NUM,ObjectValidity(t)); triob->SetChannelValidity(GEOM_CHAN_NUM,ObjectValidity(t)); return triob; } else if (obtype == patchObjectClassID) { UpdateMesh(t); PatchObject *patchob = new PatchObject(); patchob->patch = mesh; // Handy Mesh->PatchMesh conversion patchob->SetChannelValidity(TOPO_CHAN_NUM,ObjectValidity(t)); patchob->SetChannelValidity(GEOM_CHAN_NUM,ObjectValidity(t)); return patchob; } return Object::ConvertToType(t,obtype); }
void EditFaceDataMod::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node) { if (mDisabled) return; EditFaceDataModData *d = (EditFaceDataModData*)mc.localData; if (!d) mc.localData = d = new EditFaceDataModData(); if (os->obj->IsSubClassOf(triObjectClassID)) { // Access the Mesh: TriObject *tobj = (TriObject*)os->obj; Mesh & mesh = tobj->GetMesh(); // Apply our modifier's changes: d->ApplyChanges (mesh); // Update the cache used for display and hit testing: if (!d->GetCacheMesh()) d->SetCacheMesh(mesh); // Set display flags according to SO level: mesh.dispFlags = 0; mesh.SetDispFlag (levelDispFlags[selLevel]); } else if (os->obj->IsSubClassOf(polyObjectClassID)) { // Access the Mesh: PolyObject *pobj = (PolyObject*)os->obj; MNMesh & mesh = pobj->GetMesh(); // Apply our modifier's changes: d->ApplyChanges (mesh); // Update the cache used for display and hit testing: if (!d->GetCacheMNMesh()) d->SetCacheMNMesh(mesh); // Set display flags according to SO level: mesh.dispFlags = 0; mesh.SetDispFlag (mnlevelDispFlags[selLevel]); } }
bool NifImporter::ImportMesh(NiTriStripsRef triStrips) { bool ok = true; ImpNode *node = i->CreateNode(); if(!node) return false; INode *inode = node->GetINode(); TriObject *triObject = CreateNewTriObject(); node->Reference(triObject); wstring name = wide(triStrips->GetName()); node->SetName(name.c_str()); // Texture Mesh& mesh = triObject->GetMesh(); NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData()); if (triStripsData == NULL) return false; vector<Triangle> tris = triStripsData->GetTriangles(); ok |= ImportMesh(node, triObject, triStrips, triStripsData, tris); return ok; }
void FExtrudeMod::ModifyObject( TimeValue t, ModContext &mc, ObjectState *os, INode *node) { if (os->obj->IsSubClassOf(triObjectClassID)) { TriObject *tobj = (TriObject*)os->obj; Mesh &mesh = tobj->GetMesh(); Interval iv = FOREVER; float a, s; Point3 pt, center; int c; pblock->GetValue(PB_AMOUNT,t,a,iv); pblock->GetValue(PB_SCALE,t,s,iv); pblock->GetValue(PB_CENTER,t,c,iv); base->GetValue(t,&pt,iv,CTRL_ABSOLUTE); // Extrude the faces -- this just creates the new faces mesh.ExtrudeFaces(); // Build normals of selected faces only Tab<Point3> normals; if (!c) { normals.SetCount(mesh.getNumVerts()); for (int i=0; i<mesh.getNumVerts(); i++) { normals[i] = Point3(0,0,0); } for (int i=0; i<mesh.getNumFaces(); i++) { if (mesh.faceSel[i]) { Point3 norm = (mesh.verts[mesh.faces[i].v[1]]-mesh.verts[mesh.faces[i].v[0]]) ^ (mesh.verts[mesh.faces[i].v[2]]-mesh.verts[mesh.faces[i].v[1]]); for (int j=0; j<3; j++) { normals[mesh.faces[i].v[j]] += norm; } } } for (int i=0; i<mesh.getNumVerts(); i++) { normals[i] = Normalize(normals[i]); } } else { // Compute the center point base->GetValue(t,¢er,iv,CTRL_ABSOLUTE); } // Mark vertices used by selected faces BitArray sel; sel.SetSize(mesh.getNumVerts()); for (int i=0; i<mesh.getNumFaces(); i++) { if (mesh.faceSel[i]) { for (int j=0; j<3; j++) sel.Set(mesh.faces[i].v[j],TRUE); } } // Move selected verts for (int i=0; i<mesh.getNumVerts(); i++) { if (sel[i]) { if (!c) { mesh.verts[i] += normals[i]*a; } else { Point3 vect = Normalize((mesh.verts[i] * (*mc.tm)) - center); mesh.verts[i] += vect*a; } } } // Scale verts if (s!=100.0f) { s /= 100.0f; AdjEdgeList ae(mesh); AdjFaceList af(mesh,ae); FaceClusterList clust(mesh.faceSel,af); // Make sure each vertex is only scaled once. BitArray done; done.SetSize(mesh.getNumVerts()); // scale each cluster independently for (int i=0; (DWORD)i<clust.count; i++) { // First determine cluster center Point3 cent(0,0,0); int ct=0; for (int j=0; j<mesh.getNumFaces(); j++) { if (clust[j]==(DWORD)i) { for (int k=0; k<3; k++) { cent += mesh.verts[mesh.faces[j].v[k]]; ct++; } } } if (ct) cent /= float(ct); // Now scale the cluster about its center for (int j=0; j<mesh.getNumFaces(); j++) { if (clust[j]==(DWORD)i) { for (int k=0; k<3; k++) { int index = mesh.faces[j].v[k]; if (done[index]) continue; done.Set(index); mesh.verts[index] = (mesh.verts[index]-cent)*s + cent; } } } } } mesh.InvalidateTopologyCache (); os->obj->UpdateValidity(GEOM_CHAN_NUM,iv); } }
ObjectHandle TriPatchObject::CreateTriObjRep(TimeValue t) { TriObject *tri = CreateNewTriObject(); PrepareMesh(t); // Turn it into a mesh tri->GetMesh() = patch.GetMesh(); // Place it into the TriObject return(ObjectHandle(tri)); }
BOOL Building_collision_exp::ExportOneMesh(INode* node, CExportDataBuf* pDataBuf) { if(node && pDataBuf) { pDataBuf->strName = node->GetName(); ObjectState os = node->EvalWorldState(0); if (!os.obj) { return FALSE; } // Targets are actually geomobjects, but we will export them // from the camera and light objects, so we skip them here. if (os.obj->ClassID() == Class_ID(TARGET_CLASS_ID, 0)) { return FALSE; } int i; // 以后需要用到的循环变量. Matrix3 tm = node->GetObjTMAfterWSM(0); // 三角形面的索引值. int vx1 = 0, vx2 = 1, vx3 = 2; BOOL needDel; TriObject* tri = GetTriObjectFromNode(node, 0, needDel); if (!tri) { return FALSE; } Mesh* mesh = &tri->GetMesh(); pDataBuf->m_iFaceCount = mesh->getNumFaces(); pDataBuf->m_posVectorCount = mesh->getNumVerts(); Point3 v; POS pos; for (i=0; i<mesh->getNumVerts(); i++) { v = tm * mesh->verts[i]; pos.x = v.x; pos.y = v.z; pos.z = -v.y; pDataBuf->m_posVector.push_back(pos); } FACE face; for (i=0; i<mesh->getNumFaces(); i++) { face.iF1 = (WORD)mesh->faces[i].v[vx1]; face.iF2 = (WORD)mesh->faces[i].v[vx2]; face.iF3 = (WORD)mesh->faces[i].v[vx3]; pDataBuf->m_posFaceVector.push_back(face); } if (needDel) { delete tri; } } m_bIsExport = TRUE; return TRUE; }
void XsiExp::ExportMesh( INode * node, TimeValue t, int indentLevel) { ObjectState os = node->EvalWorldState(t); if (!os.obj || os.obj->SuperClassID() != GEOMOBJECT_CLASS_ID) { return; // Safety net. This shouldn't happen. } BOOL needDel; TriObject * tri = GetTriObjectFromNode(node, t, needDel); if (!tri) { // no tri object return; } // prepare mesh Mesh * mesh = &tri->GetMesh(); mesh->buildNormals(); // object offset matrix; apply to verts // swap y and z; max to soft correction Matrix3 matrix(1); // translate matrix.PreTranslate( Point3( node->GetObjOffsetPos().x, node->GetObjOffsetPos().z, -node->GetObjOffsetPos().y)); // rotate AngAxis aa( node->GetObjOffsetRot()); float temp = aa.axis.z; aa.axis.z = -aa.axis.y; aa.axis.y = temp; PreRotateMatrix(matrix, Quat( aa)); // scale ScaleValue scale = node->GetObjOffsetScale(); aa.Set( scale.q); temp = aa.axis.z; aa.axis.z = -aa.axis.y; aa.axis.y = temp; scale.q.Set( aa); temp = scale.s.z; scale.s.z = scale.s.y; scale.s.y = temp; ApplyScaling(matrix, scale); // apply root transform matrix = matrix * topMatrix; // only rotation for normals AffineParts ap; Matrix3 rotMatrix(1); decomp_affine( matrix, &ap); PreRotateMatrix( rotMatrix, ap.q); // set winding order int vx1 = 0, vx2 = 1, vx3 = 2; if (TMNegParity( node->GetNodeTM(GetStaticFrame())) != TMNegParity( matrix) ) { // negative scaling; invert winding order and normal rotation vx1 = 2; vx2 = 1; vx3 = 0; rotMatrix = rotMatrix * Matrix3( Point3(-1,0,0), Point3(0,-1,0), Point3(0,0,-1), Point3(0,0,0)); } // header TSTR indent = GetIndent(indentLevel+1); fprintf(pStream, "%s%s %s {\n",indent.data(), "Mesh", FixupName(node->GetName())); // write number of verts int numLoop = mesh->getNumVerts(); fprintf(pStream, "%s\t%d;\n",indent.data(), numLoop); // write verts for (int i = 0; i < numLoop; i++) { Point3 v = mesh->verts[i]; float temp = v.z; v.z = -v.y; v.y = temp; v = matrix * v; fprintf(pStream, "%s\t%.6f;%.6f;%.6f;%s\n", indent.data(), v.x, v.y, v.z, i == numLoop - 1 ? ";\n" : ","); } // write number of faces numLoop = mesh->getNumFaces(); fprintf(pStream, "%s\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream, "%s\t3;%d,%d,%d;%s\n", indent.data(), mesh->faces[i].v[vx1], mesh->faces[i].v[vx2], mesh->faces[i].v[vx3], i == numLoop - 1 ? ";\n" : ","); } // face materials Mtl * nodeMtl = node->GetMtl(); int numMtls = !nodeMtl || !nodeMtl->NumSubMtls() ? 1 : nodeMtl->NumSubMtls(); // write face material list header fprintf(pStream, "%s\tMeshMaterialList {\n", indent.data()); // write number of materials fprintf(pStream, "%s\t\t%d;\n", indent.data(), numMtls); // write number of faces fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write face material indices (1 for each face) for (i = 0; i < numLoop; i++) { int index = numMtls ? mesh->faces[i].getMatID() % numMtls : 0; fprintf(pStream,"%s\t\t%d%s\n", indent.data(), index, i == numLoop - 1 ? ";\n" : ","); } // write the materials ExportMaterial( node, indentLevel+2); // verts close brace fprintf(pStream, "%s\t}\n\n",indent.data()); // write normals header fprintf(pStream, "%s\t%s {\n", indent.data(), "SI_MeshNormals"); // write number of normals fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop * 3); // write normals (3 for each face) for (i = 0; i < numLoop; i++) { Face * f = &mesh->faces[i]; int vert = f->getVert(vx1); Point3 vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(vert)); float temp = vn.z; vn.z = -vn.y; vn.y = temp; vn = rotMatrix * vn; fprintf(pStream,"%s\t\t%.6f;%.6f;%.6f;,\n", indent.data(), vn.x, vn.y, vn.z); vert = f->getVert(vx2); vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(vert)); temp = vn.z; vn.z = -vn.y; vn.y = temp; vn = rotMatrix * vn; fprintf(pStream,"%s\t\t%.6f;%.6f;%.6f;,\n", indent.data(), vn.x, vn.y, vn.z); vert = f->getVert(vx3); vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(vert)); temp = vn.z; vn.z = -vn.y; vn.y = temp; vn = rotMatrix * vn; fprintf(pStream,"%s\t\t%.6f;%.6f;%.6f;%s\n", indent.data(), vn.x, vn.y, vn.z, i == numLoop - 1 ? ";\n" : ","); } // write number of faces fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream, "%s\t\t%d;3;%d,%d,%d;%s\n", indent.data(), i, i * 3 + vx1, i * 3 + vx2, i * 3 + vx3, i == numLoop - 1 ? ";\n" : ","); } // normals close brace fprintf(pStream, "%s\t}\n\n",indent.data()); // texcoords if (nodeMtl && mesh && (nodeMtl->Requirements(-1) & MTLREQ_FACEMAP)) { // facemapping numLoop = mesh->getNumFaces() * 3; // write texture coords header fprintf(pStream, "%s\tSI_MeshTextureCoords {\n", indent.data()); // write number of texture coords fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write texture coords for (int i = 0; i < numLoop; i++) { Point3 tv[3]; Face * f = &mesh->faces[i]; make_face_uv( f, tv); fprintf(pStream, "%s\t\t%.6f;%.6f;,\n", indent.data(), tv[0].x, tv[0].y); fprintf(pStream, "%s\t\t%.6f;%.6f;,\n", indent.data(), tv[1].x, tv[1].y); fprintf(pStream, "%s\t\t%.6f;%.6f;%s\n", indent.data(), tv[2].x, tv[2].y, i == numLoop - 1 ? ";\n" : ","); } // write number of faces numLoop = mesh->getNumFaces(); fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream,"%s\t\t%d;3;%d,%d,%d;%s\n", indent.data(), i, mesh->tvFace[i].t[vx1], mesh->tvFace[i].t[vx2], mesh->tvFace[i].t[vx3], i == numLoop - 1 ? ";\n" : ","); } // texture coords close brace fprintf(pStream, "%s\t}\n\n", indent.data()); } else { numLoop = mesh->getNumTVerts(); if (numLoop) { // write texture coords header fprintf(pStream, "%s\tSI_MeshTextureCoords {\n", indent.data()); // write number of texture coords fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write texture coords for (i = 0; i < numLoop; i++) { UVVert tv = mesh->tVerts[i]; fprintf(pStream, "%s\t\t%.6f;%.6f;%s\n", indent.data(), tv.x, tv.y, i == numLoop - 1 ? ";\n" : ","); } // write number of faces numLoop = mesh->getNumFaces(); fprintf(pStream, "%s\t\t%d;\n", indent.data(), numLoop); // write faces for (i = 0; i < numLoop; i++) { fprintf(pStream,"%s\t\t%d;3;%d,%d,%d;%s\n", indent.data(), i, mesh->tvFace[i].t[vx1], mesh->tvFace[i].t[vx2], mesh->tvFace[i].t[vx3], i == numLoop - 1 ? ";\n" : ","); } // texture coords close brace fprintf(pStream, "%s\t}\n\n", indent.data()); } } /* // Export color per vertex info if (GetIncludeVertexColors()) { int numCVx = mesh->numCVerts; fprintf(pStream, "%s\t%s %d\n",indent.data(), ID_MESH_NUMCVERTEX, numCVx); if (numCVx) { fprintf(pStream,"%s\t%s {\n",indent.data(), ID_MESH_CVERTLIST); for (i=0; i<numCVx; i++) { Point3 vc = mesh->vertCol[i]; fprintf(pStream, "%s\t\t%s %d\t%s\n",indent.data(), ID_MESH_VERTCOL, i, Format(vc)); } fprintf(pStream,"%s\t}\n",indent.data()); fprintf(pStream, "%s\t%s %d\n",indent.data(), ID_MESH_NUMCVFACES, mesh->getNumFaces()); fprintf(pStream, "%s\t%s {\n",indent.data(), ID_MESH_CFACELIST); for (i=0; i<mesh->getNumFaces(); i++) { fprintf(pStream,"%s\t\t%s %d\t%d\t%d\t%d\n", indent.data(), ID_MESH_CFACE, i, mesh->vcFace[i].t[vx1], mesh->vcFace[i].t[vx2], mesh->vcFace[i].t[vx3]); } fprintf(pStream, "%s\t}\n",indent.data()); } } */ // Mesh close brace fprintf(pStream, "%s}\n",indent.data()); // dispose of tri object if (needDel) { delete tri; } }
void SolidifyPW::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) { //TODO: Add the code for actually modifying the object meshInfo.Free(); if (os->obj->IsSubClassOf(triObjectClassID)) { TriObject *tobj = (TriObject*)os->obj; Mesh &mesh = tobj->GetMesh(); Interval iv = FOREVER; float a,oa; pblock->GetValue(pb_amount,t,a,iv); pblock->GetValue(pb_oamount,t,oa,iv); if (a == oa) oa += 0.00001f; BOOL overrideMatID; int matid; pblock->GetValue(pb_overridematid,t,overrideMatID,iv); pblock->GetValue(pb_matid,t,matid,iv); matid--; if (!overrideMatID) matid = -1; BOOL overridesg; int sg; pblock->GetValue(pb_overridesg,t,overridesg,iv); pblock->GetValue(pb_sg,t,sg,iv); if (!overridesg) sg = -1; int edgeMap; pblock->GetValue(pb_edgemap,t,edgeMap,iv); float tvOffset; pblock->GetValue(pb_tvoffset,t,tvOffset,iv); BOOL ioverrideMatID; int imatid; pblock->GetValue(pb_overrideinnermatid,t,ioverrideMatID,iv); pblock->GetValue(pb_innermatid,t,imatid,iv); imatid--; if (!ioverrideMatID) imatid = -1; BOOL ooverrideMatID; int omatid; pblock->GetValue(pb_overrideoutermatid,t,ooverrideMatID,iv); pblock->GetValue(pb_outermatid,t,omatid,iv); omatid--; if (!ooverrideMatID) omatid = -1; BOOL selEdges, selInner,selOuter; static BOOL selEdgesPrev = FALSE; static BOOL selInnerPrev = FALSE; static BOOL selOuterPrev = FALSE; BOOL updateUI = FALSE; pblock->GetValue(pb_seledges,t,selEdges,iv); pblock->GetValue(pb_selinner,t,selInner,iv); pblock->GetValue(pb_selouter,t,selOuter,iv); if (selEdges && (!selEdgesPrev)) updateUI = TRUE; if (selInner && (!selInnerPrev)) updateUI = TRUE; if (selOuter && (!selOuterPrev)) updateUI = TRUE; selEdgesPrev = selEdges; selInnerPrev = selInner; selOuterPrev = selOuter; if (selEdges || selInner|| selOuter) { mesh.dispFlags = DISP_SELFACES; mesh.selLevel = MESH_FACE; } int segments = 1; pblock->GetValue(pb_segments,t,segments,iv); if (segments < 1) segments = 1; BOOL fixupCorners; pblock->GetValue(pb_fixupcorners,t,fixupCorners,iv); BOOL autoSmooth; float smoothAngle; pblock->GetValue(pb_autosmooth,t,autoSmooth,iv); pblock->GetValue(pb_autosmoothangle,t,smoothAngle,iv); BOOL bevel; INode *node; pblock->GetValue(pb_bevel,t,bevel,iv); pblock->GetValue(pb_bevelshape,t,node,iv); PolyShape shape; if ((bevel) && node) { ObjectState nos = node->EvalWorldState(t); if (nos.obj->IsShapeObject()) { ShapeObject *pathOb = (ShapeObject*)nos.obj; if (!pathOb->NumberOfCurves()) { bevel = FALSE; } else { pathOb->MakePolyShape(t, shape,PSHAPE_BUILTIN_STEPS,TRUE); if (shape.lines[0].IsClosed()) bevel = FALSE; ; } } } DWORD selLevel = mesh.selLevel; mesh.faceSel.ClearAll(); if (bevel) meshInfo.MakeSolid(&mesh,segments, a,oa,matid, sg,edgeMap,tvOffset,imatid,omatid,selEdges,selInner,selOuter,fixupCorners,autoSmooth,smoothAngle,&shape.lines[0]); else meshInfo.MakeSolid(&mesh,segments, a,oa,matid, sg,edgeMap,tvOffset,imatid,omatid,selEdges,selInner,selOuter,fixupCorners,autoSmooth,smoothAngle,NULL); mesh.selLevel = selLevel; mesh.InvalidateTopologyCache (); for (int i = 0; i < mesh.numFaces; i++) { for (int j = 0; j < 3; j++) { int index = mesh.faces[j].v[j]; if ((index < 0) || (index >= mesh.numVerts)) DebugPrint(_T("Invalid face %d(%d) %d\n"),i,j,index); } } int numMaps = mesh.getNumMaps(); for (int mp = -NUM_HIDDENMAPS; mp < numMaps; mp++) { if (!mesh.mapSupport(mp)) continue; Point3 *uvw = mesh.mapVerts(mp); TVFace *uvwFace = mesh.mapFaces(mp); if ((uvw) && (uvwFace)) { int numberTVVerts = mesh.getNumMapVerts(mp); for (int i = 0; i < mesh.numFaces; i++) { for (int j = 0; j < 3; j++) { int index = uvwFace[i].t[j]; if ((index < 0) || (index >= numberTVVerts)) DebugPrint(_T("Invalid Map %d tvface %d(%d) %d\n"),mp,i,j,index); } } } } os->obj->UpdateValidity(GEOM_CHAN_NUM,iv); os->obj->UpdateValidity(TOPO_CHAN_NUM,iv); MeshNormalSpec *pNormSpec = (MeshNormalSpec *) mesh.GetInterface (MESH_NORMAL_SPEC_INTERFACE); if (pNormSpec && pNormSpec->GetNumFaces() > 0) { pNormSpec->SetParent(&mesh); pNormSpec->BuildNormals(); pNormSpec->ComputeNormals(); } if ((updateUI) && (ip)) { ip->PipeSelLevelChanged(); } } EnableUIControls(); }
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::ImportMultipleGeometry(NiNodeRef parent, vector<NiTriBasedGeomRef>& glist) { bool ok = true; if (glist.empty()) return false; ImpNode *node = i->CreateNode(); if(!node) return false; INode *inode = node->GetINode(); TriObject *triObject = CreateNewTriObject(); node->Reference(triObject); string name = parent->GetName(); node->SetName(wide(name).c_str()); // Texture Mesh& mesh = triObject->GetMesh(); vector< pair<int, int> > vert_range, tri_range; vector<Triangle> tris; vector<Vector3> verts; int submats = glist.size(); // Build list of vertices and triangles. Optional components like normals will be handled later. for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr) { NiTriBasedGeomDataRef triGeomData = StaticCast<NiTriBasedGeomData>((*itr)->GetData()); // Get verts and collapse local transform into them int nVertices = triGeomData->GetVertexCount(); vector<Vector3> subverts = triGeomData->GetVertices(); Matrix44 transform = (*itr)->GetLocalTransform(); //Apply the transformations if (transform != Matrix44::IDENTITY) { for ( unsigned int i = 0; i < subverts.size(); ++i ) subverts[i] = transform * subverts[i]; } vert_range.push_back( pair<int,int>( verts.size(), verts.size() + subverts.size()) ); verts.insert(verts.end(), subverts.begin(), subverts.end()); vector<Triangle> subtris = triGeomData->GetTriangles(); for (vector<Triangle>::iterator itr = subtris.begin(), end = subtris.end(); itr != end; ++itr) { (*itr).v1 += nVertices, (*itr).v2 += nVertices, (*itr).v3 += nVertices; } tri_range.push_back( pair<int,int>( tris.size(), tris.size() + subtris.size()) ); tris.insert(tris.end(), subtris.begin(), subtris.end()); } // Transform up-to-parent Matrix44 baseTM = (importBones) ? Matrix44::IDENTITY : parent->GetWorldTransform(); node->SetTransform(0,TOMATRIX3(baseTM)); // Set vertices and triangles mesh.setNumVerts(verts.size()); mesh.setNumTVerts(verts.size(), TRUE); for (int i=0, n=verts.size(); i < n; ++i){ Vector3 &v = verts[i]; mesh.verts[i].Set(v.x, v.y, v.z); } mesh.setNumFaces(tris.size()); mesh.setNumTVFaces(tris.size()); for (int submat=0; submat<submats; ++submat) { int t_start = tri_range[submat].first, t_end = tri_range[submat].second; for (int i=t_start; i<t_end; ++i) { Triangle& t = tris[i]; Face& f = mesh.faces[i]; f.setVerts(t.v1, t.v2, t.v3); f.Show(); f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS); f.setMatID(-1); TVFace& tf = mesh.tvFace[i]; tf.setTVerts(t.v1, t.v2, t.v3); } } mesh.buildNormals(); bool bSpecNorms = false; MultiMtl *mtl = NULL; int igeom = 0; for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr, ++igeom) { NiTriBasedGeomDataRef triGeomData = StaticCast<NiTriBasedGeomData>((*itr)->GetData()); int v_start = vert_range[igeom].first, v_end = vert_range[igeom].second; int t_start = tri_range[igeom].first, t_end = tri_range[igeom].second; // Normals vector<Vector3> subnorms = triGeomData->GetNormals(); Matrix44 rotation = (*itr)->GetLocalTransform().GetRotation(); if (rotation != Matrix44::IDENTITY) { for ( unsigned int i = 0; i < subnorms.size(); ++i ) subnorms[i] = rotation * subnorms[i]; } if (!subnorms.empty()) { #if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 5 // Initialize normals if necessary if (!bSpecNorms) { bSpecNorms = true; mesh.SpecifyNormals(); MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals(); if (NULL != specNorms) { specNorms->BuildNormals(); //specNorms->ClearAndFree(); //specNorms->SetNumFaces(tris.size()); //specNorms->SetNumNormals(n.size()); } } MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals(); if (NULL != specNorms) { Point3* norms = specNorms->GetNormalArray(); for (int i=0, n=subnorms.size(); i<n; i++){ Vector3& v = subnorms[i]; norms[i+v_start] = Point3(v.x, v.y, v.z); } //MeshNormalFace* pFaces = specNorms->GetFaceArray(); //for (int i=0; i<tris.size(); i++){ // Triangle& tri = tris[i]; // MeshNormalFace& face = pFaces[i+t_start]; // face.SpecifyNormalID(0, tri.v1); // face.SpecifyNormalID(1, tri.v2); // face.SpecifyNormalID(2, tri.v3); //} #if VERSION_3DSMAX > ((7000<<16)+(15<<8)+0) // Version 7+ specNorms->SetAllExplicit(true); #endif specNorms->CheckNormals(); } #endif } // uv texture info if (triGeomData->GetUVSetCount() > 0) { vector<TexCoord> texCoords = triGeomData->GetUVSet(0); for (int i=0, n = texCoords.size(); i<n; ++i) { TexCoord& texCoord = texCoords[i]; mesh.tVerts[i+v_start].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0); } } vector<Color4> cv = triGeomData->GetColors(); ImportVertexColor(inode, triObject, tris, cv, v_start); if ( StdMat2* submtl = ImportMaterialAndTextures(node, (*itr)) ) { if (mtl == NULL) { mtl = NewDefaultMultiMtl(); gi->GetMaterialLibrary().Add(mtl); inode->SetMtl(mtl); } // SubMatIDs do not have to be contiguous so we just use the offset mtl->SetSubMtlAndName(igeom, submtl, submtl->GetName()); for (int i=t_start; i<t_end; ++i) mesh.faces[i].setMatID(igeom); } if (enableSkinSupport) ImportSkin(node, (*itr)); } this->i->AddNodeToScene(node); inode = node->GetINode(); inode->EvalWorldState(0); for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr) { // attach child if (INode *parent = GetNode((*itr)->GetParent())) parent->AttachChild(inode, 1); inode->Hide((*itr)->GetVisibility() ? FALSE : TRUE); } if (removeDegenerateFaces) mesh.RemoveDegenerateFaces(); if (removeIllegalFaces) mesh.RemoveIllegalFaces(); if (weldVertices) WeldVertices(mesh); if (enableAutoSmooth) mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE); return ok; }
void FaceDataToColorMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) { if (mDisabled) return; IFaceDataMgr* pFDMgr = NULL; // We can work with Face Data in a couple different object types: TriObject *pTri = NULL; PolyObject *pPoly = NULL; Mesh *pMesh = NULL; MNMesh *pMNMesh = NULL; int numFaces = 0; if (os->obj->IsSubClassOf(triObjectClassID)) { pTri = (TriObject*)os->obj; pMesh = &(pTri->GetMesh()); numFaces = pMesh->getNumFaces(); // Get the face-data manager from the incoming object pFDMgr = static_cast<IFaceDataMgr*>(pMesh->GetInterface( FACEDATAMGR_INTERFACE )); } else if (os->obj->IsSubClassOf(polyObjectClassID)) { pPoly = (PolyObject*)os->obj; pMNMesh = &pPoly->GetMesh(); numFaces = pMNMesh->numf; // Get the face-data manager from the incoming object pFDMgr = static_cast<IFaceDataMgr*>(pMNMesh->GetInterface( FACEDATAMGR_INTERFACE )); } if (pFDMgr == NULL) return; //Get our parameters int channel; float r, g, b; Interval ourValidity = os->obj->ChannelValidity (t, TOPO_CHAN_NUM); mpParams->GetValue (pb_channel, t, channel, ourValidity); mpParams->GetValue (pb_red, t, r, ourValidity); mpParams->GetValue (pb_green, t, g, ourValidity); mpParams->GetValue (pb_blue, t, b, ourValidity); // Get at our SampleFaceData: SampleFaceData *pFaceData = dynamic_cast<SampleFaceData *>(pFDMgr->GetFaceDataChan (FACE_MAXSAMPLEUSE_CLSID)); // Apply the colors - different code depending on object type: if (pMesh) { pMesh->setMapSupport (-channel, true); pMesh->setNumMapVerts (-channel, 3*numFaces); TVFace *pFace = pMesh->mapFaces(-channel); VertColor *pColor = pMesh->mapVerts (-channel); int maxFaceData = pFaceData ? pFaceData->Count() : 0; for (int i=0; i<numFaces; i++) { for (int j=0; j<3; j++) { int k = i*3+j; pFace[i].t[j] = k; if (i<maxFaceData) { float fdValue = pFaceData->data[i]; VertColor val; val.x = r*fdValue; if (val.x < 0) val.x = 0.0f; if (val.x > 1) val.x = 1.0f; val.y = g*fdValue; if (val.y < 0) val.y = 0.0f; if (val.y > 1) val.y = 1.0f; val.z = b*fdValue; if (val.z < 0) val.z = 0.0f; if (val.z > 1) val.z = 1.0f; pColor[k] = val; } else pColor[k] = VertColor(0,0,0); } } } if (pMNMesh) { if ((channel == 0) && (pMNMesh->numm == 0)) pMNMesh->SetMapNum (1); pMNMesh->M(-channel)->ClearFlag (MN_DEAD); pMNMesh->M(-channel)->setNumFaces (numFaces); // Precount the number of map vertices we need: int numColors = 0; for (int i=0; i<numFaces; i++) { if (pMNMesh->f[i].GetFlag (MN_DEAD)) continue; numColors += pMNMesh->f[i].deg; } pMNMesh->M(-channel)->setNumVerts (numColors); MNMapFace *pFace = pMNMesh->M(-channel)->f; VertColor *pColor = pMNMesh->M(-channel)->v; int maxFaceData = pFaceData ? pFaceData->Count() : 0; for (int i=0, k = 0; i<numFaces; i++) { if (pMNMesh->f[i].GetFlag (MN_DEAD)) continue; pFace[i].SetSize (pMNMesh->f[i].deg); for (int j=0; j<pMNMesh->f[i].deg; j++) { pFace[i].tv[j] = k; if (i<maxFaceData) { float fdValue = pFaceData->data[i]; VertColor val; val.x = r*fdValue; if (val.x < 0) val.x = 0.0f; if (val.x > 1) val.x = 1.0f; val.y = g*fdValue; if (val.y < 0) val.y = 0.0f; if (val.y > 1) val.y = 1.0f; val.z = b*fdValue; if (val.z < 0) val.z = 0.0f; if (val.z > 1) val.z = 1.0f; pColor[k] = val; } else pColor[k] = VertColor(0,0,0); k++; } } } os->obj->SetChannelValidity (VERT_COLOR_CHAN_NUM, ourValidity); }
int ExportQuake3Model(const TCHAR *filename, ExpInterface *ei, Interface *gi, int start_time, std::list<ExportNode> lTags, std::list<ExportNode> lMeshes) { FILE *file; int i, j, totalTags, totalMeshes, current_time = 0; long pos_current, totalTris = 0, totalVerts = 0; std::list<FrameRange>::iterator range_i; std::vector<Point3> lFrameBBoxMin; std::vector<Point3> lFrameBBoxMax; long pos_tagstart; long pos_tagend; long pos_filesize; long pos_framestart; int lazynamesfixed = 0; const Point3 x_axis(1, 0, 0); const Point3 z_axis(0, 0, 1); SceneEnumProc checkScene(ei->theScene, start_time, gi); totalTags = (int)lTags.size(); if (g_tag_for_pivot) totalTags++; totalMeshes = (int)lMeshes.size(); // open file file = _tfopen(filename, _T("wb")); if (!file) { ExportError("Cannot open file '%s'.", filename); return FALSE; } ExportDebug("%s:", filename); // sync pattern and version putChars("IDP3", 4, file); put32(15, file); putChars("Darkplaces MD3 Exporter", 64, file); put32(0, file); // flags // MD3 header ExportState("Writing MD3 header"); put32(g_total_frames, file); // how many frames put32(totalTags, file); // tagsnum put32(totalMeshes, file); // meshnum put32(1, file); // maxskinnum put32(108, file); // headersize pos_tagstart = ftell(file); put32(0, file); // tagstart pos_tagend = ftell(file); put32(256, file); // tagend pos_filesize = ftell(file); put32(512, file); // filesize ExportDebug(" %i frames, %i tags, %i meshes", g_total_frames, totalTags, totalMeshes); // frame info // bbox arrays get filled while exported mesh and written back then ExportState("Writing frame info"); pos_framestart = ftell(file); lFrameBBoxMin.resize(g_total_frames); lFrameBBoxMax.resize(g_total_frames); for (i = 0; i < g_total_frames; i++) { // init frame data lFrameBBoxMin[i].Set(0, 0, 0); lFrameBBoxMax[i].Set(0, 0, 0); // put data putFloat(-1.0f, file); // bbox min vector putFloat(-1.0f, file); putFloat(-1.0f, file); putFloat( 1.0f, file); // bbox max vector putFloat(1.0f, file); putFloat(1.0f, file); putFloat(0.0f, file); // local origin (usually 0 0 0) putFloat(0.0f, file); putFloat(0.0f, file); putFloat(1.0f, file); // radius of bounding sphere putChars("", 16, file); } // tags pos_current = ftell(file); fseek(file, pos_tagstart, SEEK_SET); put32(pos_current, file); fseek(file, pos_current, SEEK_SET); // for each frame range cycle all frames and write out each tag long pos_tags = pos_current; if (totalTags) { long current_frame = 0; ExportState("Writing %i tags", totalTags); for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi); current_time = current_scene.time; // write out tags if (lTags.size()) { for (std::list<ExportNode>::iterator tag_i = lTags.begin(); tag_i != lTags.end(); tag_i++) { INode *node = current_scene[tag_i->i]->node; Matrix3 tm = node->GetObjTMAfterWSM(current_time); ExportState("Writing '%s' frame %i of %i", tag_i->name, i, g_total_frames); // tagname putChars(tag_i->name, 64, file); // origin, rotation matrix Point3 row = tm.GetRow(3); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); row = tm.GetRow(0); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); row = tm.GetRow(1); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); row = tm.GetRow(2); putFloat(row.x, file); putFloat(row.y, file); putFloat(row.z, file); } } // write the center of mass tag_pivot which is avg of all objects's pivots if (g_tag_for_pivot) { ExportState("Writing 'tag_pivot' frame %i of %i", i, g_total_frames); // write the null data as tag_pivot need to be written after actual geometry // (it needs information on frame bound boxes to get proper blendings) putChars("tag_pivot", 64, file); putFloat(0, file); putFloat(0, file); putFloat(0, file); putFloat(1, file); putFloat(0, file); putFloat(0, file); putFloat(0, file); putFloat(1, file); putFloat(0, file); putFloat(0, file); putFloat(0, file); putFloat(1, file); } } } } // write the tag object offsets pos_current = ftell(file); fseek(file, pos_tagend, SEEK_SET); put32(pos_current, file); fseek(file, pos_current, SEEK_SET); // allocate the structs used to calculate tag_pivot std::vector<Point3> tag_pivot_origin; std::vector<double> tag_pivot_volume; if (g_tag_for_pivot) { tag_pivot_origin.resize(g_total_frames); tag_pivot_volume.resize(g_total_frames); } // mesh objects // for each mesh object write uv and frames SceneEnumProc scratch(ei->theScene, start_time, gi); ExportState("Writing %i meshes", (int)lMeshes.size()); for (std::list<ExportNode>::iterator mesh_i = lMeshes.begin(); mesh_i != lMeshes.end(); mesh_i++) { bool needsDel; ExportState("Start mesh #%i", mesh_i); INode *node = checkScene[mesh_i->i]->node; Matrix3 tm = node->GetObjTMAfterWSM(start_time); TriObject *tri = GetTriObjectFromNode(node, start_time, needsDel); if (!tri) continue; // get mesh, compute normals Mesh &mesh = tri->GetMesh(); MeshNormalSpec *meshNormalSpec = mesh.GetSpecifiedNormals(); if (meshNormalSpec) { if (!meshNormalSpec->GetNumFaces()) meshNormalSpec = NULL; else { meshNormalSpec->SetParent(&mesh); meshNormalSpec->CheckNormals(); } } mesh.checkNormals(TRUE); // fix lazy object names ExportState("Attempt to fix mesh name '%s'", mesh_i->name); char meshname[64]; size_t meshnamelen = min(63, strlen(mesh_i->name)); memset(meshname, 0, 64); strncpy(meshname, mesh_i->name, meshnamelen); meshname[meshnamelen] = 0; if (!strncmp("Box", meshname, 3) || !strncmp("Sphere", meshname, 6) || !strncmp("Cylinder", meshname, 8) || !strncmp("Torus", meshname, 5) || !strncmp("Cone", meshname, 4) || !strncmp("GeoSphere", meshname, 9) || !strncmp("Tube", meshname, 4) || !strncmp("Pyramid", meshname, 7) || !strncmp("Plane", meshname, 5) || !strncmp("Teapot", meshname, 6) || !strncmp("Object", meshname, 6)) { name_conflict: lazynamesfixed++; if (lazynamesfixed == 1) strcpy(meshname, "base"); else sprintf(meshname, "base%i", lazynamesfixed); // check if it's not used by another mesh for (std::list<ExportNode>::iterator m_i = lMeshes.begin(); m_i != lMeshes.end(); m_i++) if (!strncmp(m_i->name, meshname, strlen(meshname))) goto name_conflict; // approve name ExportWarning("Lazy object name '%s' (mesh renamed to '%s').", node->GetName(), meshname); } // special mesh check bool shadow_or_collision = false; if (g_mesh_special) if (!strncmp("collision", meshname, 9) || !strncmp("shadow", meshname, 6)) shadow_or_collision = true; // get material const char *shadername = NULL; Texmap *tex = 0; Mtl *mtl = 0; if (!shadow_or_collision) { mtl = node->GetMtl(); if (mtl) { // check for multi-material if (mtl->IsMultiMtl()) { // check if it's truly multi material // we do support multi-material with only one texture (some importers set it) bool multi_material = false; MtlID matId = mesh.faces[0].getMatID(); for (i = 1; i < mesh.getNumFaces(); i++) if (mesh.faces[i].getMatID() != matId) multi_material = true; if (multi_material) if (g_mesh_multimaterials == MULTIMATERIALS_NONE) ExportWarning("Object '%s' is multimaterial and using multiple materials on its faces, that case is not yet supported (truncating to first submaterial).", node->GetName()); // switch to submaterial mtl = mtl->GetSubMtl(matId); } // get shader from material if supplied char *materialname = GetChar(mtl->GetName()); if (g_mesh_materialasshader && (strstr(materialname, "/") != NULL || strstr(materialname, "\\") != NULL)) shadername = GetChar(mtl->GetName()); else { // get texture tex = mtl->GetSubTexmap(ID_DI); if (tex) { if (tex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00)) { shadername = GetChar(((BitmapTex *)tex)->GetMapName()); if (shadername == NULL || !shadername[0]) ExportWarning("Object '%s' material '%s' has no bitmap.", tex->GetName(), node->GetName()); } else { tex = NULL; ExportWarning("Object '%s' has material with wrong texture type (only Bitmap are supported).", node->GetName()); } } else ExportWarning("Object '%s' has material but no texture.", node->GetName()); } } else ExportWarning("Object '%s' has no material.", node->GetName()); } long pos_meshstart = ftell(file); // surface object ExportState("Writing mesh '%s' header", meshname); putChars("IDP3", 4, file); putChars(meshname, 64, file); put32(0, file); // flags put32(g_total_frames, file); // framecount put32(1, file); // skincount long pos_vertexnum = ftell(file); put32(0, file); // vertexcount put32(mesh.getNumFaces(), file); // trianglecount long pos_trianglestart = ftell(file); put32(0, file); // start triangles put32(108, file); // header size long pos_texvecstart = ftell(file); put32(0, file); // texvecstart long pos_vertexstart = ftell(file); put32(16, file); // vertexstart long pos_meshsize = ftell(file); put32(32, file); // meshsize // write out a single 'skin' ExportState("Writing mesh %s texture", meshname); if (shadow_or_collision) putChars(meshname, 64, file); else if (shadername) putMaterial(shadername, mtl, tex, file); else putChars("noshader", 64, file); put32(0, file); // flags // build geometry ExportState("Building vertexes/triangles"); std::vector<ExportVertex>vVertexes; std::vector<ExportTriangle>vTriangles; vVertexes.resize(mesh.getNumVerts()); int vExtraVerts = mesh.getNumVerts(); for (i = 0; i < mesh.getNumVerts(); i++) { vVertexes[i].vert = i; vVertexes[i].normalfilled = false; // todo: check for coincident verts } int vNumExtraVerts = 0; // check normals if (!mesh.normalsBuilt && !shadow_or_collision) ExportWarning("Object '%s' does not have normals contructed.", node->GetName()); // get info for triangles const float normal_epsilon = 0.01f; vTriangles.resize(mesh.getNumFaces()); for (i = 0; i < mesh.getNumFaces(); i++) { DWORD smGroup = mesh.faces[i].getSmGroup(); ExportState("Mesh %s: checking normals for face %i of %i", meshname, i, mesh.getNumFaces()); for (j = 0; j < 3; j++) { int vert = mesh.faces[i].getVert(j); vTriangles[i].e[j] = vert; // find a right normal for this vertex and save its 'address' int vni; Point3 vn; if (!mesh.normalsBuilt || shadow_or_collision) { vn.Set(0, 0, 0); vni = 0; } else { int numNormals; RVertex *rv = mesh.getRVertPtr(vert); if (meshNormalSpec) { ExportState("face %i vert %i have normal specified", i, j); // mesh have explicit normals (i.e. Edit Normals modifier) vn = meshNormalSpec->GetNormal(i, j); vni = meshNormalSpec->GetNormalIndex(i, j); } else if (rv && rv->rFlags & SPECIFIED_NORMAL) { ExportState("face %i vert %i have SPECIFIED_NORMAL flag", i, j); // SPECIFIED_NORMAL flag vn = rv->rn.getNormal(); vni = 0; } else if (rv && (numNormals = rv->rFlags & NORCT_MASK) && smGroup) { // If there is only one vertex is found in the rn member. if (numNormals == 1) { ExportState("face %i vert %i have solid smooth group", i, j); vn = rv->rn.getNormal(); vni = 0; } else { ExportState("face %i vert %i have mixed smoothing groups", i, j); // If two or more vertices are there you need to step through them // and find the vertex with the same smoothing group as the current face. // You will find multiple normals in the ern member. for (int k = 0; k < numNormals; k++) { if (rv->ern[k].getSmGroup() & smGroup) { vn = rv->ern[k].getNormal(); vni = 1 + k; } } } } else { ExportState("face %i vert %i flat shaded", i, j); // Get the normal from the Face if no smoothing groups are there vn = mesh.getFaceNormal(i); vni = 0 - (i + 1); } } // subdivide to get all normals right if (!vVertexes[vert].normalfilled) { vVertexes[vert].normal = vn; vVertexes[vert].normalindex = vni; vVertexes[vert].normalfilled = true; } else if ((vVertexes[vert].normal - vn).Length() >= normal_epsilon) { // current vertex not matching normal - it was already filled by different smoothing group // find a vert in extra verts in case it was already created bool vert_found = false; for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++) { if (vVertexes[ev].vert == vert && (vVertexes[ev].normal - vn).Length() < normal_epsilon) { vert_found = true; vTriangles[i].e[j] = ev; break; } } // we havent found a vertex, create new if (!vert_found) { ExportVertex NewVert; NewVert.vert = vVertexes[vert].vert; NewVert.normal = vn; NewVert.normalindex = vni; NewVert.normalfilled = true; vTriangles[i].e[j] = (int)vVertexes.size(); vVertexes.push_back(NewVert); vNumExtraVerts++; } } } } int vNumExtraVertsForSmoothGroups = vNumExtraVerts; // generate UV map // VorteX: use direct maps reading since getNumTVerts()/getTVert is deprecated // max sets two default mesh maps: 0 - vertex color, 1 : UVW, 2 & up are custom ones ExportState("Building UV map"); std::vector<ExportUV>vUVMap; vUVMap.resize(vVertexes.size()); int meshMap = 1; if (!mesh.mapSupport(meshMap) || !mesh.getNumMapVerts(meshMap) || shadow_or_collision) { for (i = 0; i < mesh.getNumVerts(); i++) { vUVMap[i].u = 0.5; vUVMap[i].v = 0.5; } if (!shadow_or_collision) ExportWarning("No UV mapping was found on object '%s'.", node->GetName()); } else { UVVert *meshUV = mesh.mapVerts(meshMap); for (i = 0; i < (int)vTriangles.size(); i++) { ExportState("Mesh %s: converting tvert for face %i of %i", meshname, i, (int)vTriangles.size()); // for 3 face vertexes for (j = 0; j < 3; j++) { int vert = vTriangles[i].e[j]; int tv = mesh.tvFace[i].t[j]; UVVert &UV = meshUV[tv]; if (!vUVMap[vert].filled) { // fill uvMap vertex vUVMap[vert].u = UV.x; vUVMap[vert].v = UV.y; vUVMap[vert].filled = true; vUVMap[vert].tvert = tv; } else if (tv != vUVMap[vert].tvert) { // uvMap slot for this vertex has been filled // we should arrange triangle to other vertex, which not filled and having same shading and uv // check if any of the extra vertices can fit bool vert_found = false; for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++) { if (vVertexes[ev].vert == vert && vUVMap[vert].u == UV.x &&vUVMap[vert].v == UV.y && (vVertexes[ev].normal - vVertexes[vert].normal).Length() < normal_epsilon) { vert_found = true; vTriangles[i].e[j] = vVertexes[ev].vert; break; } } if (!vert_found) { // create new vert ExportVertex NewVert; NewVert.vert = vVertexes[vert].vert; NewVert.normal = vVertexes[vert].normal; NewVert.normalindex = vVertexes[vert].normalindex; NewVert.normalfilled = vVertexes[vert].normalfilled; vTriangles[i].e[j] = (int)vVertexes.size(); vVertexes.push_back(NewVert); vNumExtraVerts++; // create new TVert ExportUV newUV; newUV.filled = true; newUV.u = UV.x; newUV.v = UV.y; newUV.tvert = tv; vUVMap.push_back(newUV); } } } } } int vNumExtraVertsForUV = (vNumExtraVerts - vNumExtraVertsForSmoothGroups); // print some debug stats ExportDebug(" mesh %s: %i vertexes +%i %s +%i UV, %i triangles", meshname, ((int)vVertexes.size() - vNumExtraVerts), vNumExtraVertsForSmoothGroups, meshNormalSpec ? "EditNormals" : "SmoothGroups", vNumExtraVertsForUV, (int)vTriangles.size()); // fill in triangle start pos_current = ftell(file); fseek(file, pos_trianglestart, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); // detect if object have negative scale (mirrored) // in this canse we should rearrange triangles counterclockwise // so stuff will not be inverted ExportState("Mesh %s: writing %i triangles", meshname, (int)vTriangles.size()); if (DotProd(CrossProd(tm.GetRow(0), tm.GetRow(1)), tm.GetRow(2)) < 0.0) { ExportWarning("Object '%s' is mirrored (having negative scale on it's transformation)", node->GetName()); for (i = 0; i < (int)vTriangles.size(); i++) { put32(vTriangles[i].b, file); // vertex index put32(vTriangles[i].c, file); // for 3 vertices put32(vTriangles[i].a, file); // of triangle } } else { for (i = 0; i < (int)vTriangles.size(); i++) { put32(vTriangles[i].a, file); // vertex index put32(vTriangles[i].c, file); // for 3 vertices put32(vTriangles[i].b, file); // of triangle } } // fill in texvecstart // write out UV mapping coords. ExportState("Mesh %s: writing %i UV vertexes", meshname, (int)vUVMap.size()); pos_current = ftell(file); fseek(file, pos_texvecstart, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); for (i = 0; i < (int)vUVMap.size(); i++) { putFloat(vUVMap[i].u, file); // texture coord u,v putFloat(1.0f - vUVMap[i].v, file); // for vertex } vUVMap.clear(); // fill in vertexstart pos_current = ftell(file); fseek(file, pos_vertexstart, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); // fill in vertexnum pos_current = ftell(file); fseek(file, pos_vertexnum, SEEK_SET); put32((int)vVertexes.size(), file); fseek(file, pos_current, SEEK_SET); // write out for each frame the position of each vertex long current_frame = 0; ExportState("Mesh %s: writing %i frames", meshname, g_total_frames); for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { bool _needsDel; // get triobject for current frame SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi); current_time = current_scene.time; INode *_node = current_scene[mesh_i->i]->node; TriObject *_tri = GetTriObjectFromNode(_node, current_time, _needsDel); if (!_tri) continue; // get mesh, compute normals Mesh &_mesh = _tri->GetMesh(); MeshNormalSpec *_meshNormalSpec = _mesh.GetSpecifiedNormals(); if (_meshNormalSpec) { if (!_meshNormalSpec->GetNumFaces()) _meshNormalSpec = NULL; else { _meshNormalSpec->SetParent(&_mesh); _meshNormalSpec->CheckNormals(); } } _mesh.checkNormals(TRUE); // get transformations for current frame Matrix3 _tm = _node->GetObjTMAfterWSM(current_time); ExportState("Mesh %s: writing frame %i of %i", meshname, current_frame, g_total_frames); Point3 BoxMin(0, 0, 0); Point3 BoxMax(0, 0, 0); for (j = 0; j < (int)vVertexes.size(); j++) // number of vertices { ExportState("Mesh %s: transform vertex %i of %i", meshname, j, (int)vVertexes.size()); int vert = vVertexes[j].vert; Point3 &v = _tm.PointTransform(_mesh.getVert(vert)); // populate bbox data if (!shadow_or_collision) { BoxMin.x = min(BoxMin.x, v.x); BoxMin.y = min(BoxMin.y, v.y); BoxMin.z = min(BoxMin.z, v.z); BoxMax.x = max(BoxMax.x, v.x); BoxMax.y = max(BoxMax.y, v.y); BoxMax.z = max(BoxMax.z, v.z); } // write vertex double f; f = v.x * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file); f = v.y * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file); f = v.z * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file); // get normal ExportState("Mesh %s: transform vertex normal %i of %i", meshname, j, (int)vVertexes.size()); Point3 n; if (_meshNormalSpec) // mesh have explicit normals (i.e. Edit Normals modifier) n = _meshNormalSpec->Normal(vVertexes[j].normalindex); else if (!vVertexes[j].normalfilled || !_mesh.normalsBuilt) n = _mesh.getNormal(vert); else { RVertex *rv = _mesh.getRVertPtr(vert); if (vVertexes[j].normalindex < 0) n = _mesh.getFaceNormal((0 - vVertexes[j].normalindex) - 1); else if (vVertexes[j].normalindex == 0) n = rv->rn.getNormal(); else n = rv->ern[vVertexes[j].normalindex - 1].getNormal(); } // transform normal Point3 &nt = _tm.VectorTransform(n).Normalize(); // encode a normal vector into a 16-bit latitude-longitude value double lng = acos(nt.z) * 255 / (2 * pi); double lat = atan2(nt.y, nt.x) * 255 / (2 * pi); put16((((int)lat & 0xFF) << 8) | ((int)lng & 0xFF), file); } // blend the pivot positions for tag_pivot using mesh's volumes for blending power if (g_tag_for_pivot && !shadow_or_collision) { ExportState("Mesh %s: writing tag_pivot", meshname); Point3 Size = BoxMax - BoxMin; double BoxVolume = pow(Size.x * Size.y * Size.z, 0.333f); // blend matrices float blend = (float)(BoxVolume / (BoxVolume + tag_pivot_volume[current_frame])); float iblend = 1 - blend; tag_pivot_volume[current_frame] = tag_pivot_volume[current_frame] + BoxVolume; Point3 row = _tm.GetRow(3) - _node->GetObjOffsetPos(); tag_pivot_origin[current_frame].x = tag_pivot_origin[current_frame].x * iblend + row.x * blend; tag_pivot_origin[current_frame].y = tag_pivot_origin[current_frame].y * iblend + row.y * blend; tag_pivot_origin[current_frame].z = tag_pivot_origin[current_frame].z * iblend + row.z * blend; } // populate bbox data for frames lFrameBBoxMin[current_frame].x = min(lFrameBBoxMin[current_frame].x, BoxMin.x); lFrameBBoxMin[current_frame].y = min(lFrameBBoxMin[current_frame].y, BoxMin.y); lFrameBBoxMin[current_frame].z = min(lFrameBBoxMin[current_frame].z, BoxMin.z); lFrameBBoxMax[current_frame].x = max(lFrameBBoxMax[current_frame].x, BoxMax.x); lFrameBBoxMax[current_frame].y = max(lFrameBBoxMax[current_frame].y, BoxMax.y); lFrameBBoxMax[current_frame].z = max(lFrameBBoxMax[current_frame].z, BoxMax.z); // delete the working object, if necessary. if (_needsDel) delete _tri; } } // delete if necessary if (needsDel) delete tri; // fill in meshsize pos_current = ftell(file); fseek(file, pos_meshsize, SEEK_SET); put32(pos_current - pos_meshstart, file); fseek(file, pos_current, SEEK_SET); // reset back to first frame SceneEnumProc scratch(ei->theScene, start_time, gi); totalTris += (long)vTriangles.size(); totalVerts += (long)vVertexes.size(); vTriangles.clear(); vVertexes.clear(); } // write tag_pivot ExportState("Writing tag_pivot positions"); if (g_tag_for_pivot) { pos_current = ftell(file); long current_frame = 0; for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { fseek(file, pos_tags + totalTags*112*current_frame + (int)lTags.size()*112 + 64, SEEK_SET); // origin putFloat(tag_pivot_origin[current_frame].x, file); putFloat(tag_pivot_origin[current_frame].y, file); putFloat(tag_pivot_origin[current_frame].z, file); } } fseek(file, pos_current, SEEK_SET); } tag_pivot_volume.clear(); tag_pivot_origin.clear(); // write frame data ExportState("Writing culling info"); long current_frame = 0; pos_current = ftell(file); for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++) { for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++) { fseek(file, pos_framestart + current_frame*56, SEEK_SET); putFloat(lFrameBBoxMin[current_frame].x, file); // bbox min vector putFloat(lFrameBBoxMin[current_frame].y, file); putFloat(lFrameBBoxMin[current_frame].z, file); putFloat(lFrameBBoxMax[current_frame].x, file); // bbox max vector putFloat(lFrameBBoxMax[current_frame].y, file); putFloat(lFrameBBoxMax[current_frame].z, file); putFloat(0, file); // local origin (usually 0 0 0) putFloat(0, file); putFloat(0, file); putFloat(max(lFrameBBoxMin[current_frame].Length(), lFrameBBoxMax[current_frame].Length()) , file); // radius of bounding sphere } } fseek(file, pos_current, SEEK_SET); lFrameBBoxMin.clear(); lFrameBBoxMax.clear(); // fill in filesize pos_current = ftell(file); fseek(file, pos_filesize, SEEK_SET); put32(pos_current, file); fseek(file, pos_current, SEEK_SET); fclose(file); ExportDebug(" total: %i vertexes, %i triangles", totalVerts, totalTris); return TRUE; }
void SmoothMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node) { Interval valid = FOREVER; int autoSmooth, bits = 0, prevent = 0; float thresh = 0.0f; pblock->GetValue(sm_autosmooth, t, autoSmooth, valid); if (autoSmooth) { pblock->GetValue(sm_threshold, t, thresh, valid); pblock->GetValue(sm_prevent_indirect, t, prevent, valid); } else { pblock->GetValue(sm_smoothbits, t, bits, valid); } // For version 4 and later, we process patch meshes as they are and pass them on. Earlier // versions converted to TriMeshes (done below). For adding other new types of objects, add // them here! bool done = false; #ifndef NO_PATCHES if(version >= MATMOD_VER4 && os->obj->IsSubClassOf(patchObjectClassID)) { PatchObject *patchOb = (PatchObject *)os->obj; PatchMesh &pmesh = patchOb->GetPatchMesh(t); BOOL useSel = pmesh.selLevel >= PO_PATCH; if (autoSmooth) pmesh.AutoSmooth (thresh, useSel, prevent); else { for (int i=0; i<pmesh.getNumPatches(); i++) { if (!useSel || pmesh.patchSel[i]) pmesh.patches[i].smGroup = (DWORD)bits; } } pmesh.InvalidateGeomCache(); // Do this because there isn't a topo cache in PatchMesh patchOb->UpdateValidity(TOPO_CHAN_NUM,valid); done = true; } #endif // NO_PATCHES if (!done && os->obj->IsSubClassOf (polyObjectClassID)) { PolyObject *pPolyOb = (PolyObject *)os->obj; MNMesh &mesh = pPolyOb->GetMesh(); BOOL useSel = (mesh.selLevel == MNM_SL_FACE); if (autoSmooth) mesh.AutoSmooth (thresh, useSel, prevent); else { for (int faceIndex=0; faceIndex<mesh.FNum(); faceIndex++) { if (!useSel || mesh.F(faceIndex)->GetFlag(MN_SEL)) { mesh.F(faceIndex)->smGroup = (DWORD)bits; } } } // Luna task 747 // We need to rebuild the smoothing-group-based normals in the normalspec, if any: if (mesh.GetSpecifiedNormals()) { mesh.GetSpecifiedNormals()->SetParent (&mesh); mesh.GetSpecifiedNormals()->BuildNormals (); mesh.GetSpecifiedNormals()->ComputeNormals (); } pPolyOb->UpdateValidity(TOPO_CHAN_NUM,valid); done = true; } TriObject *triOb = NULL; if (!done) { if (os->obj->IsSubClassOf(triObjectClassID)) triOb = (TriObject *)os->obj; else { // Convert to triobject if we can. if(os->obj->CanConvertToType(triObjectClassID)) { TriObject *triOb = (TriObject *)os->obj->ConvertToType(t, triObjectClassID); // We'll need to stuff this back into the pipeline: os->obj = triOb; // Convert validities: Interval objValid = os->obj->ChannelValidity (t, TOPO_CHAN_NUM); triOb->SetChannelValidity (TOPO_CHAN_NUM, objValid); triOb->SetChannelValidity (GEOM_CHAN_NUM, objValid & os->obj->ChannelValidity (t, GEOM_CHAN_NUM)); triOb->SetChannelValidity (TEXMAP_CHAN_NUM, objValid & os->obj->ChannelValidity (t, TEXMAP_CHAN_NUM)); triOb->SetChannelValidity (VERT_COLOR_CHAN_NUM, objValid & os->obj->ChannelValidity (t, VERT_COLOR_CHAN_NUM)); triOb->SetChannelValidity (DISP_ATTRIB_CHAN_NUM, objValid & os->obj->ChannelValidity (t, DISP_ATTRIB_CHAN_NUM)); } } } if (triOb) { // one way or another, there's a triobject to smooth. Mesh & mesh = triOb->GetMesh(); BOOL useSel = mesh.selLevel == MESH_FACE; if (autoSmooth) mesh.AutoSmooth (thresh, useSel, prevent); else { for (int i=0; i<mesh.getNumFaces(); i++) { if (!useSel || mesh.faceSel[i]) mesh.faces[i].smGroup = (DWORD)bits; } } triOb->GetMesh().InvalidateTopologyCache(); triOb->UpdateValidity(TOPO_CHAN_NUM,valid); } }
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 UnwrapMod::ApplyMeshMapping(ObjectState *os, int CurrentChannel, TimeValue t) { // is whole mesh TriObject *tobj = (TriObject*)os->obj; // Apply our mapping Mesh &mesh = tobj->GetMesh(); if (!mesh.mapSupport(CurrentChannel)) { // allocate texture verts. Setup tv faces into a parallel // topology as the regular faces if (CurrentChannel >= mesh.getNumMaps ()) mesh.setNumMaps (CurrentChannel+1, TRUE); mesh.setMapSupport (CurrentChannel, TRUE); TVFace *tvFace = mesh.mapFaces(CurrentChannel); for (int k =0; k < mesh.numFaces;k++) { tvFace[k].t[0] = 0; tvFace[k].t[1] = 0; tvFace[k].t[2] = 0; } } TVFace *tvFace = mesh.mapFaces(CurrentChannel); int tvFaceCount = mesh.numFaces; if (mesh.selLevel!=MESH_FACE) { //copy into mesh struct for (int k=0; k<tvFaceCount; k++) { if (k < TVMaps.f.Count()) { tvFace[k].t[0] = TVMaps.f[k]->t[0]; tvFace[k].t[1] = TVMaps.f[k]->t[1]; tvFace[k].t[2] = TVMaps.f[k]->t[2]; } else { tvFace[k].t[0] = 0; tvFace[k].t[1] = 0; tvFace[k].t[2] = 0; } } //match verts mesh.setNumMapVerts (CurrentChannel, TVMaps.v.Count()); Point3 *tVerts = mesh.mapVerts(CurrentChannel); for ( k=0; k<TVMaps.v.Count(); k++) { tVerts[k] = GetPoint(t,k); } } else { //copy into mesh struct //check if mesh has existing tv faces int offset = mesh.getNumMapVerts (CurrentChannel); int current = 0; for (int k=0; k<tvFaceCount; k++) { //copy if face is selected if (mesh.faceSel[k]==1) { tvFace[k].t[0] = TVMaps.f[k]->t[0] + offset; tvFace[k].t[1] = TVMaps.f[k]->t[1] + offset; tvFace[k].t[2] = TVMaps.f[k]->t[2] + offset; } } //add our verts mesh.setNumMapVerts (CurrentChannel,TVMaps.v.Count()+offset,TRUE); Point3 *tVerts = mesh.mapVerts(CurrentChannel); for ( k=0; k<TVMaps.v.Count(); k++) tVerts[k+offset] = GetPoint(t,k); } mesh.DeleteIsoMapVerts(CurrentChannel); }