Skip to content
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

Water Simulation? #20

Closed
iUltimateLP opened this issue Mar 9, 2016 · 115 comments
Closed

Water Simulation? #20

iUltimateLP opened this issue Mar 9, 2016 · 115 comments

Comments

@iUltimateLP
Copy link
Contributor

iUltimateLP commented Mar 9, 2016

Okay, lets get to a real hot topic. Water simulation? How would I proceed? And, how do I generate just a simple ocean? I think I need to drop my project because this is all stuff I'm not aware of 😢

@miguelemosreverte
Copy link
Contributor

Hey I did it. Not the real-time flooding part, but the part where water instantly floods what it can, being the only parameter to it that it should never flood anything taller than its origin point. (Which is a gross simplification, given pascal law and all).

So, the idea is simple, for it to work fast I developed a data structure (not compressed... which is bad), that stores every possible 2d surface for a given 3d lake/ocean.

I can make a pull request if you guys think it is a good idea. Oh, but here is the kicker, and this Scheidecker probably knew all the time: About the water. You can drag and drop any water material to the surface material of a given block material. Which is awesome. Here is a screenshoot of my results: (You can find the water material I use here: https://github.com/UE4-OceanProject/OceanProject)

screenshoots:

water

And by doing some tweaking now the blocks under water blocks are rendered too:

translucent water with the blocks rendered correctly
under water surface

@iUltimateLP
Copy link
Contributor Author

That looks awesome!
Sure thing that you can apply any material you want onto stuff, so I would like a pull request 👍 😂

@miguelemosreverte
Copy link
Contributor

Give me a sec (maybe a couple days) to clean it up so it is ready to merge, and then I will make the pull request. About the data structure, on a basic level it stores the FInt3 Coordinates of the bricks that conform each one of the XY slices of a XYZ lake. Something I am working on is to reuse that information for custom pathfinding. Because if you use the collision of BrickGrid along with UE4 NavMesh you will get unrealistic movement: People will find no trouble climbing unnecesary elevations. But if you use the data of the lakes, then you could develop your own pathfinding (which could be cheaper) where the people will always walk on flat surfaces, unless its not possible.

@iUltimateLP
Copy link
Contributor Author

Nice to hear that somebody is actually working here 👍 Looking forward :)

@miguelemosreverte
Copy link
Contributor

My goal is an RTS on top of a voxel world, whats yours?

@iUltimateLP
Copy link
Contributor Author

A crossover between Starbound + Minecraft + Factorio 😂 Not even sure if I bring this to a certain point

@miguelemosreverte
Copy link
Contributor

Sounds like you are going to work a lot more than I on the gameplay dynamics. Factorio especially, is all about that. Tons of different options and winning strategies.

@iUltimateLP
Copy link
Contributor Author

Yep, and I'm not sure if I even finish this, but if I do, it'll go on Steam :)

@miguelemosreverte
Copy link
Contributor

Greenlight is the way to go. Kickstarter + Greenlight, really. Kickstarter can be used for the money, but the exposure is in my opinion more important. There you can find artists ready to work on the art on top of your game. Maybe not kickstarter, is not like I have done proper research, but on a general scale, that would be the way to go. Develop the logic, expose the product, find the team (ussually the artists), and then Greenlight. Maybe I am wrong but, again, its not like I am close to that part of the developing process so.

What I sometimes find weird is what is the motivation of Andrew to build this absolutly fantastic piece of code. I mean, you want to make your game, I want to make mine, whats up with the creator of the most complicated part of the work we are working on top of? I would not be suprised if tomorrow a game is shipped and it was made by Andrew. I am ust curious what genre would it be of. One thing is certain, it would have a Minecraft feel to it for sure.

@iUltimateLP
Copy link
Contributor Author

I don't think he is working on this currently. I hate people calling games "Minecraft Ripoffs" just because they share the way of doing a terrain and a building system...

@miguelemosreverte
Copy link
Contributor

Indeed! Is like minecraft has the monopoly of voxel use in games! That said, given the popularity of it, it can be said that games using cubes as the representation of voxels have a similar aestethic.
*aesthetic

@iUltimateLP
Copy link
Contributor Author

Does your simulation also generate water lakes through the world or does it only support "oceans" yet?

@miguelemosreverte
Copy link
Contributor

Not really a simulation, but a reduntant database. The information I am creating is implicit on the Region.BrickContents. It satifies the following query:

What bricks can I "flood" from this location. Where "flood" means access all adjacent empty bricks of the same height or less.

You could access this information by using a search algorithm every time. Or you could "cook" it. Which bring us to the redundant database.
Now, about its data structure:

It works like this. Inside the function SetBrick it checks if the brick set is of a given material. Lets say the material that floods depressions is 1. And the material used for the water effect is 9.

Then it accesses with the given coordinate to an index file that says what lake this block belongs to. On this file, absolutly all the blocks coordinates are linked to LakeIndexes. Non Empty bricks has a LakeIndex of -1, as they cannot be flooded. A LakeIndex is the key to then access the Lake information itself. Right now if you see a LakeIndex of 4, it means the LakeInitialCoordinate was X = 0, Y= 0, and Z = 4.

Anyway. With the LakeIndex found, now you can access the lake information, which is inside the file that has the LakeIndex as name.

Inside the file you can access all the floodable bricks, and for each one of them you change their material and boom. The lake/ocean is flooded.

To make things more complicated: Everytime I talk about accesing a Lake information, well, I should be saying LakeSlice information. Because there are as many slices in a lake as possible floodable heights. So say you are flooding a recipient at the middle, for it to flood only to the middle it would use the LakeSlice that correspond to the middle of it, not the one at the top. So, with that out of the way, how then can you flood the LakeSlices underneath the one you are flooding?...
Each LakeSlice has the information of what LakeSlices to flood underneath:

example

So, in this way, now you store all the floodable bricks for each LakeSlice, and the indexes of the underneath LakeSlices too.

Next complexity step: How to simulate the breaking of a dam?

You add a third reduntant information to the data structure. Now each LakeSlice stores it X, Y, and -X, and -Y frontiers. Each one is an array of brick coordinates. This way when you destroy a brick you check its LakeIndex, or LakeSliceIndex, whatever. Uhm... well, not literally the coordinates of the brick you are goind to destroy, but the one adjacents to it. That sounds right. Okay so you access the LakeSliceIndex of each one of the adjacent bricks and if that adjacent brick belongs to the frontier of the LakeSlice then you create a flooding brick on the place of the brick you just destroyed. And boom, now you can breaks dams, or any water recipient, and expect flooding.

@AndrewScheidecker
Copy link
Owner

@miguelemosreverte, that looks great!

I'd definitely merge a pull request to make translucent bricks render correctly. If you submit a PR for water flow, I'd consider merging it, but only if I was prepared to support the code (though I know my support lately has been lacking). Even if I don't merge it, the PR can still be helpful for other people who want to use your changes.

From reading your overview of how it works, my primary concern would be the performance of recomputing the flood volumes when a brick changes. If you add or remove a single brick, it could cause the flooding information to change over a very large area, so it might be hard to do incrementally. I think that's an argument for something more like Minecraft, Dwarf Fortress, and other games that implement water that flows from block to block in real-time: it localizes the effect of modifying the bricks.

What I sometimes find weird is what is the motivation of Andrew to build this absolutly fantastic piece of code. I mean, you want to make your game, I want to make mine, whats up with the creator of the most complicated part of the work we are working on top of? I would not be suprised if tomorrow a game is shipped and it was made by Andrew.

BrickGame was a fun side-project I did when UE4 was first released publically, and wanted to provide an example to folks how to implement rendering plugins like this. I've just been doing minimal maintenance work since then, except for my VXGI experiment. You don't have to worry about me competing with you if you want to release a game using this code!

@miguelemosreverte
Copy link
Contributor

I knew you where going to be specially attracted to the implementation of translucent bricks, because while flooding algorithms are cool and all, they can made with time and effort. But tweaking the render code? That requires more thought! And you specially would be, if not the only one, the one to know this since you are the creator of it!

So, yeah. With that to a side (looking back to it I just added two or three lines of code, its not impresive to look at if you do not know the complexity of the code), about the flooding algorithm:

Cellular automaton is a naive algorithm, it does not provide scalability. What I am looking for is to make the information much more redundant, to the point where PC A can say to PC B:

"PC A : Hey, remember the LakeSlice 204534 of the Region 300?
PC B: Yup.
PC A: Player 0 just flooded it. These are the holes done, so you know the flux.
PC B: Right. So given all the previously recorded data, we both know it will take an hour to fill.
PC A: Yup. Important locations flooded include: This guy castle, and etc.
PC B: I know, why are you telling me this?
PC A: Sorry, sometimes I forget the flooding logic is deterministic.
PC B: Duh."

So, the idea would be that by having a reduntant dabatase communication would be small, if at all needed. About the real-time super cool flooding effects I believe a centralized cloud-based computing could solve the problem. It would give the clients the vertices of the poligon used for the wall of incoming water. Okay, I am rammbling away. I expect to have the Pull request ready for today .

If my database teacher heard me talking about redundancies as a good thing he would be so mad...

@iUltimateLP
Copy link
Contributor Author

When I was thinking about basic water simulation, my idea was pretty simple: When you populate the blocks according to the noise (the three loops), you basicially get every block which is below a limit (like the material selection works right now) - the sea limit. Then when a block is under the sea limit, you create an additional block which goes some values below the original one in the height axis. That forms the ground of the sea. The block you had originally is the new sea block, so that gets the ocean shader on it. Two problems still: aligning the post process volume to fit under the blocks, and second: the transition between non-water and water would be rough, as non-existent.

@miguelemosreverte
Copy link
Contributor

Oh do not worry about the post-process volume. Andrew gave us the example on how to create redundant blocks on top of each other: You have the ones you use to render, and all is good. But now what about collision?? Well, as seen in the example given, you create collision boxes in the exact same spot. Do the same with post process and then with Physics Volumes (to add buoyancy forces, for example) and you are done!

I believe I should appoint this as my next task now that I see the urgency of it.
Now a problem I should solve too is when you use a tesselated material on the rendering brick, and the brick surface goes higher in space, but the collision, PP, and physics boxes are still the same size. Now thats a problem. And if Andrew does not hace a space under his slive then we are all doomed! hahaha
*spade under his sleeve not space under his slive

@iUltimateLP
Copy link
Contributor Author

I look forward to test your pull request out! Thanks for contributing to this and helping me with it the same time!

@miguelemosreverte
Copy link
Contributor

I guess so, but really, thanks to Andrew. He is exactly what every developer community needs. To code for fun speaks volume of his qualities as a programmer. Is like those olimpic runners that go to "work out" every day and by working out I mean do stuff we mortals can only imagine because we grow tired half way.

@miguelemosreverte
Copy link
Contributor

ISP change complicated downloading the repository. Downloading it now. Delay 1 day.
Oh and Nvidia open sourced Gameworks.

@iUltimateLP
Copy link
Contributor Author

Oh nice! Could this also allow glass blocks?

@miguelemosreverte
Copy link
Contributor

sure. A list of transclucent materials could be implemented so that instead of just checking for material 9 it would check for the ones you added as translucent.

@iUltimateLP
Copy link
Contributor Author

Nice stuff! I will test it out soon!

@miguelemosreverte
Copy link
Contributor

First problem found: All this time it worked because the water flooded until it found a non empty cube.
But now, by spawning translucent blocks with empty neighboors at the sides, this happens:

image

The translucent cube has vertices with index 0 at the sides. This way the only well rendered translucent cubes are the ones surrounded by other cubes: (which had been the case until not long ago):

image

image

Bassically its a matter of changing the vertex index of the two bottom vertices on the side faces of the translucent cubes. I will get onto it. But for now, you have the same code I used to make translucent surfaces for water.

See how the faces at the other side are invisible? Well they are not, they too have vertices with index 0 and go straight down to the corner of the region.

Same problem with the top surface faces: Right now it only works right if it has non empty neighboors to the sides:

image

image

image

image

image

It should be easy to fix.

@iUltimateLP
Copy link
Contributor Author

It should be easy to fix.

For you my friend, for you 😂 👍

@miguelemosreverte
Copy link
Contributor

I am trying to find a pattern:

Before (orange silouettes represent how the cubes should look like, but dont)
image
After (I add more non empty bricks to the sides)
image

@iUltimateLP
Copy link
Contributor Author

it looks like there is an vertex missing to make it a rectangle..

@miguelemosreverte
Copy link
Contributor

Yup. Vertex Index = 0 ---> Problem
image

@iUltimateLP
Copy link
Contributor Author

Couldn't you just add another vertex to that position (maybe there are two missing because of the lower one of the two)?

@miguelemosreverte
Copy link
Contributor

Exactly. Trouble is to understand why this has gone wrong, since in theory this should not be happening. But... oh well. It will be a matter of looking at the code for a while sipping coffee. Will update immediately when the culprit is found.

Edit 1: I think I solved it: Debugging breakpoints show that only once per translucent brick the boolean IsTranslucentBrick is true. Which sound right, right? Not at all. That variable used to be called something like IsWaterLocalVertex, or along something along those lines. The thing it that every time that was true a vertex was created. And behold, it was only true once per translucent brick. So only one vertex was created. What was I thinking. Anyway. That has been changed, and the code is bug free now. Enjoy your glass cubes!

@iUltimateLP
Copy link
Contributor Author

Awesome, but why does the water start expanding horizontally in the air?

@miguelemosreverte
Copy link
Contributor

i am trying to find that out right now. The algorithm is fine, bassically it says if empty downwards go down, if not then expand to the sides. The bug happens when a brick that is on the list of the ones that have water is on top of recently flooded bricks. Then it acts like this bricks are the ground and it expands too. I will solve it by saying it only can expand to the sides if the block underneath is not water. Not without permission, that is. Later on I would have to work on that. So that once the water touches ground it expands, and later on it goes higher in height and expands again.

@iUltimateLP
Copy link
Contributor Author

Thought that also. How do you manage which block is water? Again a hardcoded material index?

@miguelemosreverte
Copy link
Contributor

yup. bad habit of mine.

@miguelemosreverte
Copy link
Contributor

fixed the bug, uploading video

@miguelemosreverte
Copy link
Contributor

@miguelemosreverte
Copy link
Contributor

image
image

@iUltimateLP
Copy link
Contributor Author

Looks cool, much better like Minecraft actually 😂

@iUltimateLP
Copy link
Contributor Author

Did this actually evolved out of one water source? Isn't it a bit much? 😂

@miguelemosreverte
Copy link
Contributor

3 block sources, but still, lots of water

@iUltimateLP
Copy link
Contributor Author

Is this bad or good?

@miguelemosreverte
Copy link
Contributor

I mean, is not like all of that water came out of ust one single source, so its good. It came from three.

Hey I am working on the water vertices:
https://www.youtube.com/watch?v=jRzuxa5Bqyk&feature=youtu.be

Main problem: How to control them individually! (Probably c++ would do the trick)

@miguelemosreverte
Copy link
Contributor

image

@iUltimateLP
Copy link
Contributor Author

Does this work as intended?

@miguelemosreverte
Copy link
Contributor

No! WIP!

@miguelemosreverte
Copy link
Contributor

If you take a look at the materials created by Scheidecker, they all hack FVertexColor, I believe, and use its alpha to send the uint8 value of the AmbientOcclussion. We could copy that so that the C++ info is used by WorldPositionOffset.

@miguelemosreverte
Copy link
Contributor

The AmbientOcclusionFactor was hacked to send zeroes or ones depending on the face of the brick. If the face was the top, 1 was sent and that allowed for WorldPositionOffset to work.

Result

@miguelemosreverte
Copy link
Contributor

image

@iUltimateLP
Copy link
Contributor Author

That looks cool! Is this depending on how much water the block before has? So if you spill water on the ground, the last one is really flat?

@miguelemosreverte
Copy link
Contributor

Not yet, but as you can see, we are getting there!

@miguelemosreverte
Copy link
Contributor

where this value is constant: (to achieve what you want it should be dynamic)
image

Right now on the AmbientOcclusionFactor 128 and 0 are sent (and then clamped to 1 and 0) to multiply the WorldPositionOffset, so that only the faces on top, the ones with index 5, are affected.

We could use AmbientOcclusionFactor as the conveyor of the dynamic height too!

@miguelemosreverte
Copy link
Contributor

It would end up looking like this:
image

@iUltimateLP
Copy link
Contributor Author

Wrong picture? :D

@miguelemosreverte
Copy link
Contributor

No! The value of the Vertex Color AmbientOcclusionFactor is now whats added to the Z component of VertexNormalWS.

@iUltimateLP
Copy link
Contributor Author

Yeah now it is right, first of you had two times the same picture inside :) Keep me in contact!

@miguelemosreverte
Copy link
Contributor

hey with BFS implemented and WorldPositionOffset modifying the top faces of the water bricks I think you have a nice starting point to work towards water simulation.

I must start spending more time studying so I am going to let you continue on your own.

Some tips:

.By using AmbientOcclusionFactor now the bricks that surround water are really dark, thats a bug that requires fixing
.To continue flooding after well, flooding stops, you are going to have to implement something else. I would suggest going over to the Minecraft implementation of water and checking how they do it.

@iUltimateLP
Copy link
Contributor Author

I think they just check how many iterations are already done and have a limit there.. I only would need to check if it just expands horizontally..

@miguelemosreverte
Copy link
Contributor

miguelemosreverte commented Aug 25, 2016

After all this time I finnally made a somewhat cleaner version of the implementation of the database, both the one of the BrickContents and the one about the precomputed flooding of the water.
Here's the link:
https://github.com/miguelemosreverte/BrickGame/tree/PrecomputedWaterFloods

@miguelemosreverte
Copy link
Contributor

How's your implementation of water going?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants