Line data Source code
1 : /* Copyright (C) 2017 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "MessageHandler.h"
21 : #include "../GameLoop.h"
22 : #include "../View.h"
23 :
24 : #include "maths/MathUtil.h"
25 : #include "maths/Vector3D.h"
26 : #include "maths/Quaternion.h"
27 : #include "ps/Game.h"
28 : #include "renderer/Renderer.h"
29 : #include "graphics/GameView.h"
30 : #include "graphics/CinemaManager.h"
31 :
32 : #include "ps/World.h"
33 : #include "graphics/Terrain.h"
34 :
35 : #include <assert.h>
36 :
37 : namespace AtlasMessage {
38 :
39 0 : MESSAGEHANDLER(CameraReset)
40 : {
41 0 : if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
42 0 : return;
43 :
44 0 : CVector3D focus = g_Game->GetView()->GetCamera()->GetFocus();
45 :
46 0 : CVector3D target;
47 0 : if (!g_Game->GetWorld()->GetTerrain()->IsOnMap(focus.X, focus.Z))
48 : {
49 0 : target = CVector3D(
50 0 : g_Game->GetWorld()->GetTerrain()->GetMaxX()/2.f,
51 : focus.Y,
52 0 : g_Game->GetWorld()->GetTerrain()->GetMaxZ()/2.f);
53 : }
54 : else
55 : {
56 0 : target = focus;
57 : }
58 :
59 0 : g_Game->GetView()->ResetCameraTarget(target);
60 :
61 : UNUSED2(msg);
62 : }
63 :
64 0 : MESSAGEHANDLER(ScrollConstant)
65 : {
66 0 : if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
67 0 : return;
68 :
69 0 : if (msg->dir < 0 || msg->dir > 5)
70 : {
71 0 : debug_warn(L"ScrollConstant: invalid direction");
72 : }
73 : else
74 : {
75 0 : g_AtlasGameLoop->input.scrollSpeed[msg->dir] = msg->speed;
76 : }
77 : }
78 :
79 : // TODO: change all these g_Game->...GetCamera() bits to use the current AtlasView's
80 : // camera instead.
81 :
82 0 : MESSAGEHANDLER(Scroll)
83 : {
84 0 : if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled()) // TODO: do this better (probably a separate AtlasView class for cinematics)
85 0 : return;
86 :
87 0 : static CVector3D targetPos;
88 : static float targetDistance = 0.f;
89 :
90 0 : CMatrix3D& camera = g_Game->GetView()->GetCamera()->m_Orientation;
91 :
92 0 : static CVector3D lastCameraPos = camera.GetTranslation();
93 :
94 : // Ensure roughly correct motion when dragging is combined with other
95 : // movements.
96 0 : if (lastCameraPos != camera.GetTranslation())
97 0 : targetPos += camera.GetTranslation() - lastCameraPos;
98 :
99 : // General operation:
100 : //
101 : // When selecting a target point to drag, remember targetPos (a world-space
102 : // point on the terrain, underneath the mouse) and targetDistance (from the
103 : // camera to the target point).
104 : //
105 : // When dragging to a different position, the target point should remain
106 : // under the moved mouse; so calculate the ray through the camera and mouse,
107 : // multiply by targetDistance and add to targetPos, resulting in the required
108 : // camera position.
109 :
110 0 : if (msg->type == eScrollType::FROM)
111 : {
112 0 : targetPos = msg->pos->GetWorldSpace();
113 0 : targetDistance = (targetPos - camera.GetTranslation()).Length();
114 : }
115 0 : else if (msg->type == eScrollType::TO)
116 : {
117 0 : CVector3D origin, dir;
118 : float x, y;
119 0 : msg->pos->GetScreenSpace(x, y);
120 0 : g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, origin, dir);
121 0 : dir *= targetDistance;
122 0 : camera.Translate(targetPos - dir - origin);
123 0 : g_Game->GetView()->GetCamera()->UpdateFrustum();
124 : }
125 : else
126 : {
127 0 : debug_warn(L"Scroll: Invalid type");
128 : }
129 0 : lastCameraPos = camera.GetTranslation();
130 : }
131 :
132 0 : MESSAGEHANDLER(SmoothZoom)
133 : {
134 0 : if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
135 0 : return;
136 :
137 0 : g_AtlasGameLoop->input.zoomDelta += msg->amount;
138 : }
139 :
140 0 : MESSAGEHANDLER(RotateAround)
141 : {
142 0 : if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
143 0 : return;
144 :
145 0 : static CVector3D focusPos;
146 : static float lastX = 0.f, lastY = 0.f;
147 :
148 0 : CMatrix3D& camera = g_Game->GetView()->GetCamera()->m_Orientation;
149 :
150 0 : if (msg->type == eRotateAroundType::FROM)
151 : {
152 0 : msg->pos->GetScreenSpace(lastX, lastY); // get mouse position
153 0 : focusPos = msg->pos->GetWorldSpace(); // get point on terrain under mouse
154 : }
155 0 : else if (msg->type == eRotateAroundType::TO)
156 : {
157 : float x, y;
158 0 : msg->pos->GetScreenSpace(x, y); // get mouse position
159 :
160 : // Rotate around X and Y axes by amounts depending on the mouse delta
161 0 : float rotX = 6.f * (y-lastY) / g_Renderer.GetHeight();
162 0 : float rotY = 6.f * (x-lastX) / g_Renderer.GetWidth();
163 :
164 0 : CQuaternion q0, q1;
165 0 : q0.FromAxisAngle(camera.GetLeft(), -rotX);
166 0 : q1.FromAxisAngle(CVector3D(0.f, 1.f, 0.f), rotY);
167 0 : CQuaternion q = q0*q1;
168 :
169 0 : CVector3D origin = camera.GetTranslation();
170 0 : CVector3D offset = q.Rotate(origin - focusPos);
171 :
172 0 : q *= camera.GetRotation();
173 0 : q.Normalize(); // to avoid things blowing up when turning upside-down, for some reason I don't understand
174 0 : q.ToMatrix(camera);
175 :
176 : // Make sure up is still pointing up, regardless of any rounding errors.
177 : // (Maybe this distorts the camera in other ways, but at least the errors
178 : // are far less noticeable to me.)
179 0 : camera._21 = 0.f; // (_21 = Y component returned by GetLeft())
180 :
181 0 : camera.Translate(focusPos + offset);
182 0 : g_Game->GetView()->GetCamera()->UpdateFrustum();
183 :
184 0 : lastX = x;
185 0 : lastY = y;
186 : }
187 : else
188 : {
189 0 : debug_warn(L"RotateAround: Invalid type");
190 : }
191 : }
192 :
193 0 : MESSAGEHANDLER(LookAt)
194 : {
195 : // TODO: different camera depending on msg->view
196 0 : CCamera& camera = AtlasView::GetView_Actor()->GetCamera();
197 :
198 0 : CVector3D tgt = msg->target->GetWorldSpace();
199 0 : CVector3D eye = msg->pos->GetWorldSpace();
200 0 : tgt.Y = -tgt.Y; // ??? why is this needed?
201 0 : eye.Y = -eye.Y; // ???
202 :
203 : // Based on http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/glu/lookat.html
204 0 : CVector3D f = tgt - eye;
205 0 : f.Normalize();
206 0 : CVector3D s = f.Cross(CVector3D(0, 1, 0));
207 0 : CVector3D u = s.Cross(f);
208 0 : s.Normalize(); // (not in that man page, but necessary for correctness, and done by Mesa)
209 0 : u.Normalize();
210 : CMatrix3D M (
211 0 : s[0], s[1], s[2], 0,
212 0 : u[0], u[1], u[2], 0,
213 0 : -f[0], -f[1], -f[2], 0,
214 : 0, 0, 0, 1
215 0 : );
216 :
217 0 : camera.m_Orientation = M.GetTranspose();
218 0 : camera.m_Orientation.Translate(-eye);
219 :
220 0 : camera.UpdateFrustum();
221 0 : }
222 :
223 0 : QUERYHANDLER(GetView)
224 : {
225 0 : if (!g_Game)
226 0 : return;
227 :
228 0 : CVector3D focus = g_Game->GetView()->GetCamera()->GetFocus();
229 0 : sCameraInfo info;
230 :
231 0 : info.pX = focus.X;
232 0 : info.pY = focus.Y;
233 0 : info.pZ = focus.Z;
234 :
235 0 : CQuaternion quatRot = g_Game->GetView()->GetCamera()->GetOrientation().GetRotation();
236 0 : quatRot.Normalize();
237 0 : CVector3D rotation = quatRot.ToEulerAngles();
238 :
239 0 : info.rX = RADTODEG(rotation.X);
240 0 : info.rY = RADTODEG(rotation.Y);
241 0 : info.rZ = RADTODEG(rotation.Z);
242 :
243 0 : msg->info = info;
244 : }
245 :
246 0 : MESSAGEHANDLER(SetView)
247 : {
248 0 : if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
249 0 : return;
250 :
251 0 : CGameView* view = g_Game->GetView();
252 0 : view->ResetCameraTarget(view->GetCamera()->GetFocus());
253 :
254 0 : sCameraInfo cam = msg->info;
255 :
256 0 : view->ResetCameraTarget(CVector3D(cam.pX, cam.pY, cam.pZ));
257 :
258 : // TODO: Rotation
259 : }
260 :
261 : }
|