123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- //
- // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- // 3. This notice may not be removed or altered from any source distribution.
- //
- #include <math.h>
- #include <stdio.h>
- #include "Recast.h"
- #include "RecastAlloc.h"
- #include "RecastAssert.h"
- /// Check whether two bounding boxes overlap
- ///
- /// @param[in] aMin Min axis extents of bounding box A
- /// @param[in] aMax Max axis extents of bounding box A
- /// @param[in] bMin Min axis extents of bounding box B
- /// @param[in] bMax Max axis extents of bounding box B
- /// @returns true if the two bounding boxes overlap. False otherwise.
- static bool overlapBounds(const float* aMin, const float* aMax, const float* bMin, const float* bMax)
- {
- return
- aMin[0] <= bMax[0] && aMax[0] >= bMin[0] &&
- aMin[1] <= bMax[1] && aMax[1] >= bMin[1] &&
- aMin[2] <= bMax[2] && aMax[2] >= bMin[2];
- }
- /// Allocates a new span in the heightfield.
- /// Use a memory pool and free list to minimize actual allocations.
- ///
- /// @param[in] hf The heightfield
- /// @returns A pointer to the allocated or re-used span memory.
- static rcSpan* allocSpan(rcHeightfield& hf)
- {
- // If necessary, allocate new page and update the freelist.
- if (hf.freelist == NULL || hf.freelist->next == NULL)
- {
- // Create new page.
- // Allocate memory for the new pool.
- rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
- if (spanPool == NULL)
- {
- return NULL;
- }
- // Add the pool into the list of pools.
- spanPool->next = hf.pools;
- hf.pools = spanPool;
-
- // Add new spans to the free list.
- rcSpan* freeList = hf.freelist;
- rcSpan* head = &spanPool->items[0];
- rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL];
- do
- {
- --it;
- it->next = freeList;
- freeList = it;
- }
- while (it != head);
- hf.freelist = it;
- }
- // Pop item from the front of the free list.
- rcSpan* newSpan = hf.freelist;
- hf.freelist = hf.freelist->next;
- return newSpan;
- }
- /// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.
- /// @param[in] hf The heightfield.
- /// @param[in] span A pointer to the span to free
- static void freeSpan(rcHeightfield& hf, rcSpan* span)
- {
- if (span == NULL)
- {
- return;
- }
- // Add the span to the front of the free list.
- span->next = hf.freelist;
- hf.freelist = span;
- }
- /// Adds a span to the heightfield. If the new span overlaps existing spans,
- /// it will merge the new span with the existing ones.
- ///
- /// @param[in] hf Heightfield to add spans to
- /// @param[in] x The new span's column cell x index
- /// @param[in] z The new span's column cell z index
- /// @param[in] min The new span's minimum cell index
- /// @param[in] max The new span's maximum cell index
- /// @param[in] areaID The new span's area type ID
- /// @param[in] flagMergeThreshold How close two spans maximum extents need to be to merge area type IDs
- static bool addSpan(rcHeightfield& hf,
- const int x, const int z,
- const unsigned short min, const unsigned short max,
- const unsigned char areaID, const int flagMergeThreshold)
- {
- // Create the new span.
- rcSpan* newSpan = allocSpan(hf);
- if (newSpan == NULL)
- {
- return false;
- }
- newSpan->smin = min;
- newSpan->smax = max;
- newSpan->area = areaID;
- newSpan->next = NULL;
-
- const int columnIndex = x + z * hf.width;
- rcSpan* previousSpan = NULL;
- rcSpan* currentSpan = hf.spans[columnIndex];
-
- // Insert the new span, possibly merging it with existing spans.
- while (currentSpan != NULL)
- {
- if (currentSpan->smin > newSpan->smax)
- {
- // Current span is completely after the new span, break.
- break;
- }
-
- if (currentSpan->smax < newSpan->smin)
- {
- // Current span is completely before the new span. Keep going.
- previousSpan = currentSpan;
- currentSpan = currentSpan->next;
- }
- else
- {
- // The new span overlaps with an existing span. Merge them.
- if (currentSpan->smin < newSpan->smin)
- {
- newSpan->smin = currentSpan->smin;
- }
- if (currentSpan->smax > newSpan->smax)
- {
- newSpan->smax = currentSpan->smax;
- }
-
- // Merge flags.
- if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold)
- {
- // Higher area ID numbers indicate higher resolution priority.
- newSpan->area = rcMax(newSpan->area, currentSpan->area);
- }
-
- // Remove the current span since it's now merged with newSpan.
- // Keep going because there might be other overlapping spans that also need to be merged.
- rcSpan* next = currentSpan->next;
- freeSpan(hf, currentSpan);
- if (previousSpan)
- {
- previousSpan->next = next;
- }
- else
- {
- hf.spans[columnIndex] = next;
- }
- currentSpan = next;
- }
- }
-
- // Insert new span after prev
- if (previousSpan != NULL)
- {
- newSpan->next = previousSpan->next;
- previousSpan->next = newSpan;
- }
- else
- {
- // This span should go before the others in the list
- newSpan->next = hf.spans[columnIndex];
- hf.spans[columnIndex] = newSpan;
- }
- return true;
- }
- bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,
- const int x, const int z,
- const unsigned short spanMin, const unsigned short spanMax,
- const unsigned char areaID, const int flagMergeThreshold)
- {
- rcAssert(context);
- if (!addSpan(heightfield, x, z, spanMin, spanMax, areaID, flagMergeThreshold))
- {
- context->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
- return false;
- }
- return true;
- }
- enum rcAxis
- {
- RC_AXIS_X = 0,
- RC_AXIS_Y = 1,
- RC_AXIS_Z = 2
- };
- /// Divides a convex polygon of max 12 vertices into two convex polygons
- /// across a separating axis.
- ///
- /// @param[in] inVerts The input polygon vertices
- /// @param[in] inVertsCount The number of input polygon vertices
- /// @param[out] outVerts1 Resulting polygon 1's vertices
- /// @param[out] outVerts1Count The number of resulting polygon 1 vertices
- /// @param[out] outVerts2 Resulting polygon 2's vertices
- /// @param[out] outVerts2Count The number of resulting polygon 2 vertices
- /// @param[in] axisOffset THe offset along the specified axis
- /// @param[in] axis The separating axis
- static void dividePoly(const float* inVerts, int inVertsCount,
- float* outVerts1, int* outVerts1Count,
- float* outVerts2, int* outVerts2Count,
- float axisOffset, rcAxis axis)
- {
- rcAssert(inVertsCount <= 12);
-
- // How far positive or negative away from the separating axis is each vertex.
- float inVertAxisDelta[12];
- for (int inVert = 0; inVert < inVertsCount; ++inVert)
- {
- inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis];
- }
- int poly1Vert = 0;
- int poly2Vert = 0;
- for (int inVertA = 0, inVertB = inVertsCount - 1; inVertA < inVertsCount; inVertB = inVertA, ++inVertA)
- {
- // If the two vertices are on the same side of the separating axis
- bool sameSide = (inVertAxisDelta[inVertA] >= 0) == (inVertAxisDelta[inVertB] >= 0);
- if (!sameSide)
- {
- float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]);
- outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s;
- outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s;
- outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s;
- rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]);
- poly1Vert++;
- poly2Vert++;
-
- // add the inVertA point to the right polygon. Do NOT add points that are on the dividing line
- // since these were already added above
- if (inVertAxisDelta[inVertA] > 0)
- {
- rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
- poly1Vert++;
- }
- else if (inVertAxisDelta[inVertA] < 0)
- {
- rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
- poly2Vert++;
- }
- }
- else
- {
- // add the inVertA point to the right polygon. Addition is done even for points on the dividing line
- if (inVertAxisDelta[inVertA] >= 0)
- {
- rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
- poly1Vert++;
- if (inVertAxisDelta[inVertA] != 0)
- {
- continue;
- }
- }
- rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
- poly2Vert++;
- }
- }
- *outVerts1Count = poly1Vert;
- *outVerts2Count = poly2Vert;
- }
- /// Rasterize a single triangle to the heightfield.
- ///
- /// This code is extremely hot, so much care should be given to maintaining maximum perf here.
- ///
- /// @param[in] v0 Triangle vertex 0
- /// @param[in] v1 Triangle vertex 1
- /// @param[in] v2 Triangle vertex 2
- /// @param[in] areaID The area ID to assign to the rasterized spans
- /// @param[in] hf Heightfield to rasterize into
- /// @param[in] hfBBMin The min extents of the heightfield bounding box
- /// @param[in] hfBBMax The max extents of the heightfield bounding box
- /// @param[in] cellSize The x and z axis size of a voxel in the heightfield
- /// @param[in] inverseCellSize 1 / cellSize
- /// @param[in] inverseCellHeight 1 / cellHeight
- /// @param[in] flagMergeThreshold The threshold in which area flags will be merged
- /// @returns true if the operation completes successfully. false if there was an error adding spans to the heightfield.
- static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
- const unsigned char areaID, rcHeightfield& hf,
- const float* hfBBMin, const float* hfBBMax,
- const float cellSize, const float inverseCellSize, const float inverseCellHeight,
- const int flagMergeThreshold)
- {
- // Calculate the bounding box of the triangle.
- float triBBMin[3];
- rcVcopy(triBBMin, v0);
- rcVmin(triBBMin, v1);
- rcVmin(triBBMin, v2);
- float triBBMax[3];
- rcVcopy(triBBMax, v0);
- rcVmax(triBBMax, v1);
- rcVmax(triBBMax, v2);
- // If the triangle does not touch the bounding box of the heightfield, skip the triangle.
- if (!overlapBounds(triBBMin, triBBMax, hfBBMin, hfBBMax))
- {
- return true;
- }
- const int w = hf.width;
- const int h = hf.height;
- const float by = hfBBMax[1] - hfBBMin[1];
- // Calculate the footprint of the triangle on the grid's z-axis
- int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize);
- int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize);
- // use -1 rather than 0 to cut the polygon properly at the start of the tile
- z0 = rcClamp(z0, -1, h - 1);
- z1 = rcClamp(z1, 0, h - 1);
- // Clip the triangle into all grid cells it touches.
- float buf[7 * 3 * 4];
- float* in = buf;
- float* inRow = buf + 7 * 3;
- float* p1 = inRow + 7 * 3;
- float* p2 = p1 + 7 * 3;
- rcVcopy(&in[0], v0);
- rcVcopy(&in[1 * 3], v1);
- rcVcopy(&in[2 * 3], v2);
- int nvRow;
- int nvIn = 3;
- for (int z = z0; z <= z1; ++z)
- {
- // Clip polygon to row. Store the remaining polygon as well
- const float cellZ = hfBBMin[2] + (float)z * cellSize;
- dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z);
- rcSwap(in, p1);
-
- if (nvRow < 3)
- {
- continue;
- }
- if (z < 0)
- {
- continue;
- }
-
- // find X-axis bounds of the row
- float minX = inRow[0];
- float maxX = inRow[0];
- for (int vert = 1; vert < nvRow; ++vert)
- {
- if (minX > inRow[vert * 3])
- {
- minX = inRow[vert * 3];
- }
- if (maxX < inRow[vert * 3])
- {
- maxX = inRow[vert * 3];
- }
- }
- int x0 = (int)((minX - hfBBMin[0]) * inverseCellSize);
- int x1 = (int)((maxX - hfBBMin[0]) * inverseCellSize);
- if (x1 < 0 || x0 >= w)
- {
- continue;
- }
- x0 = rcClamp(x0, -1, w - 1);
- x1 = rcClamp(x1, 0, w - 1);
- int nv;
- int nv2 = nvRow;
- for (int x = x0; x <= x1; ++x)
- {
- // Clip polygon to column. store the remaining polygon as well
- const float cx = hfBBMin[0] + (float)x * cellSize;
- dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X);
- rcSwap(inRow, p2);
-
- if (nv < 3)
- {
- continue;
- }
- if (x < 0)
- {
- continue;
- }
-
- // Calculate min and max of the span.
- float spanMin = p1[1];
- float spanMax = p1[1];
- for (int vert = 1; vert < nv; ++vert)
- {
- spanMin = rcMin(spanMin, p1[vert * 3 + 1]);
- spanMax = rcMax(spanMax, p1[vert * 3 + 1]);
- }
- spanMin -= hfBBMin[1];
- spanMax -= hfBBMin[1];
-
- // Skip the span if it's completely outside the heightfield bounding box
- if (spanMax < 0.0f)
- {
- continue;
- }
- if (spanMin > by)
- {
- continue;
- }
-
- // Clamp the span to the heightfield bounding box.
- if (spanMin < 0.0f)
- {
- spanMin = 0;
- }
- if (spanMax > by)
- {
- spanMax = by;
- }
- // Snap the span to the heightfield height grid.
- unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, RC_SPAN_MAX_HEIGHT);
- unsigned short spanMaxCellIndex = (unsigned short)rcClamp((int)ceilf(spanMax * inverseCellHeight), (int)spanMinCellIndex + 1, RC_SPAN_MAX_HEIGHT);
- if (!addSpan(hf, x, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold))
- {
- return false;
- }
- }
- }
- return true;
- }
- bool rcRasterizeTriangle(rcContext* context,
- const float* v0, const float* v1, const float* v2,
- const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold)
- {
- rcAssert(context != NULL);
- rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
- // Rasterize the single triangle.
- const float inverseCellSize = 1.0f / heightfield.cs;
- const float inverseCellHeight = 1.0f / heightfield.ch;
- if (!rasterizeTri(v0, v1, v2, areaID, heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
- {
- context->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
- return false;
- }
- return true;
- }
- bool rcRasterizeTriangles(rcContext* context,
- const float* verts, const int /*nv*/,
- const int* tris, const unsigned char* triAreaIDs, const int numTris,
- rcHeightfield& heightfield, const int flagMergeThreshold)
- {
- rcAssert(context != NULL);
- rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
-
- // Rasterize the triangles.
- const float inverseCellSize = 1.0f / heightfield.cs;
- const float inverseCellHeight = 1.0f / heightfield.ch;
- for (int triIndex = 0; triIndex < numTris; ++triIndex)
- {
- const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
- const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
- const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
- if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
- {
- context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
- return false;
- }
- }
- return true;
- }
- bool rcRasterizeTriangles(rcContext* context,
- const float* verts, const int /*nv*/,
- const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris,
- rcHeightfield& heightfield, const int flagMergeThreshold)
- {
- rcAssert(context != NULL);
- rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
- // Rasterize the triangles.
- const float inverseCellSize = 1.0f / heightfield.cs;
- const float inverseCellHeight = 1.0f / heightfield.ch;
- for (int triIndex = 0; triIndex < numTris; ++triIndex)
- {
- const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
- const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
- const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
- if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
- {
- context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
- return false;
- }
- }
- return true;
- }
- bool rcRasterizeTriangles(rcContext* context,
- const float* verts, const unsigned char* triAreaIDs, const int numTris,
- rcHeightfield& heightfield, const int flagMergeThreshold)
- {
- rcAssert(context != NULL);
- rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
-
- // Rasterize the triangles.
- const float inverseCellSize = 1.0f / heightfield.cs;
- const float inverseCellHeight = 1.0f / heightfield.ch;
- for (int triIndex = 0; triIndex < numTris; ++triIndex)
- {
- const float* v0 = &verts[(triIndex * 3 + 0) * 3];
- const float* v1 = &verts[(triIndex * 3 + 1) * 3];
- const float* v2 = &verts[(triIndex * 3 + 2) * 3];
- if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
- {
- context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
- return false;
- }
- }
- return true;
- }
|