Line data Source code
1 : /* Copyright (C) 2021 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 "../CommandProc.h"
22 : #include "../GameLoop.h"
23 : #include "../View.h"
24 : #include "graphics/Camera.h"
25 : #include "graphics/CinemaManager.h"
26 : #include "graphics/GameView.h"
27 : #include "ps/Game.h"
28 : #include "ps/CStr.h"
29 : #include "ps/CLogger.h"
30 : #include "ps/Filesystem.h"
31 : #include "maths/MathUtil.h"
32 : #include "maths/Quaternion.h"
33 : #include "maths/Vector2D.h"
34 : #include "maths/Vector3D.h"
35 : #include "simulation2/Simulation2.h"
36 : #include "simulation2/components/ICmpCinemaManager.h"
37 :
38 :
39 : namespace AtlasMessage
40 : {
41 :
42 : const float MINIMAL_SCREEN_DISTANCE = 5.f;
43 :
44 0 : sCinemaPath ConstructCinemaPath(const CCinemaPath* source)
45 : {
46 0 : sCinemaPath path;
47 0 : const CCinemaData* data = source->GetData();
48 : //path.mode = data->m_Mode;
49 : //path.style = data->m_Style;
50 0 : path.growth = data->m_Growth;
51 0 : path.timescale = data->m_Timescale.ToFloat();
52 0 : path.change = data->m_Switch;
53 :
54 0 : return path;
55 : }
56 0 : CCinemaData ConstructCinemaData(const sCinemaPath& path)
57 : {
58 0 : CCinemaData data;
59 0 : data.m_Growth = data.m_GrowthCount = path.growth;
60 0 : data.m_Switch = path.change;
61 : //data.m_Mode = path.mode;
62 : //data.m_Style = path.style;
63 :
64 0 : return data;
65 : }
66 0 : sCinemaSplineNode ConstructCinemaNode(const SplineData& data)
67 : {
68 0 : sCinemaSplineNode node;
69 0 : node.px = data.Position.X.ToFloat();
70 0 : node.py = data.Position.Y.ToFloat();
71 0 : node.pz = data.Position.Z.ToFloat();
72 :
73 0 : node.rx = data.Rotation.X.ToFloat();
74 0 : node.ry = data.Rotation.Y.ToFloat();
75 0 : node.rz = data.Rotation.Z.ToFloat();
76 0 : node.t = data.Distance.ToFloat();
77 :
78 0 : return node;
79 : }
80 :
81 0 : std::vector<sCinemaPath> GetCurrentPaths()
82 : {
83 0 : std::vector<sCinemaPath> atlasPaths;
84 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
85 0 : if (!cmpCinemaManager)
86 0 : return atlasPaths;
87 0 : const std::map<CStrW, CCinemaPath>& paths = cmpCinemaManager->GetPaths();
88 :
89 0 : for ( std::map<CStrW, CCinemaPath>::const_iterator it=paths.begin(); it!=paths.end(); ++it )
90 : {
91 0 : sCinemaPath path = ConstructCinemaPath(&it->second);
92 0 : path.name = it->first;
93 :
94 0 : const std::vector<SplineData>& nodes = it->second.GetAllNodes();
95 0 : std::vector<sCinemaSplineNode> atlasNodes;
96 :
97 0 : for ( size_t i=0; i<nodes.size(); ++i )
98 0 : atlasNodes.push_back( ConstructCinemaNode(nodes[i]) );
99 :
100 0 : if ( !atlasNodes.empty() )
101 : {
102 0 : float back = atlasNodes.back().t;
103 0 : if ( atlasNodes.size() > 2 )
104 : {
105 0 : for ( size_t i=atlasNodes.size()-2; i>0; --i )
106 0 : atlasNodes[i].t = atlasNodes[i-1].t;
107 : }
108 0 : atlasNodes.back().t = atlasNodes.front().t;
109 0 : atlasNodes.front().t = back;
110 : }
111 0 : path.nodes = atlasNodes;
112 0 : atlasPaths.push_back(path);
113 : }
114 0 : return atlasPaths;
115 : }
116 :
117 0 : void SetCurrentPaths(const std::vector<sCinemaPath>& atlasPaths)
118 : {
119 0 : std::map<CStrW, CCinemaPath> paths;
120 :
121 0 : for ( std::vector<sCinemaPath>::const_iterator it=atlasPaths.begin(); it!=atlasPaths.end(); ++it )
122 : {
123 0 : CStrW pathName(*it->name);
124 0 : paths[pathName] = CCinemaPath();
125 0 : paths[pathName].SetTimescale(fixed::FromFloat(it->timescale));
126 :
127 0 : const sCinemaPath& atlasPath = *it;
128 0 : const std::vector<sCinemaSplineNode> nodes = *atlasPath.nodes;
129 0 : TNSpline spline;
130 0 : CCinemaData data = ConstructCinemaData(atlasPath);
131 :
132 0 : for ( size_t j=0; j<nodes.size(); ++j )
133 : {
134 0 : spline.AddNode(CFixedVector3D(fixed::FromFloat(nodes[j].px), fixed::FromFloat(nodes[j].py), fixed::FromFloat(nodes[j].pz)),
135 0 : CFixedVector3D(fixed::FromFloat(nodes[j].rx), fixed::FromFloat(nodes[j].ry), fixed::FromFloat(nodes[j].rz)), fixed::FromFloat(nodes[j].t));
136 : }
137 0 : paths[pathName] = CCinemaPath(data, spline, TNSpline());
138 : }
139 :
140 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
141 0 : if (cmpCinemaManager)
142 0 : cmpCinemaManager->SetPaths(paths);
143 0 : }
144 0 : QUERYHANDLER(GetCameraInfo)
145 : {
146 0 : sCameraInfo info;
147 0 : const CMatrix3D& cameraOrientation = g_Game->GetView()->GetCamera()->GetOrientation();
148 :
149 0 : CQuaternion quatRot = cameraOrientation.GetRotation();
150 0 : quatRot.Normalize();
151 0 : CVector3D rotation = quatRot.ToEulerAngles();
152 0 : rotation.X = RADTODEG(rotation.X);
153 0 : rotation.Y = RADTODEG(rotation.Y);
154 0 : rotation.Z = RADTODEG(rotation.Z);
155 0 : CVector3D translation = cameraOrientation.GetTranslation();
156 :
157 0 : info.pX = translation.X;
158 0 : info.pY = translation.Y;
159 0 : info.pZ = translation.Z;
160 0 : info.rX = rotation.X;
161 0 : info.rY = rotation.Y;
162 0 : info.rZ = rotation.Z;
163 0 : msg->info = info;
164 0 : }
165 :
166 0 : MESSAGEHANDLER(CinemaEvent)
167 : {
168 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
169 0 : if (!cmpCinemaManager)
170 0 : return;
171 :
172 0 : if (msg->mode == eCinemaEventMode::SMOOTH)
173 : {
174 0 : cmpCinemaManager->AddCinemaPathToQueue(*msg->path);
175 : }
176 0 : else if ( msg->mode == eCinemaEventMode::RESET )
177 : {
178 : // g_Game->GetView()->ResetCamera();
179 : }
180 : else
181 0 : ENSURE(false);
182 : }
183 :
184 0 : BEGIN_COMMAND(AddCinemaPath)
185 : {
186 0 : void Do()
187 : {
188 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
189 0 : if (!cmpCinemaManager)
190 0 : return;
191 :
192 0 : CCinemaData pathData;
193 0 : pathData.m_Name = *msg->pathName;
194 0 : pathData.m_Timescale = fixed::FromInt(1);
195 0 : pathData.m_Orientation = L"target";
196 0 : pathData.m_Mode = L"ease_inout";
197 0 : pathData.m_Style = L"default";
198 :
199 0 : CVector3D focus = g_Game->GetView()->GetCamera()->GetFocus();
200 : CFixedVector3D target(
201 : fixed::FromFloat(focus.X),
202 : fixed::FromFloat(focus.Y),
203 : fixed::FromFloat(focus.Z)
204 0 : );
205 :
206 0 : CVector3D camera = g_Game->GetView()->GetCamera()->GetOrientation().GetTranslation();
207 : CFixedVector3D position(
208 : fixed::FromFloat(camera.X),
209 : fixed::FromFloat(camera.Y),
210 : fixed::FromFloat(camera.Z)
211 0 : );
212 :
213 0 : TNSpline positionSpline;
214 0 : positionSpline.AddNode(position, CFixedVector3D(), fixed::FromInt(0));
215 :
216 0 : TNSpline targetSpline;
217 0 : targetSpline.AddNode(target, CFixedVector3D(), fixed::FromInt(0));
218 :
219 0 : cmpCinemaManager->AddPath(CCinemaPath(pathData, positionSpline, targetSpline));
220 : }
221 0 : void Redo()
222 : {
223 0 : }
224 0 : void Undo()
225 : {
226 0 : }
227 : };
228 0 : END_COMMAND(AddCinemaPath)
229 :
230 0 : BEGIN_COMMAND(DeleteCinemaPath)
231 : {
232 0 : void Do()
233 : {
234 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
235 0 : if (!cmpCinemaManager)
236 0 : return;
237 :
238 0 : cmpCinemaManager->DeletePath(*msg->pathName);
239 : }
240 0 : void Redo()
241 : {
242 0 : }
243 0 : void Undo()
244 : {
245 0 : }
246 : };
247 0 : END_COMMAND(DeleteCinemaPath)
248 :
249 0 : BEGIN_COMMAND(SetCinemaPaths)
250 : {
251 : std::vector<sCinemaPath> m_oldPaths, m_newPaths;
252 0 : void Do()
253 : {
254 0 : m_oldPaths = GetCurrentPaths();
255 0 : m_newPaths = *msg->paths;
256 0 : Redo();
257 0 : }
258 0 : void Redo()
259 : {
260 0 : SetCurrentPaths(m_newPaths);
261 0 : }
262 0 : void Undo()
263 : {
264 0 : SetCurrentPaths(m_oldPaths);
265 0 : }
266 : };
267 0 : END_COMMAND(SetCinemaPaths)
268 :
269 0 : BEGIN_COMMAND(SetCinemaPathsDrawing)
270 : {
271 0 : void Do()
272 : {
273 0 : if (g_Game && g_Game->GetView() && g_Game->GetView()->GetCinema())
274 0 : g_Game->GetView()->GetCinema()->SetPathsDrawing(msg->drawPaths);
275 0 : }
276 :
277 0 : void Redo()
278 : {
279 0 : }
280 :
281 0 : void Undo()
282 : {
283 0 : }
284 : };
285 0 : END_COMMAND(SetCinemaPathsDrawing)
286 :
287 0 : static CVector3D GetNearestPointToScreenCoords(const CVector3D& base, const CVector3D& dir, const CVector2D& screen, float lower = -1e5, float upper = 1e5)
288 : {
289 : // It uses a ternary search, because an intersection of cylinders is the complex task
290 0 : for (int i = 0; i < 64; ++i)
291 : {
292 0 : float delta = (upper - lower) / 3.0;
293 0 : float middle1 = lower + delta, middle2 = lower + 2.0f * delta;
294 0 : CVector3D p1 = base + dir * middle1, p2 = base + dir * middle2;
295 0 : CVector2D s1, s2;
296 0 : g_Game->GetView()->GetCamera()->GetScreenCoordinates(p1, s1.X, s1.Y);
297 0 : g_Game->GetView()->GetCamera()->GetScreenCoordinates(p2, s2.X, s2.Y);
298 0 : if ((s1 - screen).Length() < (s2 - screen).Length())
299 0 : upper = middle2;
300 : else
301 0 : lower = middle1;
302 : }
303 0 : return base + dir * upper;
304 : }
305 :
306 : #define GET_PATH_NODE_WITH_VALIDATION() \
307 : int index = msg->node->index; \
308 : if (index < 0) \
309 : return; \
310 : CStrW name = *msg->node->name; \
311 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); \
312 : if (!cmpCinemaManager || !cmpCinemaManager->HasPath(name)) \
313 : return; \
314 : const CCinemaPath& path = cmpCinemaManager->GetPaths().find(name)->second; \
315 : if (!msg->node->targetNode) \
316 : { \
317 : if (index >= (int)path.GetAllNodes().size()) \
318 : return; \
319 : } \
320 : else \
321 : { \
322 : if (index >= (int)path.GetTargetSpline().GetAllNodes().size()) \
323 : return; \
324 : }
325 :
326 0 : BEGIN_COMMAND(AddPathNode)
327 : {
328 0 : void Do()
329 : {
330 0 : GET_PATH_NODE_WITH_VALIDATION();
331 :
332 0 : CCinemaData data = *path.GetData();
333 0 : TNSpline positionSpline = path;
334 0 : TNSpline targetSpline = path.GetTargetSpline();
335 0 : TNSpline& spline = msg->node->targetNode ? targetSpline : positionSpline;
336 :
337 0 : CVector3D focus = g_Game->GetView()->GetCamera()->GetFocus();
338 : CFixedVector3D target(
339 : fixed::FromFloat(focus.X),
340 : fixed::FromFloat(focus.Y),
341 : fixed::FromFloat(focus.Z)
342 0 : );
343 0 : spline.InsertNode(index + 1, target, CFixedVector3D(), fixed::FromInt(1));
344 :
345 0 : spline.BuildSpline();
346 0 : cmpCinemaManager->DeletePath(name);
347 0 : cmpCinemaManager->AddPath(CCinemaPath(data, positionSpline, targetSpline));
348 : }
349 :
350 0 : void Redo()
351 : {
352 0 : }
353 :
354 0 : void Undo()
355 : {
356 0 : }
357 : };
358 0 : END_COMMAND(AddPathNode)
359 :
360 0 : BEGIN_COMMAND(DeletePathNode)
361 : {
362 0 : void Do()
363 : {
364 0 : GET_PATH_NODE_WITH_VALIDATION();
365 :
366 0 : CCinemaData data = *path.GetData();
367 0 : TNSpline positionSpline = path;
368 0 : TNSpline targetSpline = path.GetTargetSpline();
369 0 : TNSpline& spline = msg->node->targetNode ? targetSpline : positionSpline;
370 0 : if (spline.GetAllNodes().size() <= 1)
371 0 : return;
372 :
373 0 : spline.RemoveNode(index);
374 0 : spline.BuildSpline();
375 0 : cmpCinemaManager->DeletePath(name);
376 0 : cmpCinemaManager->AddPath(CCinemaPath(data, positionSpline, targetSpline));
377 :
378 0 : g_AtlasGameLoop->view->SetParam(L"movetool", false);
379 : }
380 :
381 0 : void Redo()
382 : {
383 0 : }
384 :
385 0 : void Undo()
386 : {
387 0 : }
388 : };
389 0 : END_COMMAND(DeletePathNode)
390 :
391 0 : BEGIN_COMMAND(MovePathNode)
392 : {
393 0 : void Do()
394 : {
395 0 : int axis = msg->axis;
396 0 : if (axis == AXIS_INVALID)
397 0 : return;
398 :
399 0 : GET_PATH_NODE_WITH_VALIDATION();
400 :
401 0 : CCinemaData data = *path.GetData();
402 0 : TNSpline positionSpline = path;
403 0 : TNSpline targetSpline = path.GetTargetSpline();
404 0 : TNSpline& spline = msg->node->targetNode ? targetSpline : positionSpline;
405 :
406 : // Get shift of the tool by the cursor movement
407 0 : CFixedVector3D pos = spline.GetAllNodes()[index].Position;
408 : CVector3D position(
409 : pos.X.ToFloat(),
410 : pos.Y.ToFloat(),
411 : pos.Z.ToFloat()
412 0 : );
413 0 : CVector3D axisDirection(axis & AXIS_X, axis & AXIS_Y, axis & AXIS_Z);
414 0 : CVector2D from, to;
415 0 : msg->from->GetScreenSpace(from.X, from.Y);
416 0 : msg->to->GetScreenSpace(to.X, to.Y);
417 : CVector3D shift(
418 0 : GetNearestPointToScreenCoords(position, axisDirection, to) -
419 0 : GetNearestPointToScreenCoords(position, axisDirection, from)
420 0 : );
421 :
422 : // Change, rebuild and update the path
423 0 : position += shift;
424 0 : pos += CFixedVector3D(
425 : fixed::FromFloat(shift.X),
426 : fixed::FromFloat(shift.Y),
427 : fixed::FromFloat(shift.Z)
428 : );
429 0 : spline.UpdateNodePos(index, pos);
430 0 : spline.BuildSpline();
431 0 : cmpCinemaManager->DeletePath(name);
432 0 : cmpCinemaManager->AddPath(CCinemaPath(data, positionSpline, targetSpline));
433 :
434 : // Update visual tool coordinates
435 0 : g_AtlasGameLoop->view->SetParam(L"movetool_x", position.X);
436 0 : g_AtlasGameLoop->view->SetParam(L"movetool_y", position.Y);
437 0 : g_AtlasGameLoop->view->SetParam(L"movetool_z", position.Z);
438 : }
439 :
440 0 : void Redo()
441 : {
442 0 : }
443 :
444 0 : void Undo()
445 : {
446 0 : }
447 : };
448 0 : END_COMMAND(MovePathNode)
449 :
450 0 : QUERYHANDLER(GetCinemaPaths)
451 : {
452 0 : msg->paths = GetCurrentPaths();
453 0 : }
454 :
455 0 : static bool isPathNodePicked(const TNSpline& spline, const CVector2D& cursor, AtlasMessage::sCinemaPathNode& node, bool targetNode)
456 : {
457 0 : for (size_t i = 0; i < spline.GetAllNodes().size(); ++i)
458 : {
459 0 : const SplineData& data = spline.GetAllNodes()[i];
460 : CVector3D position(
461 : data.Position.X.ToFloat(),
462 : data.Position.Y.ToFloat(),
463 : data.Position.Z.ToFloat()
464 0 : );
465 0 : CVector2D screen_pos;
466 0 : g_Game->GetView()->GetCamera()->GetScreenCoordinates(position, screen_pos.X, screen_pos.Y);
467 0 : if ((screen_pos - cursor).Length() < MINIMAL_SCREEN_DISTANCE)
468 : {
469 0 : node.index = i;
470 0 : node.targetNode = targetNode;
471 0 : g_AtlasGameLoop->view->SetParam(L"movetool", true);
472 0 : g_AtlasGameLoop->view->SetParam(L"movetool_x", position.X);
473 0 : g_AtlasGameLoop->view->SetParam(L"movetool_y", position.Y);
474 0 : g_AtlasGameLoop->view->SetParam(L"movetool_z", position.Z);
475 0 : return true;
476 : }
477 : }
478 0 : return false;
479 : }
480 :
481 0 : QUERYHANDLER(PickPathNode)
482 : {
483 0 : AtlasMessage::sCinemaPathNode node;
484 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
485 0 : if (!cmpCinemaManager)
486 : {
487 0 : msg->node = node;
488 0 : return;
489 : }
490 :
491 0 : CVector2D cursor;
492 0 : msg->pos->GetScreenSpace(cursor.X, cursor.Y);
493 :
494 0 : for (const std::pair<const CStrW, CCinemaPath>& p : cmpCinemaManager->GetPaths())
495 : {
496 0 : const CCinemaPath& path = p.second;
497 0 : if (isPathNodePicked(path, cursor, node, false) || isPathNodePicked(path.GetTargetSpline(), cursor, node, true))
498 : {
499 0 : node.name = path.GetName();
500 0 : msg->node = node;
501 0 : return;
502 : }
503 : }
504 0 : msg->node = node;
505 0 : g_AtlasGameLoop->view->SetParam(L"movetool", false);
506 : }
507 :
508 0 : static bool isAxisPicked(const CVector3D& base, const CVector3D& direction, float length, const CVector2D& cursor)
509 : {
510 0 : CVector3D position = GetNearestPointToScreenCoords(base, direction, cursor, 0, length);
511 0 : CVector2D screen_position;
512 0 : g_Game->GetView()->GetCamera()->GetScreenCoordinates(position, screen_position.X, screen_position.Y);
513 0 : return (cursor - screen_position).Length() < MINIMAL_SCREEN_DISTANCE;
514 : }
515 :
516 0 : QUERYHANDLER(PickAxis)
517 : {
518 0 : msg->axis = AXIS_INVALID;
519 :
520 0 : GET_PATH_NODE_WITH_VALIDATION();
521 :
522 0 : const TNSpline& spline = msg->node->targetNode ? path.GetTargetSpline() : path;
523 0 : CFixedVector3D pos = spline.GetAllNodes()[index].Position;
524 0 : CVector3D position(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat());
525 0 : CVector3D camera = g_Game->GetView()->GetCamera()->GetOrientation().GetTranslation();
526 0 : float scale = (position - camera).Length() / 10.0;
527 :
528 0 : CVector2D cursor;
529 0 : msg->pos->GetScreenSpace(cursor.X, cursor.Y);
530 0 : if (isAxisPicked(position, CVector3D(1, 0, 0), scale, cursor))
531 0 : msg->axis = AXIS_X;
532 0 : else if (isAxisPicked(position, CVector3D(0, 1, 0), scale, cursor))
533 0 : msg->axis = AXIS_Y;
534 0 : else if (isAxisPicked(position, CVector3D(0, 0, 1), scale, cursor))
535 0 : msg->axis = AXIS_Z;
536 : }
537 :
538 0 : MESSAGEHANDLER(ClearPathNodePreview)
539 : {
540 : UNUSED2(msg);
541 0 : g_AtlasGameLoop->view->SetParam(L"movetool", false);
542 0 : }
543 :
544 : } // namespace AtlasMessage
|