Grabbing 'slice' of collision geometry, and/or voxel testing?

Hello all,
Lately I’ve been daydreaming about a mechanic idea; a stormcloud that follows a player around, gradually flooding the area they’re in if they remain there for too long. Now naturally this could never look perfectly realistic (especially if I was the only one developing the effect) but I’d like to at least make it look/function a little more complex than “A plane gradually rises upwards”.

For the sake of this discussion/question, let’s assume I’ve already divvied up my map into ‘areas’ so any collisions won’t be tested against the whole scene tree.

I’ve already figured out that having some sort of voxel system would be a good fit for this challenge; Because the game would probably already know the height of the water and how fast it’s rising, we can filter out any vertical layers of voxels that aren’t immediately above the present water level.
After voxel simulations are done, I’d probably store information about where water can’t flow with markers, and mark areas where flow might be restricted or drizzle out.

Of course, the devil is in the details, and I’m at a loss as for how to accomplish various ‘sub-effects’ that I desire.

  • Does Panda3D have any convenience tools to determine how much volume is free/taken up within a cube?
  • Is there some way for me to get a ‘slice’ of collision geometry, almost as if it’s wireframe (+ normals) were painted on a plane? This would be useful for creating dynamic ripple effects. More ideally, I’d like to get the shape of all points of a collision solid as they protrude into the ‘into’ shape.
  • Is there some way of calculating the ideal orientation of a voxel test? the larger the cube you can use to encompass the primary area of the voxel test, the more efficient said test can be. I.E. one hypothetical tool that would be handy would be almost like the opposite of NodePath.calcTightBounds(), where instead of shrinking a cuboid around geometry, it would blow one up from a point.

Are there any features or techniques that Panda3D has/supports that I’m simply unaware of that would make this easier? Or maybe I’m on the wrong track?

You can calculate mesh intersections, but it’s annoying and you have to do it yourself. Picture the mathematical definition of a plane in whatever form this is: value = a*x+b*y+c*z. Plug in the vertex of the object, if the value is higher than value it’s on one side, else on the other. Repeat for every face of the object. Then figure out which edges don’t have both points on either side and calculate the intersection point of the line and the plane. Then you can close the collision face.

It is way easier, to treat voxels as cubes, and scale them along z according to their fill level.

I tried to do water simulation for 2-3 weeks last year and my result was that the amount of calculation necessary to make it look good is too much work. Like, writing it in a way that is performant. So you will probably want to fake that part, that’s what I settled on.

a little more complex than “A plane gradually rises upwards”.

If you don’t like planes, you can go for a more complex function or shape, but you will have to pick that shape in advance.

This would be useful for creating dynamic ripple effects.

If you mean something like this:

I would say that that’s generally not doable in real time, in panda / python. Or you have to have serious education (Masters/PhD) in fluid dynamics.

(The way they did it in blender takes advantage of the fact that they don’t have to do it in real time.)

Hmm… So is the idea that you would use this “slice” to define the shape of the ripple? i.e. such that, say, a boat produces a ripple that matches the intersection of its hull with the water-surface?

If so, then I might suggest taking an image/shader -based approach:

  • First, render the object–or perhaps a premade “slice”, or one of a set of vertically-stacked “slices”, if the object has significant protrusions that might interfere with the shape–from a top-down perspective.
  • Then take that rendering and use it as an input to a custom shader that handles your water-simulation.

(I would imagine for this a simulation less rigorous than the one posted by Max, above. Splashes and things can be handled by special effects, rather than actual simulation!)

Hmm… this is a handy technique! Thank you!!

I should specify that I only want to use voxels for calculating how long it should take for an area to fill up, and where significant outflow might occur. And by ‘ripple effects’, I was imagining an intersection with the object putting out gradually expanding patterns, based on the intersection’s shape.

Another good method!

Although, now that I come to think of it, ripples probably wouldn’t be visible in rain-flooding. However, these techniques would be useful for ‘natural’/static bodies of water if I bake them at map compilation!

I thought I described my idea in a way where it was clear that it wasn’t actual simulation; but looking back I guess I didn’t; sorry for confusing people!

My general idea is that the stormcloud would pick several xy points to start flooding at. Then, a raycast would be used to detect where the z value of these points should be. Then, voxel testing would begin from these points outward.
Without going into too much specificty on how this voxel testing would work, while the voxel grid class would vaguely remember what positions were already checked, (probably using a SparseArray) voxels themselves would get deleted after being tested.

If a line of voxels can extend far away from the initial point, then that is an outflow. The voxel grid would place additional “samples” in areas outflow goes to, which store information on what the water level should be there at any given “flooding level”.

Then, if voxels interact with level geometry in specific ways; I.E. a branch of voxels collide with a wall with sufficient outflow force; a marker is placed, which tells the game to place an effect there; I.E. a constant wave crashing back against a wall.

Hopefully this explanation makes more sense. I don’t want voxels to be updated for the full duration of the flood, just during the initial phase, until the flood is operating entirely on markers and samples.

Hmm… I’m not sure that one really needs to know the distance from source that water can reach in order to identify an outflow.

I’m reminded of an old water-simulation that I did ages ago, in C++. It was much more limited in scope, but I recall it working well, and the idea might be useful, I think.

As far as I recall, it essentially modelled water as a field of columns. Each column affected only the columns beside it–any beyond that were irrelevant.

I may be wrong–it’s been a long time–but I think that the idea was basically to have columns “share” water with their neighbours in an attempt to equalise with those neighbours.

With such a system, I could see outflows being handled simply by checking the terrain-height for each neighbouring column:

For a given neighbour-column, if its terrain-height is above the current column’s water-level (that being water height + terrain height), then treat it as “impassable”–i.e. don’t include it in the simulation. If, however, the neighbour-column’s terrain-height is below the current column’s water-level, then treat it as a normal water column.

That way water would naturally flow through outflows as it rises above their “lips”, while still being constrained by ridges.

How well this would perform in Python I don’t know. It might be worth trying–but I’d be inclined to be ready to port it to a shader, I think.

I suppose you’re right; distance from the source is really more useful for for pressure than finding outflows. (On that note, one gameplay effect id want to implement is having water flow push players around; something you need to know both pressure and flow direction for.)

This is a good idea, and it’s similar to what I want to do; the only reason I find voxels to be more useful than columns is because of roofs and layered terrain. With columns it becomes difficult to determine what layer of geometry is relevant to the flow of water.

This would absolutely perform poorly in python even with the mitigating factors I already mentioned. Running it in a separate thread could also help a little… But it’s a good thing we have interrogate. :slight_smile:

Hmm… I’m not sure that you strictly speaking need it even for pressure: since each column would affect each neighbouring column, pressure would be conveyed from one to the next. That is, if a given column is at high pressure, it is so because its neighbouring columns are exerting that pressure, and they are so because of their neighbouring columns, and so on.

I mean, a column can have a top and a bottom, it seems to me.

It depends, I suppose, on how many layers of terrain you expect to have.

1 Like