summaryrefslogtreecommitdiffhomepage
path: root/Opcode/OPC_Picking.cpp
blob: 2d0e11b534b22cbe809380c3298f1b4fb859750c (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
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
176
177
178
179
180
181
182
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
 *	OPCODE - Optimized Collision Detection
 *	Copyright (C) 2001 Pierre Terdiman
 *	Homepage: http://www.codercorner.com/Opcode.htm
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Contains code to perform "picking".
 *	\file		OPC_Picking.cpp
 *	\author		Pierre Terdiman
 *	\date		March, 20, 2001
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"

using namespace Opcode;

#ifdef OPC_RAYHIT_CALLBACK

/*
	Possible RayCollider usages:
	- boolean query (shadow feeler)
	- closest hit
	- all hits
	- number of intersection (boolean)

*/

bool Opcode::SetupAllHits(RayCollider& collider, CollisionFaces& contacts)
{
	struct Local
	{
		static void AllContacts(const CollisionFace& hit, void* user_data)
		{
			CollisionFaces* CF = (CollisionFaces*)user_data;
			CF->AddFace(hit);
		}
	};

	collider.SetFirstContact(false);
	collider.SetHitCallback(Local::AllContacts);
	collider.SetUserData(&contacts);
	return true;
}

bool Opcode::SetupClosestHit(RayCollider& collider, CollisionFace& closest_contact)
{
	struct Local
	{
		static void ClosestContact(const CollisionFace& hit, void* user_data)
		{
			CollisionFace* CF = (CollisionFace*)user_data;
			if(hit.mDistance<CF->mDistance)	*CF = hit;
		}
	};

	collider.SetFirstContact(false);
	collider.SetHitCallback(Local::ClosestContact);
	collider.SetUserData(&closest_contact);
	closest_contact.mDistance = MAX_FLOAT;
	return true;
}

bool Opcode::SetupShadowFeeler(RayCollider& collider)
{
	collider.SetFirstContact(true);
	collider.SetHitCallback(null);
	return true;
}

bool Opcode::SetupInOutTest(RayCollider& collider)
{
	collider.SetFirstContact(false);
	collider.SetHitCallback(null);
	// Results with collider.GetNbIntersections()
	return true;
}

bool Opcode::Picking(
CollisionFace& picked_face,
const Ray& world_ray, const Model& model, const Matrix4x4* world,
float min_dist, float max_dist, const IcePoint& view_point, CullModeCallback callback, void* user_data)
{
	struct Local
	{
		struct CullData
		{
			CollisionFace*			Closest;
			float					MinLimit;
			CullModeCallback		Callback;
			void*					UserData;
			IcePoint					ViewPoint;
			const MeshInterface*	IMesh;
		};

		// Called for each stabbed face
		static void RenderCullingCallback(const CollisionFace& hit, void* user_data)
		{
			CullData* Data = (CullData*)user_data;

			// Discard face if we already have a closer hit
			if(hit.mDistance>=Data->Closest->mDistance)	return;

			// Discard face if hit IcePoint is smaller than min limit. This mainly happens when the face is in front
			// of the near clip plane (or straddles it). If we keep the face nonetheless, the user can select an
			// object that he may not even be able to see, which is very annoying.
			if(hit.mDistance<=Data->MinLimit)	return;

			// This is the index of currently stabbed triangle.
			udword StabbedFaceIndex = hit.mFaceID;

			// We may keep it or not, depending on backface culling
			bool KeepIt = true;

			// Catch *render* cull mode for this face
			CullMode CM = (Data->Callback)(StabbedFaceIndex, Data->UserData);

			if(CM!=CULLMODE_NONE)	// Don't even compute culling for double-sided triangles
			{
				// Compute backface culling for current face

				VertexPointers VP;
				Data->IMesh->GetTriangle(VP, StabbedFaceIndex);
				if(VP.BackfaceCulling(Data->ViewPoint))
				{
					if(CM==CULLMODE_CW)		KeepIt = false;
				}
				else
				{
					if(CM==CULLMODE_CCW)	KeepIt = false;
				}
			}

			if(KeepIt)	*Data->Closest = hit;
		}
	};

	RayCollider RC;
	RC.SetMaxDist(max_dist);
	RC.SetTemporalCoherence(false);
	RC.SetCulling(false);		// We need all faces since some of them can be double-sided
	RC.SetFirstContact(false);
	RC.SetHitCallback(Local::RenderCullingCallback);

	picked_face.mFaceID		= INVALID_ID;
	picked_face.mDistance	= MAX_FLOAT;
	picked_face.mU			= 0.0f;
	picked_face.mV			= 0.0f;

	Local::CullData Data;
	Data.Closest			= &picked_face;
	Data.MinLimit			= min_dist;
	Data.Callback			= callback;
	Data.UserData			= user_data;
	Data.ViewPoint			= view_point;
	Data.IMesh				= model.GetMeshInterface();

	if(world)
	{
		// Get matrices
		Matrix4x4 InvWorld;
		InvertPRMatrix(InvWorld, *world);

		// Compute camera position in mesh space
		Data.ViewPoint *= InvWorld;
	}

	RC.SetUserData(&Data);
	if(RC.Collide(world_ray, model, world))
	{
		return picked_face.mFaceID!=INVALID_ID;
	}
	return false;
}

#endif