Path: blob/master/thirdparty/recastnavigation/Recast/Source/RecastRasterization.cpp
9913 views
//1// Copyright (c) 2009-2010 Mikko Mononen [email protected]2//3// This software is provided 'as-is', without any express or implied4// warranty. In no event will the authors be held liable for any damages5// arising from the use of this software.6// Permission is granted to anyone to use this software for any purpose,7// including commercial applications, and to alter it and redistribute it8// freely, subject to the following restrictions:9// 1. The origin of this software must not be misrepresented; you must not10// claim that you wrote the original software. If you use this software11// in a product, an acknowledgment in the product documentation would be12// appreciated but is not required.13// 2. Altered source versions must be plainly marked as such, and must not be14// misrepresented as being the original software.15// 3. This notice may not be removed or altered from any source distribution.16//1718#include <math.h>19#include <stdio.h>20#include "Recast.h"21#include "RecastAlloc.h"22#include "RecastAssert.h"2324/// Check whether two bounding boxes overlap25///26/// @param[in] aMin Min axis extents of bounding box A27/// @param[in] aMax Max axis extents of bounding box A28/// @param[in] bMin Min axis extents of bounding box B29/// @param[in] bMax Max axis extents of bounding box B30/// @returns true if the two bounding boxes overlap. False otherwise.31static bool overlapBounds(const float* aMin, const float* aMax, const float* bMin, const float* bMax)32{33return34aMin[0] <= bMax[0] && aMax[0] >= bMin[0] &&35aMin[1] <= bMax[1] && aMax[1] >= bMin[1] &&36aMin[2] <= bMax[2] && aMax[2] >= bMin[2];37}3839/// Allocates a new span in the heightfield.40/// Use a memory pool and free list to minimize actual allocations.41///42/// @param[in] hf The heightfield43/// @returns A pointer to the allocated or re-used span memory.44static rcSpan* allocSpan(rcHeightfield& hf)45{46// If necessary, allocate new page and update the freelist.47if (hf.freelist == NULL || hf.freelist->next == NULL)48{49// Create new page.50// Allocate memory for the new pool.51rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);52if (spanPool == NULL)53{54return NULL;55}5657// Add the pool into the list of pools.58spanPool->next = hf.pools;59hf.pools = spanPool;6061// Add new spans to the free list.62rcSpan* freeList = hf.freelist;63rcSpan* head = &spanPool->items[0];64rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL];65do66{67--it;68it->next = freeList;69freeList = it;70}71while (it != head);72hf.freelist = it;73}7475// Pop item from the front of the free list.76rcSpan* newSpan = hf.freelist;77hf.freelist = hf.freelist->next;78return newSpan;79}8081/// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.82/// @param[in] hf The heightfield.83/// @param[in] span A pointer to the span to free84static void freeSpan(rcHeightfield& hf, rcSpan* span)85{86if (span == NULL)87{88return;89}90// Add the span to the front of the free list.91span->next = hf.freelist;92hf.freelist = span;93}9495/// Adds a span to the heightfield. If the new span overlaps existing spans,96/// it will merge the new span with the existing ones.97///98/// @param[in] hf Heightfield to add spans to99/// @param[in] x The new span's column cell x index100/// @param[in] z The new span's column cell z index101/// @param[in] min The new span's minimum cell index102/// @param[in] max The new span's maximum cell index103/// @param[in] areaID The new span's area type ID104/// @param[in] flagMergeThreshold How close two spans maximum extents need to be to merge area type IDs105static bool addSpan(rcHeightfield& hf,106const int x, const int z,107const unsigned short min, const unsigned short max,108const unsigned char areaID, const int flagMergeThreshold)109{110// Create the new span.111rcSpan* newSpan = allocSpan(hf);112if (newSpan == NULL)113{114return false;115}116newSpan->smin = min;117newSpan->smax = max;118newSpan->area = areaID;119newSpan->next = NULL;120121const int columnIndex = x + z * hf.width;122rcSpan* previousSpan = NULL;123rcSpan* currentSpan = hf.spans[columnIndex];124125// Insert the new span, possibly merging it with existing spans.126while (currentSpan != NULL)127{128if (currentSpan->smin > newSpan->smax)129{130// Current span is completely after the new span, break.131break;132}133134if (currentSpan->smax < newSpan->smin)135{136// Current span is completely before the new span. Keep going.137previousSpan = currentSpan;138currentSpan = currentSpan->next;139}140else141{142// The new span overlaps with an existing span. Merge them.143if (currentSpan->smin < newSpan->smin)144{145newSpan->smin = currentSpan->smin;146}147if (currentSpan->smax > newSpan->smax)148{149newSpan->smax = currentSpan->smax;150}151152// Merge flags.153if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold)154{155// Higher area ID numbers indicate higher resolution priority.156newSpan->area = rcMax(newSpan->area, currentSpan->area);157}158159// Remove the current span since it's now merged with newSpan.160// Keep going because there might be other overlapping spans that also need to be merged.161rcSpan* next = currentSpan->next;162freeSpan(hf, currentSpan);163if (previousSpan)164{165previousSpan->next = next;166}167else168{169hf.spans[columnIndex] = next;170}171currentSpan = next;172}173}174175// Insert new span after prev176if (previousSpan != NULL)177{178newSpan->next = previousSpan->next;179previousSpan->next = newSpan;180}181else182{183// This span should go before the others in the list184newSpan->next = hf.spans[columnIndex];185hf.spans[columnIndex] = newSpan;186}187188return true;189}190191bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,192const int x, const int z,193const unsigned short spanMin, const unsigned short spanMax,194const unsigned char areaID, const int flagMergeThreshold)195{196rcAssert(context);197198if (!addSpan(heightfield, x, z, spanMin, spanMax, areaID, flagMergeThreshold))199{200context->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");201return false;202}203204return true;205}206207enum rcAxis208{209RC_AXIS_X = 0,210RC_AXIS_Y = 1,211RC_AXIS_Z = 2212};213214/// Divides a convex polygon of max 12 vertices into two convex polygons215/// across a separating axis.216///217/// @param[in] inVerts The input polygon vertices218/// @param[in] inVertsCount The number of input polygon vertices219/// @param[out] outVerts1 Resulting polygon 1's vertices220/// @param[out] outVerts1Count The number of resulting polygon 1 vertices221/// @param[out] outVerts2 Resulting polygon 2's vertices222/// @param[out] outVerts2Count The number of resulting polygon 2 vertices223/// @param[in] axisOffset THe offset along the specified axis224/// @param[in] axis The separating axis225static void dividePoly(const float* inVerts, int inVertsCount,226float* outVerts1, int* outVerts1Count,227float* outVerts2, int* outVerts2Count,228float axisOffset, rcAxis axis)229{230rcAssert(inVertsCount <= 12);231232// How far positive or negative away from the separating axis is each vertex.233float inVertAxisDelta[12];234for (int inVert = 0; inVert < inVertsCount; ++inVert)235{236inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis];237}238239int poly1Vert = 0;240int poly2Vert = 0;241for (int inVertA = 0, inVertB = inVertsCount - 1; inVertA < inVertsCount; inVertB = inVertA, ++inVertA)242{243// If the two vertices are on the same side of the separating axis244bool sameSide = (inVertAxisDelta[inVertA] >= 0) == (inVertAxisDelta[inVertB] >= 0);245246if (!sameSide)247{248float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]);249outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s;250outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s;251outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s;252rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]);253poly1Vert++;254poly2Vert++;255256// add the inVertA point to the right polygon. Do NOT add points that are on the dividing line257// since these were already added above258if (inVertAxisDelta[inVertA] > 0)259{260rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);261poly1Vert++;262}263else if (inVertAxisDelta[inVertA] < 0)264{265rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);266poly2Vert++;267}268}269else270{271// add the inVertA point to the right polygon. Addition is done even for points on the dividing line272if (inVertAxisDelta[inVertA] >= 0)273{274rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);275poly1Vert++;276if (inVertAxisDelta[inVertA] != 0)277{278continue;279}280}281rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);282poly2Vert++;283}284}285286*outVerts1Count = poly1Vert;287*outVerts2Count = poly2Vert;288}289290/// Rasterize a single triangle to the heightfield.291///292/// This code is extremely hot, so much care should be given to maintaining maximum perf here.293///294/// @param[in] v0 Triangle vertex 0295/// @param[in] v1 Triangle vertex 1296/// @param[in] v2 Triangle vertex 2297/// @param[in] areaID The area ID to assign to the rasterized spans298/// @param[in] hf Heightfield to rasterize into299/// @param[in] hfBBMin The min extents of the heightfield bounding box300/// @param[in] hfBBMax The max extents of the heightfield bounding box301/// @param[in] cellSize The x and z axis size of a voxel in the heightfield302/// @param[in] inverseCellSize 1 / cellSize303/// @param[in] inverseCellHeight 1 / cellHeight304/// @param[in] flagMergeThreshold The threshold in which area flags will be merged305/// @returns true if the operation completes successfully. false if there was an error adding spans to the heightfield.306static bool rasterizeTri(const float* v0, const float* v1, const float* v2,307const unsigned char areaID, rcHeightfield& hf,308const float* hfBBMin, const float* hfBBMax,309const float cellSize, const float inverseCellSize, const float inverseCellHeight,310const int flagMergeThreshold)311{312// Calculate the bounding box of the triangle.313float triBBMin[3];314rcVcopy(triBBMin, v0);315rcVmin(triBBMin, v1);316rcVmin(triBBMin, v2);317318float triBBMax[3];319rcVcopy(triBBMax, v0);320rcVmax(triBBMax, v1);321rcVmax(triBBMax, v2);322323// If the triangle does not touch the bounding box of the heightfield, skip the triangle.324if (!overlapBounds(triBBMin, triBBMax, hfBBMin, hfBBMax))325{326return true;327}328329const int w = hf.width;330const int h = hf.height;331const float by = hfBBMax[1] - hfBBMin[1];332333// Calculate the footprint of the triangle on the grid's z-axis334int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize);335int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize);336337// use -1 rather than 0 to cut the polygon properly at the start of the tile338z0 = rcClamp(z0, -1, h - 1);339z1 = rcClamp(z1, 0, h - 1);340341// Clip the triangle into all grid cells it touches.342float buf[7 * 3 * 4];343float* in = buf;344float* inRow = buf + 7 * 3;345float* p1 = inRow + 7 * 3;346float* p2 = p1 + 7 * 3;347348rcVcopy(&in[0], v0);349rcVcopy(&in[1 * 3], v1);350rcVcopy(&in[2 * 3], v2);351int nvRow;352int nvIn = 3;353354for (int z = z0; z <= z1; ++z)355{356// Clip polygon to row. Store the remaining polygon as well357const float cellZ = hfBBMin[2] + (float)z * cellSize;358dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z);359rcSwap(in, p1);360361if (nvRow < 3)362{363continue;364}365if (z < 0)366{367continue;368}369370// find X-axis bounds of the row371float minX = inRow[0];372float maxX = inRow[0];373for (int vert = 1; vert < nvRow; ++vert)374{375if (minX > inRow[vert * 3])376{377minX = inRow[vert * 3];378}379if (maxX < inRow[vert * 3])380{381maxX = inRow[vert * 3];382}383}384int x0 = (int)((minX - hfBBMin[0]) * inverseCellSize);385int x1 = (int)((maxX - hfBBMin[0]) * inverseCellSize);386if (x1 < 0 || x0 >= w)387{388continue;389}390x0 = rcClamp(x0, -1, w - 1);391x1 = rcClamp(x1, 0, w - 1);392393int nv;394int nv2 = nvRow;395396for (int x = x0; x <= x1; ++x)397{398// Clip polygon to column. store the remaining polygon as well399const float cx = hfBBMin[0] + (float)x * cellSize;400dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X);401rcSwap(inRow, p2);402403if (nv < 3)404{405continue;406}407if (x < 0)408{409continue;410}411412// Calculate min and max of the span.413float spanMin = p1[1];414float spanMax = p1[1];415for (int vert = 1; vert < nv; ++vert)416{417spanMin = rcMin(spanMin, p1[vert * 3 + 1]);418spanMax = rcMax(spanMax, p1[vert * 3 + 1]);419}420spanMin -= hfBBMin[1];421spanMax -= hfBBMin[1];422423// Skip the span if it's completely outside the heightfield bounding box424if (spanMax < 0.0f)425{426continue;427}428if (spanMin > by)429{430continue;431}432433// Clamp the span to the heightfield bounding box.434if (spanMin < 0.0f)435{436spanMin = 0;437}438if (spanMax > by)439{440spanMax = by;441}442443// Snap the span to the heightfield height grid.444unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, RC_SPAN_MAX_HEIGHT);445unsigned short spanMaxCellIndex = (unsigned short)rcClamp((int)ceilf(spanMax * inverseCellHeight), (int)spanMinCellIndex + 1, RC_SPAN_MAX_HEIGHT);446447if (!addSpan(hf, x, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold))448{449return false;450}451}452}453454return true;455}456457bool rcRasterizeTriangle(rcContext* context,458const float* v0, const float* v1, const float* v2,459const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold)460{461rcAssert(context != NULL);462463rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);464465// Rasterize the single triangle.466const float inverseCellSize = 1.0f / heightfield.cs;467const float inverseCellHeight = 1.0f / heightfield.ch;468if (!rasterizeTri(v0, v1, v2, areaID, heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))469{470context->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");471return false;472}473474return true;475}476477bool rcRasterizeTriangles(rcContext* context,478const float* verts, const int /*nv*/,479const int* tris, const unsigned char* triAreaIDs, const int numTris,480rcHeightfield& heightfield, const int flagMergeThreshold)481{482rcAssert(context != NULL);483484rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);485486// Rasterize the triangles.487const float inverseCellSize = 1.0f / heightfield.cs;488const float inverseCellHeight = 1.0f / heightfield.ch;489for (int triIndex = 0; triIndex < numTris; ++triIndex)490{491const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];492const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];493const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];494if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))495{496context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");497return false;498}499}500501return true;502}503504bool rcRasterizeTriangles(rcContext* context,505const float* verts, const int /*nv*/,506const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris,507rcHeightfield& heightfield, const int flagMergeThreshold)508{509rcAssert(context != NULL);510511rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);512513// Rasterize the triangles.514const float inverseCellSize = 1.0f / heightfield.cs;515const float inverseCellHeight = 1.0f / heightfield.ch;516for (int triIndex = 0; triIndex < numTris; ++triIndex)517{518const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];519const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];520const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];521if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))522{523context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");524return false;525}526}527528return true;529}530531bool rcRasterizeTriangles(rcContext* context,532const float* verts, const unsigned char* triAreaIDs, const int numTris,533rcHeightfield& heightfield, const int flagMergeThreshold)534{535rcAssert(context != NULL);536537rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);538539// Rasterize the triangles.540const float inverseCellSize = 1.0f / heightfield.cs;541const float inverseCellHeight = 1.0f / heightfield.ch;542for (int triIndex = 0; triIndex < numTris; ++triIndex)543{544const float* v0 = &verts[(triIndex * 3 + 0) * 3];545const float* v1 = &verts[(triIndex * 3 + 1) * 3];546const float* v2 = &verts[(triIndex * 3 + 2) * 3];547if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))548{549context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");550return false;551}552}553554return true;555}556557558