Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.59k stars 244 forks source link

Highlight group of voxels #202

Open dsrw opened 3 years ago

dsrw commented 3 years ago

I need a way to highlight a group of voxels. I have multiple voxel objects that need to light up in some fashion on mouse over.

What I'm planning to do is bump MAX_MATERIALS to a much higher value, then give each object its own set of materials and increase the emission energy when the object is highlighted.

Is this reasonable, assuming the total number of materials doesn't get too crazy? Is there another approach that I should consider?

Zylann commented 3 years ago

If you refer to blocky voxels, that might be a wrong approach given how the module works and depending on your real requirements. I don't really understand how adding so much more materials would help. You mean you want to replace every highlighted voxel by a secondary version which is "highlighted" and happens to have an alternate material? You could do that, but that's not very practical, especially if you need over dozens of materials just for this. Increasing material count is desirable for other reasons so it might be possible in the future to make the number of materials dynamic instead (but you know, never thought it would be needed because 8 is plenty already for a Minecraft-like game).

Also depends how many voxels you want to highlight, and how. Do you want to highlight 1? 10? 1000 voxels? ALL voxels of a specific type? Is it just a visual swap? Or should it fade in? Is it an outline? X-ray? etc. It sounds to me that depending on the need, the solution will differ. Another approach involves leaving the current terrain alone, and instead generating a secondary mesh containing just the voxels you are interested in, and overlay this on top of the game using a post-process effect. Or just generating a "highlight" mesh instance, like the box around the pointed voxel in the demo (which is a wireframe cube), instanced one or more times. Similarly, that's how Minecraft overlays breaking sprites on top of the mined blocks. You can also remove the voxel and replace it temporarily with a mesh instance with some more fancy "highlighted" animations (i.e in Minecraft terms, replace it with an "entity", like falling sand and pistons do). If you only want to highlight the pointed voxel in that way, those latter approaches are preferable. Yet another approach for large selections would require to modify the mesher so it can read a special channel and do something to the geometry when the value of that channel indicates "highlighted". Downside is higher memory usage, unless you cleanup the highlighted values after use. That's not planned for now but something related might be implemented to convey voxel data to shaders (such as color tint).

I think some similar question was raised before but I don't remember which issue it was.

Side note: problems from the same paradigm happens with GridMap and TileMap. They are designed to support massive amount of items. The more item-specific behavior you want, the least effective the system is.

dsrw commented 3 years ago

Thank you for the thorough response, and apologies for the long delay. This is what I'm doing:

https://user-images.githubusercontent.com/1416/124312349-8a08f000-db45-11eb-8d58-d177258ab8a4.mov

My app consists of voxel "objects" -- a group of voxels that should be treated as a unit. This could be a character, a vehicle, or something larger like a castle. I want to apply per-object effects. Currently this is only the highlight (emission) effect shown in the video, but in the future I plan to have others.

Right now each object gets its own instance of a ShaderMaterial, and I tweak a parameter on the material during highlight. Nothing is being swapped, but it does means I could have a few hundred ShaderMaterials (MAX_MATERIALS = 512), all using the same shader. So far performance seems ok, but I haven't done any benchmarking.

This REALLY bloats up my VoxelLibrary, as I've been duplicating all of my voxels hundreds of times, each time using a different ShaderMaterial. This also seems relatively harmless, but I'd like to find a better way to do it.

Does this seem like a reasonable way to tweak shader parameters for a subset of my voxels? Should I be concerned about having a very large VoxelLibrary?

Thank you very much. godot_voxel is fantastic.

Zylann commented 3 years ago

Given the complexity of your objects, I'm wondering if you're using the right tool for the job 🤔 Just because something is grid-based in 3D does not mean it has to be the kind of voxels this module handles, but I could be wrong.

In this module, I guess the closest you can get to something efficient is to have one VoxelTerrain per "object", and when you want to highlight one, you just swap materials or tweak shader params (could be done by script). And there should really not be that many, if you need 512 then you're really exploding the point why they were limited in the first place. But if you want to have 512 materials in one castle, then you pretty much have no choice, isnt it? Why do you have so many? On your screenshot voxels are even just one color each, that would only require one material, with proper UV-mapping for the texture and color assignment.

There was few slots originally because this module uses batching to render blocky voxels efficiently. The fewer materials, the fewer surfaces the mesh will have, and the fewer draw calls there will be. It really is expected to re-use materials as much as possible. The blocky_game demo can render a Minecraft-like landscape with trees, shrubs, water, grass, rails and glass, with just 3 materials, thanks to atlasing (and smooth terrain does everything with just one). I'd like to handle materials differently some day (although there would still be a limit), but batching also means the module is optimized for big volumes which don't change often. If you want to change big regions frequently, it won't be fast unless you do everything through shaders or materials somehow. If you dont exploit that, you may just as well switch to regular mesh instances, or maybe multimeshes (but again, if you have 512 materials in a castle, you will still have the same problem).

dsrw commented 3 years ago

This was just an example. My worlds are less complex than minecraft, but still have many thousands of blocks. I saw a pretty big performance increase switching from gridmaps to your plugin, even if I'm not using it in the most efficient way.

I don't think I was very clear, but I'm using 1 ShaderMaterial per object. A castle would have 1 ShaderMaterial and the house beside it would have another. Setting MAX_MATERIALS to 512 means I can have 512 different objects. It's working well enough, but my VoxelLibrary can grow very large (I basically need a separate copy of my voxels per object), which is something I'd like to fix.

Multiple terrains sounds interesting. If each terrain only had 1 material, would supporting hundreds of them be feasible?

Zylann commented 3 years ago

Multiple terrains sounds interesting. If each terrain only had 1 material, would supporting hundreds of them be feasible?

Yeah that's the idea. By default terrain nodes are setup as "infinite" but if your objects have defined bounds you can assign them in voxel_bounds so having multiple nodes won't be a problem (it still depends what your needs will be tho cuz separate terrains might have pros and cons) Also, are your objects editable in game?

dsrw commented 3 years ago

Yes, objects are editable, most of the time at least. https://github.com/dsrw/enu is my project, if you want to see what I'm actually building. It's a 3D live coding environment, meant to teach programming and to build simple 3D games.

Objects can move. You could build a car (or whatever), then program it to drive around. Currently I swap my voxels out for a gridmap whenever I have to move or scale something, but it sounds like I can probably just move/scale the terrain if I use one terrain per object.

I haven't had a chance to try multiple terrains yet unfortunately, but I think it will greatly simplify a lot of what I'm doing. I really appreciate the help.

dsrw commented 3 years ago

Using multiple terrains seems to be working perfectly. As long as the bounds don't get too large performance is very good. Thanks a lot.