1 /* 2 Copyright (c) 2017-2018 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.graphics.tbcamera; 29 30 import std.math; 31 32 import dlib.core.memory; 33 import dlib.math.utils; 34 import dlib.math.vector; 35 import dlib.math.matrix; 36 import dlib.math.transformation; 37 import dlib.math.quaternion; 38 39 final class TrackballCamera 40 { 41 private: 42 43 Vector3f center; 44 float distance; 45 Quaternionf rotPitch; 46 Quaternionf rotTurn; 47 Quaternionf rotRoll; 48 Matrix4x4f transform; 49 Matrix4x4f invTransform; 50 51 float rotPitchTheta = 0.0f; 52 float rotTurnTheta = 0.0f; 53 float rotRollTheta = 0.0f; 54 55 float pitch_current_theta = 0.0f; 56 float pitch_target_theta = 0.0f; 57 float turn_current_theta = 0.0f; 58 float turn_target_theta = 0.0f; 59 float roll_current_theta = 0.0f; 60 float roll_target_theta = 0.0f; 61 62 float current_move = 0.0f; 63 float target_move = 0.0f; 64 65 float current_strafe = 0.0f; 66 float target_strafe = 0.0f; 67 68 float current_zoom = 0.0f; 69 float target_zoom = 0.0f; 70 71 bool zoomIn = false; 72 float zoom_smooth = 2.0f; 73 74 Vector3f current_translate; 75 Vector3f target_translate; 76 77 public bool movingToTarget = false; 78 79 public: 80 81 this() 82 { 83 center = Vector3f(0.0f, 0.0f, 0.0f); 84 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), 0.0f); 85 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), 0.0f); 86 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), 0.0f); 87 transform = Matrix4x4f.identity; 88 invTransform = Matrix4x4f.identity; 89 distance = 10.0f; 90 91 current_translate = Vector3f(0.0f, 0.0f, 0.0f); 92 target_translate = Vector3f(0.0f, 0.0f, 0.0f); 93 } 94 95 void reset() 96 { 97 center = Vector3f(0.0f, 0.0f, 0.0f); 98 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), 0.0f); 99 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), 0.0f); 100 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), 0.0f); 101 transform = Matrix4x4f.identity; 102 invTransform = Matrix4x4f.identity; 103 distance = 10.0f; 104 105 current_translate = Vector3f(0.0f, 0.0f, 0.0f); 106 target_translate = Vector3f(0.0f, 0.0f, 0.0f); 107 108 rotPitchTheta = 0.0f; 109 rotTurnTheta = 0.0f; 110 rotRollTheta = 0.0f; 111 112 pitch_current_theta = 0.0f; 113 pitch_target_theta = 0.0f; 114 turn_current_theta = 0.0f; 115 turn_target_theta = 0.0f; 116 roll_current_theta = 0.0f; 117 roll_target_theta = 0.0f; 118 119 current_move = 0.0f; 120 target_move = 0.0f; 121 122 current_strafe = 0.0f; 123 target_strafe = 0.0f; 124 125 current_zoom = 0.0f; 126 target_zoom = 0.0f; 127 } 128 129 void update(double dt) 130 { 131 if (current_zoom < target_zoom) 132 { 133 current_zoom += (target_zoom - current_zoom) / zoom_smooth; 134 if (zoomIn) 135 zoom((target_zoom - current_zoom) / zoom_smooth); 136 else 137 zoom(-(target_zoom - current_zoom) / zoom_smooth); 138 } 139 if (current_translate != target_translate) 140 { 141 Vector3f t = (target_translate - current_translate)/30.0f; 142 current_translate += t; 143 translateTarget(t); 144 } 145 146 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), degtorad(rotPitchTheta)); 147 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), degtorad(rotTurnTheta)); 148 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), degtorad(rotRollTheta)); 149 150 Quaternionf q = rotPitch * rotTurn * rotRoll; 151 Matrix4x4f rot = q.toMatrix4x4(); 152 invTransform = translationMatrix(Vector3f(0.0f, 0.0f, -distance)) * rot * translationMatrix(center); 153 154 transform = invTransform.inverse; 155 } 156 157 void pitch(float theta) 158 { 159 rotPitchTheta += theta; 160 } 161 162 void turn(float theta) 163 { 164 rotTurnTheta += theta; 165 } 166 167 void roll(float theta) 168 { 169 rotRollTheta += theta; 170 } 171 172 float getPitch() 173 { 174 return rotPitchTheta; 175 } 176 177 float getTurn() 178 { 179 return rotTurnTheta; 180 } 181 182 float getRoll() 183 { 184 return rotRollTheta; 185 } 186 187 void pitchSmooth(float theta, float smooth) 188 { 189 pitch_target_theta += theta; 190 float pitch_theta = (pitch_target_theta - pitch_current_theta) / smooth; 191 pitch_current_theta += pitch_theta; 192 pitch(pitch_theta); 193 } 194 195 void turnSmooth(float theta, float smooth) 196 { 197 turn_target_theta += theta; 198 float turn_theta = (turn_target_theta - turn_current_theta) / smooth; 199 turn_current_theta += turn_theta; 200 turn(turn_theta); 201 } 202 203 void rollSmooth(float theta, float smooth) 204 { 205 roll_target_theta += theta; 206 float roll_theta = (roll_target_theta - roll_current_theta) / smooth; 207 roll_current_theta += roll_theta; 208 roll(roll_theta); 209 } 210 211 void setTarget(Vector3f pos) 212 { 213 center = pos; 214 } 215 216 void setTargetSmooth(Vector3f pos, float smooth) 217 { 218 current_translate = center; 219 target_translate = -pos; 220 } 221 222 void translateTarget(Vector3f pos) 223 { 224 center += pos; 225 } 226 227 Vector3f getTarget() 228 { 229 return center; 230 } 231 232 void setZoom(float z) 233 { 234 distance = z; 235 } 236 237 void zoom(float z) 238 { 239 distance -= z; 240 } 241 242 void zoomSmooth(float z, float smooth) 243 { 244 zoom_smooth = smooth; 245 246 if (z < 0) 247 zoomIn = true; 248 else 249 zoomIn = false; 250 251 target_zoom += abs(z); 252 } 253 254 Vector3f getPosition() 255 { 256 return transform.translation(); 257 } 258 259 Vector3f getRightVector() 260 { 261 return transform.right(); 262 } 263 264 Vector3f getUpVector() 265 { 266 return transform.up(); 267 } 268 269 Vector3f getDirectionVector() 270 { 271 return transform.forward(); 272 } 273 274 Matrix4x4f transformation() 275 { 276 return transform; 277 } 278 279 Matrix4x4f viewMatrix() 280 { 281 return invTransform; 282 } 283 284 Matrix4x4f invViewMatrix() 285 { 286 return transform; 287 } 288 289 float getDistance() 290 { 291 return distance; 292 } 293 294 void strafe(float speed) 295 { 296 Vector3f Forward; 297 Forward.x = cos(degtorad(rotTurnTheta)); 298 Forward.y = 0.0f; 299 Forward.z = sin(degtorad(rotTurnTheta)); 300 center += Forward * speed; 301 } 302 303 void strafeSmooth(float speed, float smooth) 304 { 305 target_move += speed; 306 float move_sp = (target_move - current_move) / smooth; 307 current_move += move_sp; 308 strafe(move_sp); 309 } 310 311 void move(float speed) 312 { 313 Vector3f Left; 314 Left.x = cos(degtorad(rotTurnTheta+90.0f)); 315 Left.y = 0.0f; 316 Left.z = sin(degtorad(rotTurnTheta+90.0f)); 317 center += Left * speed; 318 } 319 320 void moveSmooth(float speed, float smooth) 321 { 322 target_strafe += speed; 323 float strafe_sp = (target_strafe - current_strafe) / smooth; 324 current_strafe += strafe_sp; 325 move(strafe_sp); 326 } 327 328 void screenToWorld( 329 int scrx, 330 int scry, 331 int scrw, 332 int scrh, 333 float yfov, 334 ref float worldx, 335 ref float worldy, 336 bool snap) 337 { 338 Vector3f camPos = getPosition(); 339 Vector3f camDir = getDirectionVector(); 340 341 float aspect = cast(float)scrw / cast(float)scrh; 342 343 float xfov = fovXfromY(yfov, aspect); 344 345 float tfov1 = tan(yfov*PI/360.0f); 346 float tfov2 = tan(xfov*PI/360.0f); 347 348 Vector3f camUp = getUpVector() * tfov1; 349 Vector3f camRight = getRightVector() * tfov2; 350 351 float width = 1.0f - 2.0f * cast(float)(scrx) / cast(float)(scrw); 352 float height = 1.0f - 2.0f * cast(float)(scry) / cast(float)(scrh); 353 354 float mx = camDir.x + camUp.x * height + camRight.x * width; 355 float my = camDir.y + camUp.y * height + camRight.y * width; 356 float mz = camDir.z + camUp.z * height + camRight.z * width; 357 358 worldx = snap? floor(camPos.x - mx * camPos.y / my) : (camPos.x - mx * camPos.y / my); 359 worldy = snap? floor(camPos.z - mz * camPos.y / my) : (camPos.z - mz * camPos.y / my); 360 } 361 }