File: CollisionHandler.h

package info (click to toggle)
spring 106.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 55,316 kB
  • sloc: cpp: 543,954; ansic: 44,800; python: 12,575; java: 12,201; awk: 5,889; sh: 1,796; asm: 1,546; xml: 655; perl: 405; php: 211; objc: 194; makefile: 76; sed: 2
file content (175 lines) | stat: -rw-r--r-- 5,554 bytes parent folder | download | duplicates (3)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */

#ifndef COLLISION_HANDLER_H
#define COLLISION_HANDLER_H

#include "System/creg/creg_cond.h"
#include "System/float3.h"
#include "System/Matrix44f.h"

#include <algorithm>

class CSolidObject;
struct LocalModelPiece;
struct CollisionVolume;

enum {
	CQ_POINT_NO_INT = 0,
	CQ_POINT_ON_RAY = 1,
	CQ_POINT_IN_VOL = 2,
};

struct CollisionQuery {
public:
	bool SwapParams() {
		if (!AllHit() || ValidRay())
			return false;

		std::swap(t1, t0);
		std::swap(p1, p0);
		std::swap(b1, b0);
		return true;
	}
	void Transform(const CMatrix44f& m) {
		// transform intersection points (iff not a special
		// case, otherwise calling code should not use them)
		if (b0 == CQ_POINT_ON_RAY) { p0 = m.Mul(p0); }
		if (b1 == CQ_POINT_ON_RAY) { p1 = m.Mul(p1); }
	}

	void Reset(const CollisionQuery* cq = nullptr) {
		*this = (cq != nullptr) ? *cq : CollisionQuery{};
	}

	// t0 > t1 can happen when intersecting cylinder endcaps
	bool ValidRay() const { return (t0 <= t1); }
	bool InsideHit() const { return (b0 == CQ_POINT_IN_VOL); }
	bool IngressHit() const { return (b0 == CQ_POINT_ON_RAY); }
	bool EgressHit() const { return (b1 == CQ_POINT_ON_RAY); }
	bool AllHit() const { return (b0 != CQ_POINT_NO_INT && b1 != CQ_POINT_NO_INT); }
	bool AnyHit() const { return (b0 != CQ_POINT_NO_INT || b1 != CQ_POINT_NO_INT); }

	const float3& GetIngressPos() const { return p0; }
	const float3& GetEgressPos() const { return p1; }
	const float3& GetHitPos() const {
		if (IngressHit()) return GetIngressPos();
		if (EgressHit()) return GetEgressPos();
		if (InsideHit()) return p0;
		return ZeroVector;
	}

	// if the hit-position equals ZeroVector (i.e. if we have an
	// inside-hit special case), the projected distance could be
	// positive or negative depending on <dir> but we want it to
	// be 0 --> turn <pos> into a ZeroVector if InsideHit()
	float GetHitPosDist(const float3& pos, const float3& dir) const { return (std::max(0.0f, dir.dot(GetHitPos() - pos * (1 - InsideHit())))); }
	float GetIngressPosDist(const float3& pos, const float3& dir) const { return (std::max(0.0f, dir.dot(GetIngressPos() - pos))); }
	float GetEgressPosDist(const float3& pos, const float3& dir) const { return (std::max(0.0f, dir.dot(GetEgressPos() - pos))); }

	const LocalModelPiece* GetHitPiece() const { return lmp; }
	void SetHitPiece(const LocalModelPiece* p) { lmp = p; }

private:
	friend class CCollisionHandler;

	///< true (non-zero) if {in,e}gress (b{0,1}) point on ray segment
	int    b0 = CQ_POINT_NO_INT;
	int    b1 = CQ_POINT_NO_INT;
	///< distance parameter for ingress and egress point
	float  t0 = 0.0f;
	float  t1 = 0.0f;
	///< ray-volume ingress and egress points
	float3 p0;
	float3 p1;

	///< impacted piece
	const LocalModelPiece* lmp = nullptr;
};

/**
 * Responsible for detecting hits between projectiles
 * and solid objects (units, features), each SO has a
 * collision volume.
 */
class CCollisionHandler {
	public:
		static void PrintStats();

		static bool DetectHit(
			const CSolidObject* o,
			const CMatrix44f& m,
			const float3 p0,
			const float3 p1,
			CollisionQuery* cq = nullptr,
			bool forceTrace = false
		);
		static bool DetectHit(
			const CSolidObject* o,
			const CollisionVolume* v,
			const CMatrix44f& m,
			const float3 p0,
			const float3 p1,
			CollisionQuery* cq = nullptr,
			bool forceTrace = false
		);
		static bool MouseHit(
			const CSolidObject* o,
			const CMatrix44f& m,
			const float3& p0,
			const float3& p1,
			const CollisionVolume* v,
			CollisionQuery* cq = nullptr
		);

	private:
		// HITTEST_DISC helpers for DetectHit
		static bool Collision(
			const CSolidObject* o,
			const CollisionVolume* v,
			const CMatrix44f& m,
			const float3 p,
			CollisionQuery* cq
		);
		// HITTEST_CONT helpers for DetectHit
		static bool Intersect(
			const CSolidObject* o,
			const CollisionVolume* v,
			const CMatrix44f& m,
			const float3 p0,
			const float3 p1,
			CollisionQuery* cq,
			float s = 1.0f
		);

	private:
		/**
		 * Test if a point lies inside a volume.
		 * @param v volume
		 * @param m volumes transformation matrix
		 * @param p point in world-coordinates
		 */
		static bool Collision(const CollisionVolume* v, const CMatrix44f& m, const float3& p);
		static bool CollisionFootPrint(const CSolidObject* o, const float3& p);

		/**
		 * Test if a ray intersects a volume.
		 * @param v volume
		 * @param m volumes transformation matrix
		 * @param p0 start of ray (in world-coordinates)
		 * @param p1 end of ray (in world-coordinates)
		 */
		static bool Intersect(const CollisionVolume* v, const CMatrix44f& m, const float3& p0, const float3& p1, CollisionQuery* cq);
		static bool IntersectPieceTree(const CSolidObject* o, const CMatrix44f& m, const float3& p0, const float3& p1, CollisionQuery* cq);
		static bool IntersectPiecesHelper(const CSolidObject* o, const CMatrix44f& m, const float3& p0, const float3& p1, CollisionQuery* cqp);

	public:
		static bool IntersectEllipsoid(const CollisionVolume* v, const float3& pi0, const float3& pi1, CollisionQuery* cq);
		static bool IntersectCylinder(const CollisionVolume* v, const float3& pi0, const float3& pi1, CollisionQuery* cq);
		static bool IntersectBox(const CollisionVolume* v, const float3& pi0, const float3& pi1, CollisionQuery* cq);

	private:
		static unsigned int numDiscTests; // number of discrete hit-tests executed
		static unsigned int numContTests; // number of continuous hit-tests executed (inc. unsynced)
};

#endif // COLLISION_HANDLER_H