- Home /
Bounds.Encapsulate Not Working As Intended
Bounds.Encapsulate is not providing the target results for calculating scene bounds. When using the function given all of the renderers in the scene the height, width, and center are offset. We need this specifally for sampling heights at any given position because not all scenes we are using have terrain otherwise we would use the Terrain.activeTerrain.SampleHeight. By leveraging raycast and accurate scene bounds we would be able to calculate heights at any given point via raycasting down from the max height of the scene. Its not clear whether or not there are actually renderers in the scene that are causing this issue, but I literally checked everywhere and the renderers that could be viewed all were contained within the visual confines of the scene. We do have particle systems which have renderers that can't be viewed which could be the problem?
Currently we are doing the following :
public static Bounds GetSceneBounds(Scene scene)
{
GameObject[] rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
// Initialize bounds with an inital renderer in scene
Bounds b = rootGameObjects[0].GetComponentInChildren<Renderer>().bounds;
foreach(GameObject rootGameObject in SceneManager.GetActiveScene().GetRootGameObjects())
{
foreach (Renderer r in rootGameObject.GetComponentsInChildren<Renderer>())
{
b.Encapsulate(r.bounds);
}
}
return b;
}
Any suggestions?
What do you mean by "renderers that can't be viewed ". Which renderer is visible to one or multiple cameras is completely irrelevant. You iterate through ALL renderers in the scene. Have you actually tried some debugging? What height / top value do you expect? You can temporarily hardcode a limit that you expect and print out the name of any bounds that exceeds that limit as you iterate through them. Debug.Log can take an optional context parameter where you can pass a reference to a gameobject. When you click the debug message in the console unity will highlight that context object in the scene.
The Bounds struct is an extremely simple struct. The different Encapsulate methods are straight forward. If you get a top value that is higher than you expect, there has to be a renderer which has a bounds that goes up there. This is all pure math, nothing else.
@Bunny83 which renderers are visible to the unity editor scene camera is completely relevant. If I can see the renderer bounding box it would be useful for visually seeing which ones lay outside the bounds. Particle Systems see$$anonymous$$gly don't have this, but are of type renderer. Yes I debugged using DrawWireCube. The height should be the literal height of the top most renderer plus its distance from center to bounds max. So after encapsulate is complete the following :
heightInWorldSpace = bounds.center.y + ((bounds.max.y - bounds.$$anonymous$$.y) / 2)
Haven't tried the limit debug though that's a good suggestion i'll let you know the result later. I understand that as well that it uses math to calculate this. That is why it is $$anonymous$$d boggling to me that it does not provide proper results, which leads me to think that it is a renderer out of what I think the visual bounds of the scene should be.
Answer by Glurth · Dec 12, 2018 at 09:10 PM
I modified it slightly to get it running: removed the unused parameter, added a few sanity checks, and fixed a bug (the assumption that rootGameObjects[0] would exist and have a renderer).
It works fine for me. My object with a ParticleSystem, DID increase the scene bounds, when I made it large enough. So, I also added a template type parameter, which allows you to specify a specific type of renderer.
usage e.g.
Bounds sceneMeshRendererBounds = GetSceneBounds<MeshRenderer>()
modified version.
public static Bounds GetSceneBounds<T>() where T: Renderer
{
Scene currentActiveScene= SceneManager.GetActiveScene();
if (currentActiveScene == null)
{
Debug.Log("Unable to find active scene");
return new Bounds();
}
GameObject[] rootGameObjects = currentActiveScene.GetRootGameObjects();
if (rootGameObjects == null || rootGameObjects.Length == 0)
{
Debug.Log("Unable to find objects in active scene");
return new Bounds();
}
bool firstRendFound = false;
Bounds b= new Bounds();
foreach (GameObject rootGameObject in rootGameObjects)
{
foreach (Renderer r in rootGameObject.GetComponentsInChildren<T>())
{
if (firstRendFound)
b.Encapsulate(r.bounds);
else
{
b = r.bounds;
firstRendFound = true;
}
}
}
if(!firstRendFound)
Debug.Log("Unable to find any renderers in active scene");
return b;
}
$$anonymous$$y reasoning for making that renderer assumption is because creating new Bounds() also causes an offset I had read, because the initial bounds are initialized at Vector(0,0,0) which is included in the encapsulation. This gives me an idea though I will probably pass each renderer type into the function and try to find the render type that is causing the issue. We have way to many renderers in my scene to manually check them all so this is a good start. @Glurth thanks for the suggestion.
new Bounds()
does indeed include 0,0,0.. which is why new Bounds()
is ONLY used/returned when no renderers, scene or objects are found. Otherwise, that value b
is OVERWRITTEN (rather than encapsulated), when the first renderer is found. Only subsequent renders bounds are encapsulated. hmm. Guess I should have called that bool firstRendHasBeenFound