1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #ifndef COLLISION_HANDLER_H
4 #define COLLISION_HANDLER_H
5 
6 #include "System/creg/creg_cond.h"
7 #include "System/float3.h"
8 
9 struct CollisionVolume;
10 class CMatrix44f;
11 class CSolidObject;
12 class CUnit;
13 struct LocalModelPiece;
14 
15 enum {
16 	CQ_POINT_NO_INT = 0,
17 	CQ_POINT_ON_RAY = 1,
18 	CQ_POINT_IN_VOL = 2,
19 };
20 
21 struct CollisionQuery {
22 public:
CollisionQueryCollisionQuery23 	CollisionQuery(                        ): lmp(NULL) { Reset(NULL); }
CollisionQueryCollisionQuery24 	CollisionQuery(const CollisionQuery& cq): lmp(NULL) { Reset( &cq); }
25 
26 	void Reset(const CollisionQuery* cq = NULL) {
27 		// (0, 0, 0) is volume-space center, so impossible
28 		// to obtain as actual points except in the special
29 		// cases (which all calling code should check for!)
30 		// when continuous hit-detection is enabled
31 		if (cq == NULL) {
32 			b0 = CQ_POINT_NO_INT; t0 = 0.0f; p0 = ZeroVector;
33 			b1 = CQ_POINT_NO_INT; t1 = 0.0f; p1 = ZeroVector;
34 		} else {
35 			b0 = cq->b0; t0 = cq->t0; p0 = cq->p0;
36 			b1 = cq->b1; t1 = cq->t1; p1 = cq->p1;
37 
38 			lmp = cq->lmp;
39 		}
40 	}
41 
InsideHitCollisionQuery42 	bool InsideHit() const { return (b0 == CQ_POINT_IN_VOL); }
IngressHitCollisionQuery43 	bool IngressHit() const { return (b0 == CQ_POINT_ON_RAY); }
EgressHitCollisionQuery44 	bool EgressHit() const { return (b1 == CQ_POINT_ON_RAY); }
45 
GetIngressPosCollisionQuery46 	const float3& GetIngressPos() const { return p0; }
GetEgressPosCollisionQuery47 	const float3& GetEgressPos() const { return p1; }
GetHitPosCollisionQuery48 	const float3& GetHitPos() const {
49 		if (IngressHit()) return (GetIngressPos());
50 		if (EgressHit()) return (GetEgressPos());
51 		return ZeroVector;
52 	}
53 
54 	// if the hit-position equals ZeroVector (i.e. if we have an
55 	// inside-hit special case), the projected distance could be
56 	// positive or negative depending on <dir> but we want it to
57 	// be 0 --> turn <pos> into a ZeroVector if InsideHit()
GetHitPosDistCollisionQuery58 	float GetHitPosDist(const float3& pos, const float3& dir) const { return (std::max(0.0f, ((GetHitPos() - pos * (1 - InsideHit())).dot(dir)))); }
GetIngressPosDistCollisionQuery59 	float GetIngressPosDist(const float3& pos, const float3& dir) const { return (std::max(0.0f, ((GetIngressPos() - pos).dot(dir)))); }
GetEgressPosDistCollisionQuery60 	float GetEgressPosDist(const float3& pos, const float3& dir) const { return (std::max(0.0f, ((GetEgressPos() - pos).dot(dir)))); }
61 
GetHitPieceCollisionQuery62 	LocalModelPiece* GetHitPiece() const { return lmp; }
SetHitPieceCollisionQuery63 	void SetHitPiece(LocalModelPiece* p) { lmp = p; }
64 
65 private:
66 	friend class CCollisionHandler;
67 
68 	int    b0, b1;        ///< true (non-zero) if ingress (b0) or egress (b1) point on ray segment
69 	float  t0, t1;        ///< distance parameter for ingress and egress point
70 	float3 p0, p1;        ///< ray-volume ingress and egress points
71 
72 	LocalModelPiece* lmp; ///< impacted piece
73 };
74 
75 /**
76  * Responsible for detecting hits between projectiles
77  * and solid objects (units, features), each SO has a
78  * collision volume.
79  */
80 class CCollisionHandler {
81 	public:
82 		static void PrintStats();
83 
84 		static bool DetectHit(const CSolidObject* o, const float3 p0, const float3 p1, CollisionQuery* cq = NULL, bool forceTrace = false);
85 		static bool DetectHit(const CollisionVolume* v, const CSolidObject* o, const float3 p0, const float3 p1, CollisionQuery* cq, bool forceTrace = false);
86 		static bool MouseHit(const CUnit* u, const float3& p0, const float3& p1, const CollisionVolume* v, CollisionQuery* cq);
87 
88 	private:
89 		// HITTEST_DISC helpers for DetectHit
90 		static bool Collision(const CollisionVolume* v, const CSolidObject* u, const float3 p, CollisionQuery* cq);
91 		// HITTEST_CONT helpers for DetectHit
92 		static bool Intersect(const CollisionVolume* v, const CSolidObject* o, const float3 p0, const float3 p1, CollisionQuery* cq);
93 
94 	private:
95 		/**
96 		 * Test if a point lies inside a volume.
97 		 * @param v volume
98 		 * @param m volumes transformation matrix
99 		 * @param p point in world-coordinates
100 		 */
101 		static bool Collision(const CollisionVolume* v, const CMatrix44f& m, const float3& p);
102 		static bool CollisionFootPrint(const CSolidObject* o, const float3& p);
103 
104 		/**
105 		 * Test if a ray intersects a volume.
106 		 * @param v volume
107 		 * @param m volumes transformation matrix
108 		 * @param p0 start of ray (in world-coordinates)
109 		 * @param p1 end of ray (in world-coordinates)
110 		 */
111 		static bool Intersect(const CollisionVolume* v, const CMatrix44f& m, const float3& p0, const float3& p1, CollisionQuery* cq);
112 		static bool IntersectPieceTree(const CUnit* u, const float3& p0, const float3& p1, CollisionQuery* cq);
113 
114 		static bool IntersectPiecesHelper(const CUnit* u, const float3& p0, const float3& p1, CollisionQuery* cqp);
115 
116 	public:
117 		static bool IntersectEllipsoid(const CollisionVolume* v, const float3& pi0, const float3& pi1, CollisionQuery* cq);
118 		static bool IntersectCylinder(const CollisionVolume* v, const float3& pi0, const float3& pi1, CollisionQuery* cq);
119 		static bool IntersectBox(const CollisionVolume* v, const float3& pi0, const float3& pi1, CollisionQuery* cq);
120 
121 	private:
122 		static unsigned int numDiscTests; // number of discrete hit-tests executed
123 		static unsigned int numContTests; // number of continuous hit-tests executed (inc. unsynced)
124 };
125 
126 #endif // COLLISION_HANDLER_H
127