godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.34k stars 21.06k forks source link

GridMap tiles should cull faces that are covered by tiles #17811

Closed ghost closed 4 years ago

ghost commented 6 years ago

latest official 3.0.2 nVidia GeForce 1050 Ti

GridMap should cull tile faces that are covered by other tiles.

This is the tilemap test structure image

When I go inside one of the tiles I see the faces of the other tiles. image

The faces marked with X should in fact not be drawn because they are completely covered by the tile I am currently in.

This obvs will only work for FULL tiles. Tiles that are smaller than cell size will cause issues. Therefore this functionality should probably work only in script and tiles should be able to be marked as full or transparent where full = full cell size and transparent = smaller than cell size and only full will be face culled.

Zylann commented 6 years ago

In fact if you go that route you would need more than one flag, since you can make tiles that have some sides full and not others.

Also if you wanted to optimize even more, hardware instancing or mesh batching would need to be used.

For now, this culling must be done manually by baking every possible combination of meshes with faces removed, like in the platformer demo, or just accept the overhead (which limits a bit the size a gridmap can be).

ghost commented 6 years ago

@Zylann That's why I suggested should be an enhancement that is only available in script .

My idea is very simple:

Each tile type gets a new flag called full_block by default false. Note that you would only set the flag on the tile types NOT each individual tile. This flag may only be set from script but it may be possible to add this option right into the tile list or in the editor gui.

If full_block is true then completely cull any faces shared between it and any adjacent tiles also marked as full_block == true. As easy as that.

So this functionality would exist but only be available in script by manually flagging each tile type as a full_block = true and would automagically do the culling.

What do you think?

Zylann commented 6 years ago

It's a handy optimization, but perhaps it should be a mask, for adressing all 6 sides (where full would be 0x3f) so we have a finer grained tuning. Going further would need to allow setting up configuration pairs so that two given neighboring tiles will know if they should occlude their touching face or not (in case that face isn't a full quad but is occluding perfectly anyways, if you make a pipe or rounded walls for example).

Keep in mind that each tile of a gridmap still behaves like a MeshInstance for the renderer. Supporting this feature requires the MeshLibrary exporter to bake meshes by separating sides, so they can be turned on or off. However, doing that would increase the amount of draw calls because 1 cube with 4 visible faces won't be 1, but 4 objects to cull and draw by the renderer. This can be ruled out with either instancing, or mesh batching into octants.

This is a technique I've been doing in my voxel module (which uses mesh batching), and that's how I was able to render such a high amount of voxels compared to gridmap. The hard part is figuring this out for any kind of tile mesh, not just cubes. It depends how far we go.

Also remember that such optimization would work only if you take the time to fill your shapes entirely. If you make a 10x10 big cube, you will have to fill it completely with cubes otherwise there is no chance for cubes inside to be culled.

ghost commented 6 years ago

@Zylann Ah I see. You're thinking ahead. I just wanted something really basic like full cube or not as is much easier to implement. Then once that works could be expanded into what you propose. I defer to your judgement as you seem way more knowledgeable about this issue than me.

Zylann commented 6 years ago

@CarlGustavAlbertDwarfsteinYung in fact I got that far because even the simple fact of culling individual faces like you want requires already a great deal of changes, which once done would naturally be expandable to a 6-side check.

Actually the simplest thing you can do is to not cull faces, but cull entire tiles if they are fully covered on all sides, which however isn't that common and forces you to fill such regions completely, which in the end would give you the same result as if you didnt fill in the first place.

Calinou commented 4 years ago

Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine.

The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker.

If you are interested in this feature proposal, please open a new proposal on the GIP tracker following the given issue template (after checking that it doesn't exist already). Be sure to reference this closed issue if it includes any relevant discussion (which you are also encouraged to summarize in the new proposal). Thanks in advance!

Zylann commented 4 years ago

Just FYI: my voxel module, which uses batching instead of instancing, now has an automatic implementation of side face culling. It's not the most clever one, but it works pretty well and fast.

NB: If hardware instancing is considered instead of batching, side triangles can be discarded in vertex shader (outputting NaN) by providing access to cell type, neighbors and matrix in the shader.

NB 2: my module doesnt only check this, because materials must be taken into account as well. Transparent parts are given a special, but similar treatment.