Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / r2 / tool_choose_pos.cpp
blob6170e21f19245b905efa7496752a2b2c9fbfca2a
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdpch.h"
22 #include "editor.h"
23 #include "tool_choose_pos.h"
24 #include "../interface_v3/interface_manager.h"
25 #include "../global.h"
26 #include "nel/gui/group_tree.h"
27 #include "../interface_v3/group_map.h"
28 #include "../landscape_poly_drawer.h"
30 #include "../entities.h"
32 #include "nel/misc/vector.h"
33 #include "nel/misc/quat.h"
34 #include "nel/misc/aabbox.h"
35 #include "nel/3d/u_instance.h"
37 #include "game_share/object.h"
38 #include "dmc/idmc.h"
39 #include "r2_config.h"
41 #ifdef DEBUG_NEW
42 #define new DEBUG_NEW
43 #endif
45 using namespace NLPACS;
46 using namespace NLMISC;
48 namespace R2
52 // ***************************************************************
53 CToolChoosePos::CToolChoosePos(sint ghostSlot,
54 const std::string &cursValid,
55 const std::string &cursInvalid,
56 const std::vector<CPolygon2D> &polyList,
57 const CPrimLook &polyValidLook,
58 const CPrimLook &polyInvalidLook
61 _CursValid = cursValid;
62 _CursInvalid = cursInvalid;
63 _GhostSlot = ghostSlot,
64 _Valid = false;
65 _WantedAction = NoAction;
66 _WarningNoMeshOrSkeletonShown = false;
67 CEntityCL *entity = getGhost();
68 _LocalSelectBoxSize = 1;
69 if (entity)
71 entity->getMeshDefaultScale(_DefaultScale);
72 if (!entity->skeleton())
74 const CVector &hs = getEditor().getLocalSelectBox(*entity).getHalfSize();
75 _LocalSelectBoxSize = maxof(fabsf(hs.x), fabsf(hs.y), fabsf(hs.z));
78 else
80 _DefaultScale.set(1.f, 1.f, 1.f);
82 _MultiPos = false;
83 _MultiPosLocked = false;
84 _BadPlaceDecal.setTexture("*accessibility_texture*", true, true, false);
85 _TestDecal.setTexture("encyclopedia_box_zo.tga", true, true, false);
87 _PolyList = polyList;
88 _PolyRender.resize(polyList.size());
89 _PolyValidLook = polyValidLook;
90 _PolyInvalidLook = polyInvalidLook;
91 CGroupMap *gm = getWorldMap();
92 for (uint k = 0; k < _PolyRender.size(); ++k)
94 _PolyRender[k].setActive(false);
95 if (gm)
97 gm->addDeco(&_PolyRender[k]);
102 // ***************************************************************
103 void CToolChoosePos::showPolys(bool visible)
105 //H_AUTO(R2_CToolChoosePos_showPolys)
106 for (uint k = 0; k < _PolyRender.size(); ++k)
108 _PolyRender[k].setActive(visible);
112 // ***************************************************************
113 void CToolChoosePos::enableMultiPos(const std::string &cursValidMulti /*="curs_create_multi.tga"*/)
115 //H_AUTO(R2_CToolChoosePos_enableMultiPos)
116 _MultiPos = true;
117 _CursValidMulti = cursValidMulti;
120 // ***************************************************************
121 CToolChoosePos::~CToolChoosePos()
123 cancel();
124 CGroupMap *gm = getWorldMap();
125 if (gm)
127 for (uint k = 0; k < _PolyRender.size(); ++k)
129 _PolyRender[k].setActive(false);
130 gm->removeDeco(&_PolyRender[k]);
135 // ***************************************************************
136 CEntityCL *CToolChoosePos::getGhost()
138 //H_AUTO(R2_CToolChoosePos_getGhost)
139 if (_GhostSlot == -1) return NULL;
140 return EntitiesMngr.entity(_GhostSlot);
143 // ***************************************************************
144 void CToolChoosePos::hideMapSelectionAxis()
146 //H_AUTO(R2_CToolChoosePos_hideMapSelectionAxis)
147 CGroupMap *worldMap = getWorldMap();
148 if (worldMap) worldMap->setSelectionAxis(false);
151 // ***************************************************************
152 void CToolChoosePos::updateInvalidCursorOnUI()
154 //H_AUTO(R2_CToolChoosePos_updateInvalidCursorOnUI)
155 setMouseCursor(DEFAULT_CURSOR);
158 // ***************************************************************
159 void CToolChoosePos::updateBeforeRender()
161 //H_AUTO(R2_CToolChoosePos_updateBeforeRender)
162 CGroupMap *worldMap = getWorldMap();
163 CEntityCL *entity = getGhost();
164 float dtBackup = DT;
165 DT = RZ_TIME_TO_BECOME_TRANSPARENT_IN_SECOND; // don't want a transition
166 if (entity)
168 uint32 oldOpacity = CEntityCL::getOpacityMin();
169 CEntityCL::setOpacityMin(DEFAULT_ENTITY_MIN_OPACITY);
170 entity->makeTransparent(true);
171 CEntityCL::setOpacityMin(oldOpacity);
173 DT = dtBackup;
174 // Build vector for direction pointed by mouse in world
175 sint32 mouseX, mouseY;
176 getMousePos(mouseX, mouseY);
177 if (!isInScreen(mouseX, mouseY) || (isMouseOnUI() && !isMouseOnWorldMap()))
179 if (worldMap) worldMap->setSelectionAxis(false);
180 if (entity)
182 entity->show(false);
184 updateInvalidCursorOnUI();
185 showPolys(false);
186 return;
189 CTool::CWorldViewRay worldViewRay;
191 computeWorldViewRay(mouseX, mouseY, worldViewRay);
193 CVector entityPos; // the pos where the ghost will be shown
194 CVector inter; // intersection of view ray with landscape
195 _Valid = false;
196 CVector scale = _DefaultScale;
197 TRayIntersectionType rayIntersectionType = computeLandscapeRayIntersection(worldViewRay, inter);
198 switch(rayIntersectionType)
200 case NoIntersection:
202 // no collision, can't drop entity
203 entityPos = worldViewRay.Origin + 3.f * worldViewRay.Dir;
204 // change the scale so that the entity has always the same size on screen
205 float refScale = CV_FloatingShapeRefScale.get();
206 if (refScale == 0.f) refScale = 1.f;
207 if (_LocalSelectBoxSize != 0.f)
209 scale *= refScale / _LocalSelectBoxSize;
211 if (worldMap) worldMap->setSelectionAxis(false);
212 showPolys(false);
214 break;
215 case ValidPacsPos:
216 entityPos = inter;
217 _CreatePosition = entityPos;
218 _Valid = isValidChoosePos(inter); // good pos to drop entity
219 if (worldMap) worldMap->setSelectionAxis(true, inter);
220 showPolys(true);
221 break;
222 case InvalidPacsPos:
223 entityPos = inter;
224 if (worldMap) worldMap->setSelectionAxis(true, inter);
225 showPolys(true);
226 break;
227 default:
228 nlassert(0);
229 break;
231 // compute front vector
232 CVector front = - MainCam.getMatrix().getJ();
233 front.z = 0.f;
234 front.normalize();
235 // compute angle around Z
236 _CreateAngle = (float) atan2(front.y, front.x);
238 if (entity)
240 CMatrix mat;
241 if (worldViewRay.OnMiniMap && rayIntersectionType == NoIntersection)
243 entity->show(false);
245 else if (!entity->skeleton())
248 nlwarning("Selected entity for the 'create' tool has no skeleton");
249 entity->show(false);
250 return;
252 //bool loading = true;
253 NL3D::UInstance inst = entity->instance();
254 if (inst.empty())
256 if (!entity->instances().empty())
258 inst = entity->instances()[0].Current;
259 //loading = !entity->instances()[0].Loading.empty() && entity->instances()[0].Current.empty();
262 if (inst.empty())
264 if (!_WarningNoMeshOrSkeletonShown)
266 nlwarning("Selected entity for the 'create' tool has no instance");
267 _WarningNoMeshOrSkeletonShown = true;
269 entity->show(false);
270 showPolys(false);
271 return;
273 entity->show(true);
275 CQuat frontQuat(CVector::K, _CreateAngle - (float) (NLMISC::Pi / 2));
276 inst.setRotQuat(frontQuat);
277 inst.setPos(entityPos);
278 inst.setScale(scale);
279 mat = inst.getMatrix();
281 else
283 entity->show(true);
285 entity->updateVisible(T1, NULL);
286 entity->updatePos(T1, NULL);
289 CMatrix skelMatrix = entity->skeleton()->getMatrix();
290 // relative position to skeleton root
291 CVector skelRootRelativePos = entity->skeleton()->getMatrix().getPos() - entity->pos().asVector();
292 // combine quat for front face xform & anim quat
293 CQuat frontQuat(CVector::K, _CreateAngle);
294 entity->skeleton()->setRotQuat(frontQuat * entity->skeleton()->getRotQuat());
295 entity->skeleton()->setPos(entityPos + skelRootRelativePos);
296 mat.setRot(frontQuat);
297 mat.setPos(entityPos);
299 // relative position to skeleton root
300 CVector skelRootRelativePos = entity->skeleton()->getMatrix().getPos() - entity->pos().asVector();
301 // combine quat for front face xform & anim quat
302 CQuat frontQuat(CVector::K, _CreateAngle);
303 entity->skeleton()->setRotQuat(frontQuat * entity->skeleton()->getRotQuat());
304 CMatrix frontMat;
305 frontMat.setRot(frontQuat);
306 entity->skeleton()->setPos(entityPos + frontMat * skelRootRelativePos);
307 /*mat.setRot(frontQuat);
308 mat.setPos(entityPos);*/
311 // see if all pos are accessible and update the _Valid flag
312 // NB NICO : THE FOLLOWING CODE IS WORKING BUT
313 // see with others if it's useful to check this type of collisions
314 // finally -> since check is never done, limited/no interest ...
316 if (shown && entity->getPrimitive())
318 float w;
319 float h;
320 if (entity->getPrimitive()->getPrimitiveType() == UMovePrimitive::_2DOrientedBox)
322 entity->getPrimitive()->getSize(w, h);
324 else
326 w = h = 2.f * entity->getPrimitive()->getRadius();
328 // transform from [0, 1] x [0, 1] to local bbox
329 CMatrix unitBoxToLocalPrim;
330 unitBoxToLocalPrim.setScale(CVector(w, h, 1.f));
331 //unitBoxToLocalBox.setPos(CVector(localBox.getMin().x / favoid0(w), localBox.getMin().y / favoid0(h), 1.f);
332 unitBoxToLocalPrim.setPos(CVector(- 0.5f * w, - 0.5f * h, 0.f));
333 CMatrix worldMat = mat * unitBoxToLocalPrim;
334 // test if place is valid
335 CPolygon poly;
336 poly.Vertices.resize(4);
337 poly.Vertices[0].set(0.f, 0.f, 0.f);
338 poly.Vertices[1].set(1.f, 0.f, 0.f);
339 poly.Vertices[2].set(1.f, 1.f, 0.f);
340 poly.Vertices[3].set(0.f, 1.f, 0.f);
341 CPolygon2D poly2D(poly, worldMat);
343 if (getEditor().getIslandCollision().isValidPoly(poly2D))
345 _TestDecal.setWorldMatrix(worldMat);
346 _TestDecal.addToRenderList();
348 else
350 _Valid = false;
351 _BadPlaceDecal.setWorldMatrix(worldMat);
352 _BadPlaceDecal.setDiffuse(getInvalidPosColor());
353 _BadPlaceDecal.setCustomUVMatrix(true, getEditor().getIslandCollision().getWorldToAccessibilityTexMat(true));
354 _BadPlaceDecal.addToRenderList();
360 // additionnal polygons
361 if (!_PolyRender.empty() && _PolyRender[0].getActive())
363 static NLMISC::CPolygon2D tmpPoly;
364 std::vector<NLMISC::CVector2f> &verts = tmpPoly.Vertices;
365 nlassert(_PolyRender.size() == _PolyList.size());
366 CIslandCollision &ic = getEditor().getIslandCollision();
367 for (uint k = 0; k < _PolyRender.size(); ++k)
369 verts.resize(_PolyList[k].Vertices.size());
370 for (uint l = 0; l < verts.size(); ++l)
372 verts[l].set(_PolyList[k].Vertices[l].x + _CreatePosition.x,
373 _PolyList[k].Vertices[l].y + _CreatePosition.y);
375 bool validPoly = ic.isValidPoly(tmpPoly); // Test all position because entities may be created in that zone
378 // also test zone vertices because in the end, a valid zone is a zone for which all vertices
379 // are valid
380 for (uint l = 0; validPoly && l < verts.size(); ++l)
382 if (!ic.isValidPos(verts[l])) validPoly = false;
386 if (!validPoly) _Valid = false;
389 bool validPoly = true;
390 for (uint l = 0; l < verts.size() && validPoly; ++l)
392 if (!ic.isValidSegment(verts[l], verts[(l + 1) % verts.size()]))
394 validPoly = false;
395 _Valid = false;
399 _PolyRender[k].setLook(validPoly ? _PolyValidLook : _PolyInvalidLook);
400 _PolyRender[k].setVertices(verts);
401 _PolyRender[k].addDecalsToRenderList();
403 if (_PolyRender[k].getLook().Shape == CPrimLook::ClosedPolyLine)
405 NLMISC::CAABBox bbox;
406 static volatile bool showPoly = true;
407 if (showPoly)
409 CLandscapePolyDrawer::getInstance().computeBBoxFromPolygon(tmpPoly, bbox);
410 CLandscapePolyDrawer::getInstance().addPoly(tmpPoly, _PolyRender[k].getLook().EdgeLook.WorldMapColor, bbox);
412 _PolyRender[k].setWorldMapPolyColor(_PolyRender[k].getLook().EdgeLook.WorldMapColor);
416 // change mouse depending on result
417 if (!isMouseOnUI() || isMouseOnWorldMap())
420 if (_Valid)
422 if (_MultiPos && isShiftDown() && !_MultiPosLocked)
424 setMouseCursor(_CursValidMulti);
426 else
428 setMouseCursor(_CursValid);
431 else
433 setMouseCursor(_CursInvalid);
436 else
438 setMouseCursor(_CursValid);
442 // ***************************************************************
443 void CToolChoosePos::updateAfterRender()
445 //H_AUTO(R2_CToolChoosePos_updateAfterRender)
446 CTool::TSmartPtr oldThis(this); // may be changed during commit
447 switch(_WantedAction)
449 case SelectPos:
451 showPolys(false);
452 hideMapSelectionAxis();
453 if (_Valid)
455 // signal deriver that a position has been chosen
456 commit(_CreatePosition, _CreateAngle);
457 if (stopAfterCommit() && (!_MultiPos || !isShiftDown()) && !_MultiPosLocked)
459 if (_GhostSlot != -1)
461 //EntitiesMngr.remove(_GhostSlot, true);
462 CEntityCL *ghost = EntitiesMngr.entity(_GhostSlot);
463 if (ghost)
465 // just hide the slot because creation may happen just after, so keep
466 // a ref on textures and shape
467 ghost->show(false);
470 getEditor().setCurrentTool(NULL);
472 else
474 _WantedAction = NoAction;
478 break;
479 case Cancel:
481 cancel();
482 getEditor().setCurrentTool(NULL);
484 break;
485 case NoAction:
486 // no-op
487 break;
488 default:
489 nlassert(0);
490 break;
495 // ***************************************************************
496 void CToolChoosePos::removeGhostSlot()
498 //H_AUTO(R2_CToolChoosePos_removeGhostSlot)
499 if (_GhostSlot != - 1 && getGhost())
501 CEntityCL *ghost = EntitiesMngr.entity(_GhostSlot);
502 if (ghost)
504 // just hide the slot because creation may happen just after, so keep
505 // a ref on textures and shape (this avoid a release from memory)
506 ghost->show(false);
507 ghost->displayable(false);
509 //EntitiesMngr.remove(_GhostSlot, true); // remove the ghost
510 _GhostSlot = -1;
514 // ***************************************************************
515 bool CToolChoosePos::onMouseLeftButtonClicked()
517 //H_AUTO(R2_CToolChoosePos_onMouseLeftButtonClicked)
518 if (_Valid)
520 _WantedAction = SelectPos;
522 return true;
525 // ***************************************************************
526 bool CToolChoosePos::onMouseRightButtonClicked()
528 //H_AUTO(R2_CToolChoosePos_onMouseRightButtonClicked)
529 _WantedAction = Cancel;
530 return true;
533 // ***************************************************************
534 void CToolChoosePos::cancel()
536 //H_AUTO(R2_CToolChoosePos_cancel)
537 showPolys(false);
538 // TODO nico : potential crash here if called just before render (in updateBeforeRender)
539 // TODO nico : rethink the deferred action stuff to avoid this possible case
540 hideMapSelectionAxis();
541 removeGhostSlot();
547 } // R2