MousePicker.java 6.6 KB

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