Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
CharacterCameraADS.c
Go to the documentation of this file.
1// *************************************************************************************
2// ! CharacterCameraADS - Aim down sights camera
3// *************************************************************************************
4class CharacterCameraADS : CharacterCameraBase
5{
6 //------------------------------------------------------------------------------------------------
7 static const float CONST_UD_MIN = -89.0;
8 static const float CONST_UD_MAX = 89.0;
9
10 static const float CONST_LR_MIN = -160.0;
11 static const float CONST_LR_MAX = 160.0;
12
13 static const float CONST_TRANSLATIONZ_MIN = -0.1;
14
15 #ifdef ENABLE_DIAG
16 private static bool s_bDebugRegistered;
17 #endif
18
19 //------------------------------------------------------------------------------------------------
20 // constructor
22 void CharacterCameraADS(CameraHandlerComponent pCameraHandler)
23 {
24 m_iHandBoneIndex = m_OwnerCharacter.GetAnimation().GetBoneIndex("righthandprop");
25 m_fADSToFPSDeg = 45;
26 m_fFreelookFOV = GetBaseFOV();
27
28 m_WeaponManager = BaseWeaponManagerComponent.Cast(m_OwnerCharacter.FindComponent(BaseWeaponManagerComponent));
30 m_OffsetLS = "0.0 0.03 -0.07";
31
32 #ifdef ENABLE_DIAG
33 if (!s_bDebugRegistered)
34 {
35 DiagMenu.RegisterRange(SCR_DebugMenuID.DEBUGUI_CHARACTER_ADS_CAMERA, "", "ADS Camera", "Character", "0, 3, 0, 1");
36 s_bDebugRegistered = true;
37 }
38 #endif
39
40 m_iJumpAnimTagId = GameAnimationUtils.RegisterAnimationTag("TagFall");
41 }
42
43 //------------------------------------------------------------------------------------------------
44 override void OnDeactivate(ScriptedCameraItem pNextCamera)
45 {
47 }
48
49 //------------------------------------------------------------------------------------------------
50 override void OnBlendIn()
51 {
52 if (m_CameraHandler.IsCameraBlending())
53 return;
54
55 OnBlendingIn(1);
56 }
57
58 //------------------------------------------------------------------------------------------------
59 override void OnBlendOut()
60 {
61 if (m_CameraHandler.IsCameraBlending())
62 return;
63
65 }
66
67 //------------------------------------------------------------------------------------------------
68 protected float GetSightsADSActivationPercentage() // percentage of blend <0,1>
69 {
71 {
72 // Moved into BaseSightsComponent
73 // TODO@HF Find out how much overhead switching between C++/Script has
74 // Might want to check for ScriptedSightsComponent and use XXScript instead for performance
75 BaseSightsComponent optics = m_LastWeaponComponent.GetSights();
76 if (optics)
77 return optics.GetADSActivationPercentage();
78 }
79
80 return 1.0;
81 }
82
83 //------------------------------------------------------------------------------------------------
84 protected float GetSightsADSDeactivationPercentage() // percentage of blend <0,1>
85 {
87 {
88 // Moved into BaseSightsComponent
89 // TODO@HF Find out how much overhead switching between C++/Script has
90 // Might want to check for ScriptedSightsComponent and use XXScript instead for performance
91 BaseSightsComponent optics = m_LastWeaponComponent.GetSights();
92 if (optics)
93 return optics.GetADSDeactivationPercentage();
94 }
95
96 return 0.0;
97 }
98
99 //------------------------------------------------------------------------------------------------
100 protected void OnBlendingIn(float blendAlpha)
101 {
102 if (!m_WeaponManager)
103 return;
104
105 // The weapon may change at any moment
106 BaseWeaponComponent currentWeapon = m_WeaponManager.GetCurrentWeapon();
107 if (m_LastWeaponComponent && currentWeapon != m_LastWeaponComponent && m_LastWeaponComponent.IsSightADSActive())
108 m_LastWeaponComponent.SightADSDeactivated();
109
110 m_LastWeaponComponent = currentWeapon;
111
112 if (blendAlpha < GetSightsADSActivationPercentage())
113 return;
114
115 if (currentWeapon && !currentWeapon.IsSightADSActive())
116 currentWeapon.SightADSActivated();
117 }
118
119 //------------------------------------------------------------------------------------------------
120 protected void OnBlendingOut(float blendAlpha)
121 {
122 if (!m_WeaponManager)
123 return;
124
125 // The weapon may change at any moment
126 BaseWeaponComponent currentWeapon = m_WeaponManager.GetCurrentWeapon();
127 if (m_LastWeaponComponent && currentWeapon != m_LastWeaponComponent && m_LastWeaponComponent.IsSightADSActive())
128 m_LastWeaponComponent.SightADSDeactivated();
129
130 m_LastWeaponComponent = currentWeapon;
131
132 if (blendAlpha < GetSightsADSDeactivationPercentage())
133 return;
134
135 if (currentWeapon && currentWeapon.IsSightADSActive())
136 currentWeapon.SightADSDeactivated();
137
138 CameraManager cameraMgr = GetGame().GetCameraManager();
139 if (cameraMgr)
140 cameraMgr.SetOverlayCamera(null);
141 }
142
143 //------------------------------------------------------------------------------------------------
144 override void OnActivate(ScriptedCameraItem pPrevCamera, ScriptedCameraItemResult pPrevCameraResult)
145 {
146 super.OnActivate(pPrevCamera, pPrevCameraResult);
147 if (pPrevCamera)
148 {
149 vector f = pPrevCamera.GetBaseAngles();
150 m_fUpDownAngle = m_ControllerComponent.GetInputContext().GetAimingAngles()[1];
151 m_fLeftRightAngle = f[1];
152 }
153
154 m_fStabilizerAlpha = 0.0;
156 m_lastStablePos = "0 0 0";
157
158 // Reset entirety of sight blending data
160 m_bLastSightsBlend = false;
162
163 m_fProneBlend = 0.0;
164 m_fPronePistolBlend = 0.0;
165 }
166
167 //------------------------------------------------------------------------------------------------
168 protected void SolveCameraHeadAttach(ADSCameraData cameraData, out ScriptedCameraItemResult pOutResult)
169 {
171 float parentPitch = m_OwnerCharacter.GetLocalYawPitchRoll()[1];
172 cameraData.m_vLookAngles[1] = Math.Clamp(cameraData.m_vLookAngles[1] + parentPitch, CONST_UD_MIN, CONST_UD_MAX);
173
174 vector headMatrix[4];
175 m_OwnerCharacter.GetAnimation().GetBoneMatrix(GetCameraBoneIndex(), headMatrix);
176
178 vector sightsRelativeMatrix[4];
179 Math3D.MatrixInvMultiply4(headMatrix, cameraData.m_mSightsLocalMat, sightsRelativeMatrix);
180
182 float recoilPortion;
184 recoilPortion = Math.Clamp(m_AimingComponent.GetRawAimingTranslation()[2] * cameraData.m_fCamRecoilAmount, -CAMERA_RECOIL_LIMIT, CAMERA_RECOIL_LIMIT);
185
187 vector headPlane = cameraData.m_vSightsOffset;
188 headPlane[2] = headPlane[2] - recoilPortion;
189
191 Math3D.AnglesToMatrix(cameraData.m_vLookAngles, pOutResult.m_CameraTM);
192
194 vector resultPosition = SCR_Math3D.IntersectPlane(sightsRelativeMatrix[3], -sightsRelativeMatrix[2], headPlane, vector.Forward);
195
197 pOutResult.m_CameraTM[3] = resultPosition;
198 pOutResult.m_fFOV = cameraData.m_fFOV;
199 pOutResult.m_fDistance = 0;
200 pOutResult.m_fNearPlane = 0.0125;
201 pOutResult.m_bAllowInterpolation = true;
202 pOutResult.m_bUpdateWhenBlendOut = true;
203 pOutResult.m_iDirectBone = GetCameraBoneIndex();
204 pOutResult.m_iDirectBoneMode = EDirectBoneMode.RelativePosition;
205 }
206
210 protected void SolveNewMethod(ADSCameraData cameraData, out ScriptedCameraItemResult pOutResult, float pDt, bool allowInterpolation)
211 {
212 float fUseHeading = 1.0;
213
214 Animation anim = m_OwnerCharacter.GetAnimation();
215
216 // Right hand prop is where weapon is attached to
217 vector propBoneMS[4];
218 anim.GetBoneMatrix(m_iHandBoneIndex, propBoneMS);
219
220 bool bUseProneADSMethod = IsProneADS();
221 EMuzzleType muzzleType = EMuzzleType.MT_BaseMuzzle;
222
223 EWeaponType weaponType = EWeaponType.WT_NONE;
224
225 // UPDATE:
226 // This is quite silly considering what is done afterwards,
227 // but the point is that we have to fight with zeroing, as it is
228 // the only relevant aim modifier that we do not want to offset in
229 // the camera like we do with recoil or sway
230 BaseWeaponComponent currentWeapon = m_WeaponManager.GetCurrent();
231 if (currentWeapon)
232 {
233 muzzleType = currentWeapon.GetCurrentMuzzle().GetMuzzleType();
234 if (muzzleType == EMuzzleType.MT_UGLMuzzle || m_LastMuzzleType == EMuzzleType.MT_UGLMuzzle) // HACK: UGL isn't aligned in prone stance without this.
235 bUseProneADSMethod = false;
236
237 vector zeroingLS[4];
238 if (m_WeaponManager.GetCurrentWeapon().GetCurrentSightsZeroingTransform(zeroingLS))
239 Math3D.MatrixMultiply4(cameraData.m_mSightsLocalMat, zeroingLS, cameraData.m_mSightsLocalMat);
240
241 if (bUseProneADSMethod)
242 {
243 // Align camera vertically
244 vector charMat[4];
245 m_CharacterAnimationComponent.PhysicsGetTransformLS(charMat);
246 vector camUpMS = vector.Up.InvMultiply3(charMat);
247 cameraData.m_mSightsLocalMat[0] = camUpMS * cameraData.m_mSightsLocalMat[2];
248 cameraData.m_mSightsLocalMat[1] = cameraData.m_mSightsLocalMat[2] * cameraData.m_mSightsLocalMat[0];
249 }
250
251 weaponType = currentWeapon.GetWeaponType();
252 }
253
254 // Get sights relative to right hand prop bone
255 vector sightsMS[4];
256 Math3D.MatrixInvMultiply4(propBoneMS, cameraData.m_mSightsLocalMat, sightsMS);
257
258 float targetFOV = cameraData.m_fFOV;
259
260 // Sights interpolation
261 {
262 // On sights change store stable last transform
263 BaseSightsComponent currentSights = m_WeaponManager.GetCurrentSights();
264
265 // Just initialize to something in such case.
266 if (!m_LastSightsComponent && currentSights)
267 {
268 Math3D.MatrixCopy(sightsMS, m_vLastSightMS);
269 Math3D.MatrixCopy(sightsMS, m_vLastSightStMS);
270 // And in such cases, we have no reasonable source, so just
271 // disable all the blending
272 m_LastSightsComponent = currentSights;
273 m_bLastSightsBlend = false;
275 m_LastMuzzleType = muzzleType;
276 }
277 else if (m_LastSightsComponent != currentSights) // Use last stored transform if possible
278 {
281 m_LastSightsComponent = currentSights;
282 m_bLastSightsBlend = true;
284 }
285
286 // Now during the blend, calculate alpha and apply some
287 // arbitrary blend dark magic
289 {
290 // No interpolation in such cases
292 {
294 m_bLastSightsBlend = false;
295 }
296 else
297 {
299 // Terminate the blend, resetting the value.
300 if (alpha >= 1.0)
301 {
303 m_bLastSightsBlend = false;
304 m_LastMuzzleType = muzzleType;
305 }
306 else
307 {
308 vector interpBuffer[4]; // result transformation matrix
309
310 // Last->target rotation blend
311 // quaternion used for that nice quaternion interpolation that's not achievable with vectors
312 float qb[4], qt[4];
313 Math3D.MatrixToQuat(m_vLastSightStMS, qb); // start rotation+buffer
314 Math3D.MatrixToQuat(sightsMS, qt); // target rotation
315 Math3D.QuatLerp(qb, qb, qt, alpha);
316 Math3D.QuatToMatrix(qb, interpBuffer);
317
318 // Last->target position linear blend
319 interpBuffer[3] = vector.Lerp(m_vLastSightStMS[3], sightsMS[3], alpha);
320
321 // Apply the transformation
322 Math3D.MatrixCopy(interpBuffer, sightsMS);
323 targetFOV = Math.Lerp(m_fLastSightStFOV, cameraData.m_fFOV, alpha);
324
325 // Update time
327 }
328 }
329 }
330 }
331
332 // Store last transform (unstable)
333 Math3D.MatrixCopy(sightsMS, m_vLastSightMS);
334 // And last field of view
335 m_fLastSightFOV = targetFOV;
336
337 // Get sights to character MS
338 Math3D.MatrixMultiply4(propBoneMS, sightsMS, sightsMS);
339
340 // We have sights in right hand prop hand character MS..
341 vector cameraBoneMS[4];
342 anim.GetBoneMatrix(GetCameraBoneIndex(), cameraBoneMS);
343
344 // If last stable position is uninitialized, there are few cases
345 // where the interpolation will start at the character origin, creating
346 // a very visually unpleasing jump/stutter. Using head bone transformation
347 // should be at least somewhat relevant and should reduce this issue significantly.
348 if (m_lastStablePos == vector.Zero)
349 {
350 m_lastStablePos = cameraBoneMS[3];
351 }
352
353 // Now let us project the position onto the head in MS
354 // rayOrigin:
355 // sights --> sights back (towards head)
356 // planeOrigin:
357 // head --> forward ( head.z )
358
359 vector pureSightsFwd = sightsMS[2];
360
361 vector projectedPosMS = SCR_Math3D.IntersectPlane(sightsMS[3], -sightsMS[2], cameraBoneMS[3], cameraBoneMS[2]);
362 vector projToSightMS = projectedPosMS - sightsMS[3];
363 vector projToSightsDirMS = projToSightMS.Normalized();
364
365 // Let's negate translation from modifiers - this will keep our camera
366 // where it ought to be, but allow weapon to move independently of camera
367 vector aimingTranslationWeaponLS = m_AimingComponent.GetRawAimingTranslation();
368 //float zOffset = Math.Clamp(aimingTranslationWeaponLS[2], -CAMERA_RECOIL_LIMIT, CAMERA_RECOIL_LIMIT)
369
370 vector aimingTranslationMS;
371 for (int i = 0; i < 3; i++)
372 {
373 aimingTranslationMS += aimingTranslationWeaponLS[i] * sightsMS[i];
374 }
375
376 // And add additional translation, this time desired amount of Z translation
378 float recoilPortion;
380 recoilPortion = Math.Clamp(aimingTranslationWeaponLS[2] * cameraData.m_fCamRecoilAmount, -CAMERA_RECOIL_LIMIT, CAMERA_RECOIL_LIMIT);
381 vector extraTranslation = recoilPortion * sightsMS[2];
382
383 // And add that to our result
384 sightsMS[3] = sightsMS[3] - aimingTranslationMS - extraTranslation;
385
386 // Now we will disregard any previous rotation... (LOL and use aiming or freelook angles directly)
387
388 vector aimingAnglesMS = cameraData.m_vLookAngles;
389 aimingAnglesMS[1] = aimingAnglesMS[1] + m_OwnerCharacter.GetLocalYawPitchRoll()[1];
390
391 if (m_CharacterAnimationComponent.PhysicsIsLinked())
392 {
393 fUseHeading = 0;
394 aimingAnglesMS[0] = aimingAnglesMS[0] + m_OwnerCharacter.GetAimRotationModel()[0] * Math.RAD2DEG;
395
396 if (IsProneStance())
397 {
398 aimingAnglesMS[1] = aimingAnglesMS[1] - m_OwnerCharacter.GetYawPitchRoll()[1];
399 aimingAnglesMS[2] = aimingAnglesMS[2] - m_OwnerCharacter.GetYawPitchRoll()[2];
400 }
401 }
402
403 // Blend ADS on pistol when prone (otherwise camera jumps around)
404 if (weaponType == EWeaponType.WT_HANDGUN && bUseProneADSMethod) // is the weapon a handgun && is player proned
405 {
406 if (m_fPronePistolBlend < 1.0) // has blend has not finished
407 {
408 if (!m_OwnerCharacter.GetCharacterController().IsWeaponRaised() || m_fPronePistolBlend > 0.0) // is the weapon not fully raised
409 {
410 const float cameraVerticalOffset = 0.2; // slight offset so camera height matches the final camera height
411 m_fPronePistolBlend = Math.Clamp(m_fPronePistolBlend + pDt*2, 0.0, 1.0); // sped up to match animation speed
412 sightsMS[3][1] = Math.Lerp(cameraBoneMS[3][1] + cameraVerticalOffset, sightsMS[3][1], m_fPronePistolBlend);
413 }
414 }
415 }
416
417 // Use as intended
418 Math3D.AnglesToMatrix(aimingAnglesMS, sightsMS);
419
420 const float alphaEpsilon = 0.0005;
421 // Stabilize camera in certain cases
422 vector resultPosition = sightsMS[3];
423 bool isUnstable = false;
424 if (m_CmdHandler)
425 {
426 isUnstable = m_ControllerComponent.IsSprinting() ||
427 m_CmdHandler.GetTargetLadder() != null ||
428 m_ControllerComponent.IsMeleeAttack() ||
429 m_CmdHandler.GetCommandModifier_ItemChange() && m_CmdHandler.GetCommandModifier_ItemChange().IsChangingItemTag() ||
430 m_CmdHandler.IsProneStanceTransition() && m_ControllerComponent.GetMovementVelocity().LengthSq() > 0.0 ||
431 (!m_WeaponManager || !m_WeaponManager.GetCurrentSights());
432 }
433
434 CharacterAnimationComponent animComponent = m_ControllerComponent.GetAnimationComponent();
435
436 // Jump context is reset, so instead check for animation tag presence
437 // and make camera unstable during this period as hand movement is unpredictable
438 if (m_iJumpAnimTagId != -1)
439 {
440 if (animComponent && animComponent.IsPrimaryTag(m_iJumpAnimTagId))
441 isUnstable = true;
442 }
443
444 // In transitions out of ADS override stable position and use that instead,
445 // this will prevent some quirks as we expect weapon pose in ADS to be quite
446 // straight forward, which might not be the case during transitions
447 if (sm_TagADSTransitionOut != -1)
448 {
449 if (animComponent)
450 {
451 // In transitions use camera transform directly
452 if (animComponent.IsPrimaryTag(sm_TagADSTransitionOut))
453 {
454 // Unless both tags are active, at which case we just try to somehow stabilize
455 // the result, otherwise we would get jitter again
456 if (sm_TagADSTransitionIn != -1 && animComponent.IsPrimaryTag(sm_TagADSTransitionIn))
457 anim.GetBoneMatrix(sm_iCameraBoneIndex, cameraBoneMS);
458
459 m_lastStablePos = cameraBoneMS[3];
460 isUnstable = true;
461 }
462 }
463 }
464
465 if (isUnstable)
466 {
467 m_fStabilizerAlpha = Math.SmoothCD(m_fStabilizerAlpha, 1.0, m_fStabilizerAlphaVel, 0.1, 1000, pDt);
468 resultPosition = m_lastStablePos;
469 }
470 else
471 {
472 m_fStabilizerAlpha = Math.SmoothCD(m_fStabilizerAlpha, 0.0, m_fStabilizerAlphaVel, 0.1, 1000, pDt);
473 }
474
475 bool shouldStabilize = m_fStabilizerAlpha > alphaEpsilon;
476 if (shouldStabilize)
477 {
478 resultPosition = vector.Lerp(resultPosition, cameraBoneMS[3], m_fStabilizerAlpha);
479 }
480
481 // Last but not least, blend FOV based on aiming vs. freelook angles
482 // If in freelook or not fully blended out yet, update
483 if (cameraData.m_bFreeLook || m_fFreelookBlendAlpha > alphaEpsilon)
484 {
485 vector weaponAimingDir = m_AimingComponent.GetAimingRotation().AnglesToVector();
486 vector lookAimingDir = aimingAnglesMS.AnglesToVector();
487 float blendAlpha = 1.0 - (vector.Dot(weaponAimingDir, lookAimingDir) + 1.0) * 0.5;
488 const float blendOutSpeed = 3.0; // Higher values reduce the radius range of blend alpha, lower values extend further
489
490 if (cameraData.m_bFreeLook)
491 m_fFreelookBlendAlpha = Math.Clamp(Math.Sqrt(blendAlpha * blendOutSpeed), 0.0, 1.0);
492 else m_fFreelookBlendAlpha -= pDt * 5.0; // if out of freelook, make sure to blend out, in certain cases the epsilon can never be logically hit
493
494 if (m_fFreelookBlendAlpha <= alphaEpsilon)
496
497 vector mat[4];
498 vector offset = m_OffsetLS;
499 vector additiveRotation = "0 0 0";
500 m_CharacterHeadAimingComponent.GetLookTransformationLS(GetCameraBoneIndex(), EDirectBoneMode.RelativePosition, offset, additiveRotation, mat);
501 Math3D.MatrixMultiply4(cameraBoneMS, mat, mat);
502 vector goal = mat[3] + "0 0.035 -0.045"; // arbitrary offset to reduce the vnh/p (visible neck hole per pixel)
503 resultPosition = vector.Lerp(resultPosition, goal, m_fFreelookBlendAlpha);
504 }
505
506 // If the result position is behind the camera bone, the camera might collide with the chest, so we move it forward. (NO LONGER USED)
507 /*float resultNegativeZ = resultPosition[2] - cameraBoneMS[3][2];
508 if (resultNegativeZ < 0)
509 {
510 sightsMS[3] = resultPosition + (-resultNegativeZ * pureSightsFwd[2] * pureSightsFwd);
511 }
512 else
513 {
514 sightsMS[3] = resultPosition;
515 }*/
516
517 sightsMS[3] = resultPosition;
518
519 pOutResult.m_iDirectBone = m_iHandBoneIndex;
520 pOutResult.m_iDirectBoneMode = EDirectBoneMode.RelativeTransform;
521
522 // Get transformation to parent
523 if (!shouldStabilize)
524 {
525 Math3D.MatrixInvMultiply4(propBoneMS, sightsMS, pOutResult.m_CameraTM);
526 }
527 else
528 {
529 // Order of operations in hierarchical updates and the way camera interpolation
530 // works has forced my hand to recompute all of this into the prop bone (hand) space
531 // so we can ensure that we don't reparent the camera along the way, there still might
532 // be a tiny bit of grain, but this section has already caused enough of pain
533 pOutResult.m_CameraTM[3] = sightsMS[3];
534 Math3D.AnglesToMatrix(aimingAnglesMS, pOutResult.m_CameraTM);
535 vector directTM[4];
536 m_OwnerCharacter.GetAnimation().GetBoneMatrix(pOutResult.m_iDirectBone, directTM);
537 Math3D.MatrixInvMultiply4(directTM, pOutResult.m_CameraTM, pOutResult.m_CameraTM);
538 }
539
540 pOutResult.m_fFOV = Math.Lerp(targetFOV, GetBaseFOV(), m_fFreelookBlendAlpha);
541 pOutResult.m_fDistance = 0;
542 pOutResult.m_fNearPlane = 0.0125;
543 pOutResult.m_bAllowInterpolation = allowInterpolation;// && (shouldStabilize == m_bWasStabilizedLastFrame);
544 pOutResult.m_fUseHeading = fUseHeading;
545 pOutResult.m_bUpdateWhenBlendOut = true;
546 pOutResult.m_fPositionModelSpace = 0;
547
548 m_bWasStabilizedLastFrame = shouldStabilize;
549 }
550
551 //------------------------------------------------------------------------------------------------
552 protected void SolveCameraHandAttach(ADSCameraData cameraData, out ScriptedCameraItemResult pOutResult, float pDt, bool allowInterpolation)
553 {
554 Animation anim = m_OwnerCharacter.GetAnimation();
555
557 float parentPitch = m_OwnerCharacter.GetLocalYawPitchRoll()[1];
558 cameraData.m_vLookAngles[1] = Math.Clamp(cameraData.m_vLookAngles[1] + parentPitch, CONST_UD_MIN, CONST_UD_MAX);
559
560 vector headMatrix[4];
561 anim.GetBoneMatrix(GetCameraBoneIndex(), headMatrix);
562
564 vector sightsRelativeMatrix[4];
565 Math3D.MatrixInvMultiply4(headMatrix, cameraData.m_mSightsLocalMat, sightsRelativeMatrix);
566
568 float recoilPortion;
570 recoilPortion = Math.Clamp(m_AimingComponent.GetRawAimingTranslation()[2] * cameraData.m_fCamRecoilAmount, -CAMERA_RECOIL_LIMIT, CAMERA_RECOIL_LIMIT);
571
573 vector headPlane = cameraData.m_vSightsOffset;
574 headPlane[2] = headPlane[2] - recoilPortion;
575
577 vector resultPosition = SCR_Math3D.IntersectPlane(sightsRelativeMatrix[3], -sightsRelativeMatrix[2], headPlane, vector.Forward);
578
579 // transform relative to hand
580 vector handMatrix[4];
581 anim.GetBoneMatrix(m_iHandBoneIndex, handMatrix);
582
583 // Get sights relative to anchor
584 vector relativeSightsMatrix[4];
585 // Sight transform relative to head bone
586 Math3D.MatrixInvMultiply4(handMatrix, cameraData.m_mSightsLocalMat, relativeSightsMatrix);
587
588 // Prepare and apply rotation matrix
589 Math3D.AnglesToMatrix(cameraData.m_vLookAngles, pOutResult.m_CameraTM);
590
591 bool isUnstable = false;
592 if (m_CmdHandler)
593 {
594 isUnstable = m_ControllerComponent.IsSprinting() ||
595 m_CmdHandler.GetTargetLadder() != null ||
596 m_ControllerComponent.IsMeleeAttack() ||
597 m_CmdHandler.GetCommandModifier_ItemChange() && m_CmdHandler.GetCommandModifier_ItemChange().IsChangingItemTag();
598 }
599
600 if (isUnstable)
601 {
602 m_fStabilizerAlpha = Math.SmoothCD(m_fStabilizerAlpha, 1.0, m_fStabilizerAlphaVel, 0.14, 1000, pDt);
603 resultPosition = m_lastStablePos;
604 }
605 else
606 {
607 m_fStabilizerAlpha = Math.SmoothCD(m_fStabilizerAlpha, 0.0, m_fStabilizerAlphaVel, 0.14, 1000, pDt);
608 }
609
610 float obstructedAlpha = m_ControllerComponent.GetObstructionAlpha();
611
612 if (m_fStabilizerAlpha > 0 || obstructedAlpha > 0)
613 {
614 float t = Math.Max(m_fStabilizerAlpha, obstructedAlpha);
615 vector headRelativePosition = "0 0 0";
616 resultPosition = vector.Lerp(resultPosition, headRelativePosition, t);
617 }
618
619 if (!isUnstable && obstructedAlpha < 0.001)
620 {
621 m_lastStablePos = resultPosition;
622 }
623
625 resultPosition = resultPosition.Multiply4(headMatrix);
626 resultPosition = resultPosition.InvMultiply4(handMatrix);
627
629 pOutResult.m_CameraTM[3] = resultPosition;
630 pOutResult.m_fFOV = cameraData.m_fFOV;
631 pOutResult.m_fDistance = 0;
632 pOutResult.m_fNearPlane = 0.0125;
633 pOutResult.m_bAllowInterpolation = allowInterpolation;
634 pOutResult.m_bUpdateWhenBlendOut = true;
635 pOutResult.m_iDirectBone = m_iHandBoneIndex;
636 pOutResult.m_iDirectBoneMode = EDirectBoneMode.RelativePosition;
637 pOutResult.m_fPositionModelSpace = 0.0;
638 }
639
640 //------------------------------------------------------------------------------------------------
641 protected void SolveCamera2DSight(ADSCameraData cameraData, float pDt, out ScriptedCameraItemResult pOutResult)
642 {
643 // TODO: Please make camera position consistent with 2DPIPSights m_vCameraPoint
644
645 // TODO@AS: refactor
646 SCR_2DOpticsComponent sights2D = SCR_2DOpticsComponent.Cast(m_WeaponManager.GetCurrentSights());
647 BaseWeaponComponent weapon = m_WeaponManager.GetCurrentWeapon();
648
649 // The camera position shall match the position and angle of camera with PIP scope
650 vector cameraAngles;
651 if (sights2D && weapon && weapon.IsSightADSActive())
652 {
653 IEntity sightOwner = sights2D.GetOwner();
654 SCR_2DOpticsComponentClass sightData = SCR_2DOpticsComponentClass.Cast(sights2D.GetComponentData(sightOwner));
655 vector sightsOffset = sights2D.GetSightsFrontPosition(true) + sightData.GetCameraOffset() - sights2D.GetSightsOffset();
656 vector cameraOffset = sightsOffset.Multiply3(cameraData.m_mSightsLocalMat);
657 cameraData.m_mSightsLocalMat[3] = cameraData.m_mSightsLocalMat[3] + cameraOffset;
658
659 // Get world orientation of sight
660 // Turrets require getting owner transform to obtain the world transform of the sight reliably
661 vector sightMat[4];
662 sights2D.GetSightsTransform(sightMat, true);
663
664 vector ownerMat[4];
665 sightOwner.GetWorldTransform(ownerMat);
666 Math3D.MatrixMultiply3(ownerMat, sightMat, sightMat);
667
668 // Get optic transformation in world coordinates
669 vector opticMat[4];
670 sights2D.GetCameraLocalTransform(opticMat);
671 Math3D.MatrixMultiply3(ownerMat, opticMat, opticMat);
672
673 // Substract optic transformation
674 vector cameraMat[4];
675 Math3D.MatrixInvMultiply3(opticMat, sightMat, cameraMat);
676
677 cameraAngles = Math3D.MatrixToAngles(cameraMat);
678 }
679
680 SCR_2DPIPSightsComponent sightsPIP = SCR_2DPIPSightsComponent.Cast(sights2D);
681 if (sightsPIP)
682 cameraData.m_fFOV = sightsPIP.GetMainCameraFOV();
683
685 pOutResult.m_fFOV = Math.Min(GetBaseFOV(), cameraData.m_fFOV);
686
687 // Add recoil and sway, reduced by FOV for convenience
688 vector adjustedLookAngles = cameraData.m_vLookAngles;
689
690 bool bProneStance = IsProneStance();
691
692 AimingComponent aiming = m_OwnerCharacter.GetWeaponAimingComponent();
693 if (aiming)
694 {
695 if (m_ControllerComponent.GetIsWeaponDeployed())
696 {
697 if (!bProneStance)
698 adjustedLookAngles = aiming.GetAimingRotation();
699 }
700
701 // Arbitrary conversion of aiming translation to recoil angle based on current FOV
702 vector offset = aiming.GetModifiedAimingTranslation() * pOutResult.m_fFOV;
703 adjustedLookAngles = adjustedLookAngles + Vector(offset[1], offset[2], 0);
704 }
705
706 // we start clean
707 vector lookRot[4];
708
709 adjustedLookAngles = adjustedLookAngles - cameraAngles;
710 Math3D.AnglesToMatrix(adjustedLookAngles, lookRot);
711
712 // snap to bone
713 vector handBoneTM[4];
714 m_OwnerCharacter.GetAnimation().GetBoneMatrix(m_iHandBoneIndex, handBoneTM);
715
717 Math3D.MatrixInvMultiply4(handBoneTM, cameraData.m_mSightsLocalMat, pOutResult.m_CameraTM);
718 vector finalPos = pOutResult.m_CameraTM[3];
719
720 bool bUseProneADSMethod = bProneStance && !m_CmdHandler.IsProneStanceTransition() && !m_pCameraData.m_bFreeLook;
721 if (bUseProneADSMethod)
722 {
723 // Align camera vertically
724 vector charMat[4];
725 m_OwnerCharacter.GetLocalTransform(charMat);
726 vector camUpMS = vector.Up.InvMultiply3(charMat);
727 lookRot[0] = camUpMS * lookRot[2];
728 lookRot[1] = lookRot[2] * lookRot[0];
729
730 // Blend-in prone ADS
731 m_fProneBlend = Math.Clamp(m_fProneBlend + 3.0 * pDt, 0.0, 1.0);
732
733 vector adjustedLookAnglesProne = Math3D.MatrixToAngles(lookRot);
734 adjustedLookAngles = vector.Lerp(adjustedLookAngles, adjustedLookAnglesProne, m_fProneBlend);
735 Math3D.AnglesToMatrix(adjustedLookAngles, lookRot);
736
737 // No orientation interpolation while in prone
738 pOutResult.m_bInterpolateOrientation = false;
739 if (m_fProneBlend < 1.0)
740 pOutResult.m_bInterpolateOrientation = true;
741 }
742
744 if (!m_ControllerComponent.GetIsWeaponDeployed())
745 Math3D.MatrixInvMultiply4(handBoneTM, lookRot, pOutResult.m_CameraTM);
746
748 pOutResult.m_CameraTM[3] = finalPos;
749
751 pOutResult.m_iDirectBone = m_iHandBoneIndex;
752 pOutResult.m_iDirectBoneMode = EDirectBoneMode.RelativeTransform;
753 pOutResult.m_bUpdateWhenBlendOut = true; // otherwise camera stops blending out properly
754 pOutResult.m_fDistance = 0;
755 pOutResult.m_fUseHeading = 0;
756 pOutResult.m_fNearPlane = 0.025;
757 pOutResult.m_bBlendFOV = true; // otherwise FOV blend transitions awkwardly
758 }
759
760 //------------------------------------------------------------------------------------------------
761 override void OnUpdate(float pDt, out ScriptedCameraItemResult pOutResult)
762 {
763 if (m_CameraHandler && m_CameraHandler.IsCameraBlending())
764 {
765 if (m_CameraHandler.GetCurrentCamera() == this)
766 OnBlendingIn(m_CameraHandler.GetBlendAlpha(this));
767 else
768 OnBlendingOut(m_CameraHandler.GetBlendAlpha(m_CameraHandler.GetCurrentCamera()));
769 }
770
772 SightsComponent sights = m_WeaponManager.GetCurrentSights();
773 bool canFreelook = sights && sights.CanFreelook();
775 m_fFreelookFOV = GetBaseFOV();
776
777 pOutResult.m_vBaseAngles = GetBaseAngles();
778
780 vector lookAngles = m_CharacterHeadAimingComponent.GetLookAngles();
781 if (!canFreelook)
782 {
783 lookAngles[0] = m_CommandWeapons.GetAimAngleLR();
784 lookAngles[1] = m_CommandWeapons.GetAimAngleUD();
785 }
786
788 m_pCameraData.m_fDeltaTime = pDt;
789 m_pCameraData.m_vLookAngles = lookAngles;
790 m_pCameraData.m_fFOV = GetBaseFOV();
791
793 if (sights)
794 {
795 m_WeaponManager.GetCurrentSightsCameraTransform(m_pCameraData.m_mSightsLocalMat, m_pCameraData.m_fFOV);
796 m_pCameraData.m_vSightsOffset = sights.GetSightsOffset();
797 m_pCameraData.m_fCamRecoilAmount = sights.GetCameraRecoilAmount();
798 }
799 else
800 {
801 Math3D.MatrixIdentity4(m_pCameraData.m_mSightsLocalMat);
802 }
803
805 m_pCameraData.m_fFOV = Math.Min(GetBaseFOV(), m_pCameraData.m_fFOV);
806
808 // Apparently in rare cases like bandaging, weapon can be missing
809 BaseWeaponComponent currentWeapon = m_WeaponManager.GetCurrent();
810 if (currentWeapon)
811 {
812 vector zeroingMatrix[4];
813 if (currentWeapon.GetCurrentSightsZeroingTransform(zeroingMatrix))
814 {
815 // add zeroing to our initial local sights
816 zeroingMatrix[3] = -zeroingMatrix[3];
817 Math3D.MatrixMultiply4(m_pCameraData.m_mSightsLocalMat, zeroingMatrix, m_pCameraData.m_mSightsLocalMat);
818 }
819 }
820
821 // 2nd camera -> provided by sights if any
822 CameraBase overlayCamera;
823
824 //special handling for FOV blending for 2d sights
825 //special handling for eye snapping too
826 SCR_2DPIPSightsComponent pip = SCR_2DPIPSightsComponent.Cast(sights);
827 SCR_2DOpticsComponent sights2d = SCR_2DOpticsComponent.Cast(sights);
828 if (sights2d)
829 {
830 // 2D sights
831 if (!pip || SCR_Global.IsScope2DEnabled())
832 {
833 SolveCamera2DSight(m_pCameraData, pDt, pOutResult);
834 pOutResult.m_pOwner = m_OwnerCharacter;
835 pOutResult.m_pWSAttachmentReference = null;
836 return;
837 }
838
839 // Camera FOV to be used is different from the sights FOV
840 m_pCameraData.m_fFOV = pip.GetMainCameraFOV();
841 overlayCamera = pip.GetPIPCamera();
842 }
843
844 if (!canFreelook)
845 {
846 m_pCameraData.m_vLookAngles[0] = 0.0;
847 }
848
849 // Store freelook state
850 m_pCameraData.m_bFreeLook = canFreelook && (m_ControllerComponent.IsFreeLookEnabled() || m_bForceFreeLook || m_ControllerComponent.IsTrackIREnabled());
851
852 #ifdef ENABLE_DIAG
853 int solveMethod = DiagMenu.GetValue(SCR_DebugMenuID.DEBUGUI_CHARACTER_ADS_CAMERA);
854
855 if (solveMethod == 1)
856 SolveCameraHandAttach(m_pCameraData, pOutResult, pDt, false);
857 else if (solveMethod == 2)
859 else if (solveMethod == 3)
860 SolveCameraHandAttach(m_pCameraData, pOutResult, pDt, true);
861 else // :-)
862 SolveNewMethod(m_pCameraData, pOutResult, pDt, true);
863 #else
864 SolveNewMethod(m_pCameraData, pOutResult, pDt, true);
865 #endif
866
867 CameraManager cameraMgr = GetGame().GetCameraManager();
868 if (cameraMgr != null)
869 {
870 // Update overlay camera
871 if (m_pCameraData.m_bFreeLook)
872 {
873 // Supress overlay cam
874 cameraMgr.SetOverlayCamera(null);
875 }
876 else if (overlayCamera)
877 {
878 // Override overlay comera if any is used
879 cameraMgr.SetOverlayCamera(overlayCamera);
880 }
881 }
882
883 pOutResult.m_pOwner = m_OwnerCharacter;
884 pOutResult.m_pWSAttachmentReference = null;
885
886 // Apply shake
888 m_CharacterCameraHandler.AddShakeToToTransform(pOutResult.m_CameraTM, pOutResult.m_fFOV);
889
890 if (!pip)
891 return;
892
893 vector offset = pip.GetMainCameraOffset();
894 pOutResult.m_CameraTM[3] = pOutResult.m_CameraTM[3] - offset;
895 }
896
898 {
899 CharacterAnimationComponent animComponent = m_ControllerComponent.GetAnimationComponent();
900 if (animComponent)
901 {
902 CharacterMovementState charMovementState = new CharacterMovementState();
903 animComponent.GetMovementState(charMovementState);
904 return charMovementState.m_iStanceIdx == ECharacterStance.PRONE;
905 }
906
907 return false;
908 }
909
911 {
912 return IsProneStance() && !m_CmdHandler.IsProneStanceTransition() && !m_pCameraData.m_bFreeLook
913 && m_CameraHandler.GetCurrentCamera() == this;
914 }
915
916 //------------------------------------------------------------------------------------------------
917 protected BaseWeaponManagerComponent m_WeaponManager;
921 protected BaseSightsComponent m_LastSightsComponent; // last used weapon sights or null if none
922 protected vector m_vLastSightMS[4]; // last 'unstable' sight transform
923 protected vector m_vLastSightStMS[4]; // last 'stable' sight transform
924 protected float m_fLastSightFOV; // last 'unstable' sight field of view
925 protected float m_fLastSightStFOV; // last 'stable' sight field of view
926 protected bool m_bLastSightsBlend; // whether sights are being blended, don't set manually
927 protected float m_fLastSightsBlendTime; // current value of blend, don't set manually
928 protected float m_fLastSightsBlendDuration = 0.15; // duration of blend in seconds, just a bit is good enough
930
931 protected int m_iHandBoneIndex;
932 protected int m_iHeadBoneIndex;
934 protected vector m_OffsetLS;
935 protected float m_fADSToFPSDeg;
936 protected float m_fFreelookFOV;
938
939 protected float m_fStabilizerAlpha;
940 protected float m_fStabilizerAlphaVel;
941
943
944 protected float m_fFreelookBlendAlpha;
945
946 private vector m_vLastEndPos;
947
948 private EMuzzleType m_LastMuzzleType = EMuzzleType.MT_BaseMuzzle;
949 private float m_fProneBlend = 1.0;
950 private float m_fPronePistolBlend = 1.0;
951
952 protected const float CAMERA_INTERP = 0.6;
953 protected const float CAMERA_RECOIL_LIMIT = 0.25;
954}
int AnimationTagID
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
ArmaReforgerScripted GetGame()
Definition game.c:1398
override void OnDeactivate()
EDirectBoneMode
Holds data for ADS camera which can be reused for different solving methods.
float GetSightsADSDeactivationPercentage()
void SolveCameraHandAttach(ADSCameraData cameraData, out ScriptedCameraItemResult pOutResult, float pDt, bool allowInterpolation)
void SolveCamera2DSight(ADSCameraData cameraData, float pDt, out ScriptedCameraItemResult pOutResult)
BaseSightsComponent m_LastSightsComponent
float m_fADSToFPSDeg
freelook degrees for transitioning into fps pos
override void OnUpdate(float pDt, out ScriptedCameraItemResult pOutResult)
void OnBlendingIn(float blendAlpha)
AnimationTagID m_iJumpAnimTagId
BaseWeaponManagerComponent m_WeaponManager
void SolveNewMethod(ADSCameraData cameraData, out ScriptedCameraItemResult pOutResult, float pDt, bool allowInterpolation)
float GetSightsADSActivationPercentage()
override void OnActivate(ScriptedCameraItem pPrevCamera, ScriptedCameraItemResult pPrevCameraResult)
void OnBlendingOut(float blendAlpha)
int m_iHandBoneIndex
hand bone
CharacterAimingComponent m_AimingComponent
void SolveCameraHeadAttach(ADSCameraData cameraData, out ScriptedCameraItemResult pOutResult)
ref ADSCameraData m_pCameraData
vector m_OffsetLS
position offset
const float CAMERA_RECOIL_LIMIT
Maximum amount of recoil applied to camera from weapon in meters.
BaseSightsComponent m_BinocularSight
BaseWeaponComponent m_LastWeaponComponent
int m_iHeadBoneIndex
head bone
static AnimationTagID sm_TagADSTransitionIn
float m_fUpDownAngle
runtime values
CharacterCommandWeapon m_CommandWeapons
SCR_CharacterControllerComponent m_ControllerComponent
float m_fLeftRightAngle
left right angle in rad
CharacterAnimationComponent m_CharacterAnimationComponent
CharacterHeadAimingComponent m_CharacterHeadAimingComponent
ChimeraCharacter m_OwnerCharacter
SCR_CharacterCameraHandlerComponent m_CharacterCameraHandler
CharacterCommandHandlerComponent m_CmdHandler
override vector GetBaseAngles()
static AnimationTagID sm_TagADSTransitionOut
static TNodeId sm_iCameraBoneIndex
Diagnostic and developer menu system.
Definition DiagMenu.c:18
proto external void GetWorldTransform(out vector mat[])
See IEntity::GetTransform.
Definition Math.c:13
IEntity GetCameraLocalTransform(out vector matrix[4])
Contains various scripted 3D math functions.
Definition SCR_Math3D.c:3
CameraHandlerComponent m_CameraHandler
data
ECharacterStance
proto native vector Vector(float x, float y, float z)
EWeaponType
Definition EWeaponType.c:13
EMuzzleType
Definition EMuzzleType.c:13