Hi! Sorry again for delay in answering. I read this thread and also What is the overhead in the engine of dynamically adding and removing TCastleScenes? - #6 by DSK and I think I have an understanding what you’re doing. (Thanks for the detailed writeup!)
As for the 2 features you ask for: hm, admittedly neither of them are easy without significant changes to how the occlusion query works now. Read on for my thoughts and idea how to proceed:
Currently, we render this “box for occlusion query” in a somewhat special way, using once prepared TCastleRenderUnlitMesh
, with once defined “topology” (RenderBox.SetIndexes(Indexes)
). See castle-engine/src/scene/castleinternalocclusionculling.pas at master · castle-engine/castle-engine · GitHub . It is prepared in TOcclusionCullingUtilsRenderer.GLContextOpen
there.
Then for each shape we want to query, we do TOcclusionCullingUtilsRenderer.DrawBox
. This sets RenderBox.ModelViewProjection
, sets vertexes using RenderBox.SetVertexes
, renders using RenderBox.Render
.
The TCastleRenderUnlitMesh
is deliberately a very thin wrapper over a “mesh” concept on GPU. We use it in simple+efficient way (only updating box vertexes and reusing the same TCastleRenderUnlitMesh
instance to draw many boxes), because the overhead of drawing “boxes for collision query” has to be minimal. To maximize gains from occlusion query.
So this request would mean you need to supply custom data (indexes and vertexes) and change how TOcclusionCullingUtilsRenderer.DrawBox
behaves.
Note that we don’t query (right now) whether the bounding box of the object was visible, if the actual object was visible. When the object was visible, we don’t render the box – we render the actual object, and this will be tested to determine “what to do in next frame”. This can actually cause some noticeable “flapping” in whether we render the object or not – see Occlusion Culling | Manual | Castle Game Engine notes from
Sometimes object state flips between “visible” and “not visible”, making uneven frame render times. This happens when the proper shape is obscured, but its bounding box is not obscured. The issue can in general be ignored — user doesn’t see any “flipping”.
Your request would mean we need to render the “box” (or your custom replacement of it) always, from what I understand.
My overall hesitation here (why I hesitate to “just do it in the engine”) is not really how to solve the 2 details mentioned above, but it’s that we’d somewhat expose to public things that we wanted to keep private. The user should not worry how it works – I like that it’s a matter of flipping the boolean TCastleViewport.OcclusionCulling
now. This also allows us to change the rendering code easily, and we’ve done it in the past. Last time when doing this: Big renderer improvements: Correct and automatic blending sorting, more powerful batching (now cross-scene), easier and more reliable occlusion culling and occlusion sorting – Castle Game Engine . Having to maintain additional features is then problematic – they are not used in majority of cases, and they make such renderer refactors much harder, due to having to pass data that is only sometimes actually used.
My proposed solution is to instead delegate this problem to you That is, to be serious:
-
You can use TCastleRenderUnlitMesh on your side, setting them up for each required object you need to check. You are them free to reuse them or not reuse as much as your architecture allows.
-
You can perform the occlusion queries around the meshes you render yourself. The code on castle-engine/src/scene/castleinternalocclusionculling.pas at master · castle-engine/castle-engine · GitHub may be helpful. The OpenGL(ES) API for this is really simple:
Create query object using
glGenQueries(1, @OcclusionQueryId)
(you can ceate multiple objects in one go if you know you need them).Render using
glBeginQuery(QueryTarget, OcclusionQueryId); // render mesh, e.g. using MyUnlitMesh.Render(...); glEndQuery(QueryTarget);
Sometime later (best: in the next rendering frame) read it using
function OcclusionQueryHit(const OcclusionQueryId: TGLint): Boolean; var SampleCount: TGLuint; begin glGetQueryObjectuiv(OcclusionQueryId, GL_QUERY_RESULT, @SampleCount); Result := SampleCount > 0; end;
I’m using snippets from castle-engine/src/scene/castleinternalocclusionculling.pas at master · castle-engine/castle-engine · GitHub above.
You can do this in some
Render
override. See e.g. how exampleexamples/research_special_rendering_methods/test_rendering_opengl_capabilities
( castle-engine/examples/research_special_rendering_methods/test_rendering_opengl_capabilities at master · castle-engine/castle-engine · GitHub ) usesTMyMesh.LocalRender
. That example just draws wireframe box – but instead it could perform collision query of essentially any shape. I’d recommend you experiment by changing that example to perform occlusion query as shown above.
The advantages of this approach:
-
you are independent then from CGE features and how we expose occlusion culling.
-
You use
TCastleRenderUnlitMesh
, which is lean and fast, and you manage it yourself – having freedom to reuse it as much as it makes sense (e.g. if multiple queried object have the same shape). -
You decide yourself when to query, and with what mesh to query.
Let me know if this makes sense