OPC_Picking.cpp 5.2 KB

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