| 1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 2 | /* | 
|---|
| 3 |  *      OPCODE - Optimized Collision Detection | 
|---|
| 4 |  *      Copyright (C) 2001 Pierre Terdiman | 
|---|
| 5 |  *      Homepage: http://www.codercorner.com/Opcode.htm | 
|---|
| 6 |  */ | 
|---|
| 7 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 8 |  | 
|---|
| 9 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 10 | /** | 
|---|
| 11 |  *      Contains code to perform "picking". | 
|---|
| 12 |  *      \file           OPC_Picking.cpp | 
|---|
| 13 |  *      \author         Pierre Terdiman | 
|---|
| 14 |  *      \date           March, 20, 2001 | 
|---|
| 15 |  */ | 
|---|
| 16 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 17 |  | 
|---|
| 18 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 19 | // Precompiled Header | 
|---|
| 20 | #include "Stdafx.h" | 
|---|
| 21 |  | 
|---|
| 22 | using namespace Opcode; | 
|---|
| 23 |  | 
|---|
| 24 | #ifdef OPC_RAYHIT_CALLBACK | 
|---|
| 25 |  | 
|---|
| 26 | /* | 
|---|
| 27 |         Possible RayCollider usages: | 
|---|
| 28 |         - boolean query (shadow feeler) | 
|---|
| 29 |         - closest hit | 
|---|
| 30 |         - all hits | 
|---|
| 31 |         - number of intersection (boolean) | 
|---|
| 32 |  | 
|---|
| 33 | */ | 
|---|
| 34 |  | 
|---|
| 35 | bool Opcode::SetupAllHits(RayCollider& collider, CollisionFaces& contacts) | 
|---|
| 36 | { | 
|---|
| 37 |         struct Local | 
|---|
| 38 |         { | 
|---|
| 39 |                 static void AllContacts(const CollisionFace& hit, void* user_data) | 
|---|
| 40 |                 { | 
|---|
| 41 |                         CollisionFaces* CF = (CollisionFaces*)user_data; | 
|---|
| 42 |                         CF->AddFace(hit); | 
|---|
| 43 |                 } | 
|---|
| 44 |         }; | 
|---|
| 45 |  | 
|---|
| 46 |         collider.SetFirstContact(false); | 
|---|
| 47 |         collider.SetHitCallback(Local::AllContacts); | 
|---|
| 48 |         collider.SetUserData(&contacts); | 
|---|
| 49 |         return true; | 
|---|
| 50 | } | 
|---|
| 51 |  | 
|---|
| 52 | bool Opcode::SetupClosestHit(RayCollider& collider, CollisionFace& closest_contact) | 
|---|
| 53 | { | 
|---|
| 54 |         struct Local | 
|---|
| 55 |         { | 
|---|
| 56 |                 static void ClosestContact(const CollisionFace& hit, void* user_data) | 
|---|
| 57 |                 { | 
|---|
| 58 |                         CollisionFace* CF = (CollisionFace*)user_data; | 
|---|
| 59 |                         if(hit.mDistance<CF->mDistance) *CF = hit; | 
|---|
| 60 |                 } | 
|---|
| 61 |         }; | 
|---|
| 62 |  | 
|---|
| 63 |         collider.SetFirstContact(false); | 
|---|
| 64 |         collider.SetHitCallback(Local::ClosestContact); | 
|---|
| 65 |         collider.SetUserData(&closest_contact); | 
|---|
| 66 |         closest_contact.mDistance = MAX_FLOAT; | 
|---|
| 67 |         return true; | 
|---|
| 68 | } | 
|---|
| 69 |  | 
|---|
| 70 | bool Opcode::SetupShadowFeeler(RayCollider& collider) | 
|---|
| 71 | { | 
|---|
| 72 |         collider.SetFirstContact(true); | 
|---|
| 73 |         collider.SetHitCallback(null); | 
|---|
| 74 |         return true; | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | bool Opcode::SetupInOutTest(RayCollider& collider) | 
|---|
| 78 | { | 
|---|
| 79 |         collider.SetFirstContact(false); | 
|---|
| 80 |         collider.SetHitCallback(null); | 
|---|
| 81 |         // Results with collider.GetNbIntersections() | 
|---|
| 82 |         return true; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | bool Opcode::Picking( | 
|---|
| 86 | CollisionFace& picked_face, | 
|---|
| 87 | const Ray& world_ray, const Model& model, const Matrix4x4* world, | 
|---|
| 88 | float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data) | 
|---|
| 89 | { | 
|---|
| 90 |         struct Local | 
|---|
| 91 |         { | 
|---|
| 92 |                 struct CullData | 
|---|
| 93 |                 { | 
|---|
| 94 |                         CollisionFace*                  Closest; | 
|---|
| 95 |                         float                                   MinLimit; | 
|---|
| 96 |                         CullModeCallback                Callback; | 
|---|
| 97 |                         void*                                   UserData; | 
|---|
| 98 |                         Point                                   ViewPoint; | 
|---|
| 99 |                         const MeshInterface*    IMesh; | 
|---|
| 100 |                 }; | 
|---|
| 101 |  | 
|---|
| 102 |                 // Called for each stabbed face | 
|---|
| 103 |                 static void RenderCullingCallback(const CollisionFace& hit, void* user_data) | 
|---|
| 104 |                 { | 
|---|
| 105 |                         CullData* Data = (CullData*)user_data; | 
|---|
| 106 |  | 
|---|
| 107 |                         // Discard face if we already have a closer hit | 
|---|
| 108 |                         if(hit.mDistance>=Data->Closest->mDistance)     return; | 
|---|
| 109 |  | 
|---|
| 110 |                         // Discard face if hit point is smaller than min limit. This mainly happens when the face is in front | 
|---|
| 111 |                         // of the near clip plane (or straddles it). If we keep the face nonetheless, the user can select an | 
|---|
| 112 |                         // object that he may not even be able to see, which is very annoying. | 
|---|
| 113 |                         if(hit.mDistance<=Data->MinLimit)       return; | 
|---|
| 114 |  | 
|---|
| 115 |                         // This is the index of currently stabbed triangle. | 
|---|
| 116 |                         udword StabbedFaceIndex = hit.mFaceID; | 
|---|
| 117 |  | 
|---|
| 118 |                         // We may keep it or not, depending on backface culling | 
|---|
| 119 |                         bool KeepIt = true; | 
|---|
| 120 |  | 
|---|
| 121 |                         // Catch *render* cull mode for this face | 
|---|
| 122 |                         CullMode CM = (Data->Callback)(StabbedFaceIndex, Data->UserData); | 
|---|
| 123 |  | 
|---|
| 124 |                         if(CM!=CULLMODE_NONE)   // Don't even compute culling for double-sided triangles | 
|---|
| 125 |                         { | 
|---|
| 126 |                                 // Compute backface culling for current face | 
|---|
| 127 |  | 
|---|
| 128 |                                 VertexPointers VP; | 
|---|
| 129 |                                 Data->IMesh->GetTriangle(VP, StabbedFaceIndex); | 
|---|
| 130 |                                 if(VP.BackfaceCulling(Data->ViewPoint)) | 
|---|
| 131 |                                 { | 
|---|
| 132 |                                         if(CM==CULLMODE_CW)             KeepIt = false; | 
|---|
| 133 |                                 } | 
|---|
| 134 |                                 else | 
|---|
| 135 |                                 { | 
|---|
| 136 |                                         if(CM==CULLMODE_CCW)    KeepIt = false; | 
|---|
| 137 |                                 } | 
|---|
| 138 |                         } | 
|---|
| 139 |  | 
|---|
| 140 |                         if(KeepIt)      *Data->Closest = hit; | 
|---|
| 141 |                 } | 
|---|
| 142 |         }; | 
|---|
| 143 |  | 
|---|
| 144 |         RayCollider RC; | 
|---|
| 145 |         RC.SetMaxDist(max_dist); | 
|---|
| 146 |         RC.SetTemporalCoherence(false); | 
|---|
| 147 |         RC.SetCulling(false);           // We need all faces since some of them can be double-sided | 
|---|
| 148 |         RC.SetFirstContact(false); | 
|---|
| 149 |         RC.SetHitCallback(Local::RenderCullingCallback); | 
|---|
| 150 |  | 
|---|
| 151 |         picked_face.mFaceID             = INVALID_ID; | 
|---|
| 152 |         picked_face.mDistance   = MAX_FLOAT; | 
|---|
| 153 |         picked_face.mU                  = 0.0f; | 
|---|
| 154 |         picked_face.mV                  = 0.0f; | 
|---|
| 155 |  | 
|---|
| 156 |         Local::CullData Data; | 
|---|
| 157 |         Data.Closest                    = &picked_face; | 
|---|
| 158 |         Data.MinLimit                   = min_dist; | 
|---|
| 159 |         Data.Callback                   = callback; | 
|---|
| 160 |         Data.UserData                   = user_data; | 
|---|
| 161 |         Data.ViewPoint                  = view_point; | 
|---|
| 162 |         Data.IMesh                              = model.GetMeshInterface(); | 
|---|
| 163 |  | 
|---|
| 164 |         if(world) | 
|---|
| 165 |         { | 
|---|
| 166 |                 // Get matrices | 
|---|
| 167 |                 Matrix4x4 InvWorld; | 
|---|
| 168 |                 InvertPRMatrix(InvWorld, *world); | 
|---|
| 169 |  | 
|---|
| 170 |                 // Compute camera position in mesh space | 
|---|
| 171 |                 Data.ViewPoint *= InvWorld; | 
|---|
| 172 |         } | 
|---|
| 173 |  | 
|---|
| 174 |         RC.SetUserData(&Data); | 
|---|
| 175 |         if(RC.Collide(world_ray, model, world)) | 
|---|
| 176 |         { | 
|---|
| 177 |                 return picked_face.mFaceID!=INVALID_ID; | 
|---|
| 178 |         } | 
|---|
| 179 |         return false; | 
|---|
| 180 | } | 
|---|
| 181 |  | 
|---|
| 182 | #endif | 
|---|