Merge branch '164-crash-on-patching-and-possibly-right-after-login' into main/gingo...
[ryzomcore.git] / ryzom / client / src / view.cpp
blob66b575318f5957cf9a16223f35aaadb114a8ca6c
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 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
22 #include "stdpch.h"
25 /////////////
26 // INCLUDE //
27 /////////////
28 // Misc
29 #include "nel/misc/common.h"
30 #include "nel/misc/hierarchical_timer.h"
31 // Client
32 #include "view.h"
33 #include "entities.h"
34 #include "time_client.h"
35 #include "pacs_client.h"
36 // 3d
37 #include "nel/3d/u_visual_collision_manager.h"
38 #include "nel/3d/u_instance_group.h"
39 #include "nel/pacs/u_global_position.h"
40 #include "motion/user_controls.h"
43 #ifdef DEBUG_NEW
44 #define new DEBUG_NEW
45 #endif
47 using NL3D::UVisualCollisionManager;
48 extern UVisualCollisionManager *CollisionManager;
50 using namespace NLMISC;
51 using namespace std;
54 ////////////
55 // GLOBAL //
56 ////////////
57 CView View;
60 /////////////
61 // METHODS //
62 /////////////
63 //-----------------------------------------------
64 // CView :
65 // Constructor.
66 //-----------------------------------------------
67 CView::CView()
69 _ViewPos = CVector::Null;
70 _View = CVector::Null;
71 _RefinePos = CVector::Null;
72 _RearView=false;
73 _CurrentCameraDist = 0.0f;
74 _CurrentCameraSpeed = 0.0f;
75 _CurrentCameraHeight = 2.0f;
76 _MaxCameraHeight = 2.2f;
77 _MinCameraHeight = 1.0f;
78 _CollisionCameraDist = FLT_MAX;
79 _ThirPersonClusterSystem= NULL;
80 _ForceFirstPersonView= false;
81 // For 3rd person camera collision
82 _CameraCollisionDecal= 0.1f;
83 _CameraCollisionThreshold= 0.2f;
84 _CurrentCameraDistanceMax = ClientCfg.CameraDistMax;
86 }// CView //
88 //-----------------------------------------------
89 //-----------------------------------------------
90 void CView::update()
92 if(UserEntity->viewMode() == CUserEntity::FirstPV)
94 _CurrentCameraSpeed = 0.0f;
95 _CurrentCameraDist = 0.0f;
96 _CollisionCameraDist = FLT_MAX;
97 _ForceFirstPersonView= false;
99 else
101 ////////////////////////////
102 // Update Camera Distance //
103 if(ClientCfg.CameraDistance > _CurrentCameraDist)
105 // Acceleration
106 _CurrentCameraSpeed += ClientCfg.CameraAccel*DT;
107 // Speed Limit.
108 clamp(_CurrentCameraSpeed, ClientCfg.CameraSpeedMin, ClientCfg.CameraSpeedMax);
109 // Adjust Camera Distance
110 _CurrentCameraDist += _CurrentCameraSpeed*DT;
111 if(_CurrentCameraDist > ClientCfg.CameraDistance)
112 _CurrentCameraDist = ClientCfg.CameraDistance;
114 else if(ClientCfg.CameraDistance < _CurrentCameraDist)
116 // Acceleration
117 _CurrentCameraSpeed -= ClientCfg.CameraAccel*DT;
118 // Speed Limit.
119 clamp(_CurrentCameraSpeed, -ClientCfg.CameraSpeedMax, -ClientCfg.CameraSpeedMin);
120 // Adjust Camera Distance
121 _CurrentCameraDist += _CurrentCameraSpeed*DT;
122 if(_CurrentCameraDist < ClientCfg.CameraDistance)
123 _CurrentCameraDist = ClientCfg.CameraDistance;
125 //////////////////////////
126 // Update Camera Height //
127 if(ClientCfg.CameraHeight > _CurrentCameraHeight)
129 // Acceleration
130 _CurrentCameraSpeed += ClientCfg.CameraAccel*DT;
131 // Speed Limit.
132 clamp(_CurrentCameraSpeed, ClientCfg.CameraSpeedMin, ClientCfg.CameraSpeedMax);
133 // Adjust Camera Distance
134 _CurrentCameraHeight += _CurrentCameraSpeed*DT;
135 if(_CurrentCameraHeight > ClientCfg.CameraHeight)
136 _CurrentCameraHeight = ClientCfg.CameraHeight;
138 else if(ClientCfg.CameraHeight < _CurrentCameraHeight)
140 // Acceleration
141 _CurrentCameraSpeed -= ClientCfg.CameraAccel*DT;
142 // Speed Limit.
143 clamp(_CurrentCameraSpeed, -ClientCfg.CameraSpeedMax, -ClientCfg.CameraSpeedMin);
144 // Adjust Camera Distance
145 _CurrentCameraHeight += _CurrentCameraSpeed*DT;
146 if(_CurrentCameraHeight < ClientCfg.CameraHeight)
147 _CurrentCameraHeight = ClientCfg.CameraHeight;
150 }// update //
152 //-----------------------------------------------
153 // currentViewPos :
154 // Set the user position.
155 //-----------------------------------------------
156 CVector CView::currentViewPos() const
158 // clamp to the collisioned camera distance
159 float minCamDist= min(_CurrentCameraDist, _CollisionCameraDist);
161 if(_RearView)
163 CVector v;
164 if(UserEntity->viewMode() == CUserEntity::FirstPV || _ForceFirstPersonView)
166 CVector headPos;
167 UserEntity->getHeadPos(headPos);
168 return headPos;
170 else
172 // get the reverted view
173 CVector v;
174 v.x = -UserEntity->front().x;
175 v.y = -UserEntity->front().y;
176 v.z = 0.0f;
177 v.normalize();
178 // pos
179 return UserEntity->pos() + CVector(0.0f, 0.0f, 2.0f) - v*minCamDist;
182 else
184 if(UserEntity->viewMode() == CUserEntity::FirstPV || _ForceFirstPersonView)
185 return _ViewPos + CVector(0.0f, 0.0f, UserEntity->eyesHeight());
186 else
187 return _ViewPos + CVector(0.0f, 0.0f, _CurrentCameraHeight) - _View*minCamDist;
189 }// currentViewPos //
191 //-----------------------------------------------
192 // currentView :
193 //-----------------------------------------------
194 CVector CView::currentView() const
196 if(_RearView)
198 CVector v;
199 v.x = -UserEntity->front().x;
200 v.y = -UserEntity->front().y;
201 v.z = 0.0f;
202 v.normalize();
203 return v;
205 else
206 return _View;
207 }// currentView //
209 NLMISC::CQuat CView::currentViewQuat() const
211 CMatrix mat;
212 mat.setRot(CVector::I, currentView(), CVector::K);
213 mat.normalize(CMatrix::YZX);
214 return mat.getRot();
217 //-----------------------------------------------
218 // currentCameraTarget :
219 //-----------------------------------------------
220 CVector CView::currentCameraTarget() const
222 if(UserEntity->viewMode() == CUserEntity::FirstPV || _ForceFirstPersonView)
223 return currentViewPos();
224 else
226 if(_RearView)
228 return UserEntity->pos() + CVector(0.0f, 0.0f, 2.0f);
230 else
232 return _ViewPos + CVector(0.0f, 0.0f, _CurrentCameraHeight);
237 // update the max distance of the camera for player that want to play as dm (camera can be far)
238 void CView::setCameraDistanceMaxForDm()
240 _CurrentCameraDistanceMax = ClientCfg.DmCameraDistMax;
241 if (!ClientCfg.FPV)
243 cameraDistance( std::min(ClientCfg.CameraDistance, _CurrentCameraDistanceMax) );
248 // update the max distance of the camera for player that want to play as player (camera must be near)
249 void CView::setCameraDistanceMaxForPlayer()
251 _CurrentCameraDistanceMax = ClientCfg.CameraDistMax;
252 if (!ClientCfg.FPV)
254 cameraDistance( std::min(ClientCfg.CameraDistance, _CurrentCameraDistanceMax) );
258 //-----------------------------------------------
259 // cameraDistance :
260 // Change the distance from the user to the camera.
261 //-----------------------------------------------
262 void CView::cameraDistance(float dist)
264 // Internal View
265 if((dist < ClientCfg.CameraDistMin) && (dist <= ClientCfg.CameraDistance))
267 if (UserEntity && !UserEntity->isDead())
268 UserEntity->viewMode(CUserEntity::FirstPV);
269 ClientCfg.CameraDistance = dist;
271 // External View
272 else
274 if (UserEntity && !UserEntity->isDead())
275 UserEntity->viewMode(CUserEntity::ThirdPV);
276 dist = std::max(dist, ClientCfg.CameraDistMin);
277 ClientCfg.CameraDistance = std::min(dist, _CurrentCameraDistanceMax);
279 }// cameraDistance //
281 //-----------------------------------------------
282 // changeCameraHeight
283 // Change the height of the camera
284 //-----------------------------------------------
285 void CView::changeCameraHeight(bool up, bool down)
287 // If the user is not inside a building.
288 if(!UserEntity->forceIndoorFPV())
290 if(up)
292 ClientCfg.CameraHeight += 0.5f;
294 else if(down)
296 ClientCfg.CameraHeight -= 0.2f;
298 // height limit.
299 clamp(ClientCfg.CameraHeight, _MinCameraHeight, _MaxCameraHeight);
301 }// changeCameraHeight //
303 //-----------------------------------------------
304 // changeCameraDist :
305 // Change the distance of the camera
306 //-----------------------------------------------
307 void CView::changeCameraDist(bool forward, bool backward)
309 // If the user is not inside a building.
310 if(!UserEntity->forceIndoorFPV())
312 if(forward)
313 decreaseCameraDist();
314 else if(backward)
315 increaseCameraDist();
317 }// changeCameraDist //
319 //-----------------------------------------------
320 // increaseCameraDist :
321 // Increase the distance between the user and the camera
322 //-----------------------------------------------
323 void CView::increaseCameraDist()
325 // FPV -> just switch to TPV
326 if(UserEntity->viewMode() == CUserEntity::FirstPV)
327 cameraDistance(std::max(ClientCfg.CameraDistance, ClientCfg.CameraDistMin));
328 // Backward
329 else
330 cameraDistance(ClientCfg.CameraDistance+ClientCfg.CameraDistStep);
331 }// increaseCameraDist //
333 //-----------------------------------------------
334 // decreaseCameraDist :
335 // Decrease the distance between the user and the camera
336 //-----------------------------------------------
337 void CView::decreaseCameraDist()
339 // Forward only in third-person view
340 if(UserEntity->viewMode() != CUserEntity::FirstPV)
341 cameraDistance(ClientCfg.CameraDistance-ClientCfg.CameraDistStep);
342 }// decreaseCameraDist //
344 //-----------------------------------------------
345 // getCamera3rdPersonSetup:
346 //-----------------------------------------------
347 void CView::getCamera3rdPersonSetup(CVector &cameraStart, CVector &cameraEnd, CVector &cameraTestStart) const
349 float testStartDecal;
350 getCamera3rdPersonSetupInternal(cameraStart, cameraEnd, cameraTestStart, testStartDecal);
353 //-----------------------------------------------
354 // getCamera3rdPersonSetupInternal:
355 //-----------------------------------------------
356 void CView::getCamera3rdPersonSetupInternal(CVector &cameraStart, CVector &cameraEnd, CVector &cameraTestStart, float &testStartDecal) const
358 // get the camera path
359 if(_RearView)
361 CVector v;
362 v.x = - UserEntity->front().x;
363 v.y = - UserEntity->front().y;
364 v.z = 0.f;
365 v.normalize();
366 cameraStart= UserEntity->pos()+CVector(0.f,0.f,2.f);
367 // add the threshold, to avoid discontinuity at f=1, when col start
368 cameraEnd= cameraStart - v*(_CurrentCameraDist+_CameraCollisionThreshold);
370 else
372 cameraStart= _ViewPos + CVector(0.0f, 0.0f, _CurrentCameraHeight);
373 // add the threshold, to avoid discontinuity at f=1, when col start
374 cameraEnd= cameraStart - _View*(_CurrentCameraDist+_CameraCollisionThreshold);
377 // Avoid problem when player too near a wall. move back the start test
378 CVector vDir= cameraEnd - cameraStart;
379 float len = vDir.norm();
380 testStartDecal= min(len, _CameraCollisionDecal);
381 vDir.normalize();
382 cameraTestStart= cameraStart + vDir*testStartDecal;
385 //-----------------------------------------------
386 // updateCameraCollision :
387 //-----------------------------------------------
388 void CView::updateCameraCollision()
390 H_AUTO(RZ_Client_updateCameraCollision)
392 // the radius of the cylinder to test
393 const float colRadius= 0.4f;
395 // default
396 _CollisionCameraDist= FLT_MAX;
397 bool oldForceFPV= _ForceFirstPersonView;
398 _ForceFirstPersonView= false;
400 // if third person mode and CollisionManager available
401 if( UserEntity->viewMode() != CUserEntity::FirstPV &&
402 UserControls.mode() != CUserControls::AIMode &&
403 CollisionManager)
405 // Get the Cluster system where the player (not the camera!) lies
406 NLPACS::UGlobalPosition gPos;
407 if(UserEntity->getPrimitive())
408 UserEntity->getPrimitive()->getGlobalPosition(gPos, dynamicWI);
409 // get the cluster IG associated to this pacs position
410 NL3D::UInstanceGroup *pPlayerClusterSystem = getCluster(gPos);
412 // For "matis serre bug", suppose the player is "inside" if walk on a cluster system
413 CollisionManager->setPlayerInside(pPlayerClusterSystem!=NULL);
415 // **** Compute the camera collision ray
416 // get the camera path
417 CVector cameraStart, cameraEnd, cameraTestStart;
418 float testStartDecal;
419 getCamera3rdPersonSetupInternal(cameraStart, cameraEnd, cameraTestStart, testStartDecal);
421 // **** First do a single ray test from user to cameraTestStart
422 // do the test only against landscape, to avoid problems
423 // Use an approximate "pelvis", instead of getHeadPos(), because sometimes, even the head of the player
424 // can enter in landscape. Thus this case will be tested, but the player will still see inside the landscape
425 CVector pelvisPos;
426 pelvisPos= _ViewPos + CVector(0.f, 0.f, 1.f);
427 // if collide, then force first person view
428 if(CollisionManager->getRayCollision(pelvisPos, cameraTestStart, true))
430 _ForceFirstPersonView= true;
432 else
434 _ForceFirstPersonView= false;
436 // **** Clamp the camera according to collision 3D.
437 float f= CollisionManager->getCameraCollision(cameraTestStart, cameraEnd, colRadius, true);
439 // if some collision found
440 if(f<1)
442 // re-add the decalStart
443 _CollisionCameraDist= testStartDecal + (cameraEnd-cameraTestStart).norm() * f;
444 // remove the threshold
445 _CollisionCameraDist-= _CameraCollisionThreshold;
446 clamp(_CollisionCameraDist, 0, _CurrentCameraDist);
449 // **** Ensure the position in cluster system
450 // get the pos from compute above
451 cameraStart= currentCameraTarget();
452 cameraEnd= currentViewPos();
454 // parse this ray against the cluster system
455 CVector precCameraEnd= cameraEnd;
456 _ThirPersonClusterSystem= Scene->findCameraClusterSystemFromRay(pPlayerClusterSystem, cameraStart, cameraEnd);
458 // Then modify the Camera distance
459 if(precCameraEnd!=cameraEnd)
461 _CollisionCameraDist= (cameraEnd - cameraStart).norm();
462 clamp(_CollisionCameraDist, 0, _CurrentCameraDist);
468 // if difference of mode, must update userentity
469 if(oldForceFPV!=_ForceFirstPersonView)
470 UserEntity->updateVisualDisplay();