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