-
Notifications
You must be signed in to change notification settings - Fork 84
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
Terrain mesh generation/manipulation #66
Comments
|
Marching cubes Heightmap modification Perhaps just a node that loads a heightmap into whatever format we decide on. This way you can use a heightmap as a starting point, or use it as is and be able to place objects on the resulting terrain. *Work directly on vertices (Vector3) With that in mind, I wonder if it would be faster generation wise, if the modification nodes just pass along the operations to apply, and then the final Mesh node would run them all in a single loop? Maybe, maybe not. Apply Curve to Noise You can spend a lot of time tweaking this, Anyway, should adding a curve to a noise be done as a CURVE_2D input on the Simplex Noise node, or a separate node that saves the curve object into the generic noise object? Noise Combiner And yeah these noise changes will definitely be useful throughout. |
Heightmap modification
Vertex operations
Noise
|
I totally agree with you about heightmap tools. Non-destructive parametric editing all the way! :) Flatten, Erode, Smooth, etc While we don't have to generate things as fast here, if we're not careful, chaining a few of these operations could very quickly slow things to a crawl. I'm definitely keen for these types of nodes, especially an erosion node (note there's code for this in another project - perhaps Zylann's), but I do wonder how long this sort of node would run? Is using another thread for long operations an option for stopping the editor from hanging? Image manipulation The Performance aside, I think we should definitely have nodes for loading and saving a heightmap, as well as a node for setting shader parameters on a material. But in order to use the same nodes on both heightmaps and meshes, I think we should convert a heightmap to a mesh (or mesh arrays) and pass that around. Then convert back to a heightmap only when needed. Vertex operations Just like with my comments about image manipulation above, I also wonder about the overhead of passing around MeshInstances and regenerating meshes on every node. I assume it would be much faster to pass around an array of mesh arrays, with the actual mesh generation only happening at output time. Again I should test this assumption! Any thoughts? Applying a curve to noise So while a separate curve operation node would also be good, I do think it's worthwhile being able to couple a Curve object directly with a Noise object and have the changes show in the noise preview. |
If so, yes it could definitely be integrated in the noise object. |
Multi-threadingUm, this might not be higher priority than some of the other features right now!? Pool ArraysThe docs say they're optimised for memory usage, as they don't fragment the memory. But based on some discussions I read, it's not as simple as that: But it seem performance depends on what you do with the array. Testing with tool script and OS.get_ticks_msecsYup that's how I normally do it. On my system with Gogot 3.2 stable these are some significant results:
So basically don't append, especially to pool arrays, but if you access the pool arrays by index they're as fast as normal arrays, even if you pass them around (I added a tested for this too). Some other things to consider:
As an aside - a Timer node would be cool! It could pass through the input/output of other nodes, and have a second input/output to pass the msec value between two Timer nodes MeshInstance vs ArrayMesh vs Raw Surface ArraysSo I added tests for this to my fork of the benchmark project if you ever want to take a look. You're right that passing ArrayMesh around isn't significantly faster than using MeshInstance. But what I meant was that passing around the raw surface arrays should be faster, and it is.
But the bottom line is that it's can be between 25-100% times faster to pass around the raw surface arrays, rather than getting the surface arrays out and back into the ArrayMesh. Heightmap vs Vertex operationsI haven't specifically tested this yet, but in previous tests I've done, the same was true for getting raw data in and out of images. Like curves in image processing softwareYes that's a much better way to explain it - silly me. |
|
Class wrapper with surfaces and transform? Heightmaps separate What do you mean by this?
Heightmap system not part of the plugin ConceptGraphNoise |
|
HeightMapShape It's not so much about heightmap vs mesh, it's just a question of whether you have a uniform grid of points or not. I've messed with trimesh shapes for triangulated "low poly" terrain too, and while it's much slower to generate, I've never noticed any runtime performance issues with it. I didn't have lots of physics bodies interacting with it though, just a character controller. I've actually experienced more issues with the heightmap shape than with trimesh. Things like clipping on inclines when mesh resolution is low, and falling through when moving at speed. Heightmap offers control Possible Solution Then switch the flag in mesh modification nodes when the x/z of verts will be modified. Like if they're not 0 in an amount vector3 input. On the same page So yeah I think we're on the same page now. |
It's not going to be easy to have a reliable flag for x/z while making it clear for the end user what they can or can't do though. But I can start working on a prototype later this week end if I can fix two more pressing issues first. |
I was just thinking a property on the new So I'd say, lets implement Heightmap stuff on it's own for now, and see how it progresses. This flag thing isn't priority imo, but that's up to you. |
My bad, I meant I can work on a minimal set of heightmap nodes just to see how it goes |
Oh ok, but just note... I've done quite a bit of work around terrain generation in recent months, so I have bits of pretty optimised code for this stuff already. So yeah that's why I was pretty keen to work on it if possible. I have stuff like working with PoolByteArray, generating heightmaps and normalmaps from raw float arrays, an example vertex displacement shader with basic vertex coloring. Then there's the terrain mesh generation stuff, which could just be done as a simple end node for outputting the heightmap (for visualisation if nothing else). But it would definitely be good if you could setup the Heightmap input/output type, as I haven't looked into that part of the code yet, so aren't sure what's involved. I have some time this weekend too (public holiday on Monday too), so let me know what's priority for me to work on:
|
I think it's best to finish what was already started before jumping to new features. I'll setup the new data type for heightmaps but then, work on what you want the most ! |
So I've added some very basic heightmap stuff in cab8450 There's a new Nothing is optimized since it's mostly throwaway code
|
Awesome, thanks for setting that up. So yeah, writing into a PoolXArray property on an object is very slow. This is because they're only passed by reference when reading, but have copy-on-write functionality (for mutability?). The same is not true of normal Arrays. So in Anyway, this can be fixed while still using a PoolRealArray, by copying it to a local var before the loop and copying it back to the object after. In GDscript (and other languages), doing this for any variable reference external to a function is faster - when you're going to r/w it multiple times that is. But, so that no one hits this issue in future, lets stick to using generic Array's, and only convert to Pool arrays when a Godot method requires it - as their constructor functions allow this and the overhead is minimal. While this allows for much more flexibility with the
Those are much better generation times huh! :) Note that this loop is only running get_noise_2d, which is fairly fast. More complex operations will slow things down a lot more, and may make the difference between these options disappear. So I'll add those class methods (already have locally), and we can test performance again once we have more nodes to chain. I'll probably use option 1 for now. Will PR soon. Finally, in my quick benchmark test of this the other day, I was storing the object array to a local var, because it's something usually do anyway. So that's why the issue didn't show up then. Then optimising |
Thank you for the detailed explanation, that makes it a lot clearer! I completely missed the copy on write part, no wonder it was so slow. I tried generating a 2k heightmap and it worked well. It took 4 seconds on my CPU and the code only uses 1 thread so there's still room for optimization even before trying to do this in gdnative. (But that's not a priority). I'd like to experiment with new heightmap nodes but I don't want to rewrite code you may already have done before. Let me know if there's things I shouldn't be working on. |
My top priority will be adding I've also started work on:
And here's some nodes I'd like to see added (I'm keen to work on any)
So feel free to make a more final list which:
|
Awesome work! Let me know if there's strange conflicts with the base branch when making the PR. Apply Noise and Apply Image Could vector inputs be in the node by default like scalars Parameter changes How does the |
Apply Noise - ok will add this. Naming format again Having "Heightmap" in there is mostly necessary for search. But you're going to make search also use the category we could drop "Heightmap:" It is still good for visual clarity though. Blend Noise formula My current code does this in the loop of noises: I was thinking of adding a Adding multiple noises to the second input isn't strictly necessary, and it might make things easier if we don't allow that. Note: having the |
I'll add a dedicated page for naming conventions in the wiki. Blend Noise formula Having a |
Along with the height map to vertex shader node... I'm working on an apply image node that takes a texture input from an inspector property. Currently I have two vector3s for scale and offset, with the defaults fitting the image to the full size of the heightmap (like apply noise node). I could change this to take multiple box inputs for stamping multiple instances of the image onto the heightmap? Or perhaps that should be a separate node? |
It would probably be simpler if the stamping was its own node. Otherwise you have an |
Hey @HungryProton I've jumped into an even better solution for real-time heightmap editing! It's basically using the Viewport like a document in Photoshop, where the So I'm now planning to redo the It's going to be epic! Hopefully I can get something usable setup this week. |
If it works, it's going to be great! Let me know if you need anything, I'm mostly working on bug fixing for now |
Hey I'm still working on this viewport based heightmap solution, but have been busy recently. |
Hey! No worries, I've been quite busy too lately. I glad to see that your idea worked! Feel free to put it online so I can take a look. I don't know if you heard about it but I'm turning the add-on into a full standalone software. It shouldn't affect your work at all, it's still made with Godot and I'm only rewriting the interface, I'm not changing anything about the nodes. Being standalone makes it much more stable and a bit faster too! But that means ConceptGraph now works at runtime and the next big thing on my roadmap is to make work in a game, not only in an editor. I think that's something you were interested in too. I'll let you know once I made more progress on this part, it would be interesting to see how far we can go with your new height-map system 😃 |
@HungryProton we discussed a
Plane Mesh from Transforms
node for terrain here: #59 (comment)The main idea is to be able to generate terrain meshes from noise.
But it would be nice to apply other operations/deformations as well.
You asked if it would need to be a perfectly aligned grid of transforms to generate a terrain?
Yes and no.
So yes the issue is that a NODE_3D input can be a very random set of 3D points.
Mesh from Transforms - Marching Cubes?
A proper
Mesh from Transforms
node would have to apply a marching cubes algorithm I think.This would be very cool, but it's much more complex and possibly out of scope for this project?
It's definitely not what I'm intending here.
Plane Mesh options:
You pointed out that for a Plane Mesh node, a direct noise input might be better.
This solves the above problems, but it limits the possibilities.
Here's some examples of what I'd like to make possible:
Some possible solutions:
Apply multiple noises - this could be partly solved with dynamic slots on the noise input, but that wouldn't allow for applying other manipulations as well.
Merge Noises
node might help here, and could be more widely useful? But if it wasn't working on a NODE_3D array (which it can't be here), I think it would have to pass a noise wrapper class around that encapsulates looping over multiple noises when sampling a point. This wrapper could also handle any new noise implementations we might get? (I've seen you mention that).Apply a 2D curve to each noise - a CURVE_2D input on the Simplex Noise node could be worthwhile, as I can see it being useful elsewhere!?
Smooth Noise
node is another option, but again it has to be applied to a node array, or a noise wrapper class that applies the curve to the noise at sample time.This comment is already way to long, so I'll stop there.
Let me know your thoughts.
The text was updated successfully, but these errors were encountered: