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 183
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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 Point& view_point, CullModeCallback callback, void* user_data)
{
struct Local
{
struct CullData
{
CollisionFace* Closest;
float MinLimit;
CullModeCallback Callback;
void* UserData;
Point 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 point 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;
ConversionArea VC;
Data->IMesh->GetTriangle(VP, StabbedFaceIndex, VC);
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
|