Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

What is a better way to temporarily not render an entity than Disabled?

Discussion in 'Data Oriented Technology Stack' started by 5argon, Mar 8, 2019.

  1. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,184
    I want some arbitrary entity to not render.

    - If I attach Disabled, it will be excluded from too many systems. I would like to still process them without adding explicit Disabled to everywhere.
    - If I remove RenderMesh it is a hassle to remember and attach it back, now that those are populated by the conversion API.
    - If I destroy it I think that's the worst way of hiding.. there should be something simpler.
    - [WriteGroup] seems like almost a solution so I could override RenderMeshSystemV2 with my system that do nothing, but the render mesh system didn't write data and also didn't specify any write group.
    - Preferrably I want the "not render" to affect my entire entity chain linked with LinkedEntityGroup by doing it only to the top level entity.

    My current idea

    1. Make my own DisableRendering component.
    2. When attached to any entity, my DisableRenderingSystem would have a system state to remember RenderMesh of that entity before proceeding to remove the RenderMesh.
    3. Check if it has LinkedEntityGroup and do the same to all in that buffer.
    4. Restore on removing DisableRendering component.

    But it seems like a big hassle.. would it be nice if I could do something similar to checking .enabled in MonoBehaviour so that a specific component stops coming up in query while still technically exist. (But that would complicate the whole chunk thing)
     
    Last edited: Mar 8, 2019
    SamOld and jrumps like this.
  2. NoDumbQuestion

    NoDumbQuestion

    Joined:
    Nov 10, 2017
    Posts:
    172
    Move its position over 1000 from Camera Clipping planes?
     
  3. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,184
    That sounds like a good solution. So ECS also decide to render or not based on camera clipping plane? How it knows which camera? Because I can't find any Camera.main in the code.
     
  4. elcionap

    elcionap

    Joined:
    Jan 11, 2016
    Posts:
    51
    Lately i'm using a child entity with RenderMesh parented to my main/root entity and adding Disabled to skip the render. It also help when my main entity is destroyed and I need to keep the view for a few frames (mostly some death animation when using GameObject as a view but I keep the same logic for both).

    []'s
     
  5. NoDumbQuestion

    NoDumbQuestion

    Joined:
    Nov 10, 2017
    Posts:
    172
    the old hybrid render code also use culling. So the stuff outside of camera will not be render (cause shadow not render when entity on edge of camera).
    So I guess the render v2 also have culling with extra.
    Just move your stuff outside of camera and add
    CullingStatsDrawer
    to scene then press F4. See if it actually stop rendering.

    I have not test it yet but I believe in you
     
  6. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    656
    Probably you can disable entity rendering with FrozenRenderSceneTag component.
     
  7. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    132
    This should work, but it's untested
    Code (CSharp):
    1.  
    2. [UpdateBefore(typeof(RenderMeshSystem ))]
    3. public class DisableRendering : ComponentSystem {
    4.     ComponentGroup m_MainGroup;
    5.  
    6.     protected override void OnCreateManager() {
    7.         m_MainGroup = GetComponentGroup(typeof(LocalToWorld), typeof(MyOwnDisabledThatOnlyDisablesRendering));
    8.     }
    9.     protected override void OnUpdate() {
    10.         ForEach((Entity e) => {
    11.             PostUpdateCommands.RemoveComponent<LocalToWorld>(e);;
    12.         }, m_MainGroup);
    13.     }
    14. }
     
  8. Jes28

    Jes28

    Joined:
    Sep 3, 2012
    Posts:
    347
    I dont have many experience eith entity renderers but I think adding

    Code (CSharp):
    1. public struct RenderMeshDisabled : IComponentData
    2. {
    3.     public RederMesh mesh;
    4. }
    and for disable just memcopy RenderMesh component to RenderMeshDisabled, add RenderMeshDisabled to entity and remove RenderMesh.

    It just moves disabled entity to another archetype with is ECS way I think.
     
  9. Creepgin

    Creepgin

    Joined:
    Dec 14, 2010
    Posts:
    232
    Oh does removing LocalToWorld also disables mesh rendering? If so, why not just remove it in the first place (skip MyOwnDisabledThatOnlyDisablesRendering altogether)? hehe
     
  10. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    132
    I am pretty sure it does, at least some groups will not work without. I didn't check all groups nor test if this works
    I think LocalToWorld is added every cycle if it's not currently in the entity, therefore it needs to be removed every cycle, for that you need the MyOwnDisabledThatOnlyDisablesRendering component to 'tag' which entities need not to draw

    But as I said earlier, I didn't test this.



    But if LocalToWorld had to be manually added, then you could just remove it, yeah. Been too long since I used the RenderMeshSystem
     
  11. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,184
    I have looked at several options you guys mentioned (without actually trying it yet)

    Rendering is gated by RenderMesh, LocalToWorld, WorldRenderBounds, FrozenRenderSceneTag
    • FrozenRenderSceneTag : should not touch manually, it is an SCD added by sub scene with Static optimization.
    • WorldRenderBounds : Auto computed from its local bound and LocalToWorld. Added automatically when missing, so can't remove this.
    • RenderMesh : Removing this is not ideal since I don't want to lose SCD that conversion system had setup for me.
    WorldRenderBounds -> Will be used with ChunkWorldRenderBounds, seems to be an another bound specifiable per chunk but has a default of about ~20000 extents. Then intersecting with frustrum plane which I assume coming from all cameras. (The debug tools @NoDumbQuestion mentioned change its data when I hide the Scene view, indicating it uses both hidden scene camera and game camera.) So throw away method could work.

    Frozen (and in effect, Static) : This version's Frozen seems to can only stops mesh local bound x LocalToWorld -> WorldRenderBounds. Frozen cannot stop computation of TRS + Parent -> LTW. The way to stop those you need to not have any LTW.

    Next problem, LinkedEntityGroup has no other function than instantiate/destroy together. One other link we have is the Parent link, which do something only for transform system. So I think the "throw away out of bound" is the best way since it could use Parent link to hide together all children. If we are removing LTW, trying to restore it on all correct child become a problem. (Some child don't have MeshFilter, it would not receive LTW on conversion)

    Then I found one other way. There are many systems using WriteGroup leading up to LTW ([WriteGroup(LocalToWorld)]). Meaning that if I made a system with any of my own component and direct it to the same [WriteGroup(LocalToWorld)], I can stop all TRSP -> LTW chain from working when they see an Entity with my alien component.

    (?) not sure if it works this way but if it works, then the simplest way to do this might be :
    1. Attach my OutOfBoundsCulling component.
    2. My OutOfBoundsCullingSystem move the parent entity to 99999
    3. Manually trigger the whole transform chain so LTW changed.
    4. My system attach system state OutOfBoundsCullingRegistered.
    5. OutOfBoundsCullingRegistered has WriteGroup going to LocalToWorld, stopping all built-in system from writing to LocalToWorld until I remove this system state.
    6. The system turning LTW -> WorldRenderBounds didn't stop, so it get the 99999 world bounds and would be culled because of not hitting frustrum plane.
    7. My system looks for object without OutOfBoundsCulling to remove the system state.
     
  12. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,250
    @5argon

    did you ever come up with a nice solution for this?

    My solution has been this

    Code (CSharp):
    1.         protected override void OnUpdate()
    2.         {
    3.             this.Entities.With(this.hideQuery).ForEach(entity =>
    4.             {
    5.                 this.PostUpdateCommands.AddComponent(entity, default(Hidden));
    6.  
    7.                 if (this.EntityManager.HasComponent<RenderBounds>(entity))
    8.                 {
    9.                     this.PostUpdateCommands.SetComponent(
    10.                         entity,
    11.                         new RenderBounds { Value = OutOfBounds });
    12.                 }
    13.                 else if (this.EntityManager.HasComponent<MeshRenderer>(entity))
    14.                 {
    15.                     this.EntityManager.GetComponentObject<MeshRenderer>(entity).enabled = false;
    16.                 }
    17.             });
    18.  
    19.             this.Entities.With(this.showQuery).ForEach(entity =>
    20.             {
    21.                 this.PostUpdateCommands.RemoveComponent<Hidden>(entity);
    22.  
    23.                 if (this.EntityManager.HasComponent<RenderBounds>(entity))
    24.                 {
    25.                     this.PostUpdateCommands.RemoveComponent<RenderBounds>(entity);
    26.                 }
    27.                 else if (this.EntityManager.HasComponent<MeshRenderer>(entity))
    28.                 {
    29.                     this.EntityManager.GetComponentObject<MeshRenderer>(entity).enabled = true;
    30.                 }
    31.             });
    32.         }
    Basically set the RenderBounds to some way out of the way point which then updates
    WorldRenderBounds so the entity would always be culled. To show again, just remove the RenderBounds as the internal systems will re-add it - not pretty but works and isn't too much of an issue if it only happens occasionally.

    But this is causing me some issues, for starters it's started crashing randomly (not sure what the issue is but something starts infinitely looping).

    But the other main issue is even if it was working fine, I actually want to use WorldRenderBounds for certain systems which this messes up.
     
  13. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,184
    @tertle The game where I have the "throw away to 99999" system in place was put on hold, so I don't know how that will go :( I hope by the time I come back there is a solution.

    Your render bounds refreshing sounds interesting, I haven't thought of it.

    For Unity team that came across this here's a brief summary :
    - Wants something to not render.
    - Do not want to dismantle any of the components that make the hybrid renderer works in order to make it stop working temporarily, it is a hassle to get them back. (Especially if they came from game object conversion)
    - Disabled works but is too disruptive, in fact getting out of the invisible state had to be computed and turns out I now have to explicitly specify Disabled on 70% of my systems.
    - Preferrably want to do it to all entities in an entire LinkedEntityGroup tree by attaching something to only the top entity. The "throw away" works because linked entity are linked by transform.
    - Entities can't exclude itself from systems, systems must be the one that excludes entity. Except for the new WriteGroup feature maybe where an external component could cause exclude-like behaviour to something in internal package, but I don't know if there is a good way to use to achieve this.

    I am tempted to edit ECS source code and just add Visibility tag component as an exclude somewhere (that scans LinkedEntityGroup) but it would be the best if UT made it official :(
     
    Kender, thelebaron, SamOld and 2 others like this.
  14. jooleanlogic

    jooleanlogic

    Joined:
    Mar 1, 2018
    Posts:
    327
    Perhaps not worth it if only a few entities changing but could you get rid of command buffers and per entity conditionals if you use multiple queries?
    Code (CSharp):
    1. EntityManager.AddComponent(hideQuery, typeof(Hidden));
    2.  
    3. Entities.With(hideMeshQuery).
    4. ForEach<MeshRenderer>( (MeshRenderer meshRenderer) =>{
    5.     meshRenderer.enabled = false;
    6. });
    7.  
    8. Entities.With(hideRenderBoundshQuery).
    9. ForEach<RenderBounds>( (ref RenderBounds renderBounds) =>{
    10.     renderBounds.Value = OutOfBounds;
    11. });
    Or you could IJobForEach<RenderBounds>. Don't know if that allows it to run parallel with ComponentSystem if using same chunks but different component streams?
     
  15. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,250
    After finally solving my issue and realizing this wasn't the system causing the problem, I can say it actually works pretty well.

    Probably not as performant as Unity simply giving us a tag component to ignore (please do Unity) but seems to work well enough as long as you don't need WorldRenderBounds for your own uses (I've just switch to using the Physics AABB instead for the 1 system I needed it.)

    @jooleanlogic yeah I probably could, this was more just a proof of concept hadn't really considered performance yet.

    I can't use IJobForEach because my query is dynamic and you can't use dynamic components in an attribute tag.

    Code (CSharp):
    1.         public HiddenSystem(params Type[] hideTypes)
    2.         {
    3.             this.hideTypes = (hideTypes ?? Enumerable.Empty<Type>())
    4.                 .Concat(new[] { typeof(Hide) })
    5.                 .Distinct()
    6.                 .Select(ComponentType.ReadOnly)
    7.                 .ToArray();
    8.         }
    9.  
    10.         /// <inheritdoc />
    11.         protected override void OnCreate()
    12.         {
    13.             this.hideQuery = this.GetEntityQuery(new EntityQueryDesc
    14.             {
    15.                 None = new[] { ComponentType.ReadOnly<Hidden>(), },
    16.                 Any = this.hideTypes,
    17.             });
    However I will move it to a IJobChunk (which performance wise is the same/better)
     
    Last edited: May 8, 2019