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.affine;
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 }