bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh) { rcAssert(ctx); ctx->startTimer(RC_TIMER_BUILD_POLYMESH); rcVcopy(mesh.bmin, cset.bmin); rcVcopy(mesh.bmax, cset.bmax); mesh.cs = cset.cs; mesh.ch = cset.ch; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.nconts; ++i) { // Skip null contours. if (cset.conts[i].nverts < 3) continue; maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts); } if (maxVertices >= 0xfffe) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices); return false; } rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP); if (!vflags) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); return false; } memset(vflags, 0, maxVertices); mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM); if (!mesh.verts) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); return false; } mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2*2, RC_ALLOC_PERM); if (!mesh.polys) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2); return false; } mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM); if (!mesh.regs) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris); return false; } mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM); if (!mesh.areas) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris); return false; } mesh.nverts = 0; mesh.npolys = 0; mesh.nvp = nvp; mesh.maxpolys = maxTris; memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3); memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2); memset(mesh.regs, 0, sizeof(unsigned short)*maxTris); memset(mesh.areas, 0, sizeof(unsigned char)*maxTris); rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP); if (!nextVert) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); return false; } memset(nextVert, 0, sizeof(int)*maxVertices); rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); if (!firstVert) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); return false; } for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP); if (!indices) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); return false; } rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP); if (!tris) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); return false; } rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP); if (!polys) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); return false; } unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp]; for (int i = 0; i < cset.nconts; ++i) { rcContour& cont = cset.conts[i]; // Skip null contours. if (cont.nverts < 3) continue; // Triangulate contour for (int j = 0; j < cont.nverts; ++j) indices[j] = j; int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]); if (ntris <= 0) { // Bad triangulation, should not happen. /* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]); printf("\tconst float cs = %ff;\n", cset.cs); printf("\tconst float ch = %ff;\n", cset.ch); printf("\tconst int verts[] = {\n"); for (int k = 0; k < cont.nverts; ++k) { const int* v = &cont.verts[k*4]; printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]); } printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/ ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i); ntris = -ntris; } // Add and merge vertices. for (int j = 0; j < cont.nverts; ++j) { const int* v = &cont.verts[j*4]; indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2], mesh.verts, firstVert, nextVert, mesh.nverts); if (v[3] & RC_BORDER_VERTEX) { // This vertex should be removed. vflags[indices[j]] = 1; } } // Build initial polygons. int npolys = 0; memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short)); for (int j = 0; j < ntris; ++j) { int* t = &tris[j*3]; if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) { polys[npolys*nvp+0] = (unsigned short)indices[t[0]]; polys[npolys*nvp+1] = (unsigned short)indices[t[1]]; polys[npolys*nvp+2] = (unsigned short)indices[t[2]]; npolys++; } } if (!npolys) continue; // Merge polygons. if (nvp > 3) { for(;;) { // Find best polygons to merge. int bestMergeVal = 0; int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; for (int j = 0; j < npolys-1; ++j) { unsigned short* pj = &polys[j*nvp]; for (int k = j+1; k < npolys; ++k) { unsigned short* pk = &polys[k*nvp]; int ea, eb; int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); if (v > bestMergeVal) { bestMergeVal = v; bestPa = j; bestPb = k; bestEa = ea; bestEb = eb; } } } if (bestMergeVal > 0) { // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); npolys--; } else { // Could not merge any polygons, stop. break; } } } // Store polygons. for (int j = 0; j < npolys; ++j) { unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; unsigned short* q = &polys[j*nvp]; for (int k = 0; k < nvp; ++k) p[k] = q[k]; mesh.regs[mesh.npolys] = cont.reg; mesh.areas[mesh.npolys] = cont.area; mesh.npolys++; if (mesh.npolys > maxTris) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris); return false; } } } // Remove edge vertices. for (int i = 0; i < mesh.nverts; ++i) { if (vflags[i]) { if (!canRemoveVertex(ctx, mesh, (unsigned short)i)) continue; if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris)) { // Failed to remove vertex ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i); return false; } // Remove vertex // Note: mesh.nverts is already decremented inside removeVertex()! for (int j = i; j < mesh.nverts; ++j) vflags[j] = vflags[j+1]; --i; } } // Calculate adjacency. if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp)) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); return false; } // Just allocate the mesh flags array. The user is resposible to fill it. mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM); if (!mesh.flags) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys); return false; } memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys); if (mesh.nverts > 0xffff) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); } if (mesh.npolys > 0xffff) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); } ctx->stopTimer(RC_TIMER_BUILD_POLYMESH); return true; }
bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) { rcAssert(ctx); if (!nmeshes || !meshes) return true; ctx->startTimer(RC_TIMER_MERGE_POLYMESH); mesh.nvp = meshes[0]->nvp; mesh.cs = meshes[0]->cs; mesh.ch = meshes[0]->ch; rcVcopy(mesh.bmin, meshes[0]->bmin); rcVcopy(mesh.bmax, meshes[0]->bmax); int maxVerts = 0; int maxPolys = 0; int maxVertsPerMesh = 0; for (int i = 0; i < nmeshes; ++i) { rcVmin(mesh.bmin, meshes[i]->bmin); rcVmax(mesh.bmax, meshes[i]->bmax); maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts); maxVerts += meshes[i]->nverts; maxPolys += meshes[i]->npolys; } mesh.nverts = 0; mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM); if (!mesh.verts) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3); return false; } mesh.npolys = 0; mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM); if (!mesh.polys) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp); return false; } memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp); mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); if (!mesh.regs) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys); return false; } memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys); mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM); if (!mesh.areas) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys); return false; } memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys); mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); if (!mesh.flags) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys); return false; } memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys); rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP); if (!nextVert) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); return false; } memset(nextVert, 0, sizeof(int)*maxVerts); rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); if (!firstVert) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); return false; } for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM); if (!vremap) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); return false; } memset(nextVert, 0, sizeof(int)*maxVerts); for (int i = 0; i < nmeshes; ++i) { const rcPolyMesh* pmesh = meshes[i]; const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f); const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f); for (int j = 0; j < pmesh->nverts; ++j) { unsigned short* v = &pmesh->verts[j*3]; vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz, mesh.verts, firstVert, nextVert, mesh.nverts); } for (int j = 0; j < pmesh->npolys; ++j) { unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp]; unsigned short* src = &pmesh->polys[j*2*mesh.nvp]; mesh.regs[mesh.npolys] = pmesh->regs[j]; mesh.areas[mesh.npolys] = pmesh->areas[j]; mesh.flags[mesh.npolys] = pmesh->flags[j]; mesh.npolys++; for (int k = 0; k < mesh.nvp; ++k) { if (src[k] == RC_MESH_NULL_IDX) break; tgt[k] = vremap[src[k]]; } } } // Calculate adjacency. if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp)) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); return false; } if (mesh.nverts > 0xffff) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); } if (mesh.npolys > 0xffff) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); } ctx->stopTimer(RC_TIMER_MERGE_POLYMESH); return true; }
int recast_buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly) { return (int) buildMeshAdjacency(polys, npolys, nverts, vertsPerPoly); }
bool KX_NavMeshObject::BuildNavMesh() { if (m_navMesh) { delete m_navMesh; m_navMesh = NULL; } if (GetMeshCount()==0) { printf("Can't find mesh for navmesh object: %s\n", m_name.ReadPtr()); return false; } float *vertices = NULL, *dvertices = NULL; unsigned short *polys = NULL, *dtris = NULL, *dmeshes = NULL; int nverts = 0, npolys = 0, ndvertsuniq = 0, ndtris = 0; int vertsPerPoly = 0; if (!BuildVertIndArrays(vertices, nverts, polys, npolys, dmeshes, dvertices, ndvertsuniq, dtris, ndtris, vertsPerPoly ) || vertsPerPoly<3) { printf("Can't build navigation mesh data for object:%s\n", m_name.ReadPtr()); if (vertices) delete[] vertices; return false; } MT_Point3 pos; if (dmeshes==NULL) { for (int i=0; i<nverts; i++) { flipAxes(&vertices[i*3]); } for (int i=0; i<ndvertsuniq; i++) { flipAxes(&dvertices[i*3]); } } buildMeshAdjacency(polys, npolys, nverts, vertsPerPoly); float cs = 0.2f; if (!nverts || !npolys) { if (vertices) delete[] vertices; return false; } float bmin[3], bmax[3]; calcMeshBounds(vertices, nverts, bmin, bmax); //quantize vertex pos unsigned short* vertsi = new unsigned short[3*nverts]; float ics = 1.f/cs; for (int i=0; i<nverts; i++) { vertsi[3*i+0] = static_cast<unsigned short>((vertices[3*i+0]-bmin[0])*ics); vertsi[3*i+1] = static_cast<unsigned short>((vertices[3*i+1]-bmin[1])*ics); vertsi[3*i+2] = static_cast<unsigned short>((vertices[3*i+2]-bmin[2])*ics); } // Calculate data size const int headerSize = sizeof(dtStatNavMeshHeader); const int vertsSize = sizeof(float)*3*nverts; const int polysSize = sizeof(dtStatPoly)*npolys; const int nodesSize = sizeof(dtStatBVNode)*npolys*2; const int detailMeshesSize = sizeof(dtStatPolyDetail)*npolys; const int detailVertsSize = sizeof(float)*3*ndvertsuniq; const int detailTrisSize = sizeof(unsigned char)*4*ndtris; const int dataSize = headerSize + vertsSize + polysSize + nodesSize + detailMeshesSize + detailVertsSize + detailTrisSize; unsigned char* data = new unsigned char[dataSize]; if (!data) return false; memset(data, 0, dataSize); unsigned char* d = data; dtStatNavMeshHeader* header = (dtStatNavMeshHeader*)d; d += headerSize; float* navVerts = (float*)d; d += vertsSize; dtStatPoly* navPolys = (dtStatPoly*)d; d += polysSize; dtStatBVNode* navNodes = (dtStatBVNode*)d; d += nodesSize; dtStatPolyDetail* navDMeshes = (dtStatPolyDetail*)d; d += detailMeshesSize; float* navDVerts = (float*)d; d += detailVertsSize; unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize; // Store header header->magic = DT_STAT_NAVMESH_MAGIC; header->version = DT_STAT_NAVMESH_VERSION; header->npolys = npolys; header->nverts = nverts; header->cs = cs; header->bmin[0] = bmin[0]; header->bmin[1] = bmin[1]; header->bmin[2] = bmin[2]; header->bmax[0] = bmax[0]; header->bmax[1] = bmax[1]; header->bmax[2] = bmax[2]; header->ndmeshes = npolys; header->ndverts = ndvertsuniq; header->ndtris = ndtris; // Store vertices for (int i = 0; i < nverts; ++i) { const unsigned short* iv = &vertsi[i*3]; float* v = &navVerts[i*3]; v[0] = bmin[0] + iv[0] * cs; v[1] = bmin[1] + iv[1] * cs; v[2] = bmin[2] + iv[2] * cs; } //memcpy(navVerts, vertices, nverts*3*sizeof(float)); // Store polygons const unsigned short* src = polys; for (int i = 0; i < npolys; ++i) { dtStatPoly* p = &navPolys[i]; p->nv = 0; for (int j = 0; j < vertsPerPoly; ++j) { if (src[j] == 0xffff) break; p->v[j] = src[j]; p->n[j] = src[vertsPerPoly+j]+1; p->nv++; } src += vertsPerPoly*2; } header->nnodes = createBVTree(vertsi, nverts, polys, npolys, vertsPerPoly, cs, cs, npolys*2, navNodes); if (dmeshes==NULL) { //create fake detail meshes for (int i = 0; i < npolys; ++i) { dtStatPolyDetail& dtl = navDMeshes[i]; dtl.vbase = 0; dtl.nverts = 0; dtl.tbase = i; dtl.ntris = 1; } // setup triangles. unsigned char* tri = navDTris; for (size_t i=0; i<ndtris; i++) { for (size_t j=0; j<3; j++) tri[4*i+j] = j; } } else { //verts memcpy(navDVerts, dvertices, ndvertsuniq*3*sizeof(float)); //tris unsigned char* tri = navDTris; for (size_t i=0; i<ndtris; i++) { for (size_t j=0; j<3; j++) tri[4*i+j] = dtris[6*i+j]; } //detailed meshes for (int i = 0; i < npolys; ++i) { dtStatPolyDetail& dtl = navDMeshes[i]; dtl.vbase = dmeshes[i*4+0]; dtl.nverts = dmeshes[i*4+1]; dtl.tbase = dmeshes[i*4+2]; dtl.ntris = dmeshes[i*4+3]; } } m_navMesh = new dtStatNavMesh; m_navMesh->init(data, dataSize, true); delete [] vertices; /* navmesh conversion is using C guarded alloc for memory allocaitons */ MEM_freeN(polys); if (dmeshes) MEM_freeN(dmeshes); if (dtris) MEM_freeN(dtris); if (dvertices) delete [] dvertices; if (vertsi) delete [] vertsi; return true; }
bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) { rcTimeVal startTime = rcGetPerformanceTimer(); vcopy(mesh.bmin, cset.bmin); vcopy(mesh.bmax, cset.bmax); mesh.cs = cset.cs; mesh.ch = cset.ch; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.nconts; ++i) { maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts); } if (maxVertices >= 0xfffe) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices); return false; } unsigned char* vflags = 0; int* nextVert = 0; int* firstVert = 0; int* indices = 0; int* tris = 0; unsigned short* polys = 0; vflags = new unsigned char[maxVertices]; if (!vflags) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); goto failure; } memset(vflags, 0, maxVertices); mesh.verts = new unsigned short[maxVertices*3]; if (!mesh.verts) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); goto failure; } mesh.polys = new unsigned short[maxTris*nvp*2]; if (!mesh.polys) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2); goto failure; } mesh.regs = new unsigned short[maxTris]; if (!mesh.regs) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris); goto failure; } mesh.nverts = 0; mesh.npolys = 0; mesh.nvp = nvp; memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3); memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2); memset(mesh.regs, 0, sizeof(unsigned short)*maxTris); nextVert = new int[maxVertices]; if (!nextVert) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); goto failure; } memset(nextVert, 0, sizeof(int)*maxVertices); firstVert = new int[VERTEX_BUCKET_COUNT]; if (!firstVert) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); goto failure; } for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; indices = new int[maxVertsPerCont]; if (!indices) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); goto failure; } tris = new int[maxVertsPerCont*3]; if (!tris) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); goto failure; } polys = new unsigned short[(maxVertsPerCont+1)*nvp]; if (!polys) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); goto failure; } unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp]; for (int i = 0; i < cset.nconts; ++i) { rcContour& cont = cset.conts[i]; // Skip empty contours. if (cont.nverts < 3) continue; // Triangulate contour for (int j = 0; j < cont.nverts; ++j) indices[j] = j; int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]); if (ntris <= 0) { // Bad triangulation, should not happen. /* for (int k = 0; k < cont.nverts; ++k) { const int* v = &cont.verts[k*4]; printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]); if (nBadPos < 100) { badPos[nBadPos*3+0] = v[0]; badPos[nBadPos*3+1] = v[1]; badPos[nBadPos*3+2] = v[2]; nBadPos++; } }*/ ntris = -ntris; } // Add and merge vertices. for (int j = 0; j < cont.nverts; ++j) { const int* v = &cont.verts[j*4]; indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2], mesh.verts, firstVert, nextVert, mesh.nverts); if (v[3] & RC_BORDER_VERTEX) { // This vertex should be removed. vflags[indices[j]] = 1; } } // Build initial polygons. int npolys = 0; memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short)); for (int j = 0; j < ntris; ++j) { int* t = &tris[j*3]; if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) { polys[npolys*nvp+0] = (unsigned short)indices[t[0]]; polys[npolys*nvp+1] = (unsigned short)indices[t[1]]; polys[npolys*nvp+2] = (unsigned short)indices[t[2]]; npolys++; } } if (!npolys) continue; // Merge polygons. if (nvp > 3) { while (true) { // Find best polygons to merge. int bestMergeVal = 0; int bestPa, bestPb, bestEa, bestEb; for (int j = 0; j < npolys-1; ++j) { unsigned short* pj = &polys[j*nvp]; for (int k = j+1; k < npolys; ++k) { unsigned short* pk = &polys[k*nvp]; int ea, eb; int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); if (v > bestMergeVal) { bestMergeVal = v; bestPa = j; bestPb = k; bestEa = ea; bestEb = eb; } } } if (bestMergeVal > 0) { // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp); memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); npolys--; } else { // Could not merge any polygons, stop. break; } } } // Store polygons. for (int j = 0; j < npolys; ++j) { unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; unsigned short* q = &polys[j*nvp]; for (int k = 0; k < nvp; ++k) p[k] = q[k]; mesh.regs[mesh.npolys] = cont.reg; mesh.npolys++; } } // Remove edge vertices. for (int i = 0; i < mesh.nverts; ++i) { if (vflags[i]) { if (!removeVertex(mesh, i, maxTris)) goto failure; for (int j = i; j < mesh.nverts-1; ++j) vflags[j] = vflags[j+1]; --i; } } delete [] vflags; delete [] firstVert; delete [] nextVert; delete [] indices; delete [] tris; // Calculate adjacency. if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp)) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); return false; } rcTimeVal endTime = rcGetPerformanceTimer(); // if (rcGetLog()) // rcGetLog()->log(RC_LOG_PROGRESS, "Build polymesh: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); if (rcGetBuildTimes()) rcGetBuildTimes()->buildPolymesh += rcGetDeltaTimeUsec(startTime, endTime); return true; failure: delete [] vflags; delete [] tmpPoly; delete [] firstVert; delete [] nextVert; delete [] indices; delete [] tris; return false; }
bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) { if (!nmeshes || !meshes) return true; rcTimeVal startTime = rcGetPerformanceTimer(); int* nextVert = 0; int* firstVert = 0; unsigned short* vremap = 0; mesh.nvp = meshes[0]->nvp; mesh.cs = meshes[0]->cs; mesh.ch = meshes[0]->ch; vcopy(mesh.bmin, meshes[0]->bmin); vcopy(mesh.bmax, meshes[0]->bmax); int maxVerts = 0; int maxPolys = 0; int maxVertsPerMesh = 0; for (int i = 0; i < nmeshes; ++i) { vmin(mesh.bmin, meshes[i]->bmin); vmax(mesh.bmax, meshes[i]->bmax); maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts); maxVerts += meshes[i]->nverts; maxPolys += meshes[i]->npolys; } mesh.nverts = 0; mesh.verts = new unsigned short[maxVerts*3]; if (!mesh.verts) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3); return false; } mesh.npolys = 0; mesh.polys = new unsigned short[maxPolys*2*mesh.nvp]; if (!mesh.polys) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp); return false; } memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp); mesh.regs = new unsigned short[maxPolys]; if (!mesh.regs) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys); return false; } memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys); nextVert = new int[maxVerts]; if (!nextVert) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); goto failure; } memset(nextVert, 0, sizeof(int)*maxVerts); firstVert = new int[VERTEX_BUCKET_COUNT]; if (!firstVert) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); goto failure; } for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; vremap = new unsigned short[maxVertsPerMesh]; if (!vremap) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); goto failure; } memset(nextVert, 0, sizeof(int)*maxVerts); for (int i = 0; i < nmeshes; ++i) { const rcPolyMesh* pmesh = meshes[i]; const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f); const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f); for (int j = 0; j < pmesh->nverts; ++j) { unsigned short* v = &pmesh->verts[j*3]; vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz, mesh.verts, firstVert, nextVert, mesh.nverts); } for (int j = 0; j < pmesh->npolys; ++j) { unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp]; unsigned short* src = &pmesh->polys[j*2*mesh.nvp]; mesh.regs[mesh.npolys] = pmesh->regs[j]; mesh.npolys++; for (int k = 0; k < mesh.nvp; ++k) { if (src[k] == 0xffff) break; tgt[k] = vremap[src[k]]; } } } // Calculate adjacency. if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp)) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); return false; } delete [] firstVert; delete [] nextVert; delete [] vremap; rcTimeVal endTime = rcGetPerformanceTimer(); if (rcGetBuildTimes()) rcGetBuildTimes()->mergePolyMesh += rcGetDeltaTimeUsec(startTime, endTime); return true; failure: delete [] firstVert; delete [] nextVert; delete [] vremap; return false; }