MousePicker.java 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package eu.tankernn.gameEngine.util;
  2. import java.util.List;
  3. import org.lwjgl.input.Mouse;
  4. import org.lwjgl.opengl.Display;
  5. import org.lwjgl.util.vector.Matrix4f;
  6. import org.lwjgl.util.vector.Vector2f;
  7. import org.lwjgl.util.vector.Vector3f;
  8. import org.lwjgl.util.vector.Vector4f;
  9. import eu.tankernn.gameEngine.entities.Entity3D;
  10. import eu.tankernn.gameEngine.loader.models.AABB;
  11. import eu.tankernn.gameEngine.renderEngine.gui.GuiTexture;
  12. import eu.tankernn.gameEngine.terrains.Terrain;
  13. import eu.tankernn.gameEngine.terrains.TerrainPack;
  14. public class MousePicker {
  15. private static final int RECURSION_COUNT = 200;
  16. private static final float RAY_RANGE = 600;
  17. private Vector3f currentRay = new Vector3f();
  18. private ICamera camera;
  19. private Vector3f currentTerrainPoint;
  20. private Entity3D currentEntity;
  21. private GuiTexture currentGui;
  22. public MousePicker(ICamera cam) {
  23. camera = cam;
  24. }
  25. public Entity3D getCurrentEntity() {
  26. return currentEntity;
  27. }
  28. public Vector3f getCurrentTerrainPoint() {
  29. return currentTerrainPoint;
  30. }
  31. public Vector3f getCurrentRay() {
  32. return currentRay;
  33. }
  34. public GuiTexture getCurrentGui() {
  35. return currentGui;
  36. }
  37. public void update(TerrainPack terrains, List<Entity3D> entities, List<GuiTexture> guis) {
  38. currentRay = calculateMouseRay();
  39. currentGui = calculateGuiTexture(guis);
  40. if (intersectionInRange(terrains, 0, RAY_RANGE, currentRay)) {
  41. currentTerrainPoint = binarySearch(terrains, 0, 0, RAY_RANGE, currentRay);
  42. } else {
  43. currentTerrainPoint = null;
  44. }
  45. for (Entity3D e: entities) {
  46. if (entityInstersect(e)) {
  47. currentEntity = e;
  48. return;
  49. }
  50. }
  51. currentEntity = null;
  52. }
  53. private Vector3f calculateMouseRay() {
  54. float mouseX = Mouse.getX();
  55. float mouseY = Mouse.getY();
  56. Vector2f normalizedCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
  57. Vector4f clipCoords = new Vector4f(normalizedCoords.x, normalizedCoords.y, -1.0f, 1.0f);
  58. Vector4f eyeCoords = toEyeCoords(clipCoords);
  59. Vector3f worldRay = toWorldCoords(eyeCoords);
  60. return worldRay;
  61. }
  62. private Vector3f toWorldCoords(Vector4f eyeCoords) {
  63. Matrix4f invertedView = Matrix4f.invert(camera.getViewMatrix(), null);
  64. Vector4f rayWorld = Matrix4f.transform(invertedView, eyeCoords, null);
  65. Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
  66. mouseRay.normalise();
  67. return mouseRay;
  68. }
  69. private Vector4f toEyeCoords(Vector4f clipCoords) {
  70. Matrix4f invertedProjection = Matrix4f.invert(camera.getProjectionMatrix(), null);
  71. Vector4f eyeCoords = Matrix4f.transform(invertedProjection, clipCoords, null);
  72. return new Vector4f(eyeCoords.x, eyeCoords.y, -1f, 0f);
  73. }
  74. private Vector2f getNormalisedDeviceCoordinates(float mouseX, float mouseY) {
  75. float x = (2.0f * mouseX) / Display.getWidth() - 1f;
  76. float y = (2.0f * mouseY) / Display.getHeight() - 1f;
  77. return new Vector2f(x, y);
  78. }
  79. // GUI Intersect
  80. private GuiTexture calculateGuiTexture(List<GuiTexture> guis) {
  81. float mouseX = Mouse.getX();
  82. float mouseY = Mouse.getY();
  83. Vector2f mouseCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
  84. for (GuiTexture gui: guis) {
  85. float posX = gui.getPosition().x;
  86. float posY = gui.getPosition().y;
  87. float scaleX = gui.getScale().x;
  88. float scaleY = gui.getScale().y;
  89. if (mouseCoords.x > posX - scaleX && mouseCoords.x < posX + scaleX && mouseCoords.y > posY - scaleY && mouseCoords.y < posY + scaleY) {
  90. return gui;
  91. }
  92. }
  93. return null;
  94. }
  95. // #### Entity intersect ####
  96. public boolean entityInstersect(Entity3D entity) {
  97. AABB box = entity.getBoundingBox();
  98. Vector3f dirfrac = new Vector3f();
  99. dirfrac.x = 1.0f / currentRay.x;
  100. dirfrac.y = 1.0f / currentRay.y;
  101. dirfrac.z = 1.0f / currentRay.z;
  102. // lb is the corner of AABB with minimal coordinates - left bottom, rt
  103. // is maximal corner
  104. // camera.getPosition() is origin of ray
  105. float t1 = (box.getLb().x - camera.getPosition().x) * dirfrac.x;
  106. float t2 = (box.getRt().x - camera.getPosition().x) * dirfrac.x;
  107. float t3 = (box.getLb().y - camera.getPosition().y) * dirfrac.y;
  108. float t4 = (box.getRt().y - camera.getPosition().y) * dirfrac.y;
  109. float t5 = (box.getLb().z - camera.getPosition().z) * dirfrac.z;
  110. float t6 = (box.getRt().z - camera.getPosition().z) * dirfrac.z;
  111. float tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
  112. float tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
  113. // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is
  114. // behind us
  115. if (tmax < 0) {
  116. return false;
  117. }
  118. // if tmin > tmax, ray doesn't intersect AABB
  119. if (tmin > tmax) {
  120. return false;
  121. }
  122. return true;
  123. }
  124. // #### Terrain intersect ####
  125. private Vector3f getPointOnRay(Vector3f ray, float distance) {
  126. Vector3f camPos = camera.getPosition();
  127. Vector3f start = new Vector3f(camPos.x, camPos.y, camPos.z);
  128. Vector3f scaledRay = new Vector3f(ray.x * distance, ray.y * distance, ray.z * distance);
  129. return Vector3f.add(start, scaledRay, null);
  130. }
  131. private Vector3f binarySearch(TerrainPack terrains, int count, float start, float finish, Vector3f ray) {
  132. float half = start + ((finish - start) / 2f);
  133. if (count >= RECURSION_COUNT) {
  134. Vector3f endPoint = getPointOnRay(ray, half);
  135. Terrain terrain = terrains.getTerrainByWorldPos(endPoint.getX(), endPoint.getZ());
  136. return terrain != null ? endPoint : null;
  137. }
  138. if (intersectionInRange(terrains, start, half, ray)) {
  139. return binarySearch(terrains, count + 1, start, half, ray);
  140. } else {
  141. return binarySearch(terrains, count + 1, half, finish, ray);
  142. }
  143. }
  144. private boolean intersectionInRange(TerrainPack terrains, float start, float finish, Vector3f ray) {
  145. Vector3f startPoint = getPointOnRay(ray, start);
  146. Vector3f endPoint = getPointOnRay(ray, finish);
  147. return !isUnderGround(terrains, startPoint) && isUnderGround(terrains, endPoint);
  148. }
  149. private boolean isUnderGround(TerrainPack terrains, Vector3f testPoint) {
  150. return testPoint.y < terrains.getTerrainHeightByWorldPos(testPoint.x, testPoint.z);
  151. }
  152. }