OPC_Picking.cpp 5.2 KB

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