1 /* 2 Copyright (c) 2017-2020 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.ui.freeview; 29 30 import std.math; 31 32 import dlib.math.vector; 33 import dlib.math.matrix; 34 import dlib.math.quaternion; 35 import dlib.math.transformation; 36 import dlib.math.utils; 37 38 import dagon.core.event; 39 import dagon.core.keycodes; 40 import dagon.core.time; 41 import dagon.graphics.entity; 42 43 class FreeviewComponent: EntityComponent 44 { 45 int prevMouseX; 46 int prevMouseY; 47 float mouseSensibility = 0.1f; 48 49 Vector3f center; 50 float distance; 51 Quaternionf rotPitch; 52 Quaternionf rotTurn; 53 Quaternionf rotRoll; 54 Matrix4x4f transform; 55 Matrix4x4f invTransform; 56 57 float rotPitchTheta = 0.0f; 58 float rotTurnTheta = 0.0f; 59 float rotRollTheta = 0.0f; 60 61 float pitchCurrentTheta = 0.0f; 62 float pitchTargetTheta = 0.0f; 63 float turnCurrentTheta = 0.0f; 64 float turnTargetTheta = 0.0f; 65 float rollCurrentTheta = 0.0f; 66 float rollTargetTheta = 0.0f; 67 68 float currentMove = 0.0f; 69 float targetMove = 0.0f; 70 71 float currentStrafe = 0.0f; 72 float targetStrafe = 0.0f; 73 74 float currentZoom = 0.0f; 75 float targetZoom = 0.0f; 76 77 bool zoomIn = false; 78 float zoomSmoothFactor = 2.0f; 79 float translateSmoothFactor = 10.0f; 80 81 Vector3f currentTranslate; 82 Vector3f targetTranslate; 83 84 bool movingToTarget = false; 85 86 bool active = true; 87 88 this(EventManager em, Entity e) 89 { 90 super(em, e); 91 92 center = Vector3f(0.0f, 0.0f, 0.0f); 93 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), 0.0f); 94 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), 0.0f); 95 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), 0.0f); 96 transform = Matrix4x4f.identity; 97 invTransform = Matrix4x4f.identity; 98 distance = 10.0f; 99 100 currentTranslate = Vector3f(0.0f, 0.0f, 0.0f); 101 targetTranslate = Vector3f(0.0f, 0.0f, 0.0f); 102 103 pitch(45.0f); 104 turn(45.0f); 105 setZoom(20.0f); 106 } 107 108 void reset() 109 { 110 center = Vector3f(0.0f, 0.0f, 0.0f); 111 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), 0.0f); 112 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), 0.0f); 113 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), 0.0f); 114 transform = Matrix4x4f.identity; 115 invTransform = Matrix4x4f.identity; 116 distance = 10.0f; 117 118 currentTranslate = Vector3f(0.0f, 0.0f, 0.0f); 119 targetTranslate = Vector3f(0.0f, 0.0f, 0.0f); 120 121 rotPitchTheta = 0.0f; 122 rotTurnTheta = 0.0f; 123 rotRollTheta = 0.0f; 124 125 pitchCurrentTheta = 0.0f; 126 pitchTargetTheta = 0.0f; 127 turnCurrentTheta = 0.0f; 128 turnTargetTheta = 0.0f; 129 rollCurrentTheta = 0.0f; 130 rollTargetTheta = 0.0f; 131 132 currentMove = 0.0f; 133 targetMove = 0.0f; 134 135 currentStrafe = 0.0f; 136 targetStrafe = 0.0f; 137 138 currentZoom = 0.0f; 139 targetZoom = 0.0f; 140 141 pitch(45.0f); 142 turn(45.0f); 143 setZoom(20.0f); 144 } 145 146 override void update(Time time) 147 { 148 processEvents(); 149 150 if (active) 151 { 152 if (eventManager.mouseButtonPressed[MB_RIGHT]) 153 { 154 float shiftx = (eventManager.mouseX - prevMouseX) * mouseSensibility; 155 float shifty = -(eventManager.mouseY - prevMouseY) * mouseSensibility; 156 Vector3f trans = up * shifty + right * shiftx; 157 translateTarget(trans); 158 } 159 else if (eventManager.mouseButtonPressed[MB_LEFT] && eventManager.keyPressed[KEY_LCTRL]) 160 { 161 float shiftx = (eventManager.mouseX - prevMouseX); 162 float shifty = (eventManager.mouseY - prevMouseY); 163 zoom((shiftx + shifty) * 0.1f); 164 } 165 else if (eventManager.mouseButtonPressed[MB_LEFT]) 166 { 167 float t = (eventManager.mouseX - prevMouseX); 168 float p = (eventManager.mouseY - prevMouseY); 169 pitchSmooth(p, 4.0f); 170 turnSmooth(t, 4.0f); 171 } 172 173 prevMouseX = eventManager.mouseX; 174 prevMouseY = eventManager.mouseY; 175 } 176 177 if (currentZoom < targetZoom) 178 { 179 currentZoom += (targetZoom - currentZoom) / zoomSmoothFactor; 180 if (zoomIn) 181 zoom((targetZoom - currentZoom) / zoomSmoothFactor); 182 else 183 zoom(-(targetZoom - currentZoom) / zoomSmoothFactor); 184 } 185 if (currentTranslate != targetTranslate) 186 { 187 Vector3f t = (targetTranslate - currentTranslate) / translateSmoothFactor; 188 currentTranslate += t; 189 translateTarget(t); 190 } 191 192 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), degtorad(rotPitchTheta)); 193 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), degtorad(rotTurnTheta)); 194 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), degtorad(rotRollTheta)); 195 196 Quaternionf q = rotPitch * rotTurn * rotRoll; 197 Matrix4x4f rot = q.toMatrix4x4(); 198 invTransform = translationMatrix(Vector3f(0.0f, 0.0f, -distance)) * rot * translationMatrix(center); 199 200 transform = invTransform.inverse; 201 202 entity.prevTransformation = entity.transformation; 203 entity.transformation = transform; 204 entity.invTransformation = invTransform; 205 206 entity.absoluteTransformation = entity.transformation; 207 entity.invAbsoluteTransformation = entity.invTransformation; 208 entity.prevAbsoluteTransformation = entity.prevTransformation; 209 } 210 211 void setRotation(float p, float t, float r) 212 { 213 rotPitchTheta = p; 214 rotTurnTheta = t; 215 rotRollTheta = r; 216 } 217 218 void pitch(float theta) 219 { 220 rotPitchTheta += theta; 221 } 222 223 void turn(float theta) 224 { 225 rotTurnTheta += theta; 226 } 227 228 void roll(float theta) 229 { 230 rotRollTheta += theta; 231 } 232 233 float pitch() 234 { 235 return rotPitchTheta; 236 } 237 238 float turn() 239 { 240 return rotTurnTheta; 241 } 242 243 float roll() 244 { 245 return rotRollTheta; 246 } 247 248 void pitchSmooth(float theta, float smooth) 249 { 250 pitchTargetTheta += theta; 251 float pitchTheta = (pitchTargetTheta - pitchCurrentTheta) / smooth; 252 pitchCurrentTheta += pitchTheta; 253 pitch(pitchTheta); 254 } 255 256 void turnSmooth(float theta, float smooth) 257 { 258 turnTargetTheta += theta; 259 float turnTheta = (turnTargetTheta - turnCurrentTheta) / smooth; 260 turnCurrentTheta += turnTheta; 261 turn(turnTheta); 262 } 263 264 void rollSmooth(float theta, float smooth) 265 { 266 rollTargetTheta += theta; 267 float rollTheta = (rollTargetTheta - rollCurrentTheta) / smooth; 268 rollCurrentTheta += rollTheta; 269 roll(rollTheta); 270 } 271 272 void setTargetSmooth(Vector3f pos, float smooth) 273 { 274 currentTranslate = center; 275 targetTranslate = -pos; 276 } 277 278 void translateTarget(Vector3f pos) 279 { 280 center += pos; 281 } 282 283 void setZoom(float z) 284 { 285 distance = z; 286 } 287 288 void zoom(float z) 289 { 290 distance -= z; 291 } 292 293 void zoomSmooth(float z, float smooth) 294 { 295 zoomSmoothFactor = smooth; 296 297 if (z < 0) 298 zoomIn = true; 299 else 300 zoomIn = false; 301 302 targetZoom += abs(z); 303 } 304 305 Vector3f position() 306 { 307 return transform.translation(); 308 } 309 310 Vector3f right() 311 { 312 return transform.right(); 313 } 314 315 Vector3f up() 316 { 317 return transform.up(); 318 } 319 320 Vector3f direction() 321 { 322 return transform.forward(); 323 } 324 325 void strafe(float speed) 326 { 327 Vector3f forward; 328 forward.x = cos(degtorad(rotTurnTheta)); 329 forward.y = 0.0f; 330 forward.z = sin(degtorad(rotTurnTheta)); 331 center += forward * speed; 332 } 333 334 void strafeSmooth(float speed, float smooth) 335 { 336 targetMove += speed; 337 float movesp = (targetMove - currentMove) / smooth; 338 currentMove += movesp; 339 strafe(movesp); 340 } 341 342 void move(float speed) 343 { 344 Vector3f dir; 345 dir.x = cos(degtorad(rotTurnTheta + 90.0f)); 346 dir.y = 0.0f; 347 dir.z = sin(degtorad(rotTurnTheta + 90.0f)); 348 center += dir * speed; 349 } 350 351 void moveSmooth(float speed, float smooth) 352 { 353 targetStrafe += speed; 354 float strafesp = (targetStrafe - currentStrafe) / smooth; 355 currentStrafe += strafesp; 356 move(strafesp); 357 } 358 359 void screenToWorld( 360 int scrx, 361 int scry, 362 int scrw, 363 int scrh, 364 float yfov, 365 ref float worldx, 366 ref float worldy, 367 bool snap) 368 { 369 Vector3f camPos = position(); 370 Vector3f camDir = direction(); 371 372 float aspect = cast(float)scrw / cast(float)scrh; 373 374 float xfov = fovXfromY(yfov, aspect); 375 376 float tfov1 = tan(yfov*PI/360.0f); 377 float tfov2 = tan(xfov*PI/360.0f); 378 379 Vector3f camUp = up() * tfov1; 380 Vector3f camRight = right() * tfov2; 381 382 float width = 1.0f - 2.0f * cast(float)(scrx) / cast(float)(scrw); 383 float height = 1.0f - 2.0f * cast(float)(scry) / cast(float)(scrh); 384 385 float mx = camDir.x + camUp.x * height + camRight.x * width; 386 float my = camDir.y + camUp.y * height + camRight.y * width; 387 float mz = camDir.z + camUp.z * height + camRight.z * width; 388 389 worldx = snap? floor(camPos.x - mx * camPos.y / my) : (camPos.x - mx * camPos.y / my); 390 worldy = snap? floor(camPos.z - mz * camPos.y / my) : (camPos.z - mz * camPos.y / my); 391 } 392 393 override void onMouseButtonDown(int button) 394 { 395 if (!active) 396 return; 397 398 if (button == MB_LEFT) 399 { 400 prevMouseX = eventManager.mouseX; 401 prevMouseY = eventManager.mouseY; 402 } 403 } 404 405 override void onMouseWheel(int x, int y) 406 { 407 if (!active) 408 return; 409 410 zoom(cast(float)y * 0.2f); 411 } 412 }