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/camera/impl.d) 6 * Documentation: 7 * Coverage: 8 **/ 9 module liberty.camera.impl; 10 11 import liberty.core.engine; 12 import liberty.math.functions; 13 import liberty.math.transform; 14 import liberty.math.vector; 15 import liberty.math.matrix; 16 import liberty.core.platform; 17 import liberty.time; 18 import liberty.camera.constants; 19 import liberty.camera.preset; 20 import liberty.scene.meta; 21 import liberty.scene.entity; 22 import liberty.scene.impl; 23 24 /** 25 * Represents the view of the observer. 26 * Everything that is rendered to the screen is processed within the projection matrix and view matrix of a camera. 27 * Inheriths $(D Entity) class and encapsulates $(D NodeBody) macro. 28 * It has a custom constructor that calls: $(D updateCameraVectors) and adds default $(D CameraPreset). 29 **/ 30 final class Camera : Entity { 31 mixin NodeBody; 32 33 private { 34 float _yaw = -90.0f; 35 float _pitch = -30.0f; 36 float _zNear = 0.01f; 37 float _zFar = 1000.0f; 38 } 39 40 /// 41 Vector3F frontVector = Vector3F.forward; 42 /// 43 Vector3F upVector = Vector3F.up; 44 /// 45 Vector3F rightVector = Vector3F.zero; 46 /// 47 Vector3F worldUpVector = Vector3F.up; 48 /// 49 float mouseSensitivity = 0.1f; 50 /// 51 float fieldOfView = 45.0f; 52 /// 53 float movementSpeed = 10.0f; 54 /// 55 bool mouseMoveLocked; 56 /// 57 bool mouseScrollLocked; 58 /// 59 bool keyboardLocked; 60 /// 61 bool constrainPitch = true; 62 /// 63 CameraPreset preset; 64 65 /// Default camera constructor. 66 this(string id) { 67 super(id); 68 register; 69 70 updateCameraVectors; 71 preset = CameraPreset.getDefault; 72 73 component!Transform 74 .setLocation(0.0f, 3.0f, 4.0f); 75 } 76 77 /// Get camera yaw. 78 @property float yaw() { return _yaw; } 79 80 /// Get camera pitch. 81 @property float pitch() { return _pitch; } 82 83 /// Get camera zNear. 84 @property float zNear() { return _zNear; } 85 86 /// Get camera zFar. 87 @property float zFar() { return _zFar; } 88 89 /// Set camera yaw. 90 @property void yaw(float value) { 91 updateCameraVectors; 92 _yaw = value; 93 } 94 95 /// Set camera pitch. 96 @property void pitch(float value) { 97 checkPitchLimits; 98 updateCameraVectors; 99 _pitch = value; 100 } 101 102 /// Set camera zNear. 103 @property void zNear(float value) { 104 checkZNearLimits; 105 _zNear = value; 106 } 107 108 /// Set camera zFar. 109 @property void zFar(float value) { 110 checkZFarLimits; 111 _zFar = value; 112 } 113 114 /** 115 * Set keyboard listener using a camera movement direction. 116 * Works only if camera keyboard listener isn't locked. 117 * Returns reference to this so it can be used in a stream. 118 **/ 119 typeof(this) processKeyboard(CameraMovement direction) { 120 if (!keyboardLocked) { 121 const float velocity = movementSpeed * Time.getDelta(); 122 preset.runKeyboardProcess(this, direction, velocity); 123 } 124 125 return this; 126 } 127 128 /** 129 * Set mouse move listener using x and y offsets. 130 * Works only if camera mouse move listener isn't locked. 131 * If it works then it updates camera vectors at the end. 132 * Returns reference to this so it can be used in a stream. 133 **/ 134 typeof(this) processMouseMovement(float xOffset, float yOffset) { 135 if (!mouseMoveLocked) { 136 xOffset *= mouseSensitivity; 137 yOffset *= mouseSensitivity; 138 139 _yaw += xOffset; 140 _pitch += yOffset; 141 142 checkPitchLimits; 143 updateCameraVectors; 144 } 145 146 return this; 147 } 148 149 /** 150 * Set mouse scroll listener using y offset. 151 * Works only if camera mouse scroll listener isn't locked. 152 * Returns reference to this so it can be used in a stream. 153 **/ 154 typeof(this) processMouseScroll(float yOffset) { 155 if (!mouseScrollLocked) { 156 if (fieldOfView >= 1.0f && fieldOfView <= 45.0f) 157 fieldOfView -= yOffset; 158 if (fieldOfView <= 1.0f) 159 fieldOfView = 1.0f; 160 if (fieldOfView >= 45.0f) 161 fieldOfView = 45.0f; 162 } 163 164 return this; 165 } 166 167 /** 168 * Returns camera view matrix. 169 **/ 170 Matrix4F viewMatrix() { 171 return Matrix4F.lookAt( 172 component!Transform.getLocation, 173 component!Transform.getLocation + frontVector, 174 upVector 175 ); 176 } 177 178 /** 179 * Returns camera projection matrix. 180 **/ 181 Matrix4F projectionMatrix() { 182 return Matrix4F.perspective( 183 fieldOfView.radians, 184 cast(float)Platform.getWindow.getWidth, 185 cast(float)Platform.getWindow.getHeight, 186 _zNear, 187 _zFar 188 ); 189 } 190 191 private void updateCameraVectors() { 192 Vector3F front; 193 194 front.x = cos(radians(_yaw)) * cos(radians(_pitch)); 195 front.y = sin(radians(_pitch)); 196 front.z = sin(radians(_yaw)) * cos(radians(_pitch)); 197 198 frontVector = front.normalized; 199 rightVector = cross(frontVector, worldUpVector).normalized; 200 upVector = cross(rightVector, frontVector).normalized; 201 } 202 203 pragma (inline, true) 204 private void checkPitchLimits() { 205 if (constrainPitch) { 206 if (_pitch > 89.0f) 207 _pitch = 89.0f; 208 if (_pitch < -89.0f) 209 _pitch = -89.0f; 210 } 211 } 212 213 pragma (inline, true) 214 private void checkZNearLimits() { 215 if (_zNear < 0.001f) 216 _zNear = 0.001f; 217 if (_zNear > 10_000.0f) 218 _zNear = 10_000.0f; 219 } 220 221 pragma (inline, true) 222 private void checkZFarLimits() { 223 if (_zFar < 0.001f) 224 _zFar = 0.0001f; 225 if (_zFar > 10_000.0f) 226 _zFar = 10_000.0f; 227 } 228 }