1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source: $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/input/mouse/picker.d) 6 * Documentation: 7 * Coverage: 8 **/ 9 module liberty.input.mouse.picker; 10 11 import liberty.input.impl; 12 import liberty.math.transform; 13 import liberty.math.vector; 14 import liberty.math.matrix; 15 import liberty.camera.impl; 16 import liberty.framework.terrain; 17 import liberty.core.platform; 18 import liberty.core.window; 19 20 /** 21 * 22 **/ 23 final class MousePicker { 24 private { 25 static const int recursionCount = 20; 26 static const float rayRange = 60; 27 28 Camera camera; 29 Terrain terrain; 30 Vector3F currentRay; 31 Vector3F currentTerrainPoint; 32 } 33 34 /** 35 * 36 **/ 37 Vector3F getCurrentRay() { 38 return currentRay; 39 } 40 41 /** 42 * 43 **/ 44 Vector3F getCurrentTerrainPoint() { 45 return currentTerrainPoint; 46 } 47 48 /** 49 * 50 **/ 51 void update(Camera camera, Terrain terrain) { 52 this.camera = camera; 53 this.terrain = terrain; 54 55 currentRay = computeMouseRay(); 56 57 // (ISSUE #35) 58 //if (intersectionInRange(0, rayRange, currentRay)) 59 currentTerrainPoint = binarySearch(0, 0, rayRange, currentRay); 60 //else 61 // currentTerrainPoint = Vector3F(float.nan, float.nan, float.nan); 62 } 63 64 private Vector3F computeMouseRay() { 65 Vector2F normalizedCoords = Input.getNormalizedDeviceCoords(Input.getMouse().getPostion()); 66 Vector4F clipCoords = Vector4F(normalizedCoords.x , normalizedCoords.y, -1.0f, 1.0f); 67 Vector4F eyeCoords = toEyeCoords(clipCoords); 68 Vector3F worldRay = toWorldCoords(eyeCoords); 69 70 /*static int oo = 0; 71 if (oo == 40) { 72 import liberty.core.engine; 73 Logger.exception(worldRay.toString()); 74 oo = 0; 75 } 76 oo++;*/ 77 78 return worldRay; 79 } 80 81 private Vector3F toWorldCoords(Vector4F eyeCoords) { 82 Matrix4F invView = camera.viewMatrix; 83 Vector4F rayWorld = Matrix4F.transformation(invView, eyeCoords); 84 Vector3F mouseRay = Vector3F(rayWorld.x, rayWorld.y, rayWorld.z); 85 mouseRay.normalize; 86 87 return mouseRay; 88 } 89 90 private Vector4F toEyeCoords(Vector4F clipCoords) { 91 Matrix4F invProjection = camera.projectionMatrix.inverse; 92 Vector4F eyeCoords = Matrix4F.transformation(invProjection, clipCoords); 93 94 return Vector4F(eyeCoords.x, eyeCoords.y, -1.0f, 0.0f); 95 } 96 97 private Vector3F getPointOnRay(Vector3F ray, float distance) { 98 return camera.component!Transform.getLocation + ray * distance; 99 } 100 101 private Vector3F binarySearch(int count, float start, float finish, Vector3F ray) { 102 const float half = start + ((finish - start) / 2.0f); 103 104 if (count >= recursionCount) 105 return getPointOnRay(ray, half); 106 107 if (intersectionInRange(start, half, ray)) 108 return binarySearch(count + 1, start, half, ray); 109 else 110 return binarySearch(count + 1, half, finish, ray); 111 } 112 113 private bool intersectionInRange(float start, float finish, Vector3F ray) { 114 Vector3F startPoint = getPointOnRay(ray, start); 115 Vector3F endPoint = getPointOnRay(ray, finish); 116 117 return !isUnderGround(startPoint) && isUnderGround(endPoint); 118 } 119 120 private bool isUnderGround(Vector3F testPoint) { 121 float height = 0; 122 123 height = terrain.getHeight(testPoint.x, testPoint.z); 124 125 return testPoint.y < height; 126 } 127 }