//========================================================================== void CAABBox::computeIntersection(const CAABBox &b1, const CAABBox &b2) { CVector min1 = b1.getMin(), max1 = b1.getMax(), min2 = b2.getMin(), max2 = b2.getMax(); CVector minr, maxr; // don't test if intersect or not. maxr.minof(max1, max2); minr.maxof(min1, min2); setMinMax(minr, maxr); }
CAABBox CAABBox::computeAABBoxUnion(const CAABBox &b1, const CAABBox &b2) { CAABBox result; CVector min, max; CVector min1 = b1.getMin() ,max1 = b1.getMax() ,min2 = b2.getMin() ,max2 = b2.getMax(); max.maxof(max1, max2); min.minof(min1, min2); result.setMinMax(min, max); return result; }
/* * 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); }
//========================================================================== CAABBox CAABBox::transformAABBox(const CMatrix &mat, const CAABBox &box) { // TODO : optimize this a bit if possible... CAABBox result; /* OMG. Old code was false!! if we have ht= M * h then CVector(-ht.x, ht.y, ht.z) != M * CVector(-h.x, h.y, h.z) !!!! */ // compute corners. CVector p[8]; CVector min= box.getMin(); CVector max= box.getMax(); p[0].set(min.x, min.y, min.z); p[1].set(max.x, min.y, min.z); p[2].set(min.x, max.y, min.z); p[3].set(max.x, max.y, min.z); p[4].set(min.x, min.y, max.z); p[5].set(max.x, min.y, max.z); p[6].set(min.x, max.y, max.z); p[7].set(max.x, max.y, max.z); CVector tmp; min = max = mat * p[0]; // transform corners. for(uint i=1;i<8;i++) { tmp= mat * p[i]; min.minof(min, tmp); max.maxof(max, tmp); } result.setMinMax(min, max); return result; }
// *************************************************************************** bool CAABBox::intersect(const CAABBox &box) const { CVector mina = getMin(), maxa = getMax(), minb = box.getMin(), maxb = box.getMax(); return ! ( mina.x > maxb.x || mina.y > maxb.y || mina.z > maxb.z || minb.x > maxa.x || minb.y > maxa.y || minb.z > maxa.z); }
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; }
/** Check a zone and report the total number of errors */ static uint CheckZone(std::string middleZoneFile, float weldThreshold, float middleEdgeWeldThreshold) { uint numErrors = 0; uint k, l, m, n, p, q; // some loop counters // This avoid reporting errors twice (for readability) std::set<CPatchIdentPair> errorPairs; //////////////////////////// // Load the zones around // //////////////////////////// std::auto_ptr<CZone> zones[9]; std::string zoneNames[9]; CZoneInfo zoneInfos[9]; uint16 xPos, yPos; const sint16 posOffs[][2] = { {0, 0}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1} }; std::string middleZoneName = CFile::getFilenameWithoutExtension(middleZoneFile); ::getZoneCoordByName(middleZoneName.c_str(), xPos, yPos); try { std::string ext = CFile::getExtension(middleZoneFile); zones[0].reset(::LoadZone(xPos, yPos, ext.empty() ? "" : "." + ext)); if (zones[0].get() == NULL) { nlwarning("Can't load zone %s", middleZoneName.c_str()); return 0; } for (uint k = 1; k < 9; ++k) { zones[k].reset(::LoadZone(xPos + posOffs[k][0], yPos + posOffs[k][1], ext.empty() ? "" : "." + ext)); } } catch (NLMISC::Exception &e) { nlinfo("Zones loading failed : %d", e.what()); return 0; } /////////////////////////////// // retrieve datas from zones // /////////////////////////////// for (k = 0; k < 9; ++k) { ::getZoneNameByCoord(xPos + posOffs[k][0], yPos + posOffs[k][1], zoneNames[k]); if (zones[k].get() != NULL) zones[k]->retrieve(zoneInfos[k]); } // fill the quad grid CAABBox zoneBBox = zones[0]->getZoneBB().getAABBox(); float zoneSize = 2.f * weldThreshold + std::max(zoneBBox.getMax().x - zoneBBox.getMin().x, zoneBBox.getMax().y - zoneBBox.getMin().y); TPVQuadGrid qg; const uint numQGElt = 128; qg.create(numQGElt, zoneSize / numQGElt); // insert vertices in quadgrid for (k = 0; k < 9; ++k) { for (l = 0; l < zoneInfos[k].Patchs.size(); ++l) { CPatchInfo &patch = zoneInfos[k].Patchs[l]; // for each base vertex of the patch for (m = 0; m < 4; ++m) { CVector &pos = patch.Patch.Vertices[m]; CBSphere s(pos, weldThreshold); if (zoneBBox.intersect(s)) // does this vertex and its zone of influence intersect the bbox ? { CVector half(weldThreshold, weldThreshold, weldThreshold); // yes, insert it in the tree qg.insert(pos - half, pos + half, CPatchVertexInfo(k, l, m, pos)); } } } } //////////////////////////////////////////////// // check wether each patch is correctly bound // //////////////////////////////////////////////// for (l = 0; l < zoneInfos[0].Patchs.size(); ++l) { CPatchInfo &patch = zoneInfos[0].Patchs[l]; // deals with each border for (m = 0; m < 4; ++m) { // if this border is said to be bound, no need to test.. if (patch.BindEdges[m].NPatchs == 0) { // maps from an index to a (s, t) couple static const float indexToST[][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // index of this border vertices const uint vIndex[] = { m, (m + 1) & 0x03 }; bool errorFound = false; static TPVVect verts[2]; // Get vertices from other patch that could be welded with this patch boder's vertices. for (q = 0; q < 2; ++q) { //nlinfo("pos = %f, %f, %f", patch.Patch.Vertices[vIndex[q]].x, patch.Patch.Vertices[vIndex[q]].y, patch.Patch.Vertices[vIndex[q]].z); ::GetCandidateVertices(patch.Patch.Vertices[vIndex[q]], qg, verts[q], l, 0, weldThreshold); } /////////////////////////// // 1 - 1 connectivity ? // /////////////////////////// // If there is a patch that is present in the 2 lists, then this is a 1-1 error for (n = 0; n < verts[0].size() && !errorFound; ++n) { for (p = 0; p < verts[1].size() && !errorFound; ++p) { if (verts[0][n]->ZoneIndex == verts[1][p]->ZoneIndex && verts[0][n]->PatchIndex == verts[1][p]->PatchIndex) { CPatchIdent pi1(0, l); CPatchIdent pi2(verts[0][n]->ZoneIndex, verts[0][n]->PatchIndex); CPatchIdentPair errPair = std::make_pair(pi1, pi2); // if (std::find(errorPairs.begin(), errorPairs.end(), errPair) == errorPairs.end()) // error already displayed ? { nlinfo("**** Patch %d of zone %s has 1 - 1 connectivity error, try binding it with patch %d of zone %s", l + 1, middleZoneName.c_str(), verts[0][n]->PatchIndex + 1, zoneNames[verts[0][n]->ZoneIndex].c_str()); errorPairs.insert(std::make_pair(pi2, pi1)); ++numErrors; } errorFound = true; } } } if (errorFound) continue; ////////////////////////// // 1 - 2 connectivity ? // ////////////////////////// // get the position at the middle of that border CVector middlePos = patch.Patch.eval( 0.5f * (indexToST[vIndex[0]][0] + indexToST[vIndex[1]][0]), 0.5f * (indexToST[vIndex[0]][1] + indexToST[vIndex[1]][1]) ); // for each vertex of this border for (q = 0; q < 2 && !errorFound; ++q) { for (n = 0; n < verts[q].size() && !errorFound; ++n) { const CPatchVertexInfo &pv = *(verts[q][n]); // ref to the patch that share a vertex with this one const CBezierPatch &bPatch = zoneInfos[pv.ZoneIndex].Patchs[pv.PatchIndex].Patch; sint vertIndex = ::GetWeldableVertex(bPatch, pv.Pos, weldThreshold); nlassert(vertIndex != -1); // should found one.. // Follow this patch edge and see if the next / previous vertex could be welded with the middle const CVector &nextVertPos = bPatch.Vertices[(vertIndex + 1) & 0x03]; const CVector &prevVertPos = bPatch.Vertices[(vertIndex - 1) & 0x03]; if (::CanWeld(nextVertPos, middlePos, middleEdgeWeldThreshold) || ::CanWeld(prevVertPos, middlePos, middleEdgeWeldThreshold) ) { CPatchIdent pi1(0, l); CPatchIdent pi2(pv.ZoneIndex, pv.PatchIndex); CPatchIdentPair errPair = std::make_pair(pi1, pi2); // if (std::find(errorPairs.begin(), errorPairs.end(), errPair) == errorPairs.end()) // error already displayed ? { nlinfo("**** Patch %d of zone %s has 1 - 2 connectivity error, try binding it with patch %d of zone %s", l + 1, middleZoneName.c_str(), pv.PatchIndex + 1, zoneNames[pv.ZoneIndex].c_str()); errorPairs.insert(std::make_pair(pi2, pi1)); ++numErrors; } errorFound = true; break; } } } if (errorFound) continue; ////////////////////////// // 1 - 4 connectivity ? // ////////////////////////// // compute points along the border. CVector borderPos[5]; float lambda = 0.f; for (n = 0; n < 5; ++n) { borderPos[n] = patch.Patch.eval((1.f - lambda) * indexToST[vIndex[0]][0] + lambda * indexToST[vIndex[1]][0], (1.f - lambda) * indexToST[vIndex[0]][1] + lambda * indexToST[vIndex[1]][1]); lambda += 0.25f; } // Try to find a patch that shares 2 consecutives vertices for (k = 0; k < 4 && !errorFound; ++k) { ::GetCandidateVertices(borderPos[k], qg, verts[0], l, 0, middleEdgeWeldThreshold); for (p = 0; p < verts[0].size() && !errorFound; ++p) { const CPatchVertexInfo &pv = *(verts[0][p]); // ref to the patch that share a vertex with this one const CBezierPatch &bPatch = zoneInfos[pv.ZoneIndex].Patchs[pv.PatchIndex].Patch; sint vertIndex = ::GetWeldableVertex(bPatch, pv.Pos, weldThreshold); nlassert(vertIndex != -1); // should found one.. // Follow this patch edge and see if the next/ previous vertex could be welded with the next point const CVector &nextVertPos = bPatch.Vertices[(vertIndex + 1) & 0x03]; const CVector &prevVertPos = bPatch.Vertices[(vertIndex - 1) & 0x03]; if (::CanWeld(nextVertPos, borderPos[k + 1], middleEdgeWeldThreshold) || ::CanWeld(prevVertPos, borderPos[k + 1], middleEdgeWeldThreshold) ) { CPatchIdent pi1(0, l); CPatchIdent pi2(pv.ZoneIndex, pv.PatchIndex); CPatchIdentPair errPair = std::make_pair(pi1, pi2); // if (std::find(errorPairs.begin(), errorPairs.end(), errPair) == errorPairs.end()) // error already displayed ? { nlinfo("**** Patch %d of zone %s has 1 - 4 connectivity error, try binding it with patch %d of zone %s", l + 1, middleZoneName.c_str(), pv.PatchIndex + 1, zoneNames[pv.ZoneIndex].c_str()); ++numErrors; errorPairs.insert(std::make_pair(pi2, pi1)); } errorFound = true; } } } } } } //////////////////////////////// //////////////////////////////// if (numErrors != 0) { nlinfo("%d errors found", numErrors); } return numErrors; }
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; }