summaryrefslogtreecommitdiff
path: root/Runtime/NavMesh/HeightMeshQuery.cpp
blob: 2e26b6b8d63e20c2777e5ede86afe4485ac034ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include "UnityPrefix.h"
#include "HeightMeshQuery.h"
#include "Runtime/Interfaces/ITerrainManager.h"


HeightMeshQuery::HeightMeshQuery ()
: m_HeightMaps (NULL)
, m_VerticalRayOffset (0.0f)
{
}

HeightMeshQuery::~HeightMeshQuery ()
{
}

void HeightMeshQuery::Init (const HeightmapDataVector* heightMaps, float verticalRayOffset)
{
	m_HeightMaps = heightMaps;
	m_VerticalRayOffset = verticalRayOffset;
}

dtStatus HeightMeshQuery::getHeight (const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const
{
	Vector3f rayStart (pos[0], pos[1] + m_VerticalRayOffset, pos[2]);
	float geometryHeight, terrainHeight;
	bool bGeometryHit = GetGeometryHeight (tile, poly, rayStart, &geometryHeight);
	bool bTerrainHit = GetTerrainHeight (rayStart, &terrainHeight);

	if (bGeometryHit && bTerrainHit)
	{
		float geomDist = Abs (rayStart.y - geometryHeight);
		float terrainDist = Abs (rayStart.y - terrainHeight);
		if (geomDist < terrainDist)
			(*height) = geometryHeight;
		else
			(*height) = terrainHeight;

		return DT_SUCCESS;
	}
	else if (bGeometryHit)
	{
		(*height) = geometryHeight;
		return DT_SUCCESS;
	}
	else if (bTerrainHit)
	{
		(*height) = terrainHeight;
		return DT_SUCCESS;
	}
	else
	{
		(*height) = pos[1];
		return DT_FAILURE;
	}
}


// HeightMesh Intersection
// Find the height of the nearest triangle with the same 2D values.
bool HeightMeshQuery::GetGeometryHeight (const dtMeshTile* tile, const dtPoly* poly, const Vector3f& pos, float* height) const
{
	Assert (poly->getType () == DT_POLYTYPE_GROUND);
	Assert (height != NULL);
	*height = pos.y;

	bool hit = false;
	float bestHeight = std::numeric_limits<float>::infinity ();

	const unsigned int ip = (unsigned int)(poly - tile->polys);
	const dtPolyDetail* pd = &tile->detailMeshes[ip];
	for (int j = 0; j < pd->triCount; ++j)
	{
		const dtPolyDetailIndex* t = &tile->detailTris[(pd->triBase+j)*4];
		const float* v[3];
		for (int k = 0; k < 3; ++k)
		{
			if (t[k] < poly->vertCount)
				v[k] = &tile->verts[poly->verts[t[k]]*3];
			else
				v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
		}
		float h;
		if (dtClosestHeightPointTriangle (pos.GetPtr (), v[0], v[1], v[2], h))
		{
			if (Abs (pos.y - h) < Abs (pos.y - bestHeight))
			{
				*height = h;
				bestHeight = h;
				hit = true;
			}
		}
	}

	return hit;
}

bool HeightMeshQuery::GetTerrainHeight (const Vector3f& rayStart, float* height) const
{
	Assert (height != NULL);
	*height = rayStart.y;

	if (!m_HeightMaps)
		return false;

	ITerrainManager* terrain = GetITerrainManager ();
	if (terrain == NULL)
		return false;

	bool hit = false;
	const float upperBound = rayStart.y;
	float lowerBound = -std::numeric_limits<float>::infinity ();


	for (int i = 0; i < m_HeightMaps->size (); ++i)
	{
		const HeightmapData& heightmapData = (*m_HeightMaps)[i];
		float terrainHeight;

		// TODO: this should be cleaned up. Abstracting the Terrain-manager away only to feed it
		// (serialized!) PPtr's to terrain data is a half-assed abstraction at best.
		Object* terrainData = InstanceIDToObjectThreadSafe (heightmapData.terrainData.GetInstanceID ());
		bool hitTerrain = terrain->GetInterpolatedHeight (terrainData, heightmapData.position, rayStart, terrainHeight);
		if (!hitTerrain)
			continue;

		if (terrainHeight < upperBound && terrainHeight > lowerBound)
		{
			(*height) = terrainHeight;
			lowerBound = terrainHeight;
			hit = true;
		}
	}
	return hit;
}