1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
29 #include "nel/misc/common.h"
30 #include "nel/misc/hierarchical_timer.h"
34 #include "time_client.h"
35 #include "pacs_client.h"
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"
47 using NL3D::UVisualCollisionManager
;
48 extern UVisualCollisionManager
*CollisionManager
;
50 using namespace NLMISC
;
63 //-----------------------------------------------
66 //-----------------------------------------------
69 _ViewPos
= CVector::Null
;
70 _View
= CVector::Null
;
71 _RefinePos
= CVector::Null
;
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
;
88 //-----------------------------------------------
89 //-----------------------------------------------
92 if(UserEntity
->viewMode() == CUserEntity::FirstPV
)
94 _CurrentCameraSpeed
= 0.0f
;
95 _CurrentCameraDist
= 0.0f
;
96 _CollisionCameraDist
= FLT_MAX
;
97 _ForceFirstPersonView
= false;
101 ////////////////////////////
102 // Update Camera Distance //
103 if(ClientCfg
.CameraDistance
> _CurrentCameraDist
)
106 _CurrentCameraSpeed
+= ClientCfg
.CameraAccel
*DT
;
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
)
117 _CurrentCameraSpeed
-= ClientCfg
.CameraAccel
*DT
;
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
)
130 _CurrentCameraSpeed
+= ClientCfg
.CameraAccel
*DT
;
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
)
141 _CurrentCameraSpeed
-= ClientCfg
.CameraAccel
*DT
;
143 clamp(_CurrentCameraSpeed
, -ClientCfg
.CameraSpeedMax
, -ClientCfg
.CameraSpeedMin
);
144 // Adjust Camera Distance
145 _CurrentCameraHeight
+= _CurrentCameraSpeed
*DT
;
146 if(_CurrentCameraHeight
< ClientCfg
.CameraHeight
)
147 _CurrentCameraHeight
= ClientCfg
.CameraHeight
;
152 //-----------------------------------------------
154 // Set the user position.
155 //-----------------------------------------------
156 CVector
CView::currentViewPos() const
158 // clamp to the collisioned camera distance
159 float minCamDist
= min(_CurrentCameraDist
, _CollisionCameraDist
);
164 if(UserEntity
->viewMode() == CUserEntity::FirstPV
|| _ForceFirstPersonView
)
167 UserEntity
->getHeadPos(headPos
);
172 // get the reverted view
174 v
.x
= -UserEntity
->front().x
;
175 v
.y
= -UserEntity
->front().y
;
179 return UserEntity
->pos() + CVector(0.0f
, 0.0f
, 2.0f
) - v
*minCamDist
;
184 if(UserEntity
->viewMode() == CUserEntity::FirstPV
|| _ForceFirstPersonView
)
185 return _ViewPos
+ CVector(0.0f
, 0.0f
, UserEntity
->eyesHeight());
187 return _ViewPos
+ CVector(0.0f
, 0.0f
, _CurrentCameraHeight
) - _View
*minCamDist
;
189 }// currentViewPos //
191 //-----------------------------------------------
193 //-----------------------------------------------
194 CVector
CView::currentView() const
199 v
.x
= -UserEntity
->front().x
;
200 v
.y
= -UserEntity
->front().y
;
209 NLMISC::CQuat
CView::currentViewQuat() const
212 mat
.setRot(CVector::I
, currentView(), CVector::K
);
213 mat
.normalize(CMatrix::YZX
);
217 //-----------------------------------------------
218 // currentCameraTarget :
219 //-----------------------------------------------
220 CVector
CView::currentCameraTarget() const
222 if(UserEntity
->viewMode() == CUserEntity::FirstPV
|| _ForceFirstPersonView
)
223 return currentViewPos();
228 return UserEntity
->pos() + CVector(0.0f
, 0.0f
, 2.0f
);
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
;
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
;
254 cameraDistance( std::min(ClientCfg
.CameraDistance
, _CurrentCameraDistanceMax
) );
258 //-----------------------------------------------
260 // Change the distance from the user to the camera.
261 //-----------------------------------------------
262 void CView::cameraDistance(float dist
)
265 if((dist
< ClientCfg
.CameraDistMin
) && (dist
<= ClientCfg
.CameraDistance
))
267 if (UserEntity
&& !UserEntity
->isDead())
268 UserEntity
->viewMode(CUserEntity::FirstPV
);
269 ClientCfg
.CameraDistance
= dist
;
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())
292 ClientCfg
.CameraHeight
+= 0.5f
;
296 ClientCfg
.CameraHeight
-= 0.2f
;
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())
313 decreaseCameraDist();
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
));
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
362 v
.x
= - UserEntity
->front().x
;
363 v
.y
= - UserEntity
->front().y
;
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
);
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
);
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
;
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
&&
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
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;
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
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();