[216] | 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 |
---|