Skip to content

Commit

Permalink
Handle case where a mirror is ON:
Browse files Browse the repository at this point in the history
- Make object state after rendering camera more consistent:
  - When first-person camera finishes rendering:
    - rescale head back to one.
    - make first-person head shadow invisible.
  - This fixes an issue where having a mirror turned on would cause the head to be scaled to zero, which was messing the head shadow processing on the next frame.
- Defensively protect access to the HeadShadowDriver field, which is null for a short time window on application load, but non-null of the time.
  • Loading branch information
hai-vr committed Dec 27, 2024
1 parent 3c6ea61 commit fa7cfcf
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ private void OnBeforeRender()
{
BasisLocalPlayer.Instance.LocalBoneDriver.SimulateAndApply(Time.timeAsDouble,Time.deltaTime);
AfterAvatarChanges?.Invoke();
BasisLocalPlayer.Instance.HeadShadowDriver.PrepareThisFrame();

// Defensive check, this only seems to be null when the first avatar is not loaded yet.
// May be fixable by updating the prefab so that it's always defined by default.
if (BasisLocalPlayer.Instance.HeadShadowDriver)
BasisLocalPlayer.Instance.HeadShadowDriver.PrepareThisFrame();
}

public void Update()
Expand Down
40 changes: 38 additions & 2 deletions Basis/Packages/Basis Framework/Drivers/BasisLocalCameraDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public void OnEnable()
MicrophoneRecorder.MainThreadOnHasAudio += MicrophoneTransmitting;
MicrophoneRecorder.MainThreadOnHasSilence += MicrophoneNotTransmitting;
RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
RenderPipelineManager.endCameraRendering += EndCameraRendering;
BasisDeviceManagement.Instance.OnBootModeChanged += OnModeSwitch;
BasisLocalPlayer.Instance.OnPlayersHeightChanged += OnHeightChanged;
InstanceExists?.Invoke();
Expand Down Expand Up @@ -285,6 +286,7 @@ public void OnDisable()
if (HasEvents)
{
RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
RenderPipelineManager.endCameraRendering -= BeginCameraRendering;
BasisDeviceManagement.Instance.OnBootModeChanged -= OnModeSwitch;
MicrophoneRecorder.MainThreadOnHasAudio -= MicrophoneTransmitting;
MicrophoneRecorder.MainThreadOnHasSilence -= MicrophoneNotTransmitting;
Expand All @@ -297,7 +299,11 @@ public void BeginCameraRendering(ScriptableRenderContext context, Camera Camera)
{
if (Camera.GetInstanceID() == CameraInstanceID)
{
LocalPlayer.HeadShadowDriver.BeforeRenderFirstPerson();
// Defensive check, this only seems to be null when the first avatar is not loaded yet.
// May be fixable by updating the prefab so that it's always defined by default.
if (LocalPlayer.HeadShadowDriver)
LocalPlayer.HeadShadowDriver.BeforeRenderFirstPerson();

ScaleheadToZero();
if (CameraData.allowXRRendering)
{
Expand All @@ -311,11 +317,41 @@ public void BeginCameraRendering(ScriptableRenderContext context, Camera Camera)
}
else
{
LocalPlayer.HeadShadowDriver.BeforeRenderThirdPerson();
// Defensive check, this only seems to be null when the first avatar is not loaded yet.
// May be fixable by updating the prefab so that it's always defined by default.
if (LocalPlayer.HeadShadowDriver)
LocalPlayer.HeadShadowDriver.BeforeRenderThirdPerson();

ScaleHeadToNormal();
}
}
}

private void EndCameraRendering(ScriptableRenderContext context, Camera Camera)
{
if (LocalPlayer.HasAvatarDriver && LocalPlayer.AvatarDriver.References.Hashead)
{
if (Camera.GetInstanceID() == CameraInstanceID)
{
// Rescale head to avoid making the end state of the avatar
// dependent on the last rendered camera.
ScaleHeadToNormal();

// Defensive check, this only seems to be null when the first avatar is not loaded yet.
// May be fixable by updating the prefab so that it's always defined by default.
if (LocalPlayer.HeadShadowDriver)
LocalPlayer.HeadShadowDriver.AfterRenderFirstPerson();
}
else
{
// Defensive check, this only seems to be null when the first avatar is not loaded yet.
// May be fixable by updating the prefab so that it's always defined by default.
if (LocalPlayer.HeadShadowDriver)
LocalPlayer.HeadShadowDriver.AfterRenderThirdPerson();
}
}
}

public void ScaleHeadToNormal()
{
if (LocalPlayer.AvatarDriver.References.head.localScale != LocalPlayer.AvatarDriver.HeadScale)
Expand Down
33 changes: 28 additions & 5 deletions Basis/Packages/Basis Framework/Players/BasisHeadShadowDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,33 @@ public void BeforeRenderFirstPerson()
{
if (!_isShadowNecessary) return;

// Made shadow visible.
MakeShadowVisible();
}

public void AfterRenderFirstPerson()
{
if (!_isShadowNecessary) return;

// Make shadow invisible to avoid making the end state of the avatar
// dependent on the last rendered camera.
MakeShadowInvisible();
}

public void BeforeRenderThirdPerson()
{
if (!_isShadowNecessary) return;

MakeShadowInvisible();
}

public void AfterRenderThirdPerson()
{
if (!_isShadowNecessary) return;

}

private void MakeShadowVisible()
{
// There may be better ways to do this.
for (var index = 0; index < _copiesOfSkinnedMeshes.Length; index++)
{
Expand All @@ -186,11 +212,8 @@ public void BeforeRenderFirstPerson()
}
}

public void BeforeRenderThirdPerson()
private void MakeShadowInvisible()
{
if (!_isShadowNecessary) return;

// Made shadow invisible, so that it doesn't cast shadow on the thing it's trying to copy.
// There may be better ways to do this.
foreach (var copy in _copiesOfSkinnedMeshes)
{
Expand Down

0 comments on commit fa7cfcf

Please sign in to comment.