diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index 55dac51cad805..3fa0143a5841a 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -42,4 +42,4 @@ gSOAP (a portable development toolkit for C and C++ XML Web services and XML dat
recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
https://github.com/memononen/recastnavigation
- Version: 740a7ba51600a3c87ce5667ae276a38284a1ce75
+ Version: 42b96b7306d39bb7680ddb0f89d480a8296c83ff
diff --git a/dep/recastnavigation/Detour/Include/DetourNavMesh.h b/dep/recastnavigation/Detour/Include/DetourNavMesh.h
index cdd473f1aff69..782ddbc2edd17 100644
--- a/dep/recastnavigation/Detour/Include/DetourNavMesh.h
+++ b/dep/recastnavigation/Detour/Include/DetourNavMesh.h
@@ -119,6 +119,25 @@ enum dtStraightPathOptions
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing.
};
+
+/// Options for dtNavMeshQuery::findPath
+enum dtFindPathOptions
+{
+ DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued
+ DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
+};
+
+/// Options for dtNavMeshQuery::raycast
+enum dtRaycastOptions
+{
+ DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
+};
+
+
+/// Limit raycasting during any angle pahfinding
+/// The limit is given as a multiple of the character radius
+static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f;
+
/// Flags representing the type of a navigation mesh polygon.
enum dtPolyTypes
{
diff --git a/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h
index 4a5112c9eb943..c7b360dcdc60c 100644
--- a/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h
+++ b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h
@@ -41,6 +41,10 @@ class dtQueryFilter
public:
dtQueryFilter();
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual ~dtQueryFilter() { }
+#endif
+
/// Returns true if the polygon can be visited. (I.e. Is traversable.)
/// @param[in] ref The reference id of the polygon test.
/// @param[in] tile The tile containing the polygon.
@@ -115,6 +119,34 @@ class dtQueryFilter
};
+
+
+/// Provides information about raycast hit
+/// filled by dtNavMeshQuery::raycast
+/// @ingroup detour
+struct dtRaycastHit
+{
+ /// The hit parameter. (FLT_MAX if no wall hit.)
+ float t;
+
+ /// hitNormal The normal of the nearest wall hit. [(x, y, z)]
+ float hitNormal[3];
+
+ /// Pointer to an array of reference ids of the visited polygons. [opt]
+ dtPolyRef* path;
+
+ /// The number of visited polygons. [opt]
+ int pathCount;
+
+ /// The maximum number of polygons the @p path array can hold.
+ int maxPath;
+
+ /// The cost of the path until hit.
+ float pathCost;
+};
+
+
+
/// Provides the ability to perform pathfinding related queries against
/// a navigation mesh.
/// @ingroup detour
@@ -179,10 +211,11 @@ class dtNavMeshQuery
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
+ /// @param[in] options query options (see: #dtFindPathOptions)
/// @returns The status flags for the query.
dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
const float* startPos, const float* endPos,
- const dtQueryFilter* filter);
+ const dtQueryFilter* filter, const unsigned int options = 0);
/// Updates an in-progress sliced path query.
/// @param[in] maxIter The maximum number of iterations to perform.
@@ -308,6 +341,7 @@ class dtNavMeshQuery
/// Casts a 'walkability' ray along the surface of the navigation mesh from
/// the start position toward the end position.
+ /// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility.
/// @param[in] startRef The reference id of the start polygon.
/// @param[in] startPos A position within the start polygon representing
/// the start of the ray. [(x, y, z)]
@@ -323,6 +357,22 @@ class dtNavMeshQuery
const dtQueryFilter* filter,
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
+ /// Casts a 'walkability' ray along the surface of the navigation mesh from
+ /// the start position toward the end position.
+ /// @param[in] startRef The reference id of the start polygon.
+ /// @param[in] startPos A position within the start polygon representing
+ /// the start of the ray. [(x, y, z)]
+ /// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
+ /// @param[in] filter The polygon filter to apply to the query.
+ /// @param[in] flags govern how the raycast behaves. See dtRaycastOptions
+ /// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
+ /// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
+ /// @returns The status flags for the query.
+ dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter, const unsigned int options,
+ dtRaycastHit* hit, dtPolyRef prevRef = 0) const;
+
+
/// Finds the distance from the specified position to the nearest polygon wall.
/// @param[in] startRef The reference id of the polygon containing @p centerPos.
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
@@ -463,6 +513,8 @@ class dtNavMeshQuery
dtPolyRef startRef, endRef;
float startPos[3], endPos[3];
const dtQueryFilter* filter;
+ unsigned int options;
+ float raycastLimitSqr;
};
dtQueryData m_query; ///< Sliced query state.
diff --git a/dep/recastnavigation/Detour/Include/DetourNode.h b/dep/recastnavigation/Detour/Include/DetourNode.h
index b68c922d038d4..6fefdc8e0f301 100644
--- a/dep/recastnavigation/Detour/Include/DetourNode.h
+++ b/dep/recastnavigation/Detour/Include/DetourNode.h
@@ -25,6 +25,7 @@ enum dtNodeFlags
{
DT_NODE_OPEN = 0x01,
DT_NODE_CLOSED = 0x02,
+ DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
};
typedef unsigned short dtNodeIndex;
@@ -35,12 +36,17 @@ struct dtNode
float pos[3]; ///< Position of the node.
float cost; ///< Cost from previous node to current node.
float total; ///< Cost up to the node.
- unsigned int pidx : 30; ///< Index to parent node.
- unsigned int flags : 2; ///< Node flags 0/open/closed.
+ unsigned int pidx : 24; ///< Index to parent node.
+ unsigned int state : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
+ unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
dtPolyRef id; ///< Polygon ref the node corresponds to.
};
+static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state
+
+
+
class dtNodePool
{
public:
@@ -48,8 +54,12 @@ class dtNodePool
~dtNodePool();
inline void operator=(const dtNodePool&) {}
void clear();
- dtNode* getNode(dtPolyRef id);
- dtNode* findNode(dtPolyRef id);
+
+ // Get a dtNode by ref and extra state information. If there is none then - allocate
+ // There can be more than one node for the same polyRef but with different extra state information
+ dtNode* getNode(dtPolyRef id, unsigned char state=0);
+ dtNode* findNode(dtPolyRef id, unsigned char state);
+ unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes);
inline unsigned int getNodeIdx(const dtNode* node) const
{
@@ -82,6 +92,7 @@ class dtNodePool
inline int getHashSize() const { return m_hashSize; }
inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; }
inline dtNodeIndex getNext(int i) const { return m_next[i]; }
+ inline int getNodeCount() const { return m_nodeCount; }
private:
diff --git a/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp
index f1709dfd4cf3c..ec3a2946ea5b8 100644
--- a/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp
+++ b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp
@@ -1011,7 +1011,13 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
continue;
- dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ // deal explicitly with crossing tile boundaries
+ unsigned char crossSide = 0;
+ if (bestTile->links[i].side != 0xff)
+ crossSide = bestTile->links[i].side >> 1;
+
+ // get the node
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
if (!neighbourNode)
{
status |= DT_OUT_OF_NODES;
@@ -1139,7 +1145,7 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
///
dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
const float* startPos, const float* endPos,
- const dtQueryFilter* filter)
+ const dtQueryFilter* filter, const unsigned int options)
{
dtAssert(m_nav);
dtAssert(m_nodePool);
@@ -1153,6 +1159,8 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
dtVcopy(m_query.startPos, startPos);
dtVcopy(m_query.endPos, endPos);
m_query.filter = filter;
+ m_query.options = options;
+ m_query.raycastLimitSqr = FLT_MAX;
if (!startRef || !endRef)
return DT_FAILURE | DT_INVALID_PARAM;
@@ -1161,6 +1169,16 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
return DT_FAILURE | DT_INVALID_PARAM;
+ // trade quality with performance?
+ if (options & DT_FINDPATH_ANY_ANGLE)
+ {
+ // limiting to several times the character radius yields nice results. It is not sensitive
+ // so it is enough to compute it from the first tile.
+ const dtMeshTile* tile = m_nav->getTileByRef(startRef);
+ float agentRadius = tile->header->walkableRadius;
+ m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS);
+ }
+
if (startRef == endRef)
{
m_query.status = DT_SUCCESS;
@@ -1197,6 +1215,9 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
m_query.status = DT_FAILURE;
return DT_FAILURE;
}
+
+ dtRaycastHit rayHit;
+ rayHit.maxPath = 0;
int iter = 0;
while (iter < maxIter && !m_openList->empty())
@@ -1233,15 +1254,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
return m_query.status;
}
- // Get parent poly and tile.
- dtPolyRef parentRef = 0;
+ // Get parent and grand parent poly and tile.
+ dtPolyRef parentRef = 0, grandpaRef = 0;
const dtMeshTile* parentTile = 0;
const dtPoly* parentPoly = 0;
+ dtNode* parentNode = 0;
if (bestNode->pidx)
- parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ {
+ parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx);
+ parentRef = parentNode->id;
+ if (parentNode->pidx)
+ grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id;
+ }
if (parentRef)
{
- if (dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly)))
+ bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly));
+ if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) )
{
// The polygon has disappeared during the sliced query, fail.
m_query.status = DT_FAILURE;
@@ -1250,6 +1278,14 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
return m_query.status;
}
}
+
+ // decide whether to test raycast to previous nodes
+ bool tryLOS = false;
+ if (m_query.options & DT_FINDPATH_ANY_ANGLE)
+ {
+ if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr))
+ tryLOS = true;
+ }
for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
{
@@ -1268,13 +1304,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
continue;
- dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ // deal explicitly with crossing tile boundaries
+ unsigned char crossSide = 0;
+ if (bestTile->links[i].side != 0xff)
+ crossSide = bestTile->links[i].side >> 1;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
if (!neighbourNode)
{
m_query.status |= DT_OUT_OF_NODES;
continue;
}
+ // do not expand to nodes that were already visited from the same parent
+ if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx)
+ continue;
+
// If the node is visited the first time, calculate node position.
if (neighbourNode->flags == 0)
{
@@ -1287,30 +1332,44 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
float cost = 0;
float heuristic = 0;
- // Special case for last node.
- if (neighbourRef == m_query.endRef)
+ // raycast parent
+ bool foundShortCut = false;
+ rayHit.pathCost = rayHit.t = 0;
+ if (tryLOS)
{
- // Cost
+ raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef);
+ foundShortCut = rayHit.t >= 1.0f;
+ }
+
+ // update move cost
+ if (foundShortCut)
+ {
+ // shortcut found using raycast. Using shorter cost instead
+ cost = parentNode->cost + rayHit.pathCost;
+ }
+ else
+ {
+ // No shortcut found.
const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
parentRef, parentTile, parentPoly,
- bestRef, bestTile, bestPoly,
- neighbourRef, neighbourTile, neighbourPoly);
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ }
+
+ // Special case for last node.
+ if (neighbourRef == m_query.endRef)
+ {
const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos,
bestRef, bestTile, bestPoly,
neighbourRef, neighbourTile, neighbourPoly,
0, 0, 0);
- cost = bestNode->cost + curCost + endCost;
+ cost = cost + endCost;
heuristic = 0;
}
else
{
- // Cost
- const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
- parentRef, parentTile, parentPoly,
- bestRef, bestTile, bestPoly,
- neighbourRef, neighbourTile, neighbourPoly);
- cost = bestNode->cost + curCost;
heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE;
}
@@ -1324,11 +1383,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
continue;
// Add or update the node.
- neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode);
neighbourNode->id = neighbourRef;
- neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
+ neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED));
neighbourNode->cost = cost;
neighbourNode->total = total;
+ if (foundShortCut)
+ neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED);
if (neighbourNode->flags & DT_NODE_OPEN)
{
@@ -1392,11 +1453,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
dtNode* prev = 0;
dtNode* node = m_query.lastBestNode;
+ int prevRay = 0;
do
{
dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
node->pidx = m_nodePool->getNodeIdx(prev);
prev = node;
+ int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
+ node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
+ prevRay = nextRay;
node = next;
}
while (node);
@@ -1405,13 +1470,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
node = prev;
do
{
- path[n++] = node->id;
- if (n >= maxPath)
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ dtStatus status = 0;
+ if (node->flags & DT_NODE_PARENT_DETACHED)
{
- m_query.status |= DT_BUFFER_TOO_SMALL;
+ float t, normal[3];
+ int m;
+ status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
+ n += m;
+ // raycast ends on poly boundary and the path might include the next poly boundary.
+ if (path[n-1] == next->id)
+ n--; // remove to avoid duplicates
+ }
+ else
+ {
+ path[n++] = node->id;
+ if (n >= maxPath)
+ status = DT_BUFFER_TOO_SMALL;
+ }
+
+ if (status & DT_STATUS_DETAIL_MASK)
+ {
+ m_query.status |= status & DT_STATUS_DETAIL_MASK;
break;
}
- node = m_nodePool->getNodeAtIdx(node->pidx);
+ node = next;
}
while (node);
}
@@ -1457,7 +1540,7 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing
dtNode* node = 0;
for (int i = existingSize-1; i >= 0; --i)
{
- node = m_nodePool->findNode(existing[i]);
+ m_nodePool->findNodes(existing[i], &node, 1);
if (node)
break;
}
@@ -1470,11 +1553,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing
}
// Reverse the path.
+ int prevRay = 0;
do
{
dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
node->pidx = m_nodePool->getNodeIdx(prev);
prev = node;
+ int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
+ node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
+ prevRay = nextRay;
node = next;
}
while (node);
@@ -1483,13 +1570,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing
node = prev;
do
{
- path[n++] = node->id;
- if (n >= maxPath)
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ dtStatus status = 0;
+ if (node->flags & DT_NODE_PARENT_DETACHED)
+ {
+ float t, normal[3];
+ int m;
+ status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
+ n += m;
+ // raycast ends on poly boundary and the path might include the next poly boundary.
+ if (path[n-1] == next->id)
+ n--; // remove to avoid duplicates
+ }
+ else
+ {
+ path[n++] = node->id;
+ if (n >= maxPath)
+ status = DT_BUFFER_TOO_SMALL;
+ }
+
+ if (status & DT_STATUS_DETAIL_MASK)
{
- m_query.status |= DT_BUFFER_TOO_SMALL;
+ m_query.status |= status & DT_STATUS_DETAIL_MASK;
break;
}
- node = m_nodePool->getNodeAtIdx(node->pidx);
+ node = next;
}
while (node);
}
@@ -2161,6 +2266,8 @@ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly,
return DT_SUCCESS;
}
+
+
/// @par
///
/// This method is meant to be used for quick, short distance checks.
@@ -2203,73 +2310,144 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
const dtQueryFilter* filter,
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const
{
- dtAssert(m_nav);
+ dtRaycastHit hit;
+ hit.path = path;
+ hit.maxPath = maxPath;
+
+ dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit);
- *t = 0;
+ *t = hit.t;
+ if (hitNormal)
+ dtVcopy(hitNormal, hit.hitNormal);
if (pathCount)
- *pathCount = 0;
+ *pathCount = hit.pathCount;
+
+ return status;
+}
+
+
+/// @par
+///
+/// This method is meant to be used for quick, short distance checks.
+///
+/// If the path array is too small to hold the result, it will be filled as
+/// far as possible from the start postion toward the end position.
+///
+/// Using the Hit Parameter t of RaycastHit
+///
+/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit
+/// the end position. In this case the path represents a valid corridor to the
+/// end position and the value of @p hitNormal is undefined.
+///
+/// If the hit parameter is zero, then the start position is on the wall that
+/// was hit and the value of @p hitNormal is undefined.
+///
+/// If 0 < t < 1.0 then the following applies:
+///
+/// @code
+/// distanceToHitBorder = distanceToEndPosition * t
+/// hitPoint = startPos + (endPos - startPos) * t
+/// @endcode
+///
+/// Use Case Restriction
+///
+/// The raycast ignores the y-value of the end position. (2D check.) This
+/// places significant limits on how it can be used. For example:
+///
+/// Consider a scene where there is a main floor with a second floor balcony
+/// that hangs over the main floor. So the first floor mesh extends below the
+/// balcony mesh. The start position is somewhere on the first floor. The end
+/// position is on the balcony.
+///
+/// The raycast will search toward the end position along the first floor mesh.
+/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX
+/// (no wall hit), meaning it reached the end position. This is one example of why
+/// this method is meant for short distance checks.
+///
+dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter, const unsigned int options,
+ dtRaycastHit* hit, dtPolyRef prevRef) const
+{
+ dtAssert(m_nav);
+ hit->t = 0;
+ hit->pathCount = 0;
+ hit->pathCost = 0;
+
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
return DT_FAILURE | DT_INVALID_PARAM;
+ if (prevRef && !m_nav->isValidPolyRef(prevRef))
+ return DT_FAILURE | DT_INVALID_PARAM;
- dtPolyRef curRef = startRef;
- float verts[DT_VERTS_PER_POLYGON*3];
+ float dir[3], curPos[3], lastPos[3];
+ float verts[DT_VERTS_PER_POLYGON*3+3];
int n = 0;
-
- hitNormal[0] = 0;
- hitNormal[1] = 0;
- hitNormal[2] = 0;
-
+
+ dtVcopy(curPos, startPos);
+ dtVsub(dir, endPos, startPos);
+ dtVset(hit->hitNormal, 0, 0, 0);
+
dtStatus status = DT_SUCCESS;
-
+
+ const dtMeshTile* prevTile, *tile, *nextTile;
+ const dtPoly* prevPoly, *poly, *nextPoly;
+ dtPolyRef curRef, nextRef;
+
+ // The API input has been checked already, skip checking internal data.
+ nextRef = curRef = startRef;
+ tile = 0;
+ poly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
+ nextTile = prevTile = tile;
+ nextPoly = prevPoly = poly;
+ if (prevRef)
+ m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly);
+
while (curRef)
{
// Cast ray against current polygon.
- // The API input has been cheked already, skip checking internal data.
- const dtMeshTile* tile = 0;
- const dtPoly* poly = 0;
- m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
-
// Collect vertices.
int nv = 0;
for (int i = 0; i < (int)poly->vertCount; ++i)
{
dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
nv++;
- }
+ }
float tmin, tmax;
int segMin, segMax;
if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax))
{
// Could not hit the polygon, keep the old t and report hit.
- if (pathCount)
- *pathCount = n;
+ hit->pathCount = n;
return status;
}
// Keep track of furthest t so far.
- if (tmax > *t)
- *t = tmax;
+ if (tmax > hit->t)
+ hit->t = tmax;
// Store visited polygons.
- if (n < maxPath)
- path[n++] = curRef;
+ if (n < hit->maxPath)
+ hit->path[n++] = curRef;
else
status |= DT_BUFFER_TOO_SMALL;
-
+
// Ray end is completely inside the polygon.
if (segMax == -1)
{
- *t = FLT_MAX;
- if (pathCount)
- *pathCount = n;
+ hit->t = FLT_MAX;
+ hit->pathCount = n;
+
+ // add the cost
+ if (options & DT_RAYCAST_USE_COSTS)
+ hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly);
return status;
}
-
+
// Follow neighbours.
- dtPolyRef nextRef = 0;
+ nextRef = 0;
for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
{
@@ -2280,8 +2458,8 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
continue;
// Get pointer to the next polygon.
- const dtMeshTile* nextTile = 0;
- const dtPoly* nextPoly = 0;
+ nextTile = 0;
+ nextPoly = 0;
m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
// Skip off-mesh connections.
@@ -2349,6 +2527,24 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
}
}
+ // add the cost
+ if (options & DT_RAYCAST_USE_COSTS)
+ {
+ // compute the intersection point at the furthest end of the polygon
+ // and correct the height (since the raycast moves in 2d)
+ dtVcopy(lastPos, curPos);
+ dtVmad(curPos, startPos, dir, hit->t);
+ float* e1 = &verts[segMax*3];
+ float* e2 = &verts[((segMax+1)%nv)*3];
+ float eDir[3], diff[3];
+ dtVsub(eDir, e2, e1);
+ dtVsub(diff, curPos, e1);
+ float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2];
+ curPos[1] = e1[1] + eDir[1] * s;
+
+ hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly);
+ }
+
if (!nextRef)
{
// No neighbour, we hit a wall.
@@ -2360,22 +2556,25 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
const float* vb = &verts[b*3];
const float dx = vb[0] - va[0];
const float dz = vb[2] - va[2];
- hitNormal[0] = dz;
- hitNormal[1] = 0;
- hitNormal[2] = -dx;
- dtVnormalize(hitNormal);
+ hit->hitNormal[0] = dz;
+ hit->hitNormal[1] = 0;
+ hit->hitNormal[2] = -dx;
+ dtVnormalize(hit->hitNormal);
- if (pathCount)
- *pathCount = n;
+ hit->pathCount = n;
return status;
}
-
+
// No hit, advance to neighbour polygon.
+ prevRef = curRef;
curRef = nextRef;
+ prevTile = tile;
+ tile = nextTile;
+ prevPoly = poly;
+ poly = nextPoly;
}
- if (pathCount)
- *pathCount = n;
+ hit->pathCount = n;
return status;
}
@@ -3333,6 +3532,15 @@ bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter)
bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
{
if (!m_nodePool) return false;
- const dtNode* node = m_nodePool->findNode(ref);
- return node && node->flags & DT_NODE_CLOSED;
+
+ dtNode* nodes[DT_MAX_STATES_PER_NODE];
+ int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE);
+
+ for (int i=0; iflags & DT_NODE_CLOSED)
+ return true;
+ }
+
+ return false;
}
diff --git a/dep/recastnavigation/Detour/Source/DetourNode.cpp b/dep/recastnavigation/Detour/Source/DetourNode.cpp
index 4c8215e20d0b0..1d1897708f46c 100644
--- a/dep/recastnavigation/Detour/Source/DetourNode.cpp
+++ b/dep/recastnavigation/Detour/Source/DetourNode.cpp
@@ -71,27 +71,46 @@ void dtNodePool::clear()
m_nodeCount = 0;
}
-dtNode* dtNodePool::findNode(dtPolyRef id)
+unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes)
{
+ int n = 0;
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
while (i != DT_NULL_IDX)
{
if (m_nodes[i].id == id)
+ {
+ if (n >= maxNodes)
+ return n;
+ nodes[n++] = &m_nodes[i];
+ }
+ i = m_next[i];
+ }
+
+ return n;
+}
+
+dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ dtNodeIndex i = m_first[bucket];
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
return 0;
}
-dtNode* dtNodePool::getNode(dtPolyRef id)
+dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
{
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
dtNode* node = 0;
while (i != DT_NULL_IDX)
{
- if (m_nodes[i].id == id)
+ if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
@@ -108,6 +127,7 @@ dtNode* dtNodePool::getNode(dtPolyRef id)
node->cost = 0;
node->total = 0;
node->id = id;
+ node->state = state;
node->flags = 0;
m_next[i] = m_first[bucket];
diff --git a/dep/recastnavigation/Recast/Include/Recast.h b/dep/recastnavigation/Recast/Include/Recast.h
index 3f4ae96d1c9ba..66974cdbcc36f 100644
--- a/dep/recastnavigation/Recast/Include/Recast.h
+++ b/dep/recastnavigation/Recast/Include/Recast.h
@@ -338,7 +338,7 @@ struct rcHeightfieldLayer
int maxy; ///< The maximum y-bounds of usable data. (Along the z-axis.)
int hmin; ///< The minimum height bounds of usable data. (Along the y-axis.)
int hmax; ///< The maximum height bounds of usable data. (Along the y-axis.)
- unsigned char* heights; ///< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)]
+ unsigned char* heights; ///< The heightfield. [Size: width * height]
unsigned char* areas; ///< Area ids. [Size: Same as #heights]
unsigned char* cons; ///< Packed neighbor connection information. [Size: Same as #heights]
};
@@ -969,7 +969,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos,
/// @returns True if the operation completed successfully.
bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
-/// Builds region data for the heightfield using watershed partitioning.
+/// Builds region data for the heightfield using watershed partitioning.
/// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation.
/// @param[in,out] chf A populated compact heightfield.
@@ -983,6 +983,18 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
const int borderSize, const int minRegionArea, const int mergeRegionArea);
+/// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers.
+/// @ingroup recast
+/// @param[in,out] ctx The build context to use during the operation.
+/// @param[in,out] chf A populated compact heightfield.
+/// @param[in] borderSize The size of the non-navigable border around the heightfield.
+/// [Limit: >=0] [Units: vx]
+/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas.
+/// [Limit: >=0] [Units: vx].
+/// @returns True if the operation completed successfully.
+bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea);
+
/// Builds region data for the heightfield using simple monotone partitioning.
/// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation.
@@ -997,7 +1009,6 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
const int borderSize, const int minRegionArea, const int mergeRegionArea);
-
/// Sets the neighbor connection data for the specified direction.
/// @param[in] s The span to update.
/// @param[in] dir The direction to set. [Limits: 0 <= value < 4]
diff --git a/dep/recastnavigation/Recast/Source/RecastContour.cpp b/dep/recastnavigation/Recast/Source/RecastContour.cpp
index 5c324bcedfeed..8aa9d1d92a1ec 100644
--- a/dep/recastnavigation/Recast/Source/RecastContour.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastContour.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include "Recast.h"
#include "RecastAlloc.h"
#include "RecastAssert.h"
@@ -36,7 +37,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
unsigned int regs[4] = {0,0,0,0};
// Combine region and area codes in order to prevent
- // border vertices which are in between two areas to be removed.
+ // border vertices which are in between two areas to be removed.
regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
@@ -187,27 +188,6 @@ static float distancePtSeg(const int x, const int z,
const int px, const int pz,
const int qx, const int qz)
{
-/* float pqx = (float)(qx - px);
- float pqy = (float)(qy - py);
- float pqz = (float)(qz - pz);
- float dx = (float)(x - px);
- float dy = (float)(y - py);
- float dz = (float)(z - pz);
- float d = pqx*pqx + pqy*pqy + pqz*pqz;
- float t = pqx*dx + pqy*dy + pqz*dz;
- if (d > 0)
- t /= d;
- if (t < 0)
- t = 0;
- else if (t > 1)
- t = 1;
-
- dx = px + t*pqx - x;
- dy = py + t*pqy - y;
- dz = pz + t*pqz - z;
-
- return dx*dx + dy*dy + dz*dz;*/
-
float pqx = (float)(qx - px);
float pqz = (float)(qz - pz);
float dx = (float)(x - px);
@@ -257,13 +237,13 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
simplified.push(points[i*4+2]);
simplified.push(i);
}
- }
+ }
}
if (simplified.size() == 0)
{
// If there is no connections at all,
- // create some initial points for the simplification process.
+ // create some initial points for the simplification process.
// Find lower-left and upper-right vertices of the contour.
int llx = points[0];
int lly = points[1];
@@ -311,19 +291,19 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
{
int ii = (i+1) % (simplified.size()/4);
- const int ax = simplified[i*4+0];
- const int az = simplified[i*4+2];
- const int ai = simplified[i*4+3];
-
- const int bx = simplified[ii*4+0];
- const int bz = simplified[ii*4+2];
- const int bi = simplified[ii*4+3];
+ int ax = simplified[i*4+0];
+ int az = simplified[i*4+2];
+ int ai = simplified[i*4+3];
+
+ int bx = simplified[ii*4+0];
+ int bz = simplified[ii*4+2];
+ int bi = simplified[ii*4+3];
// Find maximum deviation from the segment.
float maxd = 0;
int maxi = -1;
int ci, cinc, endi;
-
+
// Traverse the segment in lexilogical order so that the
// max deviation is calculated similarly when traversing
// opposite segments.
@@ -338,6 +318,8 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
cinc = pn-1;
ci = (bi+cinc) % pn;
endi = ai;
+ rcSwap(ax, bx);
+ rcSwap(az, bz);
}
// Tessellate only outer edges or edges between areas.
@@ -397,11 +379,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
const int bx = simplified[ii*4+0];
const int bz = simplified[ii*4+2];
const int bi = simplified[ii*4+3];
-
+
// Find maximum deviation from the segment.
int maxi = -1;
int ci = (ai+1) % pn;
-
+
// Tessellate only outer edges or edges between areas.
bool tess = false;
// Wall edges.
@@ -469,32 +451,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
}
-static void removeDegenerateSegments(rcIntArray& simplified)
-{
- // Remove adjacent vertices which are equal on xz-plane,
- // or else the triangulator will get confused.
- for (int i = 0; i < simplified.size()/4; ++i)
- {
- int ni = i+1;
- if (ni >= (simplified.size()/4))
- ni = 0;
-
- if (simplified[i*4+0] == simplified[ni*4+0] &&
- simplified[i*4+2] == simplified[ni*4+2])
- {
- // Degenerate segment, remove.
- for (int j = i; j < simplified.size()/4-1; ++j)
- {
- simplified[j*4+0] = simplified[(j+1)*4+0];
- simplified[j*4+1] = simplified[(j+1)*4+1];
- simplified[j*4+2] = simplified[(j+1)*4+2];
- simplified[j*4+3] = simplified[(j+1)*4+3];
- }
- simplified.resize(simplified.size()-4);
- }
- }
-}
-
static int calcAreaOfPolygon2D(const int* verts, const int nverts)
{
int area = 0;
@@ -507,54 +463,155 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts)
return (area+1) / 2;
}
-inline bool ileft(const int* a, const int* b, const int* c)
+// TODO: these are the same as in RecastMesh.cpp, consider using the same.
+
+inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
+inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
+
+inline int area2(const int* a, const int* b, const int* c)
+{
+ return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]);
+}
+
+// Exclusive or: true iff exactly one argument is true.
+// The arguments are negated to ensure that they are 0/1
+// values. Then the bitwise Xor operator may apply.
+// (This idea is due to Michael Baldwin.)
+inline bool xorb(bool x, bool y)
+{
+ return !x ^ !y;
+}
+
+// Returns true iff c is strictly to the left of the directed
+// line through a to b.
+inline bool left(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) < 0;
+}
+
+inline bool leftOn(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) <= 0;
+}
+
+inline bool collinear(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) == 0;
+}
+
+// Returns true iff ab properly intersects cd: they share
+// a point interior to both segments. The properness of the
+// intersection is ensured by using strict leftness.
+static bool intersectProp(const int* a, const int* b, const int* c, const int* d)
+{
+ // Eliminate improper cases.
+ if (collinear(a,b,c) || collinear(a,b,d) ||
+ collinear(c,d,a) || collinear(c,d,b))
+ return false;
+
+ return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
+}
+
+// Returns T iff (a,b,c) are collinear and point c lies
+// on the closed segement ab.
+static bool between(const int* a, const int* b, const int* c)
+{
+ if (!collinear(a, b, c))
+ return false;
+ // If ab not vertical, check betweenness on x; else on y.
+ if (a[0] != b[0])
+ return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
+ else
+ return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
+}
+
+// Returns true iff segments ab and cd intersect, properly or improperly.
+static bool intersect(const int* a, const int* b, const int* c, const int* d)
+{
+ if (intersectProp(a, b, c, d))
+ return true;
+ else if (between(a, b, c) || between(a, b, d) ||
+ between(c, d, a) || between(c, d, b))
+ return true;
+ else
+ return false;
+}
+
+static bool vequal(const int* a, const int* b)
+{
+ return a[0] == b[0] && a[2] == b[2];
+}
+
+static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts)
{
- return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
+ // For each edge (k,k+1) of P
+ for (int k = 0; k < n; k++)
+ {
+ int k1 = next(k, n);
+ // Skip edges incident to i.
+ if (i == k || i == k1)
+ continue;
+ const int* p0 = &verts[k * 4];
+ const int* p1 = &verts[k1 * 4];
+ if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+ continue;
+
+ if (intersect(d0, d1, p0, p1))
+ return true;
+ }
+ return false;
}
-static void getClosestIndices(const int* vertsa, const int nvertsa,
- const int* vertsb, const int nvertsb,
- int& ia, int& ib)
+static bool inCone(int i, int n, const int* verts, const int* pj)
{
- int closestDist = 0xfffffff;
- ia = -1, ib = -1;
- for (int i = 0; i < nvertsa; ++i)
+ const int* pi = &verts[i * 4];
+ const int* pi1 = &verts[next(i, n) * 4];
+ const int* pin1 = &verts[prev(i, n) * 4];
+
+ // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+ if (leftOn(pin1, pi, pi1))
+ return left(pi, pj, pin1) && left(pj, pi, pi1);
+ // Assume (i-1,i,i+1) not collinear.
+ // else P[i] is reflex.
+ return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+
+static void removeDegenerateSegments(rcIntArray& simplified)
+{
+ // Remove adjacent vertices which are equal on xz-plane,
+ // or else the triangulator will get confused.
+ int npts = simplified.size()/4;
+ for (int i = 0; i < npts; ++i)
{
- const int in = (i+1) % nvertsa;
- const int ip = (i+nvertsa-1) % nvertsa;
- const int* va = &vertsa[i*4];
- const int* van = &vertsa[in*4];
- const int* vap = &vertsa[ip*4];
+ int ni = next(i, npts);
- for (int j = 0; j < nvertsb; ++j)
+ if (vequal(&simplified[i*4], &simplified[ni*4]))
{
- const int* vb = &vertsb[j*4];
- // vb must be "infront" of va.
- if (ileft(vap,va,vb) && ileft(va,van,vb))
+ // Degenerate segment, remove.
+ for (int j = i; j < simplified.size()/4-1; ++j)
{
- const int dx = vb[0] - va[0];
- const int dz = vb[2] - va[2];
- const int d = dx*dx + dz*dz;
- if (d < closestDist)
- {
- ia = i;
- ib = j;
- closestDist = d;
- }
+ simplified[j*4+0] = simplified[(j+1)*4+0];
+ simplified[j*4+1] = simplified[(j+1)*4+1];
+ simplified[j*4+2] = simplified[(j+1)*4+2];
+ simplified[j*4+3] = simplified[(j+1)*4+3];
}
+ simplified.resize(simplified.size()-4);
+ npts--;
}
}
}
+
static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
{
const int maxVerts = ca.nverts + cb.nverts + 2;
int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
if (!verts)
return false;
-
+
int nv = 0;
-
+
// Copy contour A.
for (int i = 0; i <= ca.nverts; ++i)
{
@@ -582,7 +639,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
rcFree(ca.verts);
ca.verts = verts;
ca.nverts = nv;
-
+
rcFree(cb.verts);
cb.verts = 0;
cb.nverts = 0;
@@ -590,18 +647,179 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
return true;
}
+struct rcContourHole
+{
+ rcContour* contour;
+ int minx, minz, leftmost;
+};
+
+struct rcContourRegion
+{
+ rcContour* outline;
+ rcContourHole* holes;
+ int nholes;
+};
+
+struct rcPotentialDiagonal
+{
+ int vert;
+ int dist;
+};
+
+// Finds the lowest leftmost vertex of a contour.
+static void findLeftMostVertex(rcContour* contour, int* minx, int* minz, int* leftmost)
+{
+ *minx = contour->verts[0];
+ *minz = contour->verts[2];
+ *leftmost = 0;
+ for (int i = 1; i < contour->nverts; i++)
+ {
+ const int x = contour->verts[i*4+0];
+ const int z = contour->verts[i*4+2];
+ if (x < *minx || (x == *minx && z < *minz))
+ {
+ *minx = x;
+ *minz = z;
+ *leftmost = i;
+ }
+ }
+}
+
+static int compareHoles(const void* va, const void* vb)
+{
+ const rcContourHole* a = (const rcContourHole*)va;
+ const rcContourHole* b = (const rcContourHole*)vb;
+ if (a->minx == b->minx)
+ {
+ if (a->minz < b->minz)
+ return -1;
+ if (a->minz > b->minz)
+ return 1;
+ }
+ else
+ {
+ if (a->minx < b->minx)
+ return -1;
+ if (a->minx > b->minx)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int compareDiagDist(const void* va, const void* vb)
+{
+ const rcPotentialDiagonal* a = (const rcPotentialDiagonal*)va;
+ const rcPotentialDiagonal* b = (const rcPotentialDiagonal*)vb;
+ if (a->dist < b->dist)
+ return -1;
+ if (a->dist > b->dist)
+ return 1;
+ return 0;
+}
+
+
+static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
+{
+ // Sort holes from left to right.
+ for (int i = 0; i < region.nholes; i++)
+ findLeftMostVertex(region.holes[i].contour, ®ion.holes[i].minx, ®ion.holes[i].minz, ®ion.holes[i].leftmost);
+
+ qsort(region.holes, region.nholes, sizeof(rcContourHole), compareHoles);
+
+ int maxVerts = region.outline->nverts;
+ for (int i = 0; i < region.nholes; i++)
+ maxVerts += region.holes[i].contour->nverts;
+
+ rcScopedDelete diags = (rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP);
+ if (!diags)
+ {
+ ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts);
+ return;
+ }
+
+ rcContour* outline = region.outline;
+
+ // Merge holes into the outline one by one.
+ for (int i = 0; i < region.nholes; i++)
+ {
+ rcContour* hole = region.holes[i].contour;
+
+ int index = -1;
+ int bestVertex = region.holes[i].leftmost;
+ for (int iter = 0; iter < hole->nverts; iter++)
+ {
+ // Find potential diagonals.
+ // The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline.
+ // ..o j-1
+ // |
+ // | * best
+ // |
+ // j o-----o j+1
+ // :
+ int ndiags = 0;
+ const int* corner = &hole->verts[bestVertex*4];
+ for (int j = 0; j < outline->nverts; j++)
+ {
+ if (inCone(j, outline->nverts, outline->verts, corner))
+ {
+ int dx = outline->verts[j*4+0] - corner[0];
+ int dz = outline->verts[j*4+2] - corner[2];
+ diags[ndiags].vert = j;
+ diags[ndiags].dist = dx*dx + dz*dz;
+ ndiags++;
+ }
+ }
+ // Sort potential diagonals by distance, we want to make the connection as short as possible.
+ qsort(diags, ndiags, sizeof(rcPotentialDiagonal), compareDiagDist);
+
+ // Find a diagonal that is not intersecting the outline not the remaining holes.
+ index = -1;
+ for (int j = 0; j < ndiags; j++)
+ {
+ const int* pt = &outline->verts[diags[j].vert*4];
+ bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
+ for (int k = i; k < region.nholes && !intersect; k++)
+ intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
+ if (!intersect)
+ {
+ index = diags[j].vert;
+ break;
+ }
+ }
+ // If found non-intersecting diagonal, stop looking.
+ if (index != -1)
+ break;
+ // All the potential diagonals for the current vertex were intersecting, try next vertex.
+ bestVertex = (bestVertex + 1) % hole->nverts;
+ }
+
+ if (index == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole);
+ continue;
+ }
+ if (!mergeContours(*region.outline, *hole, index, bestVertex))
+ {
+ ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole);
+ continue;
+ }
+ }
+}
+
+
/// @par
///
/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
/// parameters control how closely the simplified contours will match the raw contours.
///
-/// Simplified contours are generated such that the vertices for portals between areas match up.
+/// Simplified contours are generated such that the vertices for portals between areas match up.
/// (They are considered mandatory vertices.)
///
/// Setting @p maxEdgeLength to zero will disabled the edge length feature.
-///
+///
/// See the #rcConfig documentation for more information on the configuration parameters.
-///
+///
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
const float maxError, const int maxEdgeLen,
@@ -704,17 +922,17 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
verts.resize(0);
simplified.resize(0);
-
+
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
walkContour(x, y, i, chf, flags, verts);
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
-
+
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
removeDegenerateSegments(simplified);
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
-
+
// Store region->contour remap info.
// Create contour.
if (simplified.size()/4 >= 3)
@@ -722,7 +940,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
if (cset.nconts >= maxContours)
{
// Allocate more contours.
- // This can happen when there are tiny holes in the heightfield.
+ // This happens when a region has holes.
const int oldMax = maxContours;
maxContours *= 2;
rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
@@ -735,10 +953,10 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
}
rcFree(cset.conts);
cset.conts = newConts;
-
+
ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours);
}
-
+
rcContour* cont = &cset.conts[cset.nconts++];
cont->nverts = simplified.size()/4;
@@ -779,17 +997,6 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
}
}
-/* cont->cx = cont->cy = cont->cz = 0;
- for (int i = 0; i < cont->nverts; ++i)
- {
- cont->cx += cont->verts[i*4+0];
- cont->cy += cont->verts[i*4+1];
- cont->cz += cont->verts[i*4+2];
- }
- cont->cx /= cont->nverts;
- cont->cy /= cont->nverts;
- cont->cz /= cont->nverts;*/
-
cont->reg = reg;
cont->area = area;
}
@@ -797,52 +1004,100 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
}
}
- // Check and merge droppings.
- // Sometimes the previous algorithms can fail and create several contours
- // per area. This pass will try to merge the holes into the main region.
- for (int i = 0; i < cset.nconts; ++i)
+ // Merge holes if needed.
+ if (cset.nconts > 0)
{
- rcContour& cont = cset.conts[i];
- // Check if the contour is would backwards.
- if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
+ // Calculate winding of all polygons.
+ rcScopedDelete winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP);
+ if (!winding)
{
- // Find another contour which has the same region ID.
- int mergeIdx = -1;
- for (int j = 0; j < cset.nconts; ++j)
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts);
+ return false;
+ }
+ int nholes = 0;
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+ // If the contour is wound backwards, it is a hole.
+ winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1;
+ if (winding[i] < 0)
+ nholes++;
+ }
+
+ if (nholes > 0)
+ {
+ // Collect outline contour and holes contours per region.
+ // We assume that there is one outline and multiple holes.
+ const int nregions = chf.maxRegions+1;
+ rcScopedDelete regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP);
+ if (!regions)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions);
+ return false;
+ }
+ memset(regions, 0, sizeof(rcContourRegion)*nregions);
+
+ rcScopedDelete holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP);
+ if (!holes)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts);
+ return false;
+ }
+ memset(holes, 0, sizeof(rcContourHole)*cset.nconts);
+
+ for (int i = 0; i < cset.nconts; ++i)
{
- if (i == j) continue;
- if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
+ rcContour& cont = cset.conts[i];
+ // Positively would contours are outlines, negative holes.
+ if (winding[i] > 0)
{
- // Make sure the polygon is correctly oriented.
- if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
- {
- mergeIdx = j;
- break;
- }
+ if (regions[cont.reg].outline)
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg);
+ regions[cont.reg].outline = &cont;
+ }
+ else
+ {
+ regions[cont.reg].nholes++;
}
}
- if (mergeIdx == -1)
+ int index = 0;
+ for (int i = 0; i < nregions; i++)
{
- ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
+ if (regions[i].nholes > 0)
+ {
+ regions[i].holes = &holes[index];
+ index += regions[i].nholes;
+ regions[i].nholes = 0;
+ }
}
- else
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+ rcContourRegion& reg = regions[cont.reg];
+ if (winding[i] < 0)
+ reg.holes[reg.nholes++].contour = &cont;
+ }
+
+ // Finally merge each regions holes into the outline.
+ for (int i = 0; i < nregions; i++)
{
- rcContour& mcont = cset.conts[mergeIdx];
- // Merge by closest points.
- int ia = 0, ib = 0;
- getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
- if (ia == -1 || ib == -1)
+ rcContourRegion& reg = regions[i];
+ if (!reg.nholes) continue;
+
+ if (reg.outline)
{
- ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
- continue;
+ mergeRegionHoles(ctx, reg);
}
- if (!mergeContours(mcont, cont, ia, ib))
+ else
{
- ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
- continue;
+ // The region does not have an outline.
+ // This can happen if the contour becaomes selfoverlapping because of
+ // too aggressive simplification settings.
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i);
}
}
}
+
}
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
diff --git a/dep/recastnavigation/Recast/Source/RecastLayers.cpp b/dep/recastnavigation/Recast/Source/RecastLayers.cpp
index 204f72e8cb2f8..cb1a39f4bda66 100644
--- a/dep/recastnavigation/Recast/Source/RecastLayers.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastLayers.cpp
@@ -38,7 +38,7 @@ struct rcLayerRegion
unsigned char layerId; // Layer ID
unsigned char nlayers; // Layer count
unsigned char nneis; // Neighbour count
- unsigned char base; // Flag indicating if the region is hte base of merged regions.
+ unsigned char base; // Flag indicating if the region is the base of merged regions.
};
diff --git a/dep/recastnavigation/Recast/Source/RecastMesh.cpp b/dep/recastnavigation/Recast/Source/RecastMesh.cpp
index 8af609b79fbb8..e4f9c4b3629bb 100644
--- a/dep/recastnavigation/Recast/Source/RecastMesh.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastMesh.cpp
@@ -288,6 +288,53 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices)
return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
}
+
+static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
+
+ // For each edge (k,k+1) of P
+ for (int k = 0; k < n; k++)
+ {
+ int k1 = next(k, n);
+ // Skip edges incident to i or j
+ if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
+ {
+ const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
+ const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
+
+ if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+ continue;
+
+ if (intersectProp(d0, d1, p0, p1))
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool inConeLoose(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
+ const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
+ const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
+
+ // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+ if (leftOn(pin1, pi, pi1))
+ return leftOn(pi, pj, pin1) && leftOn(pj, pi, pi1);
+ // Assume (i-1,i,i+1) not collinear.
+ // else P[i] is reflex.
+ return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices)
+{
+ return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices);
+}
+
+
static int triangulate(int n, const int* verts, int* indices, int* tris)
{
int ntris = 0;
@@ -328,14 +375,41 @@ static int triangulate(int n, const int* verts, int* indices, int* tris)
if (mini == -1)
{
- // Should not happen.
-/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+ // We might get here because the contour has overlapping segments, like this:
+ //
+ // A o-o=====o---o B
+ // / |C D| \
+ // o o o o
+ // : : : :
+ // We'll try to recover by loosing up the inCone test a bit so that a diagonal
+ // like A-B or C-D can be found and we can continue.
+ minLen = -1;
+ mini = -1;
for (int i = 0; i < n; i++)
{
- printf("%d ", indices[i] & 0x0fffffff);
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+ if (diagonalLoose(i, i2, n, verts, indices))
+ {
+ const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* p2 = &verts[(indices[next(i2, n)] & 0x0fffffff) * 4];
+ int dx = p2[0] - p0[0];
+ int dy = p2[2] - p0[2];
+ int len = dx*dx + dy*dy;
+
+ if (minLen < 0 || len < minLen)
+ {
+ minLen = len;
+ mini = i;
+ }
+ }
+ }
+ if (mini == -1)
+ {
+ // The contour is messed up. This sometimes happens
+ // if the contour simplification is too aggressive.
+ return -ntris;
}
- printf("\n");*/
- return -ntris;
}
int i = mini;
@@ -1463,7 +1537,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys);
return false;
}
- memcpy(dst.flags, src.flags, sizeof(unsigned char)*src.npolys);
+ memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys);
return true;
}
diff --git a/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp
index 8325b8837074e..5cc2adf0320e8 100644
--- a/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp
@@ -56,7 +56,7 @@ inline float vdist2(const float* p, const float* q)
}
inline float vcross2(const float* p1, const float* p2, const float* p3)
-{
+{
const float u1 = p2[0] - p1[0];
const float v1 = p2[2] - p1[2];
const float u2 = p3[0] - p1[0];
@@ -68,21 +68,27 @@ static bool circumCircle(const float* p1, const float* p2, const float* p3,
float* c, float& r)
{
static const float EPS = 1e-6f;
+ // Calculate the circle relative to p1, to avoid some precision issues.
+ const float v1[3] = {0,0,0};
+ float v2[3], v3[3];
+ rcVsub(v2, p2,p1);
+ rcVsub(v3, p3,p1);
- const float cp = vcross2(p1, p2, p3);
+ const float cp = vcross2(v1, v2, v3);
if (fabsf(cp) > EPS)
{
- const float p1Sq = vdot2(p1,p1);
- const float p2Sq = vdot2(p2,p2);
- const float p3Sq = vdot2(p3,p3);
- c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp);
- c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp);
- r = vdist2(c, p1);
+ const float v1Sq = vdot2(v1,v1);
+ const float v2Sq = vdot2(v2,v2);
+ const float v3Sq = vdot2(v3,v3);
+ c[0] = (v1Sq*(v2[2]-v3[2]) + v2Sq*(v3[2]-v1[2]) + v3Sq*(v1[2]-v2[2])) / (2*cp);
+ c[1] = 0;
+ c[2] = (v1Sq*(v3[0]-v2[0]) + v2Sq*(v1[0]-v3[0]) + v3Sq*(v2[0]-v1[0])) / (2*cp);
+ r = vdist2(c, v1);
+ rcVadd(c, c, p1);
return true;
}
-
- c[0] = p1[0];
- c[2] = p1[2];
+
+ rcVcopy(c, p1);
r = 0;
return false;
}
@@ -93,7 +99,7 @@ static float distPtTri(const float* p, const float* a, const float* b, const flo
rcVsub(v0, c,a);
rcVsub(v1, b,a);
rcVsub(v2, p,a);
-
+
const float dot00 = vdot2(v0, v0);
const float dot01 = vdot2(v0, v1);
const float dot02 = vdot2(v0, v2);
@@ -178,7 +184,7 @@ static float distToTriMesh(const float* p, const float* verts, const int /*nvert
static float distToPoly(int nvert, const float* verts, const float* p)
{
-
+
float dmin = FLT_MAX;
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++)
@@ -216,22 +222,13 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue;
const unsigned short nh = hp.data[nx+nz*hp.width];
if (nh == RC_UNSET_HEIGHT) continue;
-
+
const float d = fabsf(nh*ch - fy);
if (d < dmin)
{
h = nh;
dmin = d;
}
-
-/* const float dx = (nx+0.5f)*cs - fx;
- const float dz = (nz+0.5f)*cs - fz;
- const float d = dx*dx+dz*dz;
- if (d < dmin)
- {
- h = nh;
- dmin = d;
- } */
}
}
return h;
@@ -263,7 +260,7 @@ static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges,
return UNDEF;
}
- // Add edge if not already in the triangulation.
+ // Add edge if not already in the triangulation.
int e = findEdge(edges, nedges, s, t);
if (e == UNDEF)
{
@@ -286,7 +283,7 @@ static void updateLeftFace(int* e, int s, int t, int f)
e[2] = f;
else if (e[1] == s && e[0] == t && e[3] == UNDEF)
e[3] = f;
-}
+}
static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d)
{
@@ -298,7 +295,7 @@ static int overlapSegSeg2d(const float* a, const float* b, const float* c, const
float a4 = a3 + a2 - a1;
if (a3 * a4 < 0.0f)
return 1;
- }
+ }
return 0;
}
@@ -320,7 +317,7 @@ static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1,
static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e)
{
static const float EPS = 1e-5f;
-
+
int* edge = &edges[e*4];
// Cache s and t.
@@ -337,11 +334,11 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges
}
else
{
- // Edge already completed.
+ // Edge already completed.
return;
}
- // Find best point on left of edge.
+ // Find best point on left of edge.
int pt = npts;
float c[3] = {0,0,0};
float r = -1;
@@ -385,20 +382,20 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges
}
}
- // Add new triangle or update edge info if s-t is on hull.
+ // Add new triangle or update edge info if s-t is on hull.
if (pt < npts)
{
- // Update face information of edge being completed.
+ // Update face information of edge being completed.
updateLeftFace(&edges[e*4], s, t, nfaces);
- // Add new edge or update face info of old edge.
+ // Add new edge or update face info of old edge.
e = findEdge(edges, nedges, pt, s);
if (e == UNDEF)
addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF);
else
updateLeftFace(&edges[e*4], pt, s, nfaces);
- // Add new edge or update face info of old edge.
+ // Add new edge or update face info of old edge.
e = findEdge(edges, nedges, t, pt);
if (e == UNDEF)
addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF);
@@ -434,7 +431,7 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
currentEdge++;
}
-
+
// Create tris
tris.resize(nfaces*4);
for (int i = 0; i < nfaces*4; ++i)
@@ -489,6 +486,103 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
}
}
+// Calculate minimum extend of the polygon.
+static float polyMinExtent(const float* verts, const int nverts)
+{
+ float minDist = FLT_MAX;
+ for (int i = 0; i < nverts; i++)
+ {
+ const int ni = (i+1) % nverts;
+ const float* p1 = &verts[i*3];
+ const float* p2 = &verts[ni*3];
+ float maxEdgeDist = 0;
+ for (int j = 0; j < nverts; j++)
+ {
+ if (j == i || j == ni) continue;
+ float d = distancePtSeg2d(&verts[j*3], p1,p2);
+ maxEdgeDist = rcMax(maxEdgeDist, d);
+ }
+ minDist = rcMin(minDist, maxEdgeDist);
+ }
+ return rcSqrt(minDist);
+}
+
+inline int next(int i, int n)
+{
+ return (i+1) % n;
+}
+
+inline int prev(int i, int n)
+{
+ return (i + n-1) % n;
+}
+
+static void triangulateHull(const int nverts, const float* verts, const int nhull, const int* hull, rcIntArray& tris)
+{
+ int start = 0, left = 1, right = nhull-1;
+
+ // Start from an ear with shortest perimeter.
+ // This tends to favor well formed triangles as starting point.
+ float dmin = 0;
+ for (int i = 0; i < nhull; i++)
+ {
+ int pi = prev(i, nhull);
+ int ni = next(i, nhull);
+ const float* pv = &verts[hull[pi]*3];
+ const float* cv = &verts[hull[i]*3];
+ const float* nv = &verts[hull[ni]*3];
+ const float d = vdist2(pv,cv) + vdist2(cv,nv) + vdist2(nv,pv);
+ if (d < dmin)
+ {
+ start = i;
+ left = ni;
+ right = pi;
+ dmin = d;
+ }
+ }
+
+ // Add first triangle
+ tris.push(hull[start]);
+ tris.push(hull[left]);
+ tris.push(hull[right]);
+ tris.push(0);
+
+ // Triangulate the polygon by moving left or right,
+ // depending on which triangle has shorter perimeter.
+ // This heuristic was chose emprically, since it seems
+ // handle tesselated straight edges well.
+ while (next(left, nhull) != right)
+ {
+ // Check to see if se should advance left or right.
+ int nleft = next(left, nhull);
+ int nright = prev(right, nhull);
+
+ const float* cvleft = &verts[hull[left]*3];
+ const float* nvleft = &verts[hull[nleft]*3];
+ const float* cvright = &verts[hull[right]*3];
+ const float* nvright = &verts[hull[nright]*3];
+ const float dleft = vdist2(cvleft, nvleft) + vdist2(nvleft, cvright);
+ const float dright = vdist2(cvright, nvright) + vdist2(cvleft, nvright);
+
+ if (dleft < dright)
+ {
+ tris.push(hull[left]);
+ tris.push(hull[nleft]);
+ tris.push(hull[right]);
+ tris.push(0);
+ left = nleft;
+ }
+ else
+ {
+ tris.push(hull[left]);
+ tris.push(hull[nright]);
+ tris.push(hull[right]);
+ tris.push(0);
+ right = nright;
+ }
+ }
+}
+
inline float getJitterX(const int i)
{
@@ -512,16 +606,22 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
float edge[(MAX_VERTS_PER_EDGE+1)*3];
int hull[MAX_VERTS];
int nhull = 0;
-
+
nverts = 0;
-
+
for (int i = 0; i < nin; ++i)
rcVcopy(&verts[i*3], &in[i*3]);
nverts = nin;
+ edges.resize(0);
+ tris.resize(0);
+
const float cs = chf.cs;
const float ics = 1.0f/cs;
+ // Calculate minimum extents of the polygon based on input data.
+ float minExtent = polyMinExtent(verts, nverts);
+
// Tessellate outlines.
// This is done in separate pass in order to ensure
// seamless height values across the ply boundaries.
@@ -628,27 +728,26 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
}
}
-
+ // If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points.
+ if (minExtent < sampleDist*2)
+ {
+ triangulateHull(nverts, verts, nhull, hull, tris);
+ return true;
+ }
+
// Tessellate the base mesh.
- edges.resize(0);
- tris.resize(0);
-
- delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+ // We're using the triangulateHull instead of delaunayHull as it tends to
+ // create a bit better triangulation for long thing triangles when there
+ // are no internal points.
+ triangulateHull(nverts, verts, nhull, hull, tris);
if (tris.size() == 0)
{
// Could not triangulate the poly, make sure there is some valid data there.
- ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data.");
- for (int i = 2; i < nverts; ++i)
- {
- tris.push(0);
- tris.push(i-1);
- tris.push(i);
- tris.push(0);
- }
+ ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon (%d verts).", nverts);
return true;
}
-
+
if (sampleDist > 0)
{
// Create sample locations in a grid.
@@ -681,7 +780,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
samples.push(0); // Not added
}
}
-
+
// Add the samples starting from the one that has the most
// error. The procedure stops when all samples are added
// or when the max error is within treshold.
@@ -690,7 +789,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
{
if (nverts >= MAX_VERTS)
break;
-
+
// Find sample with most error.
float bestpt[3] = {0,0,0};
float bestd = 0;
@@ -728,24 +827,24 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
edges.resize(0);
tris.resize(0);
delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
- }
+ }
}
-
+
const int ntris = tris.size()/4;
if (ntris > MAX_TRIS)
{
tris.resize(MAX_TRIS*4);
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS);
}
-
+
return true;
}
static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
- const unsigned short* poly, const int npoly,
- const unsigned short* verts, const int bs,
- rcHeightPatch& hp, rcIntArray& stack)
+ const unsigned short* poly, const int npoly,
+ const unsigned short* verts, const int bs,
+ rcHeightPatch& hp, rcIntArray& stack)
{
// Floodfill the heightfield to get 2D height data,
// starting at vertex locations as seeds.
@@ -849,7 +948,7 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
continue;
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
-
+
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
hp.data[idx] = 1;
@@ -858,9 +957,9 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
stack.push(ai);
}
}
-
+
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
-
+
// Mark start locations.
for (int i = 0; i < stack.size(); i += 3)
{
@@ -870,8 +969,8 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
const rcCompactSpan& cs = chf.spans[ci];
hp.data[idx] = cs.y;
-
- // getHeightData seeds are given in coordinates with borders
+
+ // getHeightData seeds are given in coordinates with borders
stack[i+0] += bs;
stack[i+1] += bs;
}
@@ -888,12 +987,12 @@ static void getHeightData(const rcCompactHeightfield& chf,
{
// Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices.
-
+
stack.resize(0);
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
bool empty = true;
-
+
// Copy the height from the same region, and mark region borders
// as seed points to fill the rest.
for (int hy = 0; hy < hp.height; hy++)
@@ -911,7 +1010,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
// Store height
hp.data[hx + hy*hp.width] = s.y;
empty = false;
-
+
// If any of the neighbours is not in same region,
// add the current location as flood fill start
bool border = false;
@@ -940,8 +1039,8 @@ static void getHeightData(const rcCompactHeightfield& chf,
}
}
}
- }
-
+ }
+
// if the polygon does not contian any points from the current region (rare, but happens)
// then use the cells closest to the polygon vertices as seeds to fill the height field
if (empty)
@@ -963,7 +1062,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3));
stack.resize(stack.size()-RETRACT_SIZE*3);
}
-
+
const rcCompactSpan& cs = chf.spans[ci];
for (int dir = 0; dir < 4; ++dir)
{
@@ -982,9 +1081,9 @@ static void getHeightData(const rcCompactHeightfield& chf,
const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir);
const rcCompactSpan& as = chf.spans[ai];
-
+
hp.data[hx + hy*hp.width] = as.y;
-
+
stack.push(ax);
stack.push(ay);
stack.push(ai);
@@ -999,7 +1098,7 @@ static unsigned char getEdgeFlags(const float* va, const float* vb,
static const float thrSqr = rcSqr(0.001f);
for (int i = 0, j = npoly-1; i < npoly; j=i++)
{
- if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
+ if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr)
return 1;
}
@@ -1028,7 +1127,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
-
+
if (mesh.nverts == 0 || mesh.npolys == 0)
return true;
@@ -1107,10 +1206,10 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
return false;
}
-
+
int vcap = nPolyVerts+nPolyVerts/2;
int tcap = vcap*2;
-
+
dmesh.nverts = 0;
dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
if (!dmesh.verts)
@@ -1119,7 +1218,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
return false;
}
dmesh.ntris = 0;
- dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM);
+ dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM);
if (!dmesh.tris)
{
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
@@ -1158,7 +1257,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
{
return false;
}
-
+
// Move detail verts to world space.
for (int j = 0; j < nverts; ++j)
{
@@ -1173,21 +1272,21 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
poly[j*3+1] += orig[1];
poly[j*3+2] += orig[2];
}
-
+
// Store detail submesh.
const int ntris = tris.size()/4;
-
+
dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts;
dmesh.meshes[i*4+1] = (unsigned int)nverts;
dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris;
- dmesh.meshes[i*4+3] = (unsigned int)ntris;
+ dmesh.meshes[i*4+3] = (unsigned int)ntris;
// Store vertices, allocate more memory if necessary.
if (dmesh.nverts+nverts > vcap)
{
while (dmesh.nverts+nverts > vcap)
vcap += 256;
-
+
float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
if (!newv)
{
@@ -1233,9 +1332,9 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
dmesh.ntris++;
}
}
-
+
ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
-
+
return true;
}
@@ -1245,11 +1344,11 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
rcAssert(ctx);
ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
-
+
int maxVerts = 0;
int maxTris = 0;
int maxMeshes = 0;
-
+
for (int i = 0; i < nmeshes; ++i)
{
if (!meshes[i]) continue;
@@ -1257,7 +1356,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
maxTris += meshes[i]->ntris;
maxMeshes += meshes[i]->nmeshes;
}
-
+
mesh.nmeshes = 0;
mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM);
if (!mesh.meshes)
@@ -1265,7 +1364,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
return false;
}
-
+
mesh.ntris = 0;
mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM);
if (!mesh.tris)
@@ -1273,7 +1372,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
return false;
}
-
+
mesh.nverts = 0;
mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM);
if (!mesh.verts)
@@ -1297,7 +1396,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
dst[3] = src[3];
mesh.nmeshes++;
}
-
+
for (int k = 0; k < dm->nverts; ++k)
{
rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
@@ -1312,7 +1411,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
mesh.ntris++;
}
}
-
+
ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
return true;
diff --git a/dep/recastnavigation/Recast/Source/RecastRegion.cpp b/dep/recastnavigation/Recast/Source/RecastRegion.cpp
index 589fac292030b..38bc4ff476fd1 100644
--- a/dep/recastnavigation/Recast/Source/RecastRegion.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastRegion.cpp
@@ -315,6 +315,7 @@ static bool floodRegion(int x, int y, int i,
srcReg[ci] = 0;
continue;
}
+
count++;
// Expand neighbours.
@@ -516,7 +517,11 @@ struct rcRegion
id(i),
areaType(0),
remap(false),
- visited(false)
+ visited(false),
+ overlap(false),
+ connectsToBorder(false),
+ ymin(0xffff),
+ ymax(0)
{}
int spanCount; // Number of spans belonging to this region
@@ -524,6 +529,9 @@ struct rcRegion
unsigned char areaType; // Are type.
bool remap;
bool visited;
+ bool overlap;
+ bool connectsToBorder;
+ unsigned short ymin, ymax;
rcIntArray connections;
rcIntArray floors;
};
@@ -768,10 +776,11 @@ static void walkContour(int x, int y, int i, int dir,
}
}
-static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
- unsigned short& maxRegionId,
- rcCompactHeightfield& chf,
- unsigned short* srcReg)
+
+static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, rcIntArray& overlaps)
{
const int w = chf.width;
const int h = chf.height;
@@ -780,7 +789,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
if (!regions)
{
- ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
+ ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg);
return false;
}
@@ -803,7 +812,6 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
rcRegion& reg = regions[r];
reg.spanCount++;
-
// Update floors.
for (int j = (int)c.index; j < ni; ++j)
{
@@ -811,6 +819,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
unsigned short floorId = srcReg[j];
if (floorId == 0 || floorId >= nreg)
continue;
+ if (floorId == r)
+ reg.overlap = true;
addUniqueFloorRegion(reg, floorId);
}
@@ -906,7 +916,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
}
}
}
-
+
// Merge too small regions to neighbour regions.
int mergeCount = 0 ;
do
@@ -916,7 +926,9 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
{
rcRegion& reg = regions[i];
if (reg.id == 0 || (reg.id & RC_BORDER_REG))
- continue;
+ continue;
+ if (reg.overlap)
+ continue;
if (reg.spanCount == 0)
continue;
@@ -933,7 +945,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
{
if (reg.connections[j] & RC_BORDER_REG) continue;
rcRegion& mreg = regions[reg.connections[j]];
- if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue;
+ if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) || mreg.overlap) continue;
if (mreg.spanCount < smallest &&
canMergeWithRegion(reg, mreg) &&
canMergeWithRegion(mreg, reg))
@@ -997,6 +1009,224 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
}
maxRegionId = regIdGen;
+ // Remap regions.
+ for (int i = 0; i < chf.spanCount; ++i)
+ {
+ if ((srcReg[i] & RC_BORDER_REG) == 0)
+ srcReg[i] = regions[srcReg[i]].id;
+ }
+
+ // Return regions that we found to be overlapping.
+ for (int i = 0; i < nreg; ++i)
+ if (regions[i].overlap)
+ overlaps.push(regions[i].id);
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].~rcRegion();
+ rcFree(regions);
+
+
+ return true;
+}
+
+
+static void addUniqueConnection(rcRegion& reg, int n)
+{
+ for (int i = 0; i < reg.connections.size(); ++i)
+ if (reg.connections[i] == n)
+ return;
+ reg.connections.push(n);
+}
+
+static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, rcIntArray& overlaps)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ const int nreg = maxRegionId+1;
+ rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
+ if (!regions)
+ {
+ ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg);
+ return false;
+ }
+
+ // Construct regions
+ for (int i = 0; i < nreg; ++i)
+ new(®ions[i]) rcRegion((unsigned short)i);
+
+ // Find region neighbours and overlapping regions.
+ rcIntArray lregs(32);
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ lregs.resize(0);
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ const unsigned short ri = srcReg[i];
+ if (ri == 0 || ri >= nreg) continue;
+ rcRegion& reg = regions[ri];
+
+ reg.spanCount++;
+
+ reg.ymin = rcMin(reg.ymin, s.y);
+ reg.ymax = rcMax(reg.ymax, s.y);
+
+ // Collect all region layers.
+ lregs.push(ri);
+
+ // Update neighbours
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ const unsigned short rai = srcReg[ai];
+ if (rai > 0 && rai < nreg && rai != ri)
+ addUniqueConnection(reg, rai);
+ if (rai & RC_BORDER_REG)
+ reg.connectsToBorder = true;
+ }
+ }
+
+ }
+
+ // Update overlapping regions.
+ for (int i = 0; i < lregs.size()-1; ++i)
+ {
+ for (int j = i+1; j < lregs.size(); ++j)
+ {
+ if (lregs[i] != lregs[j])
+ {
+ rcRegion& ri = regions[lregs[i]];
+ rcRegion& rj = regions[lregs[j]];
+ addUniqueFloorRegion(ri, lregs[j]);
+ addUniqueFloorRegion(rj, lregs[i]);
+ }
+ }
+ }
+
+ }
+ }
+
+ // Create 2D layers from regions.
+ unsigned short layerId = 1;
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].id = 0;
+
+ // Merge montone regions to create non-overlapping areas.
+ rcIntArray stack(32);
+ for (int i = 1; i < nreg; ++i)
+ {
+ rcRegion& root = regions[i];
+ // Skip already visited.
+ if (root.id != 0)
+ continue;
+
+ // Start search.
+ root.id = layerId;
+
+ stack.resize(0);
+ stack.push(i);
+
+ while (stack.size() > 0)
+ {
+ // Pop front
+ rcRegion& reg = regions[stack[0]];
+ for (int j = 0; j < stack.size()-1; ++j)
+ stack[j] = stack[j+1];
+ stack.resize(stack.size()-1);
+
+ const int ncons = (int)reg.connections.size();
+ for (int j = 0; j < ncons; ++j)
+ {
+ const int nei = reg.connections[j];
+ rcRegion& regn = regions[nei];
+ // Skip already visited.
+ if (regn.id != 0)
+ continue;
+ // Skip if the neighbour is overlapping root region.
+ bool overlap = false;
+ for (int k = 0; k < root.floors.size(); k++)
+ {
+ if (root.floors[k] == nei)
+ {
+ overlap = true;
+ break;
+ }
+ }
+ if (overlap)
+ continue;
+
+ // Deepen
+ stack.push(nei);
+
+ // Mark layer id
+ regn.id = layerId;
+ // Merge current layers to root.
+ for (int k = 0; k < regn.floors.size(); ++k)
+ addUniqueFloorRegion(root, regn.floors[k]);
+ root.ymin = rcMin(root.ymin, regn.ymin);
+ root.ymax = rcMax(root.ymax, regn.ymax);
+ root.spanCount += regn.spanCount;
+ regn.spanCount = 0;
+ root.connectsToBorder = root.connectsToBorder || regn.connectsToBorder;
+ }
+ }
+
+ layerId++;
+ }
+
+ // Remove small regions
+ for (int i = 0; i < nreg; ++i)
+ {
+ if (regions[i].spanCount > 0 && regions[i].spanCount < minRegionArea && !regions[i].connectsToBorder)
+ {
+ unsigned short reg = regions[i].id;
+ for (int j = 0; j < nreg; ++j)
+ if (regions[j].id == reg)
+ regions[j].id = 0;
+ }
+ }
+
+ // Compress region Ids.
+ for (int i = 0; i < nreg; ++i)
+ {
+ regions[i].remap = false;
+ if (regions[i].id == 0) continue; // Skip nil regions.
+ if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
+ regions[i].remap = true;
+ }
+
+ unsigned short regIdGen = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ if (!regions[i].remap)
+ continue;
+ unsigned short oldId = regions[i].id;
+ unsigned short newId = ++regIdGen;
+ for (int j = i; j < nreg; ++j)
+ {
+ if (regions[j].id == oldId)
+ {
+ regions[j].id = newId;
+ regions[j].remap = false;
+ }
+ }
+ }
+ maxRegionId = regIdGen;
+
// Remap regions.
for (int i = 0; i < chf.spanCount; ++i)
{
@@ -1011,6 +1241,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
return true;
}
+
+
/// @par
///
/// This is usually the second to the last step in creating a fully built
@@ -1256,13 +1488,17 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
}
}
+
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
- // Filter out small regions.
+ // Merge regions and filter out small regions.
+ rcIntArray overlaps;
chf.maxRegions = id;
- if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps))
return false;
+ // Monotone partitioning does not generate overlapping regions.
+
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Store the result out.
@@ -1407,11 +1643,18 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
- // Filter out small regions.
+ // Merge regions and filter out smalle regions.
+ rcIntArray overlaps;
chf.maxRegions = regionId;
- if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps))
return false;
-
+
+ // If overlapping regions were found during merging, split those regions.
+ if (overlaps.size() > 0)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size());
+ }
+
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Write the result out.
@@ -1424,3 +1667,157 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
}
+bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+ unsigned short id = 1;
+
+ rcScopedDelete srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!srcReg)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
+
+ const int nsweeps = rcMax(chf.width,chf.height);
+ rcScopedDelete sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP);
+ if (!sweeps)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
+ return false;
+ }
+
+
+ // Mark border regions.
+ if (borderSize > 0)
+ {
+ // Make sure border will not overflow.
+ const int bw = rcMin(w, borderSize);
+ const int bh = rcMin(h, borderSize);
+ // Paint regions
+ paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
+
+ chf.borderSize = borderSize;
+ }
+
+ rcIntArray prev(256);
+
+ // Sweep one line at a time.
+ for (int y = borderSize; y < h-borderSize; ++y)
+ {
+ // Collect spans from this row.
+ prev.resize(id+1);
+ memset(&prev[0],0,sizeof(int)*id);
+ unsigned short rid = 1;
+
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ if (chf.areas[i] == RC_NULL_AREA) continue;
+
+ // -x
+ unsigned short previd = 0;
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ previd = srcReg[ai];
+ }
+
+ if (!previd)
+ {
+ previd = rid++;
+ sweeps[previd].rid = previd;
+ sweeps[previd].ns = 0;
+ sweeps[previd].nei = 0;
+ }
+
+ // -y
+ if (rcGetCon(s,3) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ {
+ unsigned short nr = srcReg[ai];
+ if (!sweeps[previd].nei || sweeps[previd].nei == nr)
+ {
+ sweeps[previd].nei = nr;
+ sweeps[previd].ns++;
+ prev[nr]++;
+ }
+ else
+ {
+ sweeps[previd].nei = RC_NULL_NEI;
+ }
+ }
+ }
+
+ srcReg[i] = previd;
+ }
+ }
+
+ // Create unique ID.
+ for (int i = 1; i < rid; ++i)
+ {
+ if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
+ prev[sweeps[i].nei] == (int)sweeps[i].ns)
+ {
+ sweeps[i].id = sweeps[i].nei;
+ }
+ else
+ {
+ sweeps[i].id = id++;
+ }
+ }
+
+ // Remap IDs
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (srcReg[i] > 0 && srcReg[i] < rid)
+ srcReg[i] = sweeps[srcReg[i]].id;
+ }
+ }
+ }
+
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Merge monotone regions to layers and remove small regions.
+ rcIntArray overlaps;
+ chf.maxRegions = id;
+ if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+
+ // Store the result out.
+ for (int i = 0; i < chf.spanCount; ++i)
+ chf.spans[i].reg = srcReg[i];
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
+
+ return true;
+}
diff --git a/dep/recastnavigation/recastnavigation.diff b/dep/recastnavigation/recastnavigation.diff
index 68e976c955e4a..42bab6474e390 100644
--- a/dep/recastnavigation/recastnavigation.diff
+++ b/dep/recastnavigation/recastnavigation.diff
@@ -1,25 +1,21 @@
- Detour/Include/DetourNavMesh.h | 76 +-
- Detour/Include/DetourNavMeshQuery.h | 0
- Detour/Include/DetourNode.h | 0
- Detour/Source/DetourCommon.cpp | 4 +-
- Detour/Source/DetourNavMesh.cpp | 32 +-
- Detour/Source/DetourNavMeshBuilder.cpp | 6 +-
- Detour/Source/DetourNavMeshQuery.cpp | 9 +-
- Detour/Source/DetourNode.cpp | 29 +-
- DetourCrowd/Include/DetourObstacleAvoidance.h | 0
- DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +-
- Recast/Include/Recast.h | 8 +-
- Recast/Source/RecastContour.cpp | 0
- Recast/Source/RecastLayers.cpp | 0
- Recast/Source/RecastMesh.cpp | 0
- Recast/Source/RecastMeshDetail.cpp | 0
- Recast/Source/RecastRegion.cpp | 0
- RecastDemo/Contrib/stb_truetype.h | 3612 ++++++++++++------------
- RecastDemo/Include/NavMeshPruneTool.h | 0
- 18 files changed, 1865 insertions(+), 1917 deletions(-)
+From b84e9ffbd1d1e1fb2f5d78cc53d2bb7b56c3fce3 Mon Sep 17 00:00:00 2001
+From: jackpoz
+Date: Fri, 20 Jun 2014 23:15:04 +0200
+Subject: [PATCH] Add custom trinitycore changes
+
+---
+ Detour/Include/DetourNavMesh.h | 76 ++++++++------------------
+ Detour/Source/DetourCommon.cpp | 4 +-
+ Detour/Source/DetourNavMesh.cpp | 32 ++++-------
+ Detour/Source/DetourNavMeshBuilder.cpp | 6 +-
+ Detour/Source/DetourNavMeshQuery.cpp | 9 +--
+ Detour/Source/DetourNode.cpp | 29 +++-------
+ DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +-
+ Recast/Include/Recast.h | 8 +--
+ 8 files changed, 59 insertions(+), 111 deletions(-)
diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h
-index 95a63e4..cdd473f 100644
+index 1060845..782ddbc 100644
--- a/Detour/Include/DetourNavMesh.h
+++ b/Detour/Include/DetourNavMesh.h
@@ -22,39 +22,35 @@
@@ -93,7 +89,7 @@ index 95a63e4..cdd473f 100644
/// Tile flags used for various functions and fields.
/// For an example, see dtNavMesh::addTile().
enum dtTileFlags
-@@ -492,11 +494,7 @@ public:
+@@ -511,11 +513,7 @@ public:
/// @param[in] ip The index of the polygon within the tile.
inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
{
@@ -105,7 +101,7 @@ index 95a63e4..cdd473f 100644
}
/// Decodes a standard polygon reference.
-@@ -508,21 +506,12 @@ public:
+@@ -527,21 +525,12 @@ public:
/// @see #encodePolyId
inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
{
@@ -127,7 +123,7 @@ index 95a63e4..cdd473f 100644
}
/// Extracts a tile's salt value from the specified polygon reference.
-@@ -531,13 +520,8 @@ public:
+@@ -550,13 +539,8 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
{
@@ -141,7 +137,7 @@ index 95a63e4..cdd473f 100644
}
/// Extracts the tile's index from the specified polygon reference.
-@@ -546,13 +530,8 @@ public:
+@@ -565,13 +549,8 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
{
@@ -155,7 +151,7 @@ index 95a63e4..cdd473f 100644
}
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
-@@ -561,13 +540,8 @@ public:
+@@ -580,13 +559,8 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
{
@@ -169,7 +165,7 @@ index 95a63e4..cdd473f 100644
}
/// @}
-@@ -626,11 +600,9 @@ private:
+@@ -645,11 +619,9 @@ private:
dtMeshTile* m_nextFree; ///< Freelist of tiles.
dtMeshTile* m_tiles; ///< List of tiles.
@@ -305,7 +301,7 @@ index 1bf271b..9d8471b 100644
int curNode = 0;
diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp
-index 2e30464..f1709df 100644
+index 9debb4d..ec3a294 100644
--- a/Detour/Source/DetourNavMeshQuery.cpp
+++ b/Detour/Source/DetourNavMeshQuery.cpp
@@ -16,13 +16,13 @@
@@ -335,7 +331,7 @@ index 2e30464..f1709df 100644
dtNavMeshQuery* dtAllocNavMeshQuery()
-@@ -3305,7 +3306,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
+@@ -3504,7 +3505,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
dtVsub(hitNormal, centerPos, hitPos);
dtVnormalize(hitNormal);
@@ -345,7 +341,7 @@ index 2e30464..f1709df 100644
return status;
}
diff --git a/Detour/Source/DetourNode.cpp b/Detour/Source/DetourNode.cpp
-index 57cb206..4c8215e 100644
+index 5cf6548..1d18977 100644
--- a/Detour/Source/DetourNode.cpp
+++ b/Detour/Source/DetourNode.cpp
@@ -22,30 +22,17 @@
@@ -422,7 +418,7 @@ index 0fad9ef..d3f90b7 100644
// Always add sample at zero
pat[npat*2+0] = 0;
diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h
-index 336837e..3f4ae96 100644
+index 83ca606..66974cd 100644
--- a/Recast/Include/Recast.h
+++ b/Recast/Include/Recast.h
@@ -243,7 +243,7 @@ struct rcConfig
@@ -447,3620 +443,6 @@ index 336837e..3f4ae96 100644
rcSpan* next; ///< The next span higher up in column.
};
-diff --git a/RecastDemo/Contrib/stb_truetype.h b/RecastDemo/Contrib/stb_truetype.h
-index fd72578..92dc8c2 100644
---- a/RecastDemo/Contrib/stb_truetype.h
-+++ b/RecastDemo/Contrib/stb_truetype.h
-@@ -1,1806 +1,1806 @@
--// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools
--//
--// This library processes TrueType files:
--// parse files
--// extract glyph metrics
--// extract glyph shapes
--// render glyphs to one-channel bitmaps with antialiasing (box filter)
--//
--// Todo:
--// non-MS cmaps
--// crashproof on bad data
--// hinting
--// subpixel positioning when rendering bitmap
--// cleartype-style AA
--//
--// ADDITIONAL CONTRIBUTORS
--//
--// Mikko Mononen: compound shape support, more cmap formats
--//
--// VERSIONS
--//
--// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
--// userdata, malloc-from-userdata, non-zero fill (STB)
--// 0.2 (2009-03-11) Fix unsigned/signed char warnings
--// 0.1 (2009-03-09) First public release
--//
--// USAGE
--//
--// Include this file in whatever places neeed to refer to it. In ONE C/C++
--// file, write:
--// #define STB_TRUETYPE_IMPLEMENTATION
--// before the #include of this file. This expands out the actual
--// implementation into that C/C++ file.
--//
--// Look at the header-file sections below for the API, but here's a quick skim:
--//
--// Simple 3D API (don't ship this, but it's fine for tools and quick start,
--// and you can cut and paste from it to move to more advanced)
--// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
--// stbtt_GetBakedQuad() -- compute quad to draw for a given char
--//
--// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
--// stbtt_InitFont()
--// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
--//
--// Render a unicode codepoint to a bitmap
--// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
--// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
--// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
--//
--// Character advance/positioning
--// stbtt_GetCodepointHMetrics()
--// stbtt_GetFontVMetrics()
--//
--// NOTES
--//
--// The system uses the raw data found in the .ttf file without changing it
--// and without building auxiliary data structures. This is a bit inefficient
--// on little-endian systems (the data is big-endian), but assuming you're
--// caching the bitmaps or glyph shapes this shouldn't be a big deal.
--//
--// It appears to be very hard to programmatically determine what font a
--// given file is in a general way. I provide an API for this, but I don't
--// recommend it.
--//
--//
--// SOURCE STATISTICS (based on v0.3, 1800 LOC)
--//
--// Documentation & header file 350 LOC \___ 500 LOC documentation
--// Sample code 140 LOC /
--// Truetype parsing 580 LOC ---- 600 LOC TrueType
--// Software rasterization 240 LOC \ .
--// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation
--// Bitmap management 70 LOC /
--// Baked bitmap interface 70 LOC /
--// Font name matching & access 150 LOC ---- 150
--// C runtime library abstraction 60 LOC ---- 60
--
--
--//////////////////////////////////////////////////////////////////////////////
--//////////////////////////////////////////////////////////////////////////////
--////
--//// SAMPLE PROGRAMS
--////
--//
--// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
--//
--#if 0
--#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
--#include "stb_truetype.h"
--
--char ttf_buffer[1<<20];
--unsigned char temp_bitmap[512*512];
--
--stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs
--GLstbtt_uint ftex;
--
--void my_stbtt_initfont(void)
--{
-- fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
-- stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
-- // can free ttf_buffer at this point
-- glGenTextures(1, &ftex);
-- glBindTexture(GL_TEXTURE_2D, ftex);
-- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
-- // can free temp_bitmap at this point
-- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
--}
--
--void my_stbtt_print(float x, float y, char *text)
--{
-- // assume orthographic projection with units = screen pixels, origin at top left
-- glBindTexture(GL_TEXTURE_2D, ftex);
-- glBegin(GL_QUADS);
-- while (*text) {
-- if (*text >= 32 && *text < 128) {
-- stbtt_aligned_quad q;
-- stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d
-- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
-- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
-- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
-- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
-- }
-- ++text;
-- }
-- glEnd();
--}
--#endif
--//
--//
--//////////////////////////////////////////////////////////////////////////////
--//
--// Complete program (this compiles): get a single bitmap, print as ASCII art
--//
--#if 0
--#include
--#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
--#include "stb_truetype.h"
--
--char ttf_buffer[1<<25];
--
--int main(int argc, char **argv)
--{
-- stbtt_fontinfo font;
-- unsigned char *bitmap;
-- int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
--
-- fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
--
-- stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
-- bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
--
-- for (j=0; j < h; ++j) {
-- for (i=0; i < w; ++i)
-- putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
-- putchar('\n');
-- }
-- return 0;
--}
--#endif
--//
--// Output:
--//
--// .ii.
--// @@@@@@.
--// V@Mio@@o
--// :i. V@V
--// :oM@@M
--// :@@@MM@M
--// @@o o@M
--// :@@. M@M
--// @@@o@@@@
--// :M@@V:@@.
--//
--//////////////////////////////////////////////////////////////////////////////
--//
--// Complete program: print "Hello World!" banner, with bugs
--//
--#if 0
--int main(int arg, char **argv)
--{
-- unsigned char screen[20][79];
-- int i,j, pos=0;
-- float scale;
-- char *text = "Heljo World!";
--
-- fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
-- stbtt_InitFont(&font, buffer, 0);
--
-- scale = stbtt_ScaleForPixelHeight(&font, 16);
-- memset(screen, 0, sizeof(screen));
--
-- while (*text) {
-- int advance,lsb,x0,y0,x1,y1, newpos, baseline=13;
-- stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb);
-- stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1);
-- newpos = pos + (int) (lsb * scale) + x0;
-- stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text);
-- // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
-- // because this API is really for baking character bitmaps into textures
-- pos += (int) (advance * scale);
-- ++text;
-- }
--
-- for (j=0; j < 20; ++j) {
-- for (i=0; i < 79; ++i)
-- putchar(" .:ioVM@"[screen[j][i]>>5]);
-- putchar('\n');
-- }
--
-- return 0;
--}
--#endif
--
--
--//////////////////////////////////////////////////////////////////////////////
--//////////////////////////////////////////////////////////////////////////////
--////
--//// INTEGRATION WITH RUNTIME LIBRARIES
--////
--
--#ifdef STB_TRUETYPE_IMPLEMENTATION
-- // #define your own (u)stbtt_int8/16/32 before including to override this
-- #ifndef stbtt_uint8
-- typedef unsigned char stbtt_uint8;
-- typedef signed char stbtt_int8;
-- typedef unsigned short stbtt_uint16;
-- typedef signed short stbtt_int16;
-- typedef unsigned int stbtt_uint32;
-- typedef signed int stbtt_int32;
-- #endif
--
-- typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
-- typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
--
-- // #define your own STBTT_sort() to override this to avoid qsort
-- #ifndef STBTT_sort
-- #include
-- #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func)
-- #endif
--
-- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
-- #ifndef STBTT_ifloor
-- #include
-- #define STBTT_ifloor(x) ((int) floor(x))
-- #define STBTT_iceil(x) ((int) ceil(x))
-- #endif
--
-- // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
-- #ifndef STBTT_malloc
-- #include
-- #define STBTT_malloc(x,u) malloc(x)
-- #define STBTT_free(x,u) free(x)
-- #endif
--
-- #ifndef STBTT_assert
-- #include
-- #define STBTT_assert(x) assert(x)
-- #endif
--
-- #ifndef STBTT_strlen
-- #include
-- #define STBTT_strlen(x) strlen(x)
-- #endif
--
-- #ifndef STBTT_memcpy
-- #include
-- #define STBTT_memcpy memcpy
-- #define STBTT_memset memset
-- #endif
--#endif
--
--///////////////////////////////////////////////////////////////////////////////
--///////////////////////////////////////////////////////////////////////////////
--////
--//// INTERFACE
--////
--////
--
--#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
--#define __STB_INCLUDE_STB_TRUETYPE_H__
--
--#ifdef __cplusplus
--extern "C" {
--#endif
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// TEXTURE BAKING API
--//
--// If you use this API, you only have to call two functions ever.
--//
--
--typedef struct
--{
-- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
-- float xoff,yoff,xadvance;
--} stbtt_bakedchar;
--
--extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-- float pixel_height, // height of font in pixels
-- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-- int first_char, int num_chars, // characters to bake
-- stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
--// if return is positive, the first unused row of the bitmap
--// if return is negative, returns the negative of the number of characters that fit
--// if return is 0, no characters fit and no rows were used
--// This uses a very crappy packing.
--
--typedef struct
--{
-- float x0,y0,s0,t0; // top-left
-- float x1,y1,s1,t1; // bottom-right
--} stbtt_aligned_quad;
--
--extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
-- int char_index, // character to display
-- float *xpos, float *ypos, // pointers to current position in screen pixel space
-- stbtt_aligned_quad *q, // output: quad to draw
-- int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
--// Call GetBakedQuad with char_index = 'character - first_char', and it
--// creates the quad you need to draw and advances the current position.
--// It's inefficient; you might want to c&p it and optimize it.
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// FONT LOADING
--//
--//
--
--extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
--// Each .ttf file may have more than one font. Each has a sequential index
--// number starting from 0. Call this function to get the font offset for a
--// given index; it returns -1 if the index is out of range. A regular .ttf
--// file will only define one font and it always be at offset 0, so it will
--// return '0' for index 0, and -1 for all other indices. You can just skip
--// this step if you know it's that kind of font.
--
--
--// The following structure is defined publically so you can declare one on
--// the stack or as a global or etc.
--typedef struct
--{
-- void *userdata;
-- unsigned char *data; // pointer to .ttf file
-- int fontstart; // offset of start of font
--
-- int numGlyphs; // number of glyphs, needed for range checking
--
-- int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf
-- int index_map; // a cmap mapping for our chosen character encoding
-- int indexToLocFormat; // format needed to map from glyph index to glyph
--} stbtt_fontinfo;
--
--extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
--// Given an offset into the file that defines a font, this function builds
--// the necessary cached info for the rest of the system. You must allocate
--// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
--// need to do anything special to free it, because the contents are a pure
--// cache with no additional data structures. Returns 0 on failure.
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// CHARACTER TO GLYPH-INDEX CONVERSIOn
--
--int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
--// If you're going to perform multiple operations on the same character
--// and you want a speed-up, call this function with the character you're
--// going to process, then use glyph-based functions instead of the
--// codepoint-based functions.
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// CHARACTER PROPERTIES
--//
--
--extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
--// computes a scale factor to produce a font whose "height" is 'pixels' tall.
--// Height is measured as the distance from the highest ascender to the lowest
--// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
--// and computing:
--// scale = pixels / (ascent - descent)
--// so if you prefer to measure height by the ascent only, use a similar calculation.
--
--extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
--// ascent is the coordinate above the baseline the font extends; descent
--// is the coordinate below the baseline the font extends (i.e. it is typically negative)
--// lineGap is the spacing between one row's descent and the next row's ascent...
--// so you should advance the vertical position by "*ascent - *descent + *lineGap"
--// these are expressed in unscaled coordinates
--
--extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
--// leftSideBearing is the offset from the current horizontal position to the left edge of the character
--// advanceWidth is the offset from the current horizontal position to the next horizontal position
--// these are expressed in unscaled coordinates
--
--extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
--// an additional amount to add to the 'advance' value between ch1 and ch2
--// @TODO; for now always returns 0!
--
--extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
--// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
--
--extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
--extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
--extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
--// as above, but takes one or more glyph indices for greater efficiency
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// GLYPH SHAPES (you probably don't need these, but they have to go before
--// the bitmaps for C declaration-order reasons)
--//
--
--#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
-- enum {
-- STBTT_vmove=1,
-- STBTT_vline,
-- STBTT_vcurve
-- };
--#endif
--
--#ifndef stbtt_vertex // you can predefine this to use different values
-- // (we share this with other code at RAD)
-- #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
-- typedef struct
-- {
-- stbtt_vertex_type x,y,cx,cy;
-- unsigned char type,padding;
-- } stbtt_vertex;
--#endif
--
--extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
--extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
--// returns # of vertices and fills *vertices with the pointer to them
--// these are expressed in "unscaled" coordinates
--
--extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
--// frees the data allocated above
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// BITMAP RENDERING
--//
--
--extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
--// frees the bitmap allocated below
--
--extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
--// allocates a large-enough single-channel 8bpp bitmap and renders the
--// specified character/glyph at the specified scale into it, with
--// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
--// *width & *height are filled out with the width & height of the bitmap,
--// which is stored left-to-right, top-to-bottom.
--//
--// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
--
--extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
--// the same as above, but you pass in storage for the bitmap in the form
--// of 'output', with row spacing of 'out_stride' bytes. the bitmap is
--// clipped to out_w/out_h bytes. call the next function to get the
--// height and width and positioning info
--
--extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
--// get the bbox of the bitmap centered around the glyph origin; so the
--// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
--// the bitmap top left is (leftSideBearing*scale,iy0).
--// (Note that the bitmap uses y-increases-down, but the shape uses
--// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
--
--extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
--extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
--extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
--
--//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
--
--// @TODO: don't expose this structure
--typedef struct
--{
-- int w,h,stride;
-- unsigned char *pixels;
--} stbtt__bitmap;
--
--extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata);
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// Finding the right font...
--//
--// You should really just solve this offline, keep your own tables
--// of what font is what, and don't try to get it out of the .ttf file.
--// That's because getting it out of the .ttf file is really hard, because
--// the names in the file can appear in many possible encodings, in many
--// possible languages, and e.g. if you need a case-insensitive comparison,
--// the details of that depend on the encoding & language in a complex way
--// (actually underspecified in truetype, but also gigantic).
--//
--// But you can use the provided functions in two possible ways:
--// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
--// unicode-encoded names to try to find the font you want;
--// you can run this before calling stbtt_InitFont()
--//
--// stbtt_GetFontNameString() lets you get any of the various strings
--// from the file yourself and do your own comparisons on them.
--// You have to have called stbtt_InitFont() first.
--
--
--extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
--// returns the offset (not index) of the font that matches, or -1 if none
--// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
--// if you use any other flag, use a font name like "Arial"; this checks
--// the 'macStyle' header field; i don't know if fonts set this consistently
--#define STBTT_MACSTYLE_DONTCARE 0
--#define STBTT_MACSTYLE_BOLD 1
--#define STBTT_MACSTYLE_ITALIC 2
--#define STBTT_MACSTYLE_UNDERSCORE 4
--#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
--
--extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
--// returns 1/0 whether the first string interpreted as utf8 is identical to
--// the second string interpreted as big-endian utf16... useful for strings from next func
--
--extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
--// returns the string (which may be big-endian double byte, e.g. for unicode)
--// and puts the length in bytes in *length.
--//
--// some of the values for the IDs are below; for more see the truetype spec:
--// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
--// http://www.microsoft.com/typography/otspec/name.htm
--
--enum { // platformID
-- STBTT_PLATFORM_ID_UNICODE =0,
-- STBTT_PLATFORM_ID_MAC =1,
-- STBTT_PLATFORM_ID_ISO =2,
-- STBTT_PLATFORM_ID_MICROSOFT =3
--};
--
--enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
-- STBTT_UNICODE_EID_UNICODE_1_0 =0,
-- STBTT_UNICODE_EID_UNICODE_1_1 =1,
-- STBTT_UNICODE_EID_ISO_10646 =2,
-- STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
-- STBTT_UNICODE_EID_UNICODE_2_0_FULL=4,
--};
--
--enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
-- STBTT_MS_EID_SYMBOL =0,
-- STBTT_MS_EID_UNICODE_BMP =1,
-- STBTT_MS_EID_SHIFTJIS =2,
-- STBTT_MS_EID_UNICODE_FULL =10,
--};
--
--enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
-- STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
-- STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
-- STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
-- STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7,
--};
--
--enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
-- // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
-- STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
-- STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
-- STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
-- STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
-- STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
-- STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D,
--};
--
--enum { // languageID for STBTT_PLATFORM_ID_MAC
-- STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
-- STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
-- STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
-- STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
-- STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
-- STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
-- STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19,
--};
--
--#ifdef __cplusplus
--}
--#endif
--
--#endif // __STB_INCLUDE_STB_TRUETYPE_H__
--
--///////////////////////////////////////////////////////////////////////////////
--///////////////////////////////////////////////////////////////////////////////
--////
--//// IMPLEMENTATION
--////
--////
--
--#ifdef STB_TRUETYPE_IMPLEMENTATION
--
--//////////////////////////////////////////////////////////////////////////
--//
--// accessors to parse data from file
--//
--
--// on platforms that don't allow misaligned reads, if we want to allow
--// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
--
--#define ttBYTE(p) (* (stbtt_uint8 *) (p))
--#define ttCHAR(p) (* (stbtt_int8 *) (p))
--#define ttFixed(p) ttLONG(p)
--
--#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
--
-- #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
-- #define ttSHORT(p) (* (stbtt_int16 *) (p))
-- #define ttULONG(p) (* (stbtt_uint32 *) (p))
-- #define ttLONG(p) (* (stbtt_int32 *) (p))
--
--#else
--
-- stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-- stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-- stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-- stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
--
--#endif
--
--#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
--#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
--
--static int stbtt__isfont(const stbtt_uint8 *font)
--{
-- // check the version number
-- if (stbtt_tag(font, "1")) return 1; // TrueType 1
-- if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
-- if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
-- if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
-- return 0;
--}
--
--// @OPTIMIZE: binary search
--static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
--{
-- stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
-- stbtt_uint32 tabledir = fontstart + 12;
-- stbtt_int32 i;
-- for (i=0; i < num_tables; ++i) {
-- stbtt_uint32 loc = tabledir + 16*i;
-- if (stbtt_tag(data+loc+0, tag))
-- return ttULONG(data+loc+8);
-- }
-- return 0;
--}
--
--int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
--{
-- // if it's just a font, there's only one valid index
-- if (stbtt__isfont(font_collection))
-- return index == 0 ? 0 : -1;
--
-- // check if it's a TTC
-- if (stbtt_tag(font_collection, "ttcf")) {
-- // version 1?
-- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
-- stbtt_int32 n = ttLONG(font_collection+8);
-- if (index >= n)
-- return -1;
-- return ttULONG(font_collection+12+index*14);
-- }
-- }
-- return -1;
--}
--
--int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
--{
-- stbtt_uint8 *data = (stbtt_uint8 *) data2;
-- stbtt_uint32 cmap, t;
-- stbtt_int32 i,numTables;
--
-- info->data = data;
-- info->fontstart = fontstart;
--
-- cmap = stbtt__find_table(data, fontstart, "cmap");
-- info->loca = stbtt__find_table(data, fontstart, "loca");
-- info->head = stbtt__find_table(data, fontstart, "head");
-- info->glyf = stbtt__find_table(data, fontstart, "glyf");
-- info->hhea = stbtt__find_table(data, fontstart, "hhea");
-- info->hmtx = stbtt__find_table(data, fontstart, "hmtx");
-- if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
-- return 0;
--
-- t = stbtt__find_table(data, fontstart, "maxp");
-- if (t)
-- info->numGlyphs = ttUSHORT(data+t+4);
-- else
-- info->numGlyphs = 0xffff;
--
-- // find a cmap encoding table we understand *now* to avoid searching
-- // later. (todo: could make this installable)
-- // the same regardless of glyph.
-- numTables = ttUSHORT(data + cmap + 2);
-- info->index_map = 0;
-- for (i=0; i < numTables; ++i) {
-- stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
-- // find an encoding we understand:
-- switch(ttUSHORT(data+encoding_record)) {
-- case STBTT_PLATFORM_ID_MICROSOFT:
-- switch (ttUSHORT(data+encoding_record+2)) {
-- case STBTT_MS_EID_UNICODE_BMP:
-- case STBTT_MS_EID_UNICODE_FULL:
-- // MS/Unicode
-- info->index_map = cmap + ttULONG(data+encoding_record+4);
-- break;
-- }
-- break;
-- }
-- }
-- if (info->index_map == 0)
-- return 0;
--
-- info->indexToLocFormat = ttUSHORT(data+info->head + 50);
-- return 1;
--}
--
--int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
--{
-- stbtt_uint8 *data = info->data;
-- stbtt_uint32 index_map = info->index_map;
--
-- stbtt_uint16 format = ttUSHORT(data + index_map + 0);
-- if (format == 0) { // apple byte encoding
-- stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
-- if (unicode_codepoint < bytes-6)
-- return ttBYTE(data + index_map + 6 + unicode_codepoint);
-- return 0;
-- } else if (format == 6) {
-- stbtt_uint32 first = ttUSHORT(data + index_map + 6);
-- stbtt_uint32 count = ttUSHORT(data + index_map + 8);
-- if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
-- return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
-- return 0;
-- } else if (format == 2) {
-- STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
-- return 0;
-- } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
-- stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
-- stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
-- stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
-- stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
-- stbtt_uint16 item, offset, start, end;
--
-- // do a binary search of the segments
-- stbtt_uint32 endCount = index_map + 14;
-- stbtt_uint32 search = endCount;
--
-- if (unicode_codepoint > 0xffff)
-- return 0;
--
-- // they lie from endCount .. endCount + segCount
-- // but searchRange is the nearest power of two, so...
-- if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
-- search += rangeShift*2;
--
-- // now decrement to bias correctly to find smallest
-- search -= 2;
-- while (entrySelector) {
-- stbtt_uint16 start, end;
-- searchRange >>= 1;
-- start = ttUSHORT(data + search + 2 + segcount*2 + 2);
-- end = ttUSHORT(data + search + 2);
-- start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2);
-- end = ttUSHORT(data + search + searchRange*2);
-- if (unicode_codepoint > end)
-- search += searchRange*2;
-- --entrySelector;
-- }
-- search += 2;
--
-- item = (stbtt_uint16) ((search - endCount) >> 1);
--
-- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
-- start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
-- end = ttUSHORT(data + index_map + 14 + 2 + 2*item);
-- if (unicode_codepoint < start)
-- return 0;
--
-- offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
-- if (offset == 0)
-- return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item);
--
-- return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
-- } else if (format == 12) {
-- stbtt_uint16 ngroups = ttUSHORT(data+index_map+6);
-- stbtt_int32 low,high;
-- low = 0; high = (stbtt_int32)ngroups;
-- // Binary search the right group.
-- while (low <= high) {
-- stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
-- stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
-- stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
-- if ((stbtt_uint32) unicode_codepoint < start_char)
-- high = mid-1;
-- else if ((stbtt_uint32) unicode_codepoint > end_char)
-- low = mid+1;
-- else {
-- stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
-- return start_glyph + unicode_codepoint-start_char;
-- }
-- }
-- return 0; // not found
-- }
-- // @TODO
-- STBTT_assert(0);
-- return 0;
--}
--
--int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
--{
-- return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
--}
--
--static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy)
--{
-- v->type = type;
-- v->x = x;
-- v->y = y;
-- v->cx = cx;
-- v->cy = cy;
--}
--
--static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
--{
-- int g1,g2;
--
-- if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
-- if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
--
-- if (info->indexToLocFormat == 0) {
-- g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
-- g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
-- } else {
-- g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
-- g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
-- }
--
-- return g1==g2 ? -1 : g1; // if length is 0, return -1
--}
--
--int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
--{
-- int g = stbtt__GetGlyfOffset(info, glyph_index);
-- if (g < 0) return 0;
--
-- if (x0) *x0 = ttSHORT(info->data + g + 2);
-- if (y0) *y0 = ttSHORT(info->data + g + 4);
-- if (x1) *x1 = ttSHORT(info->data + g + 6);
-- if (y1) *y1 = ttSHORT(info->data + g + 8);
-- return 1;
--}
--
--int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
--{
-- return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
--}
--
--int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
--{
-- stbtt_int16 numberOfContours;
-- stbtt_uint8 *endPtsOfContours;
-- stbtt_uint8 *data = info->data;
-- stbtt_vertex *vertices=0;
-- int num_vertices=0;
-- int g = stbtt__GetGlyfOffset(info, glyph_index);
--
-- *pvertices = NULL;
--
-- if (g < 0) return 0;
--
-- numberOfContours = ttSHORT(data + g);
--
-- if (numberOfContours > 0) {
-- stbtt_uint8 flags=0,flagcount;
-- stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off;
-- stbtt_int16 x,y,cx,cy,sx,sy;
-- stbtt_uint8 *points;
-- endPtsOfContours = (data + g + 10);
-- ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
-- points = data + g + 10 + numberOfContours * 2 + 2 + ins;
--
-- n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
--
-- m = n + numberOfContours; // a loose bound on how many vertices we might need
-- vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
-- if (vertices == 0)
-- return 0;
--
-- next_move = 0;
-- flagcount=0;
--
-- // in first pass, we load uninterpreted data into the allocated array
-- // above, shifted to the end of the array so we won't overwrite it when
-- // we create our final data starting from the front
--
-- off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
--
-- // first load flags
--
-- for (i=0; i < n; ++i) {
-- if (flagcount == 0) {
-- flags = *points++;
-- if (flags & 8)
-- flagcount = *points++;
-- } else
-- --flagcount;
-- vertices[off+i].type = flags;
-- }
--
-- // now load x coordinates
-- x=0;
-- for (i=0; i < n; ++i) {
-- flags = vertices[off+i].type;
-- if (flags & 2) {
-- stbtt_int16 dx = *points++;
-- x += (flags & 16) ? dx : -dx; // ???
-- } else {
-- if (!(flags & 16)) {
-- x = x + (stbtt_int16) (points[0]*256 + points[1]);
-- points += 2;
-- }
-- }
-- vertices[off+i].x = x;
-- }
--
-- // now load y coordinates
-- y=0;
-- for (i=0; i < n; ++i) {
-- flags = vertices[off+i].type;
-- if (flags & 4) {
-- stbtt_int16 dy = *points++;
-- y += (flags & 32) ? dy : -dy; // ???
-- } else {
-- if (!(flags & 32)) {
-- y = y + (stbtt_int16) (points[0]*256 + points[1]);
-- points += 2;
-- }
-- }
-- vertices[off+i].y = y;
-- }
--
-- // now convert them to our format
-- num_vertices=0;
-- sx = sy = cx = cy = 0;
-- for (i=0; i < n; ++i) {
-- flags = vertices[off+i].type;
-- x = (stbtt_int16) vertices[off+i].x;
-- y = (stbtt_int16) vertices[off+i].y;
-- if (next_move == i) {
-- // when we get to the end, we have to close the shape explicitly
-- if (i != 0) {
-- if (was_off)
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-- else
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-- }
--
-- // now start the new one
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0);
-- next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
-- ++j;
-- was_off = 0;
-- sx = x;
-- sy = y;
-- } else {
-- if (!(flags & 1)) { // if it's a curve
-- if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
-- cx = x;
-- cy = y;
-- was_off = 1;
-- } else {
-- if (was_off)
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
-- else
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
-- was_off = 0;
-- }
-- }
-- }
-- if (i != 0) {
-- if (was_off)
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-- else
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-- }
-- } else if (numberOfContours == -1) {
-- // Compound shapes.
-- int more = 1;
-- stbtt_uint8 *comp = data + g + 10;
-- num_vertices = 0;
-- vertices = 0;
-- while (more) {
-- stbtt_uint16 flags, gidx;
-- int comp_num_verts = 0, i;
-- stbtt_vertex *comp_verts = 0, *tmp = 0;
-- float mtx[6] = {1,0,0,1,0,0}, m, n;
--
-- flags = ttSHORT(comp); comp+=2;
-- gidx = ttSHORT(comp); comp+=2;
--
-- if (flags & 2) { // XY values
-- if (flags & 1) { // shorts
-- mtx[4] = ttSHORT(comp); comp+=2;
-- mtx[5] = ttSHORT(comp); comp+=2;
-- } else {
-- mtx[4] = ttCHAR(comp); comp+=1;
-- mtx[5] = ttCHAR(comp); comp+=1;
-- }
-- }
-- else {
-- // @TODO handle matching point
-- STBTT_assert(0);
-- }
-- if (flags & (1<<3)) { // WE_HAVE_A_SCALE
-- mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[1] = mtx[2] = 0;
-- } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
-- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[1] = mtx[2] = 0;
-- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-- } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
-- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-- }
--
-- // Find transformation scales.
-- m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
-- n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
--
-- // Get indexed glyph.
-- comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
-- if (comp_num_verts > 0) {
-- // Transform vertices.
-- for (i = 0; i < comp_num_verts; ++i) {
-- stbtt_vertex* v = &comp_verts[i];
-- stbtt_vertex_type x,y;
-- x=v->x; y=v->y;
-- v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-- v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-- x=v->cx; y=v->cy;
-- v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-- v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-- }
-- // Append vertices.
-- tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
-- if (!tmp) {
-- if (vertices) STBTT_free(vertices, info->userdata);
-- if (comp_verts) STBTT_free(comp_verts, info->userdata);
-- return 0;
-- }
-- if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
-- memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
-- if (vertices) STBTT_free(vertices, info->userdata);
-- vertices = tmp;
-- STBTT_free(comp_verts, info->userdata);
-- num_vertices += comp_num_verts;
-- }
-- // More components ?
-- more = flags & (1<<5);
-- }
-- } else if (numberOfContours < 0) {
-- // @TODO other compound variations?
-- STBTT_assert(0);
-- } else {
-- // numberOfCounters == 0, do nothing
-- }
--
-- *pvertices = vertices;
-- return num_vertices;
--}
--
--void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
--{
-- stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
-- if (glyph_index < numOfLongHorMetrics) {
-- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
-- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
-- } else {
-- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
-- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
-- }
--}
--
--int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/)
--{
-- return 0;
--}
--
--int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/)
--{
-- return 0;
--}
--
--void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
--{
-- stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
--}
--
--void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
--{
-- if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
-- if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
-- if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
--}
--
--float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
--{
-- int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
-- return (float) height / fheight;
--}
--
--void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
--{
-- STBTT_free(v, info->userdata);
--}
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// antialiasing software rasterizer
--//
--
--void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
--{
-- int x0,y0,x1,y1;
-- if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1))
-- x0=y0=x1=y1=0; // e.g. space character
-- // now move to integral bboxes (treating pixels as little squares, what pixels get touched)?
-- if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x);
-- if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y);
-- if (ix1) *ix1 = STBTT_iceil (x1 * scale_x);
-- if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y);
--}
--
--void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
--{
-- stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1);
--}
--
--typedef struct stbtt__edge {
-- float x0,y0, x1,y1;
-- int invert;
--} stbtt__edge;
--
--typedef struct stbtt__active_edge
--{
-- int x,dx;
-- float ey;
-- struct stbtt__active_edge *next;
-- int valid;
--} stbtt__active_edge;
--
--#define FIXSHIFT 10
--#define FIX (1 << FIXSHIFT)
--#define FIXMASK (FIX-1)
--
--static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata)
--{
-- stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!!
-- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
-- STBTT_assert(e->y0 <= start_point);
-- if (!z) return z;
-- // round dx down to avoid going too far
-- if (dxdy < 0)
-- z->dx = -STBTT_ifloor(FIX * -dxdy);
-- else
-- z->dx = STBTT_ifloor(FIX * dxdy);
-- z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0)));
-- z->x -= off_x * FIX;
-- z->ey = e->y1;
-- z->next = 0;
-- z->valid = e->invert ? 1 : -1;
-- return z;
--}
--
--// note: this routine clips fills that extend off the edges... ideally this
--// wouldn't happen, but it could happen if the truetype glyph bounding boxes
--// are wrong, or if the user supplies a too-small bitmap
--static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
--{
-- // non-zero winding fill
-- int x0=0, w=0;
--
-- while (e) {
-- if (w == 0) {
-- // if we're currently at zero, we need to record the edge start point
-- x0 = e->x; w += e->valid;
-- } else {
-- int x1 = e->x; w += e->valid;
-- // if we went to zero, we need to draw
-- if (w == 0) {
-- int i = x0 >> FIXSHIFT;
-- int j = x1 >> FIXSHIFT;
--
-- if (i < len && j >= 0) {
-- if (i == j) {
-- // x0,x1 are the same pixel, so compute combined coverage
-- scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT);
-- } else {
-- if (i >= 0) // add antialiasing for x0
-- scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT);
-- else
-- i = -1; // clip
--
-- if (j < len) // add antialiasing for x1
-- scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT);
-- else
-- j = len; // clip
--
-- for (++i; i < j; ++i) // fill pixels between x0 and x1
-- scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
-- }
-- }
-- }
-- }
--
-- e = e->next;
-- }
--}
--
--static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
--{
-- stbtt__active_edge *active = NULL;
-- int y,j=0;
-- int max_weight = (255 / vsubsample); // weight per vertical scanline
-- int s; // vertical subsample index
-- unsigned char scanline_data[512], *scanline;
--
-- if (result->w > 512)
-- scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
-- else
-- scanline = scanline_data;
--
-- y = off_y * vsubsample;
-- e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
--
-- while (j < result->h) {
-- STBTT_memset(scanline, 0, result->w);
-- for (s=0; s < vsubsample; ++s) {
-- // find center of pixel for this scanline
-- float scan_y = y + 0.5f;
-- stbtt__active_edge **step = &active;
--
-- // update all active edges;
-- // remove all active edges that terminate before the center of this scanline
-- while (*step) {
-- stbtt__active_edge * z = *step;
-- if (z->ey <= scan_y) {
-- *step = z->next; // delete from list
-- STBTT_assert(z->valid);
-- z->valid = 0;
-- STBTT_free(z, userdata);
-- } else {
-- z->x += z->dx; // advance to position for current scanline
-- step = &((*step)->next); // advance through list
-- }
-- }
--
-- // resort the list if needed
-- for(;;) {
-- int changed=0;
-- step = &active;
-- while (*step && (*step)->next) {
-- if ((*step)->x > (*step)->next->x) {
-- stbtt__active_edge *t = *step;
-- stbtt__active_edge *q = t->next;
--
-- t->next = q->next;
-- q->next = t;
-- *step = q;
-- changed = 1;
-- }
-- step = &(*step)->next;
-- }
-- if (!changed) break;
-- }
--
-- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
-- while (e->y0 <= scan_y) {
-- if (e->y1 > scan_y) {
-- stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata);
-- // find insertion point
-- if (active == NULL)
-- active = z;
-- else if (z->x < active->x) {
-- // insert at front
-- z->next = active;
-- active = z;
-- } else {
-- // find thing to insert AFTER
-- stbtt__active_edge *p = active;
-- while (p->next && p->next->x < z->x)
-- p = p->next;
-- // at this point, p->next->x is NOT < z->x
-- z->next = p->next;
-- p->next = z;
-- }
-- }
-- ++e;
-- }
--
-- // now process all active edges in XOR fashion
-- if (active)
-- stbtt__fill_active_edges(scanline, result->w, active, max_weight);
--
-- ++y;
-- }
-- STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
-- ++j;
-- }
--
-- while (active) {
-- stbtt__active_edge *z = active;
-- active = active->next;
-- STBTT_free(z, userdata);
-- }
--
-- if (scanline != scanline_data)
-- STBTT_free(scanline, userdata);
--}
--
--static int stbtt__edge_compare(const void *p, const void *q)
--{
-- stbtt__edge *a = (stbtt__edge *) p;
-- stbtt__edge *b = (stbtt__edge *) q;
--
-- if (a->y0 < b->y0) return -1;
-- if (a->y0 > b->y0) return 1;
-- return 0;
--}
--
--typedef struct
--{
-- float x,y;
--} stbtt__point;
--
--static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata)
--{
-- float y_scale_inv = invert ? -scale_y : scale_y;
-- stbtt__edge *e;
-- int n,i,j,k,m;
-- int vsubsample = result->h < 8 ? 15 : 5;
-- // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
--
-- // now we have to blow out the windings into explicit edge lists
-- n = 0;
-- for (i=0; i < windings; ++i)
-- n += wcount[i];
--
-- e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
-- if (e == 0) return;
-- n = 0;
--
-- m=0;
-- for (i=0; i < windings; ++i) {
-- stbtt__point *p = pts + m;
-- m += wcount[i];
-- j = wcount[i]-1;
-- for (k=0; k < wcount[i]; j=k++) {
-- int a=k,b=j;
-- // skip the edge if horizontal
-- if (p[j].y == p[k].y)
-- continue;
-- // add edge from j to k to the list
-- e[n].invert = 0;
-- if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
-- e[n].invert = 1;
-- a=j,b=k;
-- }
-- e[n].x0 = p[a].x * scale_x;
-- e[n].y0 = p[a].y * y_scale_inv * vsubsample;
-- e[n].x1 = p[b].x * scale_x;
-- e[n].y1 = p[b].y * y_scale_inv * vsubsample;
-- ++n;
-- }
-- }
--
-- // now sort the edges by their highest point (should snap to integer, and then by x)
-- STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
--
-- // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
-- stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
--
-- STBTT_free(e, userdata);
--}
--
--static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
--{
-- if (!points) return; // during first pass, it's unallocated
-- points[n].x = x;
-- points[n].y = y;
--}
--
--// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
--static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
--{
-- // midpoint
-- float mx = (x0 + 2*x1 + x2)/4;
-- float my = (y0 + 2*y1 + y2)/4;
-- // versus directly drawn line
-- float dx = (x0+x2)/2 - mx;
-- float dy = (y0+y2)/2 - my;
-- if (n > 16) // 65536 segments on one curve better be enough!
-- return 1;
-- if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
-- stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
-- stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
-- } else {
-- stbtt__add_point(points, *num_points,x2,y2);
-- *num_points = *num_points+1;
-- }
-- return 1;
--}
--
--// returns number of contours
--stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
--{
-- stbtt__point *points=0;
-- int num_points=0;
--
-- float objspace_flatness_squared = objspace_flatness * objspace_flatness;
-- int i,n=0,start=0, pass;
--
-- // count how many "moves" there are to get the contour count
-- for (i=0; i < num_verts; ++i)
-- if (vertices[i].type == STBTT_vmove)
-- ++n;
--
-- *num_contours = n;
-- if (n == 0) return 0;
--
-- *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
--
-- if (*contour_lengths == 0) {
-- *num_contours = 0;
-- return 0;
-- }
--
-- // make two passes through the points so we don't need to realloc
-- for (pass=0; pass < 2; ++pass) {
-- float x=0,y=0;
-- if (pass == 1) {
-- points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
-- if (points == NULL) goto error;
-- }
-- num_points = 0;
-- n= -1;
-- for (i=0; i < num_verts; ++i) {
-- switch (vertices[i].type) {
-- case STBTT_vmove:
-- // start the next contour
-- if (n >= 0)
-- (*contour_lengths)[n] = num_points - start;
-- ++n;
-- start = num_points;
--
-- x = vertices[i].x, y = vertices[i].y;
-- stbtt__add_point(points, num_points++, x,y);
-- break;
-- case STBTT_vline:
-- x = vertices[i].x, y = vertices[i].y;
-- stbtt__add_point(points, num_points++, x, y);
-- break;
-- case STBTT_vcurve:
-- stbtt__tesselate_curve(points, &num_points, x,y,
-- vertices[i].cx, vertices[i].cy,
-- vertices[i].x, vertices[i].y,
-- objspace_flatness_squared, 0);
-- x = vertices[i].x, y = vertices[i].y;
-- break;
-- }
-- }
-- (*contour_lengths)[n] = num_points - start;
-- }
--
-- return points;
--error:
-- STBTT_free(points, userdata);
-- STBTT_free(*contour_lengths, userdata);
-- *contour_lengths = 0;
-- *num_contours = 0;
-- return NULL;
--}
--
--void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata)
--{
-- float scale = scale_x > scale_y ? scale_y : scale_x;
-- int winding_count, *winding_lengths;
-- stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
-- if (windings) {
-- stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata);
-- STBTT_free(winding_lengths, userdata);
-- STBTT_free(windings, userdata);
-- }
--}
--
--void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
--{
-- STBTT_free(bitmap, userdata);
--}
--
--unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
--{
-- int ix0,iy0,ix1,iy1;
-- stbtt__bitmap gbm;
-- stbtt_vertex *vertices;
-- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
--
-- if (scale_x == 0) scale_x = scale_y;
-- if (scale_y == 0) {
-- if (scale_x == 0) return NULL;
-- scale_y = scale_x;
-- }
--
-- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1);
--
-- // now we get the size
-- gbm.w = (ix1 - ix0);
-- gbm.h = (iy1 - iy0);
-- gbm.pixels = NULL; // in case we error
--
-- if (width ) *width = gbm.w;
-- if (height) *height = gbm.h;
-- if (xoff ) *xoff = ix0;
-- if (yoff ) *yoff = iy0;
--
-- if (gbm.w && gbm.h) {
-- gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
-- if (gbm.pixels) {
-- gbm.stride = gbm.w;
--
-- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata);
-- }
-- }
-- STBTT_free(vertices, info->userdata);
-- return gbm.pixels;
--}
--
--void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
--{
-- int ix0,iy0;
-- stbtt_vertex *vertices;
-- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-- stbtt__bitmap gbm;
--
-- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0);
-- gbm.pixels = output;
-- gbm.w = out_w;
-- gbm.h = out_h;
-- gbm.stride = out_stride;
--
-- if (gbm.w && gbm.h)
-- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata);
--
-- STBTT_free(vertices, info->userdata);
--}
--
--unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
--{
-- return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
--}
--
--void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
--{
-- stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint));
--}
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// bitmap baking
--//
--// This is SUPER-SHITTY packing to keep source code small
--
--extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-- float pixel_height, // height of font in pixels
-- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-- int first_char, int num_chars, // characters to bake
-- stbtt_bakedchar *chardata)
--{
-- float scale;
-- int x,y,bottom_y, i;
-- stbtt_fontinfo f;
-- stbtt_InitFont(&f, data, offset);
-- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
-- x=y=1;
-- bottom_y = 1;
--
-- scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
--
-- for (i=0; i < num_chars; ++i) {
-- int advance, lsb, x0,y0,x1,y1,gw,gh;
-- int g = stbtt_FindGlyphIndex(&f, first_char + i);
-- stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
-- stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
-- gw = x1-x0;
-- gh = y1-y0;
-- if (x + gw + 1 >= pw)
-- y = bottom_y, x = 1; // advance to next row
-- if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
-- return -i;
-- STBTT_assert(x+gw < pw);
-- STBTT_assert(y+gh < ph);
-- stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
-- chardata[i].x0 = (stbtt_int16) x;
-- chardata[i].y0 = (stbtt_int16) y;
-- chardata[i].x1 = (stbtt_int16) (x + gw);
-- chardata[i].y1 = (stbtt_int16) (y + gh);
-- chardata[i].xadvance = scale * advance;
-- chardata[i].xoff = (float) x0;
-- chardata[i].yoff = (float) y0;
-- x = x + gw + 2;
-- if (y+gh+2 > bottom_y)
-- bottom_y = y+gh+2;
-- }
-- return bottom_y;
--}
--
--void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
--{
-- float d3d_bias = opengl_fillrule ? 0 : -0.5f;
-- float ipw = 1.0f / pw, iph = 1.0f / ph;
-- stbtt_bakedchar *b = chardata + char_index;
-- int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5);
-- int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5);
--
-- q->x0 = round_x + d3d_bias;
-- q->y0 = round_y + d3d_bias;
-- q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
-- q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
--
-- q->s0 = b->x0 * ipw;
-- q->t0 = b->y0 * ipw;
-- q->s1 = b->x1 * iph;
-- q->t1 = b->y1 * iph;
--
-- *xpos += b->xadvance;
--}
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// font name matching -- recommended not to use this
--//
--
--// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
--static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
--{
-- stbtt_int32 i=0;
--
-- // convert utf16 to utf8 and compare the results while converting
-- while (len2) {
-- stbtt_uint16 ch = s2[0]*256 + s2[1];
-- if (ch < 0x80) {
-- if (i >= len1) return -1;
-- if (s1[i++] != ch) return -1;
-- } else if (ch < 0x800) {
-- if (i+1 >= len1) return -1;
-- if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
-- if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
-- } else if (ch >= 0xd800 && ch < 0xdc00) {
-- stbtt_uint32 c;
-- stbtt_uint16 ch2 = s2[2]*256 + s2[3];
-- if (i+3 >= len1) return -1;
-- c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
-- if (s1[i++] != 0xf0 + (c >> 18)) return -1;
-- if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
-- if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
-- if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
-- s2 += 2; // plus another 2 below
-- len2 -= 2;
-- } else if (ch >= 0xdc00 && ch < 0xe000) {
-- return -1;
-- } else {
-- if (i+2 >= len1) return -1;
-- if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
-- if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
-- if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
-- }
-- s2 += 2;
-- len2 -= 2;
-- }
-- return i;
--}
--
--int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
--{
-- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
--}
--
--// returns results in whatever encoding you request... but note that 2-byte encodings
--// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
--char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
--{
-- stbtt_int32 i,count,stringOffset;
-- stbtt_uint8 *fc = font->data;
-- stbtt_uint32 offset = font->fontstart;
-- stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
-- if (!nm) return NULL;
--
-- count = ttUSHORT(fc+nm+2);
-- stringOffset = nm + ttUSHORT(fc+nm+4);
-- for (i=0; i < count; ++i) {
-- stbtt_uint32 loc = nm + 6 + 12 * i;
-- if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
-- && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
-- *length = ttUSHORT(fc+loc+8);
-- return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
-- }
-- }
-- return NULL;
--}
--
--static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
--{
-- stbtt_int32 i;
-- stbtt_int32 count = ttUSHORT(fc+nm+2);
-- stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
--
-- for (i=0; i < count; ++i) {
-- stbtt_uint32 loc = nm + 6 + 12 * i;
-- stbtt_int32 id = ttUSHORT(fc+loc+6);
-- if (id == target_id) {
-- // find the encoding
-- stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
--
-- // is this a Unicode encoding?
-- if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
-- stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10);
--
-- // check if there's a prefix match
-- stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
-- if (matchlen >= 0) {
-- // check for target_id+1 immediately following, with same encoding & language
-- if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
-- stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10);
-- if (slen == 0) {
-- if (matchlen == nlen)
-- return 1;
-- } else if (matchlen < nlen && name[matchlen] == ' ') {
-- ++matchlen;
-- if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
-- return 1;
-- }
-- } else {
-- // if nothing immediately following
-- if (matchlen == nlen)
-- return 1;
-- }
-- }
-- }
--
-- // @TODO handle other encodings
-- }
-- }
-- return 0;
--}
--
--static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
--{
-- stbtt_int32 nlen = STBTT_strlen((char *) name);
-- stbtt_uint32 nm,hd;
-- if (!stbtt__isfont(fc+offset)) return 0;
--
-- // check italics/bold/underline flags in macStyle...
-- if (flags) {
-- hd = stbtt__find_table(fc, offset, "head");
-- if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
-- }
--
-- nm = stbtt__find_table(fc, offset, "name");
-- if (!nm) return 0;
--
-- if (flags) {
-- // if we checked the macStyle flags, then just check the family and ignore the subfamily
-- if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-- } else {
-- if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-- }
--
-- return 0;
--}
--
--int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
--{
-- stbtt_int32 i;
-- for (i=0;;++i) {
-- stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
-- if (off < 0) return off;
-- if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
-- return off;
-- }
--}
--
--#endif // STB_TRUETYPE_IMPLEMENTATION
-+// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools
-+//
-+// This library processes TrueType files:
-+// parse files
-+// extract glyph metrics
-+// extract glyph shapes
-+// render glyphs to one-channel bitmaps with antialiasing (box filter)
-+//
-+// Todo:
-+// non-MS cmaps
-+// crashproof on bad data
-+// hinting
-+// subpixel positioning when rendering bitmap
-+// cleartype-style AA
-+//
-+// ADDITIONAL CONTRIBUTORS
-+//
-+// Mikko Mononen: compound shape support, more cmap formats
-+//
-+// VERSIONS
-+//
-+// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
-+// userdata, malloc-from-userdata, non-zero fill (STB)
-+// 0.2 (2009-03-11) Fix unsigned/signed char warnings
-+// 0.1 (2009-03-09) First public release
-+//
-+// USAGE
-+//
-+// Include this file in whatever places neeed to refer to it. In ONE C/C++
-+// file, write:
-+// #define STB_TRUETYPE_IMPLEMENTATION
-+// before the #include of this file. This expands out the actual
-+// implementation into that C/C++ file.
-+//
-+// Look at the header-file sections below for the API, but here's a quick skim:
-+//
-+// Simple 3D API (don't ship this, but it's fine for tools and quick start,
-+// and you can cut and paste from it to move to more advanced)
-+// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
-+// stbtt_GetBakedQuad() -- compute quad to draw for a given char
-+//
-+// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
-+// stbtt_InitFont()
-+// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
-+//
-+// Render a unicode codepoint to a bitmap
-+// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
-+// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
-+// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
-+//
-+// Character advance/positioning
-+// stbtt_GetCodepointHMetrics()
-+// stbtt_GetFontVMetrics()
-+//
-+// NOTES
-+//
-+// The system uses the raw data found in the .ttf file without changing it
-+// and without building auxiliary data structures. This is a bit inefficient
-+// on little-endian systems (the data is big-endian), but assuming you're
-+// caching the bitmaps or glyph shapes this shouldn't be a big deal.
-+//
-+// It appears to be very hard to programmatically determine what font a
-+// given file is in a general way. I provide an API for this, but I don't
-+// recommend it.
-+//
-+//
-+// SOURCE STATISTICS (based on v0.3, 1800 LOC)
-+//
-+// Documentation & header file 350 LOC \___ 500 LOC documentation
-+// Sample code 140 LOC /
-+// Truetype parsing 580 LOC ---- 600 LOC TrueType
-+// Software rasterization 240 LOC \ .
-+// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation
-+// Bitmap management 70 LOC /
-+// Baked bitmap interface 70 LOC /
-+// Font name matching & access 150 LOC ---- 150
-+// C runtime library abstraction 60 LOC ---- 60
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//////////////////////////////////////////////////////////////////////////////
-+////
-+//// SAMPLE PROGRAMS
-+////
-+//
-+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
-+//
-+#if 0
-+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-+#include "stb_truetype.h"
-+
-+char ttf_buffer[1<<20];
-+unsigned char temp_bitmap[512*512];
-+
-+stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs
-+GLstbtt_uint ftex;
-+
-+void my_stbtt_initfont(void)
-+{
-+ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
-+ stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
-+ // can free ttf_buffer at this point
-+ glGenTextures(1, &ftex);
-+ glBindTexture(GL_TEXTURE_2D, ftex);
-+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
-+ // can free temp_bitmap at this point
-+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-+}
-+
-+void my_stbtt_print(float x, float y, char *text)
-+{
-+ // assume orthographic projection with units = screen pixels, origin at top left
-+ glBindTexture(GL_TEXTURE_2D, ftex);
-+ glBegin(GL_QUADS);
-+ while (*text) {
-+ if (*text >= 32 && *text < 128) {
-+ stbtt_aligned_quad q;
-+ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d
-+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
-+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
-+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
-+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
-+ }
-+ ++text;
-+ }
-+ glEnd();
-+}
-+#endif
-+//
-+//
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// Complete program (this compiles): get a single bitmap, print as ASCII art
-+//
-+#if 0
-+#include
-+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-+#include "stb_truetype.h"
-+
-+char ttf_buffer[1<<25];
-+
-+int main(int argc, char **argv)
-+{
-+ stbtt_fontinfo font;
-+ unsigned char *bitmap;
-+ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
-+
-+ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
-+
-+ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
-+ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
-+
-+ for (j=0; j < h; ++j) {
-+ for (i=0; i < w; ++i)
-+ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
-+ putchar('\n');
-+ }
-+ return 0;
-+}
-+#endif
-+//
-+// Output:
-+//
-+// .ii.
-+// @@@@@@.
-+// V@Mio@@o
-+// :i. V@V
-+// :oM@@M
-+// :@@@MM@M
-+// @@o o@M
-+// :@@. M@M
-+// @@@o@@@@
-+// :M@@V:@@.
-+//
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// Complete program: print "Hello World!" banner, with bugs
-+//
-+#if 0
-+int main(int arg, char **argv)
-+{
-+ unsigned char screen[20][79];
-+ int i,j, pos=0;
-+ float scale;
-+ char *text = "Heljo World!";
-+
-+ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
-+ stbtt_InitFont(&font, buffer, 0);
-+
-+ scale = stbtt_ScaleForPixelHeight(&font, 16);
-+ memset(screen, 0, sizeof(screen));
-+
-+ while (*text) {
-+ int advance,lsb,x0,y0,x1,y1, newpos, baseline=13;
-+ stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb);
-+ stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1);
-+ newpos = pos + (int) (lsb * scale) + x0;
-+ stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text);
-+ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
-+ // because this API is really for baking character bitmaps into textures
-+ pos += (int) (advance * scale);
-+ ++text;
-+ }
-+
-+ for (j=0; j < 20; ++j) {
-+ for (i=0; i < 79; ++i)
-+ putchar(" .:ioVM@"[screen[j][i]>>5]);
-+ putchar('\n');
-+ }
-+
-+ return 0;
-+}
-+#endif
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//////////////////////////////////////////////////////////////////////////////
-+////
-+//// INTEGRATION WITH RUNTIME LIBRARIES
-+////
-+
-+#ifdef STB_TRUETYPE_IMPLEMENTATION
-+ // #define your own (u)stbtt_int8/16/32 before including to override this
-+ #ifndef stbtt_uint8
-+ typedef unsigned char stbtt_uint8;
-+ typedef signed char stbtt_int8;
-+ typedef unsigned short stbtt_uint16;
-+ typedef signed short stbtt_int16;
-+ typedef unsigned int stbtt_uint32;
-+ typedef signed int stbtt_int32;
-+ #endif
-+
-+ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
-+ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
-+
-+ // #define your own STBTT_sort() to override this to avoid qsort
-+ #ifndef STBTT_sort
-+ #include
-+ #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func)
-+ #endif
-+
-+ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
-+ #ifndef STBTT_ifloor
-+ #include
-+ #define STBTT_ifloor(x) ((int) floor(x))
-+ #define STBTT_iceil(x) ((int) ceil(x))
-+ #endif
-+
-+ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
-+ #ifndef STBTT_malloc
-+ #include
-+ #define STBTT_malloc(x,u) malloc(x)
-+ #define STBTT_free(x,u) free(x)
-+ #endif
-+
-+ #ifndef STBTT_assert
-+ #include
-+ #define STBTT_assert(x) assert(x)
-+ #endif
-+
-+ #ifndef STBTT_strlen
-+ #include
-+ #define STBTT_strlen(x) strlen(x)
-+ #endif
-+
-+ #ifndef STBTT_memcpy
-+ #include
-+ #define STBTT_memcpy memcpy
-+ #define STBTT_memset memset
-+ #endif
-+#endif
-+
-+///////////////////////////////////////////////////////////////////////////////
-+///////////////////////////////////////////////////////////////////////////////
-+////
-+//// INTERFACE
-+////
-+////
-+
-+#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
-+#define __STB_INCLUDE_STB_TRUETYPE_H__
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// TEXTURE BAKING API
-+//
-+// If you use this API, you only have to call two functions ever.
-+//
-+
-+typedef struct
-+{
-+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
-+ float xoff,yoff,xadvance;
-+} stbtt_bakedchar;
-+
-+extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-+ float pixel_height, // height of font in pixels
-+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-+ int first_char, int num_chars, // characters to bake
-+ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
-+// if return is positive, the first unused row of the bitmap
-+// if return is negative, returns the negative of the number of characters that fit
-+// if return is 0, no characters fit and no rows were used
-+// This uses a very crappy packing.
-+
-+typedef struct
-+{
-+ float x0,y0,s0,t0; // top-left
-+ float x1,y1,s1,t1; // bottom-right
-+} stbtt_aligned_quad;
-+
-+extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
-+ int char_index, // character to display
-+ float *xpos, float *ypos, // pointers to current position in screen pixel space
-+ stbtt_aligned_quad *q, // output: quad to draw
-+ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
-+// Call GetBakedQuad with char_index = 'character - first_char', and it
-+// creates the quad you need to draw and advances the current position.
-+// It's inefficient; you might want to c&p it and optimize it.
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// FONT LOADING
-+//
-+//
-+
-+extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
-+// Each .ttf file may have more than one font. Each has a sequential index
-+// number starting from 0. Call this function to get the font offset for a
-+// given index; it returns -1 if the index is out of range. A regular .ttf
-+// file will only define one font and it always be at offset 0, so it will
-+// return '0' for index 0, and -1 for all other indices. You can just skip
-+// this step if you know it's that kind of font.
-+
-+
-+// The following structure is defined publically so you can declare one on
-+// the stack or as a global or etc.
-+typedef struct
-+{
-+ void *userdata;
-+ unsigned char *data; // pointer to .ttf file
-+ int fontstart; // offset of start of font
-+
-+ int numGlyphs; // number of glyphs, needed for range checking
-+
-+ int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf
-+ int index_map; // a cmap mapping for our chosen character encoding
-+ int indexToLocFormat; // format needed to map from glyph index to glyph
-+} stbtt_fontinfo;
-+
-+extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
-+// Given an offset into the file that defines a font, this function builds
-+// the necessary cached info for the rest of the system. You must allocate
-+// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
-+// need to do anything special to free it, because the contents are a pure
-+// cache with no additional data structures. Returns 0 on failure.
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// CHARACTER TO GLYPH-INDEX CONVERSIOn
-+
-+int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
-+// If you're going to perform multiple operations on the same character
-+// and you want a speed-up, call this function with the character you're
-+// going to process, then use glyph-based functions instead of the
-+// codepoint-based functions.
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// CHARACTER PROPERTIES
-+//
-+
-+extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
-+// computes a scale factor to produce a font whose "height" is 'pixels' tall.
-+// Height is measured as the distance from the highest ascender to the lowest
-+// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
-+// and computing:
-+// scale = pixels / (ascent - descent)
-+// so if you prefer to measure height by the ascent only, use a similar calculation.
-+
-+extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
-+// ascent is the coordinate above the baseline the font extends; descent
-+// is the coordinate below the baseline the font extends (i.e. it is typically negative)
-+// lineGap is the spacing between one row's descent and the next row's ascent...
-+// so you should advance the vertical position by "*ascent - *descent + *lineGap"
-+// these are expressed in unscaled coordinates
-+
-+extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
-+// leftSideBearing is the offset from the current horizontal position to the left edge of the character
-+// advanceWidth is the offset from the current horizontal position to the next horizontal position
-+// these are expressed in unscaled coordinates
-+
-+extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
-+// an additional amount to add to the 'advance' value between ch1 and ch2
-+// @TODO; for now always returns 0!
-+
-+extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
-+// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
-+
-+extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
-+extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
-+extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
-+// as above, but takes one or more glyph indices for greater efficiency
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// GLYPH SHAPES (you probably don't need these, but they have to go before
-+// the bitmaps for C declaration-order reasons)
-+//
-+
-+#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
-+ enum {
-+ STBTT_vmove=1,
-+ STBTT_vline,
-+ STBTT_vcurve
-+ };
-+#endif
-+
-+#ifndef stbtt_vertex // you can predefine this to use different values
-+ // (we share this with other code at RAD)
-+ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
-+ typedef struct
-+ {
-+ stbtt_vertex_type x,y,cx,cy;
-+ unsigned char type,padding;
-+ } stbtt_vertex;
-+#endif
-+
-+extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
-+extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
-+// returns # of vertices and fills *vertices with the pointer to them
-+// these are expressed in "unscaled" coordinates
-+
-+extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
-+// frees the data allocated above
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// BITMAP RENDERING
-+//
-+
-+extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
-+// frees the bitmap allocated below
-+
-+extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
-+// allocates a large-enough single-channel 8bpp bitmap and renders the
-+// specified character/glyph at the specified scale into it, with
-+// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
-+// *width & *height are filled out with the width & height of the bitmap,
-+// which is stored left-to-right, top-to-bottom.
-+//
-+// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
-+
-+extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
-+// the same as above, but you pass in storage for the bitmap in the form
-+// of 'output', with row spacing of 'out_stride' bytes. the bitmap is
-+// clipped to out_w/out_h bytes. call the next function to get the
-+// height and width and positioning info
-+
-+extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-+// get the bbox of the bitmap centered around the glyph origin; so the
-+// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
-+// the bitmap top left is (leftSideBearing*scale,iy0).
-+// (Note that the bitmap uses y-increases-down, but the shape uses
-+// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
-+
-+extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
-+extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-+extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
-+
-+//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-+
-+// @TODO: don't expose this structure
-+typedef struct
-+{
-+ int w,h,stride;
-+ unsigned char *pixels;
-+} stbtt__bitmap;
-+
-+extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata);
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// Finding the right font...
-+//
-+// You should really just solve this offline, keep your own tables
-+// of what font is what, and don't try to get it out of the .ttf file.
-+// That's because getting it out of the .ttf file is really hard, because
-+// the names in the file can appear in many possible encodings, in many
-+// possible languages, and e.g. if you need a case-insensitive comparison,
-+// the details of that depend on the encoding & language in a complex way
-+// (actually underspecified in truetype, but also gigantic).
-+//
-+// But you can use the provided functions in two possible ways:
-+// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
-+// unicode-encoded names to try to find the font you want;
-+// you can run this before calling stbtt_InitFont()
-+//
-+// stbtt_GetFontNameString() lets you get any of the various strings
-+// from the file yourself and do your own comparisons on them.
-+// You have to have called stbtt_InitFont() first.
-+
-+
-+extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
-+// returns the offset (not index) of the font that matches, or -1 if none
-+// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
-+// if you use any other flag, use a font name like "Arial"; this checks
-+// the 'macStyle' header field; i don't know if fonts set this consistently
-+#define STBTT_MACSTYLE_DONTCARE 0
-+#define STBTT_MACSTYLE_BOLD 1
-+#define STBTT_MACSTYLE_ITALIC 2
-+#define STBTT_MACSTYLE_UNDERSCORE 4
-+#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
-+
-+extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
-+// returns 1/0 whether the first string interpreted as utf8 is identical to
-+// the second string interpreted as big-endian utf16... useful for strings from next func
-+
-+extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
-+// returns the string (which may be big-endian double byte, e.g. for unicode)
-+// and puts the length in bytes in *length.
-+//
-+// some of the values for the IDs are below; for more see the truetype spec:
-+// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
-+// http://www.microsoft.com/typography/otspec/name.htm
-+
-+enum { // platformID
-+ STBTT_PLATFORM_ID_UNICODE =0,
-+ STBTT_PLATFORM_ID_MAC =1,
-+ STBTT_PLATFORM_ID_ISO =2,
-+ STBTT_PLATFORM_ID_MICROSOFT =3
-+};
-+
-+enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
-+ STBTT_UNICODE_EID_UNICODE_1_0 =0,
-+ STBTT_UNICODE_EID_UNICODE_1_1 =1,
-+ STBTT_UNICODE_EID_ISO_10646 =2,
-+ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
-+ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4,
-+};
-+
-+enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
-+ STBTT_MS_EID_SYMBOL =0,
-+ STBTT_MS_EID_UNICODE_BMP =1,
-+ STBTT_MS_EID_SHIFTJIS =2,
-+ STBTT_MS_EID_UNICODE_FULL =10,
-+};
-+
-+enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
-+ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
-+ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
-+ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
-+ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7,
-+};
-+
-+enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
-+ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
-+ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
-+ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
-+ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
-+ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
-+ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
-+ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D,
-+};
-+
-+enum { // languageID for STBTT_PLATFORM_ID_MAC
-+ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
-+ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
-+ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
-+ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
-+ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
-+ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
-+ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19,
-+};
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif // __STB_INCLUDE_STB_TRUETYPE_H__
-+
-+///////////////////////////////////////////////////////////////////////////////
-+///////////////////////////////////////////////////////////////////////////////
-+////
-+//// IMPLEMENTATION
-+////
-+////
-+
-+#ifdef STB_TRUETYPE_IMPLEMENTATION
-+
-+//////////////////////////////////////////////////////////////////////////
-+//
-+// accessors to parse data from file
-+//
-+
-+// on platforms that don't allow misaligned reads, if we want to allow
-+// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
-+
-+#define ttBYTE(p) (* (stbtt_uint8 *) (p))
-+#define ttCHAR(p) (* (stbtt_int8 *) (p))
-+#define ttFixed(p) ttLONG(p)
-+
-+#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
-+
-+ #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
-+ #define ttSHORT(p) (* (stbtt_int16 *) (p))
-+ #define ttULONG(p) (* (stbtt_uint32 *) (p))
-+ #define ttLONG(p) (* (stbtt_int32 *) (p))
-+
-+#else
-+
-+ stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-+ stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-+ stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-+ stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-+
-+#endif
-+
-+#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
-+#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
-+
-+static int stbtt__isfont(const stbtt_uint8 *font)
-+{
-+ // check the version number
-+ if (stbtt_tag(font, "1")) return 1; // TrueType 1
-+ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
-+ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
-+ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
-+ return 0;
-+}
-+
-+// @OPTIMIZE: binary search
-+static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
-+{
-+ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
-+ stbtt_uint32 tabledir = fontstart + 12;
-+ stbtt_int32 i;
-+ for (i=0; i < num_tables; ++i) {
-+ stbtt_uint32 loc = tabledir + 16*i;
-+ if (stbtt_tag(data+loc+0, tag))
-+ return ttULONG(data+loc+8);
-+ }
-+ return 0;
-+}
-+
-+int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
-+{
-+ // if it's just a font, there's only one valid index
-+ if (stbtt__isfont(font_collection))
-+ return index == 0 ? 0 : -1;
-+
-+ // check if it's a TTC
-+ if (stbtt_tag(font_collection, "ttcf")) {
-+ // version 1?
-+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
-+ stbtt_int32 n = ttLONG(font_collection+8);
-+ if (index >= n)
-+ return -1;
-+ return ttULONG(font_collection+12+index*14);
-+ }
-+ }
-+ return -1;
-+}
-+
-+int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
-+{
-+ stbtt_uint8 *data = (stbtt_uint8 *) data2;
-+ stbtt_uint32 cmap, t;
-+ stbtt_int32 i,numTables;
-+
-+ info->data = data;
-+ info->fontstart = fontstart;
-+
-+ cmap = stbtt__find_table(data, fontstart, "cmap");
-+ info->loca = stbtt__find_table(data, fontstart, "loca");
-+ info->head = stbtt__find_table(data, fontstart, "head");
-+ info->glyf = stbtt__find_table(data, fontstart, "glyf");
-+ info->hhea = stbtt__find_table(data, fontstart, "hhea");
-+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx");
-+ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
-+ return 0;
-+
-+ t = stbtt__find_table(data, fontstart, "maxp");
-+ if (t)
-+ info->numGlyphs = ttUSHORT(data+t+4);
-+ else
-+ info->numGlyphs = 0xffff;
-+
-+ // find a cmap encoding table we understand *now* to avoid searching
-+ // later. (todo: could make this installable)
-+ // the same regardless of glyph.
-+ numTables = ttUSHORT(data + cmap + 2);
-+ info->index_map = 0;
-+ for (i=0; i < numTables; ++i) {
-+ stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
-+ // find an encoding we understand:
-+ switch(ttUSHORT(data+encoding_record)) {
-+ case STBTT_PLATFORM_ID_MICROSOFT:
-+ switch (ttUSHORT(data+encoding_record+2)) {
-+ case STBTT_MS_EID_UNICODE_BMP:
-+ case STBTT_MS_EID_UNICODE_FULL:
-+ // MS/Unicode
-+ info->index_map = cmap + ttULONG(data+encoding_record+4);
-+ break;
-+ }
-+ break;
-+ }
-+ }
-+ if (info->index_map == 0)
-+ return 0;
-+
-+ info->indexToLocFormat = ttUSHORT(data+info->head + 50);
-+ return 1;
-+}
-+
-+int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
-+{
-+ stbtt_uint8 *data = info->data;
-+ stbtt_uint32 index_map = info->index_map;
-+
-+ stbtt_uint16 format = ttUSHORT(data + index_map + 0);
-+ if (format == 0) { // apple byte encoding
-+ stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
-+ if (unicode_codepoint < bytes-6)
-+ return ttBYTE(data + index_map + 6 + unicode_codepoint);
-+ return 0;
-+ } else if (format == 6) {
-+ stbtt_uint32 first = ttUSHORT(data + index_map + 6);
-+ stbtt_uint32 count = ttUSHORT(data + index_map + 8);
-+ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
-+ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
-+ return 0;
-+ } else if (format == 2) {
-+ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
-+ return 0;
-+ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
-+ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
-+ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
-+ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
-+ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
-+ stbtt_uint16 item, offset, start, end;
-+
-+ // do a binary search of the segments
-+ stbtt_uint32 endCount = index_map + 14;
-+ stbtt_uint32 search = endCount;
-+
-+ if (unicode_codepoint > 0xffff)
-+ return 0;
-+
-+ // they lie from endCount .. endCount + segCount
-+ // but searchRange is the nearest power of two, so...
-+ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
-+ search += rangeShift*2;
-+
-+ // now decrement to bias correctly to find smallest
-+ search -= 2;
-+ while (entrySelector) {
-+ stbtt_uint16 start, end;
-+ searchRange >>= 1;
-+ start = ttUSHORT(data + search + 2 + segcount*2 + 2);
-+ end = ttUSHORT(data + search + 2);
-+ start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2);
-+ end = ttUSHORT(data + search + searchRange*2);
-+ if (unicode_codepoint > end)
-+ search += searchRange*2;
-+ --entrySelector;
-+ }
-+ search += 2;
-+
-+ item = (stbtt_uint16) ((search - endCount) >> 1);
-+
-+ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
-+ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
-+ end = ttUSHORT(data + index_map + 14 + 2 + 2*item);
-+ if (unicode_codepoint < start)
-+ return 0;
-+
-+ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
-+ if (offset == 0)
-+ return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item);
-+
-+ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
-+ } else if (format == 12) {
-+ stbtt_uint16 ngroups = ttUSHORT(data+index_map+6);
-+ stbtt_int32 low,high;
-+ low = 0; high = (stbtt_int32)ngroups;
-+ // Binary search the right group.
-+ while (low <= high) {
-+ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
-+ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
-+ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
-+ if ((stbtt_uint32) unicode_codepoint < start_char)
-+ high = mid-1;
-+ else if ((stbtt_uint32) unicode_codepoint > end_char)
-+ low = mid+1;
-+ else {
-+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
-+ return start_glyph + unicode_codepoint-start_char;
-+ }
-+ }
-+ return 0; // not found
-+ }
-+ // @TODO
-+ STBTT_assert(0);
-+ return 0;
-+}
-+
-+int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
-+{
-+ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
-+}
-+
-+static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy)
-+{
-+ v->type = type;
-+ v->x = x;
-+ v->y = y;
-+ v->cx = cx;
-+ v->cy = cy;
-+}
-+
-+static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
-+{
-+ int g1,g2;
-+
-+ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
-+ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
-+
-+ if (info->indexToLocFormat == 0) {
-+ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
-+ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
-+ } else {
-+ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
-+ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
-+ }
-+
-+ return g1==g2 ? -1 : g1; // if length is 0, return -1
-+}
-+
-+int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
-+{
-+ int g = stbtt__GetGlyfOffset(info, glyph_index);
-+ if (g < 0) return 0;
-+
-+ if (x0) *x0 = ttSHORT(info->data + g + 2);
-+ if (y0) *y0 = ttSHORT(info->data + g + 4);
-+ if (x1) *x1 = ttSHORT(info->data + g + 6);
-+ if (y1) *y1 = ttSHORT(info->data + g + 8);
-+ return 1;
-+}
-+
-+int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
-+{
-+ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
-+}
-+
-+int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-+{
-+ stbtt_int16 numberOfContours;
-+ stbtt_uint8 *endPtsOfContours;
-+ stbtt_uint8 *data = info->data;
-+ stbtt_vertex *vertices=0;
-+ int num_vertices=0;
-+ int g = stbtt__GetGlyfOffset(info, glyph_index);
-+
-+ *pvertices = NULL;
-+
-+ if (g < 0) return 0;
-+
-+ numberOfContours = ttSHORT(data + g);
-+
-+ if (numberOfContours > 0) {
-+ stbtt_uint8 flags=0,flagcount;
-+ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off;
-+ stbtt_int16 x,y,cx,cy,sx,sy;
-+ stbtt_uint8 *points;
-+ endPtsOfContours = (data + g + 10);
-+ ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
-+ points = data + g + 10 + numberOfContours * 2 + 2 + ins;
-+
-+ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
-+
-+ m = n + numberOfContours; // a loose bound on how many vertices we might need
-+ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
-+ if (vertices == 0)
-+ return 0;
-+
-+ next_move = 0;
-+ flagcount=0;
-+
-+ // in first pass, we load uninterpreted data into the allocated array
-+ // above, shifted to the end of the array so we won't overwrite it when
-+ // we create our final data starting from the front
-+
-+ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
-+
-+ // first load flags
-+
-+ for (i=0; i < n; ++i) {
-+ if (flagcount == 0) {
-+ flags = *points++;
-+ if (flags & 8)
-+ flagcount = *points++;
-+ } else
-+ --flagcount;
-+ vertices[off+i].type = flags;
-+ }
-+
-+ // now load x coordinates
-+ x=0;
-+ for (i=0; i < n; ++i) {
-+ flags = vertices[off+i].type;
-+ if (flags & 2) {
-+ stbtt_int16 dx = *points++;
-+ x += (flags & 16) ? dx : -dx; // ???
-+ } else {
-+ if (!(flags & 16)) {
-+ x = x + (stbtt_int16) (points[0]*256 + points[1]);
-+ points += 2;
-+ }
-+ }
-+ vertices[off+i].x = x;
-+ }
-+
-+ // now load y coordinates
-+ y=0;
-+ for (i=0; i < n; ++i) {
-+ flags = vertices[off+i].type;
-+ if (flags & 4) {
-+ stbtt_int16 dy = *points++;
-+ y += (flags & 32) ? dy : -dy; // ???
-+ } else {
-+ if (!(flags & 32)) {
-+ y = y + (stbtt_int16) (points[0]*256 + points[1]);
-+ points += 2;
-+ }
-+ }
-+ vertices[off+i].y = y;
-+ }
-+
-+ // now convert them to our format
-+ num_vertices=0;
-+ sx = sy = cx = cy = 0;
-+ for (i=0; i < n; ++i) {
-+ flags = vertices[off+i].type;
-+ x = (stbtt_int16) vertices[off+i].x;
-+ y = (stbtt_int16) vertices[off+i].y;
-+ if (next_move == i) {
-+ // when we get to the end, we have to close the shape explicitly
-+ if (i != 0) {
-+ if (was_off)
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-+ else
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-+ }
-+
-+ // now start the new one
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0);
-+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
-+ ++j;
-+ was_off = 0;
-+ sx = x;
-+ sy = y;
-+ } else {
-+ if (!(flags & 1)) { // if it's a curve
-+ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
-+ cx = x;
-+ cy = y;
-+ was_off = 1;
-+ } else {
-+ if (was_off)
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
-+ else
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
-+ was_off = 0;
-+ }
-+ }
-+ }
-+ if (i != 0) {
-+ if (was_off)
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-+ else
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-+ }
-+ } else if (numberOfContours == -1) {
-+ // Compound shapes.
-+ int more = 1;
-+ stbtt_uint8 *comp = data + g + 10;
-+ num_vertices = 0;
-+ vertices = 0;
-+ while (more) {
-+ stbtt_uint16 flags, gidx;
-+ int comp_num_verts = 0, i;
-+ stbtt_vertex *comp_verts = 0, *tmp = 0;
-+ float mtx[6] = {1,0,0,1,0,0}, m, n;
-+
-+ flags = ttSHORT(comp); comp+=2;
-+ gidx = ttSHORT(comp); comp+=2;
-+
-+ if (flags & 2) { // XY values
-+ if (flags & 1) { // shorts
-+ mtx[4] = ttSHORT(comp); comp+=2;
-+ mtx[5] = ttSHORT(comp); comp+=2;
-+ } else {
-+ mtx[4] = ttCHAR(comp); comp+=1;
-+ mtx[5] = ttCHAR(comp); comp+=1;
-+ }
-+ }
-+ else {
-+ // @TODO handle matching point
-+ STBTT_assert(0);
-+ }
-+ if (flags & (1<<3)) { // WE_HAVE_A_SCALE
-+ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[1] = mtx[2] = 0;
-+ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
-+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[1] = mtx[2] = 0;
-+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-+ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
-+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-+ }
-+
-+ // Find transformation scales.
-+ m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
-+ n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
-+
-+ // Get indexed glyph.
-+ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
-+ if (comp_num_verts > 0) {
-+ // Transform vertices.
-+ for (i = 0; i < comp_num_verts; ++i) {
-+ stbtt_vertex* v = &comp_verts[i];
-+ stbtt_vertex_type x,y;
-+ x=v->x; y=v->y;
-+ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-+ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-+ x=v->cx; y=v->cy;
-+ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-+ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-+ }
-+ // Append vertices.
-+ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
-+ if (!tmp) {
-+ if (vertices) STBTT_free(vertices, info->userdata);
-+ if (comp_verts) STBTT_free(comp_verts, info->userdata);
-+ return 0;
-+ }
-+ if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
-+ memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
-+ if (vertices) STBTT_free(vertices, info->userdata);
-+ vertices = tmp;
-+ STBTT_free(comp_verts, info->userdata);
-+ num_vertices += comp_num_verts;
-+ }
-+ // More components ?
-+ more = flags & (1<<5);
-+ }
-+ } else if (numberOfContours < 0) {
-+ // @TODO other compound variations?
-+ STBTT_assert(0);
-+ } else {
-+ // numberOfCounters == 0, do nothing
-+ }
-+
-+ *pvertices = vertices;
-+ return num_vertices;
-+}
-+
-+void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
-+{
-+ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
-+ if (glyph_index < numOfLongHorMetrics) {
-+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
-+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
-+ } else {
-+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
-+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
-+ }
-+}
-+
-+int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/)
-+{
-+ return 0;
-+}
-+
-+int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/)
-+{
-+ return 0;
-+}
-+
-+void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
-+{
-+ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
-+}
-+
-+void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
-+{
-+ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
-+ if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
-+ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
-+}
-+
-+float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
-+{
-+ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
-+ return (float) height / fheight;
-+}
-+
-+void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
-+{
-+ STBTT_free(v, info->userdata);
-+}
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// antialiasing software rasterizer
-+//
-+
-+void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-+{
-+ int x0,y0,x1,y1;
-+ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1))
-+ x0=y0=x1=y1=0; // e.g. space character
-+ // now move to integral bboxes (treating pixels as little squares, what pixels get touched)?
-+ if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x);
-+ if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y);
-+ if (ix1) *ix1 = STBTT_iceil (x1 * scale_x);
-+ if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y);
-+}
-+
-+void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-+{
-+ stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1);
-+}
-+
-+typedef struct stbtt__edge {
-+ float x0,y0, x1,y1;
-+ int invert;
-+} stbtt__edge;
-+
-+typedef struct stbtt__active_edge
-+{
-+ int x,dx;
-+ float ey;
-+ struct stbtt__active_edge *next;
-+ int valid;
-+} stbtt__active_edge;
-+
-+#define FIXSHIFT 10
-+#define FIX (1 << FIXSHIFT)
-+#define FIXMASK (FIX-1)
-+
-+static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata)
-+{
-+ stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!!
-+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
-+ STBTT_assert(e->y0 <= start_point);
-+ if (!z) return z;
-+ // round dx down to avoid going too far
-+ if (dxdy < 0)
-+ z->dx = -STBTT_ifloor(FIX * -dxdy);
-+ else
-+ z->dx = STBTT_ifloor(FIX * dxdy);
-+ z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0)));
-+ z->x -= off_x * FIX;
-+ z->ey = e->y1;
-+ z->next = 0;
-+ z->valid = e->invert ? 1 : -1;
-+ return z;
-+}
-+
-+// note: this routine clips fills that extend off the edges... ideally this
-+// wouldn't happen, but it could happen if the truetype glyph bounding boxes
-+// are wrong, or if the user supplies a too-small bitmap
-+static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
-+{
-+ // non-zero winding fill
-+ int x0=0, w=0;
-+
-+ while (e) {
-+ if (w == 0) {
-+ // if we're currently at zero, we need to record the edge start point
-+ x0 = e->x; w += e->valid;
-+ } else {
-+ int x1 = e->x; w += e->valid;
-+ // if we went to zero, we need to draw
-+ if (w == 0) {
-+ int i = x0 >> FIXSHIFT;
-+ int j = x1 >> FIXSHIFT;
-+
-+ if (i < len && j >= 0) {
-+ if (i == j) {
-+ // x0,x1 are the same pixel, so compute combined coverage
-+ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT);
-+ } else {
-+ if (i >= 0) // add antialiasing for x0
-+ scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT);
-+ else
-+ i = -1; // clip
-+
-+ if (j < len) // add antialiasing for x1
-+ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT);
-+ else
-+ j = len; // clip
-+
-+ for (++i; i < j; ++i) // fill pixels between x0 and x1
-+ scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
-+ }
-+ }
-+ }
-+ }
-+
-+ e = e->next;
-+ }
-+}
-+
-+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
-+{
-+ stbtt__active_edge *active = NULL;
-+ int y,j=0;
-+ int max_weight = (255 / vsubsample); // weight per vertical scanline
-+ int s; // vertical subsample index
-+ unsigned char scanline_data[512], *scanline;
-+
-+ if (result->w > 512)
-+ scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
-+ else
-+ scanline = scanline_data;
-+
-+ y = off_y * vsubsample;
-+ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
-+
-+ while (j < result->h) {
-+ STBTT_memset(scanline, 0, result->w);
-+ for (s=0; s < vsubsample; ++s) {
-+ // find center of pixel for this scanline
-+ float scan_y = y + 0.5f;
-+ stbtt__active_edge **step = &active;
-+
-+ // update all active edges;
-+ // remove all active edges that terminate before the center of this scanline
-+ while (*step) {
-+ stbtt__active_edge * z = *step;
-+ if (z->ey <= scan_y) {
-+ *step = z->next; // delete from list
-+ STBTT_assert(z->valid);
-+ z->valid = 0;
-+ STBTT_free(z, userdata);
-+ } else {
-+ z->x += z->dx; // advance to position for current scanline
-+ step = &((*step)->next); // advance through list
-+ }
-+ }
-+
-+ // resort the list if needed
-+ for(;;) {
-+ int changed=0;
-+ step = &active;
-+ while (*step && (*step)->next) {
-+ if ((*step)->x > (*step)->next->x) {
-+ stbtt__active_edge *t = *step;
-+ stbtt__active_edge *q = t->next;
-+
-+ t->next = q->next;
-+ q->next = t;
-+ *step = q;
-+ changed = 1;
-+ }
-+ step = &(*step)->next;
-+ }
-+ if (!changed) break;
-+ }
-+
-+ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
-+ while (e->y0 <= scan_y) {
-+ if (e->y1 > scan_y) {
-+ stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata);
-+ // find insertion point
-+ if (active == NULL)
-+ active = z;
-+ else if (z->x < active->x) {
-+ // insert at front
-+ z->next = active;
-+ active = z;
-+ } else {
-+ // find thing to insert AFTER
-+ stbtt__active_edge *p = active;
-+ while (p->next && p->next->x < z->x)
-+ p = p->next;
-+ // at this point, p->next->x is NOT < z->x
-+ z->next = p->next;
-+ p->next = z;
-+ }
-+ }
-+ ++e;
-+ }
-+
-+ // now process all active edges in XOR fashion
-+ if (active)
-+ stbtt__fill_active_edges(scanline, result->w, active, max_weight);
-+
-+ ++y;
-+ }
-+ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
-+ ++j;
-+ }
-+
-+ while (active) {
-+ stbtt__active_edge *z = active;
-+ active = active->next;
-+ STBTT_free(z, userdata);
-+ }
-+
-+ if (scanline != scanline_data)
-+ STBTT_free(scanline, userdata);
-+}
-+
-+static int stbtt__edge_compare(const void *p, const void *q)
-+{
-+ stbtt__edge *a = (stbtt__edge *) p;
-+ stbtt__edge *b = (stbtt__edge *) q;
-+
-+ if (a->y0 < b->y0) return -1;
-+ if (a->y0 > b->y0) return 1;
-+ return 0;
-+}
-+
-+typedef struct
-+{
-+ float x,y;
-+} stbtt__point;
-+
-+static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata)
-+{
-+ float y_scale_inv = invert ? -scale_y : scale_y;
-+ stbtt__edge *e;
-+ int n,i,j,k,m;
-+ int vsubsample = result->h < 8 ? 15 : 5;
-+ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
-+
-+ // now we have to blow out the windings into explicit edge lists
-+ n = 0;
-+ for (i=0; i < windings; ++i)
-+ n += wcount[i];
-+
-+ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
-+ if (e == 0) return;
-+ n = 0;
-+
-+ m=0;
-+ for (i=0; i < windings; ++i) {
-+ stbtt__point *p = pts + m;
-+ m += wcount[i];
-+ j = wcount[i]-1;
-+ for (k=0; k < wcount[i]; j=k++) {
-+ int a=k,b=j;
-+ // skip the edge if horizontal
-+ if (p[j].y == p[k].y)
-+ continue;
-+ // add edge from j to k to the list
-+ e[n].invert = 0;
-+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
-+ e[n].invert = 1;
-+ a=j,b=k;
-+ }
-+ e[n].x0 = p[a].x * scale_x;
-+ e[n].y0 = p[a].y * y_scale_inv * vsubsample;
-+ e[n].x1 = p[b].x * scale_x;
-+ e[n].y1 = p[b].y * y_scale_inv * vsubsample;
-+ ++n;
-+ }
-+ }
-+
-+ // now sort the edges by their highest point (should snap to integer, and then by x)
-+ STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
-+
-+ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
-+ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
-+
-+ STBTT_free(e, userdata);
-+}
-+
-+static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
-+{
-+ if (!points) return; // during first pass, it's unallocated
-+ points[n].x = x;
-+ points[n].y = y;
-+}
-+
-+// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
-+static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
-+{
-+ // midpoint
-+ float mx = (x0 + 2*x1 + x2)/4;
-+ float my = (y0 + 2*y1 + y2)/4;
-+ // versus directly drawn line
-+ float dx = (x0+x2)/2 - mx;
-+ float dy = (y0+y2)/2 - my;
-+ if (n > 16) // 65536 segments on one curve better be enough!
-+ return 1;
-+ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
-+ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
-+ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
-+ } else {
-+ stbtt__add_point(points, *num_points,x2,y2);
-+ *num_points = *num_points+1;
-+ }
-+ return 1;
-+}
-+
-+// returns number of contours
-+stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
-+{
-+ stbtt__point *points=0;
-+ int num_points=0;
-+
-+ float objspace_flatness_squared = objspace_flatness * objspace_flatness;
-+ int i,n=0,start=0, pass;
-+
-+ // count how many "moves" there are to get the contour count
-+ for (i=0; i < num_verts; ++i)
-+ if (vertices[i].type == STBTT_vmove)
-+ ++n;
-+
-+ *num_contours = n;
-+ if (n == 0) return 0;
-+
-+ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
-+
-+ if (*contour_lengths == 0) {
-+ *num_contours = 0;
-+ return 0;
-+ }
-+
-+ // make two passes through the points so we don't need to realloc
-+ for (pass=0; pass < 2; ++pass) {
-+ float x=0,y=0;
-+ if (pass == 1) {
-+ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
-+ if (points == NULL) goto error;
-+ }
-+ num_points = 0;
-+ n= -1;
-+ for (i=0; i < num_verts; ++i) {
-+ switch (vertices[i].type) {
-+ case STBTT_vmove:
-+ // start the next contour
-+ if (n >= 0)
-+ (*contour_lengths)[n] = num_points - start;
-+ ++n;
-+ start = num_points;
-+
-+ x = vertices[i].x, y = vertices[i].y;
-+ stbtt__add_point(points, num_points++, x,y);
-+ break;
-+ case STBTT_vline:
-+ x = vertices[i].x, y = vertices[i].y;
-+ stbtt__add_point(points, num_points++, x, y);
-+ break;
-+ case STBTT_vcurve:
-+ stbtt__tesselate_curve(points, &num_points, x,y,
-+ vertices[i].cx, vertices[i].cy,
-+ vertices[i].x, vertices[i].y,
-+ objspace_flatness_squared, 0);
-+ x = vertices[i].x, y = vertices[i].y;
-+ break;
-+ }
-+ }
-+ (*contour_lengths)[n] = num_points - start;
-+ }
-+
-+ return points;
-+error:
-+ STBTT_free(points, userdata);
-+ STBTT_free(*contour_lengths, userdata);
-+ *contour_lengths = 0;
-+ *num_contours = 0;
-+ return NULL;
-+}
-+
-+void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata)
-+{
-+ float scale = scale_x > scale_y ? scale_y : scale_x;
-+ int winding_count, *winding_lengths;
-+ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
-+ if (windings) {
-+ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata);
-+ STBTT_free(winding_lengths, userdata);
-+ STBTT_free(windings, userdata);
-+ }
-+}
-+
-+void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
-+{
-+ STBTT_free(bitmap, userdata);
-+}
-+
-+unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
-+{
-+ int ix0,iy0,ix1,iy1;
-+ stbtt__bitmap gbm;
-+ stbtt_vertex *vertices;
-+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-+
-+ if (scale_x == 0) scale_x = scale_y;
-+ if (scale_y == 0) {
-+ if (scale_x == 0) return NULL;
-+ scale_y = scale_x;
-+ }
-+
-+ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1);
-+
-+ // now we get the size
-+ gbm.w = (ix1 - ix0);
-+ gbm.h = (iy1 - iy0);
-+ gbm.pixels = NULL; // in case we error
-+
-+ if (width ) *width = gbm.w;
-+ if (height) *height = gbm.h;
-+ if (xoff ) *xoff = ix0;
-+ if (yoff ) *yoff = iy0;
-+
-+ if (gbm.w && gbm.h) {
-+ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
-+ if (gbm.pixels) {
-+ gbm.stride = gbm.w;
-+
-+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata);
-+ }
-+ }
-+ STBTT_free(vertices, info->userdata);
-+ return gbm.pixels;
-+}
-+
-+void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
-+{
-+ int ix0,iy0;
-+ stbtt_vertex *vertices;
-+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-+ stbtt__bitmap gbm;
-+
-+ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0);
-+ gbm.pixels = output;
-+ gbm.w = out_w;
-+ gbm.h = out_h;
-+ gbm.stride = out_stride;
-+
-+ if (gbm.w && gbm.h)
-+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata);
-+
-+ STBTT_free(vertices, info->userdata);
-+}
-+
-+unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
-+{
-+ return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
-+}
-+
-+void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
-+{
-+ stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint));
-+}
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// bitmap baking
-+//
-+// This is SUPER-SHITTY packing to keep source code small
-+
-+extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-+ float pixel_height, // height of font in pixels
-+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-+ int first_char, int num_chars, // characters to bake
-+ stbtt_bakedchar *chardata)
-+{
-+ float scale;
-+ int x,y,bottom_y, i;
-+ stbtt_fontinfo f;
-+ stbtt_InitFont(&f, data, offset);
-+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
-+ x=y=1;
-+ bottom_y = 1;
-+
-+ scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
-+
-+ for (i=0; i < num_chars; ++i) {
-+ int advance, lsb, x0,y0,x1,y1,gw,gh;
-+ int g = stbtt_FindGlyphIndex(&f, first_char + i);
-+ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
-+ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
-+ gw = x1-x0;
-+ gh = y1-y0;
-+ if (x + gw + 1 >= pw)
-+ y = bottom_y, x = 1; // advance to next row
-+ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
-+ return -i;
-+ STBTT_assert(x+gw < pw);
-+ STBTT_assert(y+gh < ph);
-+ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
-+ chardata[i].x0 = (stbtt_int16) x;
-+ chardata[i].y0 = (stbtt_int16) y;
-+ chardata[i].x1 = (stbtt_int16) (x + gw);
-+ chardata[i].y1 = (stbtt_int16) (y + gh);
-+ chardata[i].xadvance = scale * advance;
-+ chardata[i].xoff = (float) x0;
-+ chardata[i].yoff = (float) y0;
-+ x = x + gw + 2;
-+ if (y+gh+2 > bottom_y)
-+ bottom_y = y+gh+2;
-+ }
-+ return bottom_y;
-+}
-+
-+void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
-+{
-+ float d3d_bias = opengl_fillrule ? 0 : -0.5f;
-+ float ipw = 1.0f / pw, iph = 1.0f / ph;
-+ stbtt_bakedchar *b = chardata + char_index;
-+ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5);
-+ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5);
-+
-+ q->x0 = round_x + d3d_bias;
-+ q->y0 = round_y + d3d_bias;
-+ q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
-+ q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
-+
-+ q->s0 = b->x0 * ipw;
-+ q->t0 = b->y0 * ipw;
-+ q->s1 = b->x1 * iph;
-+ q->t1 = b->y1 * iph;
-+
-+ *xpos += b->xadvance;
-+}
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// font name matching -- recommended not to use this
-+//
-+
-+// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
-+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
-+{
-+ stbtt_int32 i=0;
-+
-+ // convert utf16 to utf8 and compare the results while converting
-+ while (len2) {
-+ stbtt_uint16 ch = s2[0]*256 + s2[1];
-+ if (ch < 0x80) {
-+ if (i >= len1) return -1;
-+ if (s1[i++] != ch) return -1;
-+ } else if (ch < 0x800) {
-+ if (i+1 >= len1) return -1;
-+ if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
-+ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
-+ } else if (ch >= 0xd800 && ch < 0xdc00) {
-+ stbtt_uint32 c;
-+ stbtt_uint16 ch2 = s2[2]*256 + s2[3];
-+ if (i+3 >= len1) return -1;
-+ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
-+ if (s1[i++] != 0xf0 + (c >> 18)) return -1;
-+ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
-+ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
-+ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
-+ s2 += 2; // plus another 2 below
-+ len2 -= 2;
-+ } else if (ch >= 0xdc00 && ch < 0xe000) {
-+ return -1;
-+ } else {
-+ if (i+2 >= len1) return -1;
-+ if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
-+ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
-+ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
-+ }
-+ s2 += 2;
-+ len2 -= 2;
-+ }
-+ return i;
-+}
-+
-+int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
-+{
-+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
-+}
-+
-+// returns results in whatever encoding you request... but note that 2-byte encodings
-+// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
-+char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
-+{
-+ stbtt_int32 i,count,stringOffset;
-+ stbtt_uint8 *fc = font->data;
-+ stbtt_uint32 offset = font->fontstart;
-+ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
-+ if (!nm) return NULL;
-+
-+ count = ttUSHORT(fc+nm+2);
-+ stringOffset = nm + ttUSHORT(fc+nm+4);
-+ for (i=0; i < count; ++i) {
-+ stbtt_uint32 loc = nm + 6 + 12 * i;
-+ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
-+ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
-+ *length = ttUSHORT(fc+loc+8);
-+ return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
-+ }
-+ }
-+ return NULL;
-+}
-+
-+static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
-+{
-+ stbtt_int32 i;
-+ stbtt_int32 count = ttUSHORT(fc+nm+2);
-+ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
-+
-+ for (i=0; i < count; ++i) {
-+ stbtt_uint32 loc = nm + 6 + 12 * i;
-+ stbtt_int32 id = ttUSHORT(fc+loc+6);
-+ if (id == target_id) {
-+ // find the encoding
-+ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
-+
-+ // is this a Unicode encoding?
-+ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
-+ stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10);
-+
-+ // check if there's a prefix match
-+ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
-+ if (matchlen >= 0) {
-+ // check for target_id+1 immediately following, with same encoding & language
-+ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
-+ stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10);
-+ if (slen == 0) {
-+ if (matchlen == nlen)
-+ return 1;
-+ } else if (matchlen < nlen && name[matchlen] == ' ') {
-+ ++matchlen;
-+ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
-+ return 1;
-+ }
-+ } else {
-+ // if nothing immediately following
-+ if (matchlen == nlen)
-+ return 1;
-+ }
-+ }
-+ }
-+
-+ // @TODO handle other encodings
-+ }
-+ }
-+ return 0;
-+}
-+
-+static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
-+{
-+ stbtt_int32 nlen = STBTT_strlen((char *) name);
-+ stbtt_uint32 nm,hd;
-+ if (!stbtt__isfont(fc+offset)) return 0;
-+
-+ // check italics/bold/underline flags in macStyle...
-+ if (flags) {
-+ hd = stbtt__find_table(fc, offset, "head");
-+ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
-+ }
-+
-+ nm = stbtt__find_table(fc, offset, "name");
-+ if (!nm) return 0;
-+
-+ if (flags) {
-+ // if we checked the macStyle flags, then just check the family and ignore the subfamily
-+ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-+ } else {
-+ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-+ }
-+
-+ return 0;
-+}
-+
-+int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
-+{
-+ stbtt_int32 i;
-+ for (i=0;;++i) {
-+ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
-+ if (off < 0) return off;
-+ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
-+ return off;
-+ }
-+}
-+
-+#endif // STB_TRUETYPE_IMPLEMENTATION
+--
+1.9.0.msysgit.0
+