// *************************************************************************** bool CVisualCollisionMesh::build(const std::vector<CVector> &vertices, const std::vector<uint32> &triangles, CVertexBuffer &vbForShadowRender) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ uint i; // if no vertices, or no triangles, abort if(vertices.empty()) return false; if(triangles.empty()) return false; // vertices and triangles id are stored in uint16 form. so their should not be more than 65535*3 indices if(vertices.size()>65535 || triangles.size()>65535*3) return false; // copy _Vertices= vertices; // compress indexes to 16 bits _Triangles.resize(triangles.size()); for(i=0;i<_Triangles.size();i++) _Triangles[i]= (uint16)triangles[i]; // Build the Local bbox for this col mesh CAABBox localBBox; localBBox.setCenter(vertices[0]); for(i=1;i<vertices.size();i++) localBBox.extend(vertices[i]); // Build the Static Grid uint numTris= (uint)triangles.size()/3; _QuadGrid.create(16, numTris, localBBox); // Add all triangles for(i=0;i<numTris;i++) { CAABBox bb; bb.setCenter(_Vertices[_Triangles[i*3+0]]); bb.extend(_Vertices[_Triangles[i*3+1]]); bb.extend(_Vertices[_Triangles[i*3+2]]); _QuadGrid.add(i, bb); } // compile _QuadGrid.compile(); // Keep a RefPtr on the AGP vertex Buffer for shadow receiving _VertexBuffer= &vbForShadowRender; return true; }
/* * render() */ void CPrimChecker::render(CPrimZone *zone, uint8 bits) { if (zone->VPoints.size() < 3) return; string name; if (zone->getPropertyByName("name", name) && Verbose) nlinfo("Rendering CPrimZone '%s'", name.c_str()); // get the bouding box of the CPrimZone CAABBox box; box.setCenter(zone->VPoints[0]); box.setHalfSize(CVector::Null); uint i; for (i=1; i<zone->VPoints.size(); ++i) box.extend(zone->VPoints[i]); sint32 xmin, ymin, xmax, ymax; xmin = (sint32)(floor(box.getMin().x)); ymin = (sint32)(floor(box.getMin().y)); xmax = (sint32)(ceil(box.getMax().x)); ymax = (sint32)(ceil(box.getMax().y)); // Fill grid with points that belong to the CPrimZone sint32 x, y; for (y=ymin; y<=ymax; ++y) for (x=xmin; x<=xmax; ++x) if (zone->contains(CVector((float)x, (float)y, 0.0f))) _Grid.set(x, y, bits); }
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(); }
CAABBox NLPACS::CSurfElement::getBBox() const { CAABBox box; box.setCenter((*Vertices)[Tri[0]]); box.extend((*Vertices)[Tri[1]]); box.extend((*Vertices)[Tri[2]]); return box; }
static CAABBox getSnappedBBox(CVector v0, CVector v1, CVector v2, const CAABBox &bbox) { snapAccuracyBit(v0); snapAccuracyBit(v1); snapAccuracyBit(v2); CAABBox box; box.setCenter(v0); box.extend(v1); box.extend(v2); return box; }
// *************************************************************************** NLMISC::CAABBox CVisualCollisionMesh::computeWorldBBox(const CMatrix &instanceMatrix) { CAABBox ret; if(!_Vertices.empty()) { ret.setCenter(instanceMatrix*_Vertices[0]); for(uint i=1;i<_Vertices.size();i++) { ret.extend(instanceMatrix*_Vertices[i]); } } return ret; }
CAABBox NLPACS::CZoneTessellation::computeBBox() const { CAABBox zbox; bool set = false; uint i; if (_Vertices.size() == 0) return zbox; zbox.setCenter(_Vertices[0]); for (i=1; i<_Vertices.size(); ++i) zbox.extend(_Vertices[i]); return zbox; }
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); }
int main(int argc, char **argv) { // Filter addSearchPath NLMISC::createDebug(); InfoLog->addNegativeFilter("adding the path"); createDebug(); try { // Init init(); uint i, j, k; for (i=0; i<IGs.size(); ++i) { // load ig associated to the zone string igName = IGs[i]+".ig"; CIFile igStream(CPath::lookup(igName)); CInstanceGroup ig; igStream.serial(ig); CAABBox igBBox; bool boxSet = false; nlinfo("Generating BBOX for %s", igName.c_str()); // search in group for water instance for (j=0; j<ig._InstancesInfos.size(); ++j) { /* Ben: c'est degueulasse, mais c'est les coders a la 3D, y savent pas coder Hld: ouai, mais ca marche pas ton truc, alors p'tet qu'on sait pas coder mais toi non plus :p Special Dedicace to SupaGreg! string shapeName = ig._InstancesInfos[j].Name+".shape"; */ string shapeName = ig._InstancesInfos[j].Name; if (CFile::getExtension (shapeName) == "") shapeName += ".shape"; if (NonWaterShapes.find(shapeName) != NonWaterShapes.end()) continue; string shapeNameLookup = CPath::lookup (shapeName, false, false); if (!shapeNameLookup.empty()) { CIFile f; if (f.open (shapeNameLookup)) { CShapeStream shape; shape.serial(f); CWaterShape *wshape = dynamic_cast<CWaterShape *>(shape.getShapePointer()); if (wshape == NULL) { NonWaterShapes.insert(shapeName); continue; } CMatrix matrix; ig.getInstanceMatrix(j, matrix); CPolygon wpoly; wshape->getShapeInWorldSpace(wpoly); for (k=0; k<wpoly.Vertices.size(); ++k) { if (boxSet) { igBBox.extend(matrix * wpoly.Vertices[k]); } else { igBBox.setCenter(matrix * wpoly.Vertices[k]); boxSet = true; } } } else { nlwarning ("Can't load shape %s", shapeNameLookup.c_str()); } } else { NonWaterShapes.insert(shapeName); } } if (boxSet) { Boxes.push_back(CIGBox(igName, igBBox)); nlinfo("Bbox: (%.1f,%.1f)-(%.1f,%.1f)", igBBox.getMin().x, igBBox.getMin().y, igBBox.getMax().x, igBBox.getMax().y); } } COFile output(Output); output.serialCont(Boxes); } catch (Exception &e) { fprintf (stderr,"main trapped an exception: '%s'\n", e.what ()); } #ifndef NL_DEBUG catch (...) { fprintf(stderr,"main trapped an unknown exception\n"); } #endif // NL_DEBUG return 0; }
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; } } }
void UpdatePrimitives () { // Get the tools window CMainFrame *mainWnd = getMainFrame (); if (mainWnd) { CToolsLogic *toolWnd = dynamic_cast<CToolsLogic*>(getMainFrame ()->m_wndSplitter.GetPane(0,1)); // Sort the list static vector<CDatabaseLocatorPointer> toSort; toSort.clear (); CWorldEditorDoc *doc = getDocument (); std::list<NLLIGO::IPrimitive*>::iterator ite = ModifiedPrimitive.begin (); while (ite != ModifiedPrimitive.end ()) { CDatabaseLocatorPointer locator; doc->getLocator (locator, *ite); toSort.push_back (locator); ite++; } sort (toSort.begin (), toSort.end ()); // For each modified primitive sint i; sint count = (sint)toSort.size (); for (i=count-1; i>=0; i--) { CDatabaseLocatorPointer locator; doc->getLocator (locator, toSort[i].Primitive); IPrimitiveEditor *primitiveEditor = getPrimitiveEditor (const_cast<IPrimitive*>(toSort[i].Primitive)); // Logic tree structure modified ? if (primitiveEditor->_Channels & LogicTreeStruct) { // Remove from the tree primitiveEditor->removeFromLogicTree (); } } // Selection is changed ? bool selectionChanged = false; // For each modified primitive for (i=0; i<count; i++) { const IPrimitive *primitive = toSort[i].Primitive; CDatabaseLocatorPointer locator; doc->getLocator (locator, primitive); IPrimitiveEditor *primitiveEditor = getPrimitiveEditor (const_cast<IPrimitive*>(primitive)); // Quad grid ? if (primitiveEditor->_Channels & QuadTree) { // Remove from the container primitiveEditor->removeFromQuadGrid (); // Num points uint pointCount = (primitive)->getNumVector (); if (pointCount > 0) { // Point pointer const CPrimVector *points = (primitive)->getPrimVector (); // BBox CAABBox bbox; bbox.setCenter (points[0]); // Extend the bbox uint j; for (j=1; j<pointCount; j++) { bbox.extend (points[j]); } // Insert in the quadtree primitiveEditor->_QuadIterator = PrimitiveQuadGrid.insert (bbox.getMin (), bbox.getMax (), const_cast<IPrimitive*> (primitive)); // Get the linked primitives const IPrimitive* linkedPrimitive = theApp.Config.getLinkedPrimitive (*primitive); // Is this primitive linked with another one ? if (linkedPrimitive) { IPrimitiveEditor *primitiveEditorLinked = getPrimitiveEditor (const_cast<IPrimitive*>(linkedPrimitive)); if (linkedPrimitive->getNumVector () > 0) { bbox.setCenter (points[0]); bbox.extend (linkedPrimitive->getPrimVector ()[0]); // Insert in the quadtree primitiveEditor->_QuadIteratorLink = PrimitiveQuadGrid.insert (bbox.getMin (), bbox.getMax (), CQuadGridEntry (const_cast<IPrimitive*> (primitive), const_cast<IPrimitive*> (linkedPrimitive))); } } } // Validate primitiveEditor->_Channels &= ~QuadTree; } // Logic tree structure ? if (primitiveEditor->_Channels & LogicTreeStruct) { // Add the primitive AddPrimitivesLogicTree (locator, primitiveEditor, toolWnd); // The flag is validated by AddPrimitivesLogicTree } // Logic tree parameters ? if (primitiveEditor->_Channels & LogicTreeParam) { // Update tree item parameters if (toolWnd) toolWnd->updatePrimitive (primitiveEditor->_TreeItem, locator); // Validate primitiveEditor->_Channels &= ~LogicTreeParam; } // Selection ? if (primitiveEditor->_Channels & _SelectionSelectState) { // Update the selection UpdatePrimitiveSelection (primitiveEditor, locator, selectionChanged); // Validate primitiveEditor->_Channels &= ~_SelectionSelectState; } // Remove from the modified list nlassert (primitiveEditor->_Channels == 0); ModifiedPrimitive.erase (primitiveEditor->_ModifiedIterator); primitiveEditor->_ModifiedIterator = ModifiedPrimitive.end (); } /*// Change dialog selection ? if (selectionChanged) { if ( pDlg ) pDlg->changeSelection (Selection); } */ nlassert (ModifiedPrimitive.size ()==0); } }
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; }