void computeSurfaceQuadTree(CInteriorSurface &surface, CSurfaceQuadTree &quad) { uint i, j; CAABBox box; bool first = true; for (i=0; i<surface.Faces.size(); ++i) { for (j=0; j<3; ++j) { const CVector &v = surface.CollisionMeshBuild->Vertices[surface.CollisionMeshBuild->Faces[surface.Faces[i]].V[j]]; if (first) box.setCenter(v), first=false; else box.extend(v); } } quad.clear(); quad.init(4.0f, 6, box.getCenter(), std::max(box.getHalfSize().x, box.getHalfSize().y)); for (i=0; i<surface.Faces.size(); ++i) { for (j=0; j<3; ++j) { const CVector &v = surface.CollisionMeshBuild->Vertices[surface.CollisionMeshBuild->Faces[surface.Faces[i]].V[j]]; quad.addVertex(v); } } quad.compile(); }
// If the bbox has a null size, then mark it void void removeVoid() { if (!IsVoid && Box.getHalfSize() == CVector::Null) { IsVoid = true; } }
/////////////// // FUNCTIONS // /////////////// //----------------------------------------------- // initPACS : // Initialize PACS. //----------------------------------------------- void initPACS(const char* rbank, const char* gr, NLMISC::IProgressCallback &/* progress */) { // Check old PACS is well released. nlassertex(RB==0, ("RB should be Null before the init.")); nlassertex(GR==0, ("GR should be Null before the init.")); nlassertex(PACS==0, ("PACS should be Null before the init.")); if(rbank != 0 && gr != 0) { RB = NLPACS::URetrieverBank::createRetrieverBank(rbank, false); GR = NLPACS::UGlobalRetriever::createGlobalRetriever(gr, RB); if (GR) { CAABBox cbox = GR->getBBox(); uint gw = (uint)(cbox.getHalfSize().x*2.0 / RYZOM_ENTITY_SIZE_MAX) + 1; uint gh = (uint)(cbox.getHalfSize().y*2.0 / RYZOM_ENTITY_SIZE_MAX) + 1; PACS = UMoveContainer::createMoveContainer(GR, gw, gh, RYZOM_ENTITY_SIZE_MAX, 2); } else nlwarning("Could not create global retriever for %s, %s", rbank, gr); } // Try to create a PACS with another method. if(PACS == 0) PACS = UMoveContainer::createMoveContainer(15000.0, -25000.0, 20000.0, -20000.0, 16, 16, RYZOM_ENTITY_SIZE_MAX, 2); // Set the static world image. if(PACS) PACS->setAsStatic(staticWI); else nlwarning("initPACS: cannot create PACS at all."); }// initPACS //
void computeRetriever(CCollisionMeshBuild &cmb, CLocalRetriever &lr, CVector &translation, bool useCmbTrivialTranslation) { // set the retriever lr.setType(CLocalRetriever::Interior); // if should use the own cmb bbox, then compute it if (useCmbTrivialTranslation) { translation = cmb.computeTrivialTranslation(); // snap the translation vector to a meter wide grid translation.x = (float)ceil(translation.x); translation.y = (float)ceil(translation.y); translation.z = 0.0f; } uint i, j; for (i=0; i<cmb.Faces.size(); ++i) { CVector normal = ((cmb.Vertices[cmb.Faces[i].V[1]]-cmb.Vertices[cmb.Faces[i].V[0]])^(cmb.Vertices[cmb.Faces[i].V[2]]-cmb.Vertices[cmb.Faces[i].V[0]])).normed(); if (normal.z < 0.0f) { nlwarning("Face %d in cmb (%s) has negative normal! -- face is flipped", i, cmb.Faces[i].Surface == CCollisionFace::InteriorSurfaceFirst ? "interior" : "exterior"); /* std::swap(cmb.Faces[i].V[1], cmb.Faces[i].V[2]); std::swap(cmb.Faces[i].Visibility[1], cmb.Faces[i].Visibility[2]); */ } } // first link faces /* linkMesh(cmb, false); linkMesh(cmb, true); */ vector<string> errors; cmb.link(false, errors); cmb.link(true, errors); if (!errors.empty()) { nlwarning("Edge issues reported !!"); uint i; for (i=0; i<errors.size(); ++i) nlwarning("%s", errors[i].c_str()); nlerror("Can't continue."); } // translate the meshbuild to the local axis cmb.translate(translation); // find the exterior mesh border CExteriorMesh extMesh; buildExteriorMesh(cmb, extMesh); lr.setExteriorMesh(extMesh); // build the surfaces in the local retriever buildSurfaces(cmb, lr); // create the snapping faces and vertices // after the build surfaces because the InternalSurfaceId is filled within buildSurfaces()... buildSnapping(cmb, lr); // lr.computeLoopsAndTips(); lr.findBorderChains(); lr.updateChainIds(); lr.computeTopologies(); lr.unify(); lr.computeCollisionChainQuad(); /* // for (i=0; i<lr.getSurfaces().size(); ++i) lr.dumpSurface(i); */ // linkExteriorToInterior(lr); // compute the bbox of the retriever CAABBox bbox; bool first = true; for (i=0; i<extMesh.getEdges().size(); ++i) if (!first) bbox.extend(extMesh.getEdge(i).Start); else bbox.setCenter(extMesh.getEdge(i).Start), first=false; for (i=0; i<lr.getOrderedChains().size(); ++i) for (j=0; j<lr.getOrderedChain(i).getVertices().size(); ++j) if (!first) bbox.extend(lr.getOrderedChain(i)[j].unpack3f()); else bbox.setCenter(lr.getOrderedChain(i)[j].unpack3f()), first=false; CVector bboxhs = bbox.getHalfSize(); bboxhs.z = 10000.0f; bbox.setHalfSize(bboxhs); lr.setBBox(bbox); }
// *************************************************************************** void NLPACS::CZoneTessellation::build() { sint el; uint i, j; NL3D::CLandscape landscape; landscape.init(); vector<CVector> normals; vector<CVector> vectorCheck; bool useNoHmZones = true; { NL3D::CLandscape landscapeNoHm; landscapeNoHm.init(); // // load the 9 landscape zones // for (i=0; i<_ZoneIds.size(); ++i) { string filename = getZoneNameById(_ZoneIds[i])+ZoneExt; CIFile file(CPath::lookup(filename)); CZone zone; zone.serial(file); file.close(); if (Verbose) nlinfo("use zone %s %d", filename.c_str(), zone.getZoneId()); if (zone.getZoneId() != _ZoneIds[i]) { nlwarning ("Zone %s ID is wrong. Abort.", filename.c_str()); return; } landscape.addZone(zone); if (useNoHmZones) { string filenameNH = getZoneNameById(_ZoneIds[i])+ZoneNHExt; string loadZ = CPath::lookup(filenameNH, false, false); if (!loadZ.empty()) { CIFile fileNH(loadZ); CZone zoneNH; zoneNH.serial(fileNH); fileNH.close(); if (zoneNH.getZoneId() != _ZoneIds[i]) { nlwarning ("Zone %s ID is wrong. Abort.", filenameNH.c_str()); return; } landscapeNoHm.addZone(zoneNH); } else { useNoHmZones = false; } } _ZonePtrs.push_back(landscape.getZone(_ZoneIds[i])); } landscape.setNoiseMode(false); landscape.checkBinds(); if (useNoHmZones) { landscapeNoHm.setNoiseMode(false); landscapeNoHm.checkBinds(); } BestFittingBBox.setCenter(CVector::Null); BestFittingBBox.setHalfSize(CVector::Null); BestFittingBBoxSetuped= false; // Compute best fitting bbox for (i=0; i<_ZoneIds.size(); ++i) { if (_ZoneIds[i] == CentralZoneId) { if(_ZonePtrs[i]->getNumPatchs()>0) { BestFittingBBox = _ZonePtrs[i]->getZoneBB().getAABBox(); BestFittingBBoxSetuped= true; } } } CAABBox enlBBox = BestFittingBBox; enlBBox.setHalfSize(enlBBox.getHalfSize()+CVector(8.0f, 8.0f, 1000.0f)); // Add neighbor patch for (i=0; i<_ZoneIds.size(); ++i) { if (_ZoneIds[i] == CentralZoneId) { for (j=0; (sint)j<_ZonePtrs[i]->getNumPatchs(); ++j) { landscape.excludePatchFromRefineAll(_ZoneIds[i], j, false); if (useNoHmZones) landscapeNoHm.excludePatchFromRefineAll(_ZoneIds[i], j, false); } if (Verbose) nlinfo(" - selected %d/%d patches for zone %d", _ZonePtrs[i]->getNumPatchs(), _ZonePtrs[i]->getNumPatchs(), _ZoneIds[i]); } else { uint nump = 0; for (j=0; (sint)j<_ZonePtrs[i]->getNumPatchs(); ++j) { CAABBox pbox = _ZonePtrs[i]->getPatch(j)->buildBBox(); bool inters = enlBBox.intersect(pbox); if (inters) { landscape.excludePatchFromRefineAll(_ZoneIds[i], j, false); if (useNoHmZones) landscapeNoHm.excludePatchFromRefineAll(_ZoneIds[i], j, false); ++nump; } else { landscape.excludePatchFromRefineAll(_ZoneIds[i], j, true); if (useNoHmZones) landscapeNoHm.excludePatchFromRefineAll(_ZoneIds[i], j, true); } } if (Verbose) nlinfo(" - selected %d/%d patches for zone %d", nump, _ZonePtrs[i]->getNumPatchs(), _ZoneIds[i]); } } // tessellate the landscape, get the leaves (the tessellation faces), and convert them // into surf elements if (Verbose) nlinfo("Compute landscape tessellation"); if (Verbose) nlinfo(" - tessellate landscape"); if (useNoHmZones) { // Before tesselate, verify that the 2 landscape zones have at least the same binds! // Else there will be errors because of not the same tesselation checkSameLandscapeHmBinds(landscape, landscapeNoHm); // Tesselate landscapeNoHm.setThreshold(0.0f); landscapeNoHm.setTileMaxSubdivision(TessellateLevel); landscapeNoHm.refineAll(CVector::Null); landscapeNoHm.averageTesselationVertices(); // get the faces vector<const CTessFace *> leavesNoHm; landscapeNoHm.getTessellationLeaves(leavesNoHm); for (el=0; el<(sint)leavesNoHm.size(); ++el) { const CTessFace *face = leavesNoHm[el]; const CVector *v[3]; // get the vertices of the face v[0] = &(face->VBase->EndPos); v[1] = &(face->VLeft->EndPos); v[2] = &(face->VRight->EndPos); normals.push_back( ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed() ); vectorCheck.push_back(*(v[0])); vectorCheck.push_back(*(v[1])); vectorCheck.push_back(*(v[2])); } } } // Build the lanscape with heightmap landscape.setThreshold(0.0f); landscape.setTileMaxSubdivision(TessellateLevel); landscape.refineAll(CVector::Null); landscape.averageTesselationVertices(); vector<const CTessFace *> leaves; landscape.getTessellationLeaves(leaves); if (Verbose) { if (useNoHmZones) nlinfo(" - used no height map zones"); nlinfo(" - generated %d leaves", leaves.size()); } // If don't use NoHm zones, build normals and vectorCheck directly from std landscape if (!useNoHmZones) { for (el=0; el<(sint)leaves.size(); ++el) { const CTessFace *face = leaves[el]; const CVector *v[3]; // get the vertices of the face v[0] = &(face->VBase->EndPos); v[1] = &(face->VLeft->EndPos); v[2] = &(face->VRight->EndPos); normals.push_back( ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed() ); vectorCheck.push_back(*(v[0])); vectorCheck.push_back(*(v[1])); vectorCheck.push_back(*(v[2])); } } // check that there is the same number of faces from landscape with and without heightmap if (normals.size() != leaves.size()) { nlwarning ("ERROR : The heightmaped landscape has not the same number of polygon than the nonheightmaped landscape: %d/%d.", normals.size(), leaves.size()); exit (0); } // generate a vector of vertices and of surf element CHashMap<const CVector *, uint32, CHashPtr<const CVector> > vremap; CHashMap<const CVector *, uint32, CHashPtr<const CVector> >::iterator vremapit; CHashMap<const CTessFace *, CSurfElement *, CHashPtr<const CTessFace> > fremap; CHashMap<const CTessFace *, CSurfElement *, CHashPtr<const CTessFace> >::iterator fremapit; _Vertices.clear(); _Tessellation.resize(leaves.size()); if (Verbose) nlinfo(" - make and remap surface elements"); for (el=0; el<(sint)leaves.size(); ++el) fremap[leaves[el]] = &(_Tessellation[el]); uint check = 0; float dist, maxdist = 0.0f; for (el=0; el<(sint)leaves.size(); ++el) { const CTessFace *face = leaves[el]; const CVector *v[3]; CSurfElement &element = _Tessellation[el]; // setup zone id element.ZoneId = face->Patch->getZone()->getZoneId(); // get the vertices of the face v[0] = &(face->VBase->EndPos); v[1] = &(face->VLeft->EndPos); v[2] = &(face->VRight->EndPos); { CVector vcheck; vcheck = vectorCheck[check++] - *(v[0]); vcheck.z = 0; dist = vcheck.norm(); if (dist > maxdist) maxdist = dist; //nlassert(vcheck.norm() < 0.1f); vcheck = vectorCheck[check++] - *(v[1]); vcheck.z = 0; dist = vcheck.norm(); if (dist > maxdist) maxdist = dist; //nlassert(vcheck.norm() < 0.1f); vcheck = vectorCheck[check++] - *(v[2]); vcheck.z = 0; dist = vcheck.norm(); if (dist > maxdist) maxdist = dist; //nlassert(vcheck.norm() < 0.1f); } //element.Normal = ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed(); element.Normal = normals[el]; // search the vertices in the map for (i=0; i<3; ++i) { // if doesn't exist, create a new vertex if ((vremapit = vremap.find(v[i])) == vremap.end()) { element.Tri[i] = (uint32)_Vertices.size(); _Vertices.push_back(*(v[i])); vremap.insert(make_pair(v[i], element.Tri[i])); } // else use previous else { element.Tri[i] = vremapit->second; } } // setup the vertices pointer element.Vertices = &_Vertices; CTessFace *edge[3]; edge[0] = face->FBase; edge[1] = face->FRight; edge[2] = face->FLeft; for (i=0; i<3; ++i) { fremapit = fremap.find(edge[i]); element.EdgeLinks[i] = (fremapit != fremap.end() ? fremapit->second : NULL); } } for (el=0; el<(sint)_Tessellation.size(); ++el) { // add the element to the list of valid elements Elements.push_back(&(_Tessellation[el])); } landscape.clear(); }
void NLPACS::CZoneTessellation::compile() { sint el; uint i; CAABBox tbox = computeBBox(); bool HasInvertedUnderWater = false; // setup cliffs for (el=0; el<(sint)Elements.size(); ++el) { CSurfElement &element = *(Elements[el]); // a cliff ? if (element.Normal.z < 0.0) { CVector &v0 = _Vertices[element.Tri[0]], &v1 = _Vertices[element.Tri[1]], &v2 = _Vertices[element.Tri[2]]; uint8 bits0 = PrimChecker.get((uint)v0.x, (uint)v0.y); uint8 bits1 = PrimChecker.get((uint)v1.x, (uint)v1.y); uint8 bits2 = PrimChecker.get((uint)v2.x, (uint)v2.y); bool w0 = ((bits0&CPrimChecker::Water) != 0); bool w1 = ((bits1&CPrimChecker::Water) != 0); bool w2 = ((bits2&CPrimChecker::Water) != 0); if ((bits0 & CPrimChecker::Water)!=0 || (bits1 & CPrimChecker::Water)!=0 || (bits2 & CPrimChecker::Water)!=0) { uint ws = 0; uint16 ws0 = PrimChecker.index((uint)v0.x, (uint)v0.y); uint16 ws1 = PrimChecker.index((uint)v1.x, (uint)v1.y); uint16 ws2 = PrimChecker.index((uint)v2.x, (uint)v2.y); if ((w0 && w1 && ws0 == ws1) || (w0 && w2 && ws0 == ws2)) ws = ws0; else if (w1 && w2 && ws1 == ws2) ws = ws1; else if (w0) ws = ws0; else if (w1) ws = ws1; else if (w2) ws = ws2; float minz = std::min(_Vertices[element.Tri[0]].z, std::min(_Vertices[element.Tri[1]].z, _Vertices[element.Tri[2]].z)); bool exists; float wh = PrimChecker.waterHeight(ws, exists)+WaterThreshold; // if (minz <= wh) { CPolygon p(v0, v1, v2); PrimChecker.renderBits(p, CPrimChecker::Cliff); HasInvertedUnderWater = true; } } } } if (HasInvertedUnderWater) { nlwarning("zone '%s' has reversed landscape under water", (getZoneNameById((uint16)CentralZoneId)+ZoneExt).c_str()); } // compute elements features if (Verbose) nlinfo("compute elements quantas"); for (el=0; el<(sint)Elements.size(); ++el) { CSurfElement &element = *(Elements[el]); element.ElemId = el; element.computeQuantas(this); } if (ReduceSurfaces) { // optimizes the number of generated segments // it also smoothes a bit the surface border // it seems that 3 consecutive passes are optimal to reduce // nasty granularity if (Verbose) nlinfo("reduce surfaces"); uint i; sint p; for (i=0; i<3; ++i) { for (p=0; p<(sint)Elements.size(); ++p) { CSurfElement &e = *(Elements[p]); CSurfElement &e0 = *e.EdgeLinks[0], &e1 = *e.EdgeLinks[1], &e2 = *e.EdgeLinks[2]; if (e.IsMergable && &e0 != NULL && &e1 != NULL && &e2 != NULL && e.ZoneId == e0.ZoneId && e.ZoneId == e1.ZoneId && e.ZoneId == e2.ZoneId && !e.ForceInvalid) { // Strong optimization // merge the element quantas to the neighbors' quantas which are the most numerous // quantas are evaluated individually if (e0.IsValid && e1.IsValid) e.IsValid = true; if (e1.IsValid && e2.IsValid) e.IsValid = true; if (e0.IsValid && e2.IsValid) e.IsValid = true; if (e0.QuantHeight == e1.QuantHeight) e.QuantHeight = e0.QuantHeight; if (e1.QuantHeight == e2.QuantHeight) e.QuantHeight = e1.QuantHeight; if (e0.QuantHeight == e2.QuantHeight) e.QuantHeight = e2.QuantHeight; if (e0.IsValid && e1.IsValid && e0.WaterShape == e1.WaterShape && e0.IsUnderWater == e1.IsUnderWater) { e.WaterShape = e0.WaterShape; e.IsUnderWater = e0.IsUnderWater; } if (e1.IsValid && e2.IsValid && e1.WaterShape == e2.WaterShape && e1.IsUnderWater == e2.IsUnderWater) { e.WaterShape = e1.WaterShape; e.IsUnderWater = e1.IsUnderWater; } if (e0.IsValid && e2.IsValid && e0.WaterShape == e2.WaterShape && e0.IsUnderWater == e2.IsUnderWater) { e.WaterShape = e2.WaterShape; e.IsUnderWater = e2.IsUnderWater; } } } } for (p=0; p<(sint)Elements.size(); ++p) { CSurfElement &e = *(Elements[p]); CSurfElement &e0 = *e.EdgeLinks[0], &e1 = *e.EdgeLinks[1], &e2 = *e.EdgeLinks[2]; if (&e != NULL && &e0 != NULL && &e1 != NULL && &e2 != NULL && e.IsValid && e0.IsValid && e1.IsValid && e2.IsValid && !e.IsUnderWater && e0.IsUnderWater && e1.IsUnderWater && e2.IsUnderWater) { nlwarning("isolated submerged element '%d' !", p); } } } // translates vertices to the local axis sint64 vx, vy, vz, tx, ty, tz; tx = float2Fixed(Translation.x); ty = float2Fixed(Translation.y); tz = float2Fixed(Translation.z); uint p; for (i=0; i<_Vertices.size(); ++i) { vx = float2Fixed(_Vertices[i].x) + tx; vy = float2Fixed(_Vertices[i].y) + ty; vz = float2Fixed(_Vertices[i].z) + tz; _Vertices[i] = CVector(fixed2Float(vx), fixed2Float(vy), fixed2Float(vz)); } if(BestFittingBBoxSetuped) BestFittingBBox.setCenter(BestFittingBBox.getCenter()+Translation); // //if (false) { // // first pass of flood fill // allow detecting landscape irregularities // if (Verbose) nlinfo("build and flood fill surfaces -- pass 1"); uint32 surfId = 0; // + (ZoneId<<16); for (p=0; p<Elements.size(); ++p) { if (Elements[p]->SurfaceId == UnaffectedSurfaceId) { Surfaces.push_back(CComputableSurface()); CComputableSurface &surf = Surfaces.back(); surf.BorderKeeper = &Borders; surf.floodFill(Elements[p], surfId++, CSurfElemCompareSimple(), this); surf.BBox = BestFittingBBox; bool force = false; if (surf.Area < 30.0f && surf.Elements.size() > 0) { uint i; CAABBox aabbox; aabbox.setCenter((*surf.Elements[0]->Vertices)[surf.Elements[0]->Tri[0]]); for (i=0; i<surf.Elements.size(); ++i) { aabbox.extend((*surf.Elements[i]->Vertices)[surf.Elements[i]->Tri[0]]); aabbox.extend((*surf.Elements[i]->Vertices)[surf.Elements[i]->Tri[1]]); aabbox.extend((*surf.Elements[i]->Vertices)[surf.Elements[i]->Tri[2]]); } // swap all suface elements validity if (!surf.Elements[0]->ForceInvalid && aabbox.getHalfSize().z < 1.5f) { for (i=0; i<surf.Elements.size(); ++i) { surf.Elements[i]->IsValid = !surf.Elements[i]->IsValid; } if (Verbose) nlinfo("Reverted surface %d (%d elements, water=%d)", surfId-1, surf.Elements.size(), (surf.IsUnderWater ? 1 : 0)); } } } } Surfaces.clear(); ExtSurfaces.clear(); } vector<CSurfElement*> elDup; for (el=0; el<(sint)Elements.size(); ++el) if (Elements[el]->IsValid) elDup.push_back(Elements[el]); Elements = elDup; elDup.clear(); for (el=0; el<(sint)Elements.size(); ++el) { CSurfElement &element = *(Elements[el]); element.SurfaceId = UnaffectedSurfaceId; uint i; for (i=0; i<3; ++i) if (element.EdgeLinks[i] != NULL && !element.EdgeLinks[i]->IsValid) element.EdgeLinks[i] = NULL; } // { if (Verbose) nlinfo("build and flood fill surfaces"); uint32 surfId = 0; // + (ZoneId<<16); uint totalSurf = 0; sint32 extSurf = -1024; for (p=0; p<Elements.size(); ++p) { if (Elements[p]->SurfaceId == UnaffectedSurfaceId) { bool elInCentral = (Elements[p]->ZoneId == CentralZoneId); ++totalSurf; sint32 thisSurfId = (elInCentral) ? surfId++ : extSurf--; if (elInCentral) Surfaces.push_back(CComputableSurface()); else ExtSurfaces.push_back(CComputableSurface()); CComputableSurface &surf = (elInCentral) ? Surfaces.back() : ExtSurfaces.back(); surf.BorderKeeper = &Borders; surf.floodFill(Elements[p], thisSurfId, CSurfElemCompareNormal(), this); surf.BBox = BestFittingBBox; } } if (Verbose) { nlinfo("%d surfaces generated", totalSurf); for (p=0; p<Surfaces.size(); ++p) { nlinfo("surf %d: %d elements", p, Surfaces[p].Elements.size()); if (Surfaces[p].Elements.size() == 1) { nlinfo("elm: %d", Surfaces[p].Elements[0]->ElemId); } } } } // flag vertices that are pointed by more than 2 surfaces VerticesFlags.resize(_Vertices.size(), 0); for (p=0; p<Elements.size(); ++p) { CSurfElement *elem = Elements[p]; sint32 s = elem->SurfaceId; sint32 s0 = (elem->EdgeLinks[0] != NULL ? elem->EdgeLinks[0]->SurfaceId : UnaffectedSurfaceId); sint32 s1 = (elem->EdgeLinks[1] != NULL ? elem->EdgeLinks[1]->SurfaceId : UnaffectedSurfaceId); sint32 s2 = (elem->EdgeLinks[2] != NULL ? elem->EdgeLinks[2]->SurfaceId : UnaffectedSurfaceId); if (s != s0 && s != s1 && s0 != s1) { VerticesFlags[elem->Tri[2]] = 1; } if (s != s1 && s != s2 && s1 != s2) { VerticesFlags[elem->Tri[0]] = 1; } if (s != s2 && s != s0 && s2 != s0) { VerticesFlags[elem->Tri[1]] = 1; } } }
bool CExportNel::mirrorPhysiqueSelection(INode &node, TimeValue tvTime, const std::vector<uint> &vertIn, float threshold) { bool ok; uint i; // no vertices selected? if(vertIn.empty()) return true; // **** Get all the skeleton node std::vector<INode*> skeletonNodes; INode *skelRoot= getSkeletonRootBone(node); if(!skelRoot) return false; getObjectNodes(skeletonNodes, tvTime, skelRoot); // **** Build the Vector (world) part std::vector<CTempSkinVertex> tempVertex; uint vertCount; // Get a pointer on the object's node. ObjectState os = node.EvalWorldState(tvTime); Object *obj = os.obj; // Check if there is an object ok= false; if (obj) { // Object can be converted in triObject ? if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) { // Get a triobject from the node TriObject *tri = (TriObject*)obj->ConvertToType(tvTime, Class_ID(TRIOBJ_CLASS_ID, 0)); if (tri) { // Note that the TriObject should only be deleted // if the pointer to it is not equal to the object // pointer that called ConvertToType() bool deleteIt=false; if (obj != tri) deleteIt = true; // Get the node matrix. TODO: Matrix headhache? /*Matrix3 nodeMatrixMax; CMatrix nodeMatrix; getLocalMatrix (nodeMatrixMax, node, tvTime); convertMatrix (nodeMatrix, nodeMatrixMax);*/ // retrive Position geometry vertCount= tri->NumPoints(); tempVertex.resize(vertCount); for(uint i=0;i<vertCount;i++) { Point3 v= tri->GetPoint(i); tempVertex[i].Pos.set(v.x, v.y, v.z); } // Delete the triObject if we should... if (deleteIt) tri->MaybeAutoDelete(); tri = NULL; // ok! ok= true; } } } if(!ok) return false; // no vertices? abort if(vertCount==0) return true; // **** Mark all Input vertices for(i=0;i<vertIn.size();i++) { nlassert(vertIn[i]<vertCount); tempVertex[vertIn[i]].Input= true; } // **** Build the output vertices std::vector<uint> vertOut; vertOut.reserve(tempVertex.size()); // Build the in bbox CAABBox bbox; bbox.setCenter(tempVertex[vertIn[0]].Pos); for(i=0;i<vertIn.size();i++) { bbox.extend(tempVertex[vertIn[i]].Pos); } bbox.setHalfSize(bbox.getHalfSize()+CVector(threshold, threshold, threshold)); // mirror in X CVector vMin= bbox.getMin(); CVector vMax= bbox.getMax(); vMin.x= -vMin.x; vMax.x= -vMax.x; std::swap(vMin.x, vMax.x); bbox.setMinMax(vMin, vMax); // get all out vertices in the mirrored bbox. for(i=0;i<tempVertex.size();i++) { if(bbox.include(tempVertex[i].Pos)) { vertOut.push_back(i); } } // **** Build the skin information // Get the skin modifier Modifier* skin=getModifier (&node, PHYSIQUE_CLASS_ID); // Found it ? ok= false; if (skin) { // Get a com_skin2 interface IPhysiqueExport *physiqueInterface=(IPhysiqueExport *)skin->GetInterface (I_PHYINTERFACE); // Found com_skin2 ? if (physiqueInterface) { // Get local data IPhyContextExport *localData= physiqueInterface->GetContextInterface(&node); // Found ? if (localData) { // Use rigid export localData->ConvertToRigid (TRUE); // Allow blending localData->AllowBlending (TRUE); // Skinned ok=true; // TODO? nlassert(tempVertex.size()<=(uint)localData->GetNumberVertices()); // For each vertex for (uint vert=0; vert<vertCount; vert++) { // Get a vertex interface IPhyVertexExport *vertexInterface= localData->GetVertexInterface (vert); // Check if it is a rigid vertex or a blended vertex IPhyRigidVertex *rigidInterface=NULL; IPhyBlendedRigidVertex *blendedInterface=NULL; int type=vertexInterface->GetVertexType (); if (type==RIGID_TYPE) { // this is a rigid vertex rigidInterface=(IPhyRigidVertex*)vertexInterface; } else { // It must be a blendable vertex nlassert (type==RIGID_BLENDED_TYPE); blendedInterface=(IPhyBlendedRigidVertex*)vertexInterface; } // Get bones count for this vertex uint boneCount; if (blendedInterface) { // If blenvertex, only one bone boneCount=blendedInterface->GetNumberNodes(); } else { // If rigid vertex, only one bone boneCount=1; } if(boneCount>TEMP_MAX_WEIGHT) boneCount= TEMP_MAX_WEIGHT; // NB: if input 0, won't be mirrored tempVertex[vert].NumWeight= boneCount; for(uint bone=0;bone<boneCount;bone++) { if (blendedInterface) { tempVertex[vert].Bone[bone]= blendedInterface->GetNode(bone); nlassert(tempVertex[vert].Bone[bone]); tempVertex[vert].Weight[bone]= blendedInterface->GetWeight(bone); } else { tempVertex[vert].Bone[bone]= rigidInterface->GetNode(); tempVertex[vert].Weight[bone]= 1; } } // Release vertex interfaces localData->ReleaseVertexInterface (vertexInterface); } } // release context interface physiqueInterface->ReleaseContextInterface(localData); } // Release the interface skin->ReleaseInterface (I_PHYINTERFACE, physiqueInterface); } if(!ok) return false; // **** Real Algo stuff: // For all vertices wanted to be mirrored std::vector<CSortVertex> sortVert; sortVert.reserve(tempVertex.size()); for(i=0;i<vertIn.size();i++) { CTempSkinVertex &svIn= tempVertex[vertIn[i]]; // if it still has no bones set, skip if(svIn.NumWeight==0) continue; // mirror vert to test CVector vertTest= svIn.Pos; vertTest.x*= -1; // get the best vertex sortVert.clear(); // Search for all output vertices if ones match for(uint j=0;j<vertOut.size();j++) { uint dstIdx= vertOut[j]; nlassert(dstIdx<tempVertex.size()); CTempSkinVertex &skinv= tempVertex[dstIdx]; // take only if not an input, and if not already mirrored if(!skinv.Input && !skinv.Mirrored) { CSortVertex sortv; sortv.Index= dstIdx; sortv.SqrDist= (skinv.Pos - vertTest).sqrnorm(); // Finally, take it only if sufficiently near if(sortv.SqrDist <= threshold*threshold) sortVert.push_back(sortv); } } // if some found. if(!sortVert.empty()) { // sort array. std::sort(sortVert.begin(), sortVert.end()); // take the first, mirror setup uint dstIdx= sortVert[0].Index; tempVertex[dstIdx].NumWeight= svIn.NumWeight; for(uint k=0;k<svIn.NumWeight;k++) { tempVertex[dstIdx].Weight[k]= svIn.Weight[k]; tempVertex[dstIdx].Bone[k]= getMirrorBone( skeletonNodes, svIn.Bone[k] ); } // mark as mirrored! tempVertex[dstIdx].Mirrored= true; } } // **** Write the result to the skin. ok= false; if (skin) { // Get a com_skin2 interface IPhysiqueImport *physiqueInterface=(IPhysiqueImport *)skin->GetInterface (I_PHYIMPORT); // Found com_skin2 ? if (physiqueInterface) { // Get local data IPhyContextImport *localData= physiqueInterface->GetContextInterface(&node); // TODO? nlassert(tempVertex.size()<=(uint)localData->GetNumberVertices()); // Found ? if (localData) { // Skinned ok=true; for(uint i=0;i<tempVertex.size();i++) { CTempSkinVertex &sv= tempVertex[i]; // if its a mirrored output vertex if(sv.Mirrored) { IPhyBlendedRigidVertexImport *blendedInterface= NULL; blendedInterface= (IPhyBlendedRigidVertexImport*)localData->SetVertexInterface(i, RIGID_BLENDED_TYPE); if(blendedInterface) { // set the vertex data for(uint bone=0;bone<sv.NumWeight;bone++) { blendedInterface->SetWeightedNode(sv.Bone[bone], sv.Weight[bone], bone==0); } // UI bonus: lock it blendedInterface->LockVertex(TRUE); // release localData->ReleaseVertexInterface(blendedInterface); } } } } // release physiqueInterface->ReleaseContextInterface(localData); } // release skin->ReleaseInterface(I_PHYIMPORT, physiqueInterface); } return ok; }