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 }