1 /* 2 Copyright (c) 2013-2015 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module dgl.graphics.tbcamera; 30 31 import std.math; 32 33 import derelict.opengl.gl; 34 import derelict.opengl.glu; 35 36 import dlib.core.memory; 37 import dlib.math.utils; 38 import dlib.math.vector; 39 import dlib.math.matrix; 40 import dlib.math.transformation; 41 import dlib.math.quaternion; 42 43 import dgl.core.interfaces; 44 import dgl.graphics.camera; 45 46 final class TrackballCamera: Modifier, Camera 47 { 48 private: 49 50 Vector3f center; 51 float distance; 52 Quaternionf rotPitch; 53 Quaternionf rotTurn; 54 Quaternionf rotRoll; 55 Matrix4x4f transform; 56 57 float rotPitchTheta = 0.0f; 58 float rotTurnTheta = 0.0f; 59 float rotRollTheta = 0.0f; 60 61 float pitch_current_theta = 0.0f; 62 float pitch_target_theta = 0.0f; 63 float turn_current_theta = 0.0f; 64 float turn_target_theta = 0.0f; 65 float roll_current_theta = 0.0f; 66 float roll_target_theta = 0.0f; 67 68 float current_move = 0.0f; 69 float target_move = 0.0f; 70 71 float current_strafe = 0.0f; 72 float target_strafe = 0.0f; 73 74 float current_zoom = 0.0f; 75 float target_zoom = 0.0f; 76 77 bool zoomIn = false; 78 float zoom_smooth = 2.0f; 79 80 Vector3f current_translate; 81 Vector3f target_translate; 82 83 public bool movingToTarget = false; 84 85 public: 86 87 this() 88 { 89 center = Vector3f(0.0f, 0.0f, 0.0f); 90 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), 0.0f); 91 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), 0.0f); 92 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), 0.0f); 93 transform = Matrix4x4f.identity; 94 distance = 10.0f; 95 96 current_translate = Vector3f(0.0f, 0.0f, 0.0f); 97 target_translate = Vector3f(0.0f, 0.0f, 0.0f); 98 } 99 100 void bind(double delta) 101 { 102 if (current_zoom < target_zoom) 103 { 104 current_zoom += (target_zoom - current_zoom) / zoom_smooth; 105 if (zoomIn) 106 zoom((target_zoom - current_zoom) / zoom_smooth); 107 else 108 zoom(-(target_zoom - current_zoom) / zoom_smooth); 109 } 110 if (current_translate != target_translate) 111 { 112 Vector3f t = (target_translate - current_translate)/30.0f; 113 current_translate += t; 114 translateTarget(t); 115 } 116 117 glPushMatrix(); 118 119 rotPitch = rotationQuaternion(Vector3f(1.0f,0.0f,0.0f), degtorad(rotPitchTheta)); 120 rotTurn = rotationQuaternion(Vector3f(0.0f,1.0f,0.0f), degtorad(rotTurnTheta)); 121 rotRoll = rotationQuaternion(Vector3f(0.0f,0.0f,1.0f), degtorad(rotRollTheta)); 122 123 Quaternionf q = rotPitch * rotTurn * rotRoll; 124 Matrix4x4f rot = q.toMatrix4x4(); 125 transform = translationMatrix(Vector3f(0.0f, 0.0f, -distance)) * rot * translationMatrix(center); 126 glLoadMatrixf(transform.arrayof.ptr); 127 128 transform.invert(); 129 } 130 131 void unbind() 132 { 133 glPopMatrix(); 134 } 135 136 void pitch(float theta) 137 { 138 rotPitchTheta += theta; 139 } 140 141 void turn(float theta) 142 { 143 rotTurnTheta += theta; 144 } 145 146 void roll(float theta) 147 { 148 rotRollTheta += theta; 149 } 150 151 float getPitch() 152 { 153 return rotPitchTheta; 154 } 155 156 float getTurn() 157 { 158 return rotTurnTheta; 159 } 160 161 float getRoll() 162 { 163 return rotRollTheta; 164 } 165 166 void pitchSmooth(float theta, float smooth) 167 { 168 pitch_target_theta += theta; 169 float pitch_theta = (pitch_target_theta - pitch_current_theta) / smooth; 170 pitch_current_theta += pitch_theta; 171 pitch(pitch_theta); 172 } 173 174 void turnSmooth(float theta, float smooth) 175 { 176 turn_target_theta += theta; 177 float turn_theta = (turn_target_theta - turn_current_theta) / smooth; 178 turn_current_theta += turn_theta; 179 turn(turn_theta); 180 } 181 182 void rollSmooth(float theta, float smooth) 183 { 184 roll_target_theta += theta; 185 float roll_theta = (roll_target_theta - roll_current_theta) / smooth; 186 roll_current_theta += roll_theta; 187 roll(roll_theta); 188 } 189 190 void setTarget(Vector3f pos) 191 { 192 center = pos; 193 } 194 195 void setTargetSmooth(Vector3f pos, float smooth) 196 { 197 current_translate = center; 198 target_translate = -pos; 199 } 200 201 void translateTarget(Vector3f pos) 202 { 203 center += pos; 204 } 205 206 Vector3f getTarget() 207 { 208 return center; 209 } 210 211 void setZoom(float z) 212 { 213 distance = z; 214 } 215 216 void zoom(float z) 217 { 218 distance -= z; 219 } 220 221 void zoomSmooth(float z, float smooth) 222 { 223 zoom_smooth = smooth; 224 225 if (z < 0) 226 zoomIn = true; 227 else 228 zoomIn = false; 229 230 target_zoom += abs(z); 231 } 232 233 Vector3f getPosition() 234 { 235 return transform.translation(); 236 } 237 238 Vector3f getRightVector() 239 { 240 return transform.right(); 241 } 242 243 Vector3f getUpVector() 244 { 245 return transform.up(); 246 } 247 248 Vector3f getDirectionVector() 249 { 250 return transform.forward(); 251 } 252 253 Matrix4x4f getTransform() 254 { 255 return transform; 256 } 257 258 float getDistance() 259 { 260 return distance; 261 } 262 263 void strafe(float speed) 264 { 265 Vector3f Forward; 266 Forward.x = cos(degtorad(rotTurnTheta)); 267 Forward.y = 0.0f; 268 Forward.z = sin(degtorad(rotTurnTheta)); 269 center += Forward * speed; 270 } 271 272 void strafeSmooth(float speed, float smooth) 273 { 274 target_move += speed; 275 float move_sp = (target_move - current_move) / smooth; 276 current_move += move_sp; 277 strafe(move_sp); 278 } 279 280 void move(float speed) 281 { 282 Vector3f Left; 283 Left.x = cos(degtorad(rotTurnTheta+90.0f)); 284 Left.y = 0.0f; 285 Left.z = sin(degtorad(rotTurnTheta+90.0f)); 286 center += Left * speed; 287 } 288 289 void moveSmooth(float speed, float smooth) 290 { 291 target_strafe += speed; 292 float strafe_sp = (target_strafe - current_strafe) / smooth; 293 current_strafe += strafe_sp; 294 move(strafe_sp); 295 } 296 297 void screenToWorld( 298 int scrx, 299 int scry, 300 int scrw, 301 int scrh, 302 float yfov, 303 ref float worldx, 304 ref float worldy, 305 bool snap) 306 { 307 Vector3f camPos = getPosition(); 308 Vector3f camDir = getDirectionVector(); 309 310 float aspect = cast(float)scrw / cast(float)scrh; 311 312 float xfov = fovXfromY(yfov, aspect); 313 314 float tfov1 = tan(yfov*PI/360.0f); 315 float tfov2 = tan(xfov*PI/360.0f); 316 317 Vector3f camUp = getUpVector() * tfov1; 318 Vector3f camRight = getRightVector() * tfov2; 319 320 float width = 1.0f - 2.0f * cast(float)(scrx) / cast(float)(scrw); 321 float height = 1.0f - 2.0f * cast(float)(scry) / cast(float)(scrh); 322 323 float mx = camDir.x + camUp.x * height + camRight.x * width; 324 float my = camDir.y + camUp.y * height + camRight.y * width; 325 float mz = camDir.z + camUp.z * height + camRight.z * width; 326 327 worldx = snap? floor(camPos.x - mx * camPos.y / my) : (camPos.x - mx * camPos.y / my); 328 worldy = snap? floor(camPos.z - mz * camPos.y / my) : (camPos.z - mz * camPos.y / my); 329 } 330 331 void free() 332 { 333 Delete(this); 334 } 335 }