-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
[3.x] Add OccluderShapeMesh #52347
[3.x] Add OccluderShapeMesh #52347
Conversation
08f6565
to
0720471
Compare
I've started testing this. Great work so far 🙂 Testing project: test_occluder_mesh_3.x.zip After baking and hiding the Occluder gizmo using the View > Gizmos menu, rendering is slow due to the overly complex occluder, so I tried to enable simplification. Unfortunately, even with simplification set to 1 and the occluder baked again, I still get a lot of stuttering when moving around. (The culling is working as expected, still.) We should see about enabling occluder simplification by default – a value around 0.1-0.2 would be a good default. There's an usability issue I noticed too. If you create an Occluder node, the Bake menu option won't be available yet. This is expected, however, if you create an OccluderShapeMesh in the inspector, the Bake menu option will only appear once you select another node and select the Occluder node again. To resolve this, I suggest we make the Bake button always visible regardless of the Occluder node having an OccluderShapeMesh resource assigned. If no shape resource is assigned, display a dialog that asks the user to choose a bake path, then create the OccluderShapeMesh resource and bake it immediately. If an OccluderShapeSphere resource is assigned, display an error dialog telling the user that they should clear the OccluderShapeSphere resource in the inspector before baking an occluder mesh. You can look at how to pop up a node selection dialog in the CPUParticles menu option "Create Emission Points From Node". See |
Some behaviors that I think are bugs / confusing:
|
Good feedback. Yeah there is more work to do, just the basics are working now, but I thought it would be good for you guys to try in advance for exactly this reason to get feedback on how to improve it.
I'll try this out. Currently with the baking, depending on the level, with too sensitive settings it is easily possible to overload the system - usually with a large level I would start with a large input threshold size and gradually lower it to increase the polys, rather than the other way around. Maybe this setting can be changed so that the slider is a target number of input polys, that way it is more likely to work at the same speed whatever the size / complexity of level. I'll try this zip file in a second to give more feedback. I agree having about adding a progress bar and cancel button, and having a warning if the bake is going to take too long. There's also quite a bit in the baking that can be optimized more there I suspect.
I'll take a look. Aside from just drawing the gizmo (which could be expensive if you have e.g. > 2000 occluder polys) there is the possibility of a stall with how it currently shows the debug active polys, as it has to retrieve them from the VisualServer. This didn't happen on mine, but if it is a problem I can add some shortcut to add different type of thread protection to prevent stalling (this is already done e.g. for the portal callbacks).
Possibly .. simplification has to be used with care at the moment I think. @JFonS was discussing this the other day for raster occlusion, which faces the same problem with simplification. If a convex mesh is simplified there is normally not a problem because the simplified version appears 'behind' the wall surface. But for a concave surface there is the possibility that the simplified version will appear in front of e.g. a wall, and start occluding stuff that should not be occluded. Curved / overly complex surfaces in general can be a problem for these reasons, so it is usually better to exclude them completely (using the input threshold, or using layer masks or a different scene branch, or marking them as e.g. portal_mode DYNAMIC (which will prevent baking if you are not using portals) .. we could also potentially add a separate toggle somewhere to prevent occlusion baking like in 4.x).
Yup I noticed that too, just hadn't got around to trying to fix it. It may be possible to get the editor plugin to refresh when you change the resource, that way the button will show. In fact this would be generally useful editor functionality imo, not just for this particular situation.
Hmm, yes it may be something like this could be added to make things more user friendly (although it does deviate slightly from the usual node / resource paradigm, as it does somewhat assume the occluder node will primarily be used for baking).
Ah yes this is a bug I had noticed on occasions. I'm hoping this is just a stale gizmo and a call to
Ah yes this may be because the occlusion is not updated until a frame is rendered. So when baking is complete if I can request an editor screen refresh this should solve this. Presumably it doesn't occur in the continuous editor update mode?
Yes I haven't thought this through very thoroughly yet, I basically copied the idea of checking standard materials for a transparent flag from @JFonS baker in 4.0, but this hasn't been properly tested yet. Custom shaders that use transparency / additive etc there may be some way of detecting. This can of course be done manually by selecting which meshes to bake but it would be nice to have it as automated as possible.
Again copied straight from the 4.0 baker for now .. I haven't yet properly tested this / written docs. I'm actually not absolutely sure we need it (especially if we add a 'bake occluder' flag to visual instances), but I'll check with @JFonS the use case. It was pretty easy to add anyway.
Yes this is not done yet, but I should be able to add in a similar way to the Portal system. I'll have a look whether any common code can be put in a more sensible place (like constructing the CSG meshes if I remember right). Some things to be aware ofIt isn't highly optimized (or optimized at all in some cases! 😁 ) in terms of algorithms, especially for the baking and the choosing the best polys. I'm just concentrating on getting the basics working at first (i.e. something is better than nothing!). This can be improved over time, probably several fold. For choosing best polys I may end up using e.g. a BVH against the camera planes as a first step. At the moment it is using brute force clipping every poly to screen space. This is probably fine for a few hundred polys but more than that some space partitioning / AABBs will probably help. The actual geometrical culling technique I think can also be considerably improved in terms of rejecting more positives, but this basic version does work and provide a fairly decent starting point. It probably be time consuming refining this so I'd rather get the other functionality working well first, and we can refine this over e.g. the next beta. |
There do seem to still be some improvements to the baking which would be nice to make. In particular in this scene there are a lot of convex polys which are not being merged because several need to be merged at once to maintain a convex face .. and at the moment it only does it one at a time. I might try reworking it to flood fill out and find all the coplanar faces in one go, then arrange these into the lowest number of convex polys, rather than doing it incrementally. It might work faster too. Mesh must be splitJust having a look at this level. It's quite good for testing purposes, even though it is unlikely to work well, it's a nice kind of worst case. First of all, having a whole level (or most) as a single mesh means there can effectively be no occlusion culling, as only whole meshes are occluded. So such a mesh would at the very least need to be split into many parts. It looks like this mesh has been imported with multiple surfaces instead of multiple Simplification valuesIt does show I need to tweak the simplification parameter that is passed to the mesh optimizer library. I wasn't sure exactly how this works, but it appears to be a fraction of the overall world size, so a value of e.g. 0.1 might not create much simplification in a small model, but in a large model it creates a lot. I already have code that is Level building technique - large polys versus polygon soupThe main problem with the level as far as the current system is that it has mostly not been made with any large occluding areas. This is an issue of modeling mostly .. For instance when building a large wall with a box against it, you can either build a single large wall, and a box, or some artists will build the box then split the wall around the box so that there is no unneeded area behind the box. The former approach is much easier to find large occluders, which the latter makes things far more difficult. The artwork approach tends to depend on the artist and the engine and tech they are making the level for. In some cases the latter can be better, but in this case the former works better. The good news is that a lot of simple levels and beginner designers tend to use the former approach, and it is quicker to work with anyway. Manual OccludersWhen a level comes as a polygon soup like this, you are probably better off manually adding some occluders (e.g. planes) in a separate branch, then baking these. I'm planning later to add an Baking AIThe reason it is so hard for the baker to automatically create large occluders 'from nowhere', is that it can't make any assumptions about your level design. Some baking AI that works in one situation might break the occlusion in another. So at the moment you do have to work in a sensible way to help the system. Raster occlusion faces many of the same problems. Portals conveniently works around a lot of these problems, because it essentially doesn't care what is in a room, the only things that need to be specified precisely are the portals themselves. This can actually make portals less work in some situations. This is a common theme in occlusion, there is no "one sized fits all" approach. FloorsSomething I experimented with that might be helpful on this level was the ability to prevent formation of occluders on floors and ceilings. Especially in a level such as this, it can potentially be quite handy, so I might add this to the Inspector. Essentially I just check an abs dot product of the poly normal with an up vector, and if it is close to the up vector, it ignores it. Things like terrain pointing almost straight down is pretty much useless for occlusion anyway. The screenshot above is with floors removed. Occluders facing outwardSomething I haven't added yet that will also help reject occluders during baking is that some simply cover the outside of the map, and could never occlude anything anyway. Consider a wall at the side of a map for instance. There is no point in having an occluder on it, in terms of baking or consideration at runtime. This might be possible by doing a search out behind the occluder for other geometry .. if none is found, don't add it, something like that. |
This approach sounds interesting – I've never thought about it before. It might be worth porting to the raster occlusion in 4.x too. It won't work as well for highly vertical levels, but most game levels aren't highly vertical (they often have 1-3 floors at most). Should this be exposed as a float property such as |
I'm just adding it right now, it was already mostly in the code, just not exposed. Am thinking in terms of an angle in degrees, called something like I think in the long run it should be able to combine this with some AI to detect whether there can be geometry below / above the poly in question, similar to the edge of map technique I mentioned earlier. Or you could manually mark some floors as not affected, I'm not sure yet what would be the best approach. |
Most angular property values in Godot are in radians rather than degrees, so I'd prefer to be consistent with other properties here. In 4.0, we have a way to convert degrees to radians in the inspector transparently (including a visible |
Add OccluderShapeMesh, glue to Occluder and gizmos etc.
0720471
to
c7cca16
Compare
Ah I can change this. My personal preference is I find it depends on the setting. Degrees do have the advantage that they are easy to visualize for the user, and they can be stored as integers for things like this. For anything in radians, I would normally have to get the calculator out and convert from degrees manually. But in some cases setting directly in radians can help. In actual fact it is used as a dot product in reality, and even this is easier to work with than radians, as at least it is between 0.0 and 1.0... 😁 Or even 0-100 I'll probably redo the baking code significantly soon, this map has some good cases to cover. Once I have a good technique down and dusted we can put in the niceties like multi-threading and progress dialog... I've already got code for this from doing the lightmapping.
A lot of the principles involved in the baking here may be common for the raster occlusion in 4.0, so it's possible we may end up sharing some code / techniques. It may be something like the OccluderShapeMesh can't be used 'as is' for raster occlusion, for instance. |
Is there a reason cuboid/rectangular occluders are not mentioned here? I think a lot of the time, just some blocking out would be all that is needed rather than baking meshes. As far as I can tell, baking isn't exactly trivial and I for one would love to have a simple cuboid shape with a gizmo similar to the CSG cubes alongside mesh baking. It would still help a lot, especially with large buildings and the like for simpler or just less intensive games. |
Yes I'm planning on doing these too, but there's quite likely some shared implementation details in the VIsualServer side, so it makes sense to do the Meshes first. It may end up that boxes are implemented just a particular type of Mesh (i.e. when Meshes are working, you get boxes for free, aside from making the gizmos etc). Whereas spheres aren't sensible to do as meshes, hence they were done first. |
Hey @lawnjelly - I have a question for you about the previous image. What is the frames per second with the OccluderShapeMesh on the scene shown? |
The editor FPS counter in Either way, it's irrelevant in this particular scene as the whole scene is a single mesh. This means nothing will be occluded until small dynamic objects are added to the scene. |
Ah ok, Was not aware of this thank you Calinou for filling me in on this. Bit of a follow up question then for @lawnjelly: For a bit of context - I am using Qodot in 3.4 RC1 to build out level geometry and I am splitting that level geometry across multiple scenes for performance reasons. |
Yes, absolutely fine. The reason it is a mesh rather than a poly is just so things don't get out of hand in terms of number of nodes, and traversing the scene tree, but once it is in the visual server, the number of nodes doesn't matter. In terms of what to expect as far as performance is concerned, it very much depends. At the extreme end you can get gains approaching what you would have with a properly portalled level, especially when you have large simple occluding walls. On the other hand if you have complex wall geometry that cannot be made into large occluders, you will get little benefit, because several small occluders cannot generally be merged (merging occluders is something that raster occlusion is much better at). However I'm aiming to additionally have the ability to either add single occluders manually (like anti-portals), or mark e.g. invisible geometry like a MeshInstance plane as an occluder, which should make it more generally useful, at the cost of some manual work. |
71cb8d3
to
c58391c
Compare
Thank you! |
Closing this as has been considerably rewritten since, and will be a second PR after #57361. |
Add OccluderShapeMesh, glue to Occluder and gizmos etc.
This is still DRAFT, there is more work to do, but it should be able to start to be tested. As with OccluderSpheres, it can either be used with the portal system or be used on its own.
Includes:
Following on from the Occluder Spheres, the second geometric occluder type I've called 'Mesh'. It is basically a bunch of convex polygons (with shared verts) where each polygon can be used as an occluder.
Baking
You assign a nodepath to a branch of the scene tree you want to use, and it recursively searches for meshes, and for each face decides whether to add it to the occluder mesh based on a threshold input size (area at the moment) and threshold output area. Preventing small faces is important for two reasons:
That said you would often have a lower input threshold than output, because input faces will be merged when possible to form larger faces.
Occluder polys shown in magenta, active occluder polys shown in cyan:
The baking has three steps:
STATIC
, not using transparent spatial material, and matching the layer mask selected in the baker)Baked data is saved with the OccluderShapeMesh resource, but you can potentially bake them at level startup if desired to save on download size / work with procedural levels.
Inspector for the OccluderShapeMesh (subject to change)
Runtime
At runtime similar to the spheres, the occlusion culler selects the most important polys based on a screen space metric to use for culling on any particular frame. You might have 1000 or so polys in the level, but only be using e.g. the default 8 at any one time (this can be changed in project settings).
The metric for finding the best occluders also removes polys that are themselves occluded by closer polys. There is no point in processing the further occluder if a closer one will already reject the objects.
Occluder merging
The biggest challenge for geometric occluders (as opposed to raster) is that it is far more difficult to deal with cases where an object is occluded by two or more polys, but not wholely occluded by any one of them. To this end the system has a basic geometric method for occluding objects that are behind 2 adjacent faces. This may be able to be improved to work with more faces in the future.
This roughly corresponds to two adjoining walls, or a wall and a floor, or wall and ceiling. This can work reasonably in many circumstances providing the occluding polys are large.
Scene
Occlusion off
Occlusion on
Pros
Cons
Dynamic use
While I was primarily envisaging larger numbers of polys used for static parts of a level, there is support for dynamic occluders, as with the spheres. If you transform an
Occluder
node (or parent etc) the mesh will be software transformed in the VisualServer and the new positions used for occlusion. This obviously works better if you don't go overboard with the number of polys in a dynamic occluder. A spaceship can probably have e.g. a box shape around it. As well as being faster, this also works better because larger occluder polys = better occlusion.Typical use
Occluder
node to the sceneOccluderShapeMesh
in theOccluder
inspectorBake
in the toolbarYou can turn on and off occlusion in the View menu, and you may want to turn off the occluder gizmo once set up, as it provides a little 'too much' info at times... 😄
Comparison of methods
Status
Currently redoing the baking with a different method, flood filling neighbouring faces with similar normals, finding edges, then splitting into large convex polys. This seems to be much faster and is working much better, but still needs a few days work.
Update
This is taking longer than expected as there are a lot of special cases to deal with in the baking (I've spent 2/3 weeks solid on the baking), so it may well get pushed back to 3.5. I would rather get the existing functionality in 3.4 polished and out there, and spend longer on this to make sure it is robust. I'll keep working on it, but on a 'ready when ready' basis rather than pushing for a deadline. After the baking there are also some optimizations to choosing the best polys to do.