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 }