Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement camera cache utility class #852

Merged
merged 10 commits into from
Aug 15, 2017
Merged

Conversation

Zod-
Copy link

@Zod- Zod- commented Aug 12, 2017

Fixes #848

This adds a new Utility class that allows the use a cached reference to the main camera via CameraCache.main. Additionally, a method to refresh the reference is provided with CameraCache.Refresh(camera). On the first call where the cached camera is null, Refresh(Camera.main) is called to initialize. Should I add this to documentation somewhere? What do you think of the names?

I moved MathUtils.GetHorizontalFieldOfViewRadians and MathUtils.IsInFov to a new CameraExtensions since they were both relying on and using Camera.main. Both Methods are not marked as obsolete and use the new extension with the cached camera instead. There was another method in Utils.MoveObjectInFrontOfUser that also uses the camera but I didn't move that one into Camera because I thought it didn't fit as well. But maybe camera.MoveObjectInFront might not be so bad.

Most scripts were simple replacements from Camera.main to CameraCache.main where I also added a local variable in some cases where it's used more than once.

2 Scripts cache the reference to the camera once on start: ScaleByDistance, MoveWithObject. Not sure how to deal with those. Maybe do it like the GazeManager that checks for the GazeTransform on every update?

Lastly GestureInteractiveData does this:

 protected Camera MainCamera { get { return Camera.main; } }

I changed it to use CameraCache but wasn't comfortable to just completely remove it and use local variables where it's used.

Camera.main calls a FindByTag on the whole scene to
return the reference everytime. Cache the reference to
the main camera instead and provide a method to
overwrite it if necessary
Both GetHorizontalFieldOfViewRadians and IsInFov use
Camera.main to get the reference to the main camera in
their calculations. Set the old methods obsolte and move
them into new camera extensions
Most scripts are simple replacement. Some scripts cache
the reference themselves: ScaleByDistance, MoveWithObject
@msftclas
Copy link

@Zod-,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by Microsoft. We will now review your pull request.
Thanks,
Microsoft Pull Request Bot

/// </summary>
/// <param name="newMain">New main camera to cache</param>
/// <returns></returns>
private static Camera CacheMain(Camera newMain)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class name already tells us its the CameraCache, so maybe name this method something like SetMain

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized the same and renamed it in a later commit to CameraCache.Refresh(camera). How do you like that one? Maybe another suggestion?
(I also totally forgot to change the automatically generated private to public which i fixed in the same commit)

Copy link
Contributor

@StephenHodgson StephenHodgson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

I noticed there's places where we are caching our cached camera, when we can now just use a direct call to the CachedCamera.main

public static bool IsInFOV(this Camera camera, Vector3 position)
{
float verticalFovHalf = camera.fieldOfView / 2;
float horizontalFovHalf = camera.GetHorizontalFieldOfViewRadians() * Mathf.Rad2Deg / 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: * 0.5f

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this, also added it to GetHorizontalFieldOfViewRadians

@@ -100,16 +101,17 @@ private void Update()
}
else
{
Camera mainCamera = CameraCache.main;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why cache again?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really for caching but I'm just used to have a local reference when I use something more than once. Using the full call to reference it every time and sometimes even twice in the same line can make the code quite a bit longer. I could change it back in cases where it's not as bad.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO calling it directly actually helps legibility, even if it's just a bit longer. Less Spaghetti.
Not that performance is an issue here, but local references do add to the heap (albeit negligible now days). Not a big deal.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change it back in cases like this where there are fewer calls

/// <summary>
/// Returns a cached reference to the main camera and uses Camera.main if it hasn't been cached yet.
/// </summary>
public static Camera main
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: public variables should be PascalCase.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this is very optional because it's following the same pattern as unity to get the main camera. Less work to refactor, but maybe making it PascalCase will show there's a difference between usages?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same in mind to keep it the same as the unity call. But that's a good point to see a larger difference. One could mistake CameraCache.main for Camera.main when skimming over the code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to PascalCase

Altough it seemed to make sense first to keep it the
same as the unity api but to make a clearer difference
between the two rename: main > Main.
@@ -61,7 +61,8 @@ private enum FrustumPlanes

private void Start()
{
Depth = Mathf.Clamp(Depth, Camera.main.nearClipPlane, Camera.main.farClipPlane);
Camera mainCamera = CameraCache.main;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cache here, then again on line 104.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, it's needed because it's the update. Please disregard comments.

Copy link
Contributor

@StephenHodgson StephenHodgson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@Zod-
Copy link
Author

Zod- commented Aug 12, 2017

Should I still change the local variable caches or is it ok?

@StephenHodgson
Copy link
Contributor

I think it's okay, not a big deal. I'll leave that up to you.

@@ -93,26 +92,23 @@ private void Start()
// Update the direction indicator's position and orientation every frame.
private void Update()
{
// No object to track?
if (TargetObject == null || pointer == null)
if (!HasObjectsToTrack()) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit braces { return; }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,30 @@
using UnityEngine;

namespace HoloToolkit.Unity

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the CameraCache be in the HoloToolkit.Unity.Input namespace? That is where our other camera related stuff lives right now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine, and the class should also include the license info in header.

@@ -0,0 +1,36 @@
using UnityEngine;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit License header

Copy link

@jessemcculloch jessemcculloch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. Approved

@jessemcculloch jessemcculloch merged commit 4451dcb into microsoft:master Aug 15, 2017
@keveleigh
Copy link
Contributor

This PR is giving me errors and appears to have broken GazeManager:
image

@StephenHodgson
Copy link
Contributor

@keveleigh, same here. Workaround I found is assigning the gaze transform manually. Use the camera prefab you've got in your scene.

@jessemcculloch
Copy link

jessemcculloch commented Aug 15, 2017

I'm not getting any errors. How are you guys testing?

Nevermind, There it showed up

@StephenHodgson
Copy link
Contributor

Could be 2017.2 specific?

@keveleigh
Copy link
Contributor

@StephenHodgson Sweet, thanks for the workaround!

@jessemcculloch I pulled master, loaded in Unity 2017.1.0f3, opened the InputTapTest scene, and hit the Play button.

@Zod-
Copy link
Author

Zod- commented Aug 15, 2017

For some reason it doesn't seem to like GazeTransform = GazeTransform ?? CameraCache.Main.transform
Writing the full statement somehow works:

            if (GazeTransform == null)
            {
                GazeTransform = CameraCache.Main.transform;
            }
            else
            {
                GazeTransform = GazeTransform;
            }

@StephenHodgson
Copy link
Contributor

StephenHodgson commented Aug 15, 2017

Sometimes null coalescing doesn't work for components in Unity for some reason. It's hit or miss.

I think it's because even when a field is null, Unity still assigns an object that's still technically null to Monobehaviours, but not null in memory.

@keveleigh
Copy link
Contributor

@Zod- I was just writing up the same finding :) Unity overrides == for GameObjects, which doesn't appear to play nicely with ??. From that blog post:

It behaves inconsistently with the ?? operator, which also does a null check, but that one does a pure c# null check, and cannot be bypassed to call our custom null check.

@Zod-
Copy link
Author

Zod- commented Aug 15, 2017

Sorry about that I've fixed it in #864

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants