• RuntimesGodot
  • [Godot] Spine Backface Culling Feature

@Mario I want to inquire about what technical difficulties you had with backface culling if you tried to implement it into Godot runtime. I know Godot v3.5 only really support backface cull in 3D spatial nodes. Your response is much appreciated since the workaround would affect Spine weighted meshes and animation keying.

Related Discussions
...

I haven't tried implementing it because, as you also stated, it's really only supported in SpatialNodeinstances, which we currently do not support.

One possible approach for CanvasItem/Node2D would be to do the culling on the CPU: before submitting the mesh, iterate through all triangles, calculate their winding order, and replace culled triangles with degenerate triangles, e.g. by setting all indices of the triangle to the same index.

The issue with that is that it can become a bit complex if you also take into account negative scale.

Maybe ShaderMaterial could be coerced to do backface culling somehow, but I haven't looked into that too deeply. A quick look tells me it's not really possible.

    SilverStraw then I suppose all we can do is manual culling on the CPU, or implement a SpineSprite3D (for which there is an issue)

    2 Monate später

    I enabled 2D backface culling glEnable(GL_CULL_FACE) on the GLES3 reset_canvas method line in Godot 3.5 C++ source (https://github.com/godotengine/godot/blob/3.5/drivers/gles3/rasterizer_canvas_base_gles3.cpp#LL939C26-L939C26). GLES2 has an equivalent method also. The drawback is that it affects the editor GUI as well. I tried adding a working blackface culling option to the CanvasItem but I wasn't successful.

    The editor popup windows' background texture disappear when you enable backface culling on canvas reset 😅. This gdscript plugin patches the missing background textures by adding something in (hopefully).

    tool
    extends EditorPlugin
    
    var background_texture = preload("res://solid_color.png")
    
    func _enter_tree() -> void:
    	var base :Control = get_editor_interface().get_base_control()
    	var class_names_filter :Array =  [
    		"EditorAbout",
    		"EditorExport",
    		"EditorFileDialog",
    		"EditorSettings",
    		"ExportTemplateManager",
    		"PluginConfigDialog",
    		"ProjectSettingsEditor",
    		"ProjectExportDialog",
    		"ScriptCreateDialog"
    	]
    	var result :Array = []
    	self.findByClass( base, class_names_filter, result )
    	for control in result:
    		self.add_TextureRect( control )
    	result = []
    	#For what ever reason, assigning show_behind_parent for Popup controls in the same loop
    	#as the rest of the other control classes make the other TextureRect nodes disappear.
    	self.findByClass( base, ["Popup"], result )
    	for control in result:
    		var t :TextureRect = self.add_TextureRect( control )
    		t.show_behind_parent = true
    		#t.self_modulate.a = .5
    
    func findByClass(node :Node, class_names :Array, result :Array ) -> void:
    	for class_Name in class_names:
    		if node.is_class( class_Name ) :
    			result.push_back(node)
    	for child in node.get_children():
    		self.findByClass( child, class_names, result )
    
    func add_TextureRect( control_node :Control ) -> TextureRect:
    	var base: Control = get_editor_interface().get_base_control()
    	var texture_rect := TextureRect.new()
    	texture_rect.texture = background_texture
    	texture_rect.expand = true
    	texture_rect.stretch_mode = TextureRect.STRETCH_TILE
    	texture_rect.modulate = Color.gray
    	connect("tree_exiting", texture_rect, "queue_free")
    	control_node.add_child( texture_rect )
    	self.match_rect(texture_rect, control_node)
    	control_node.move_child( texture_rect, 0 )
    	return texture_rect
    
    func match_rect( of :Control, to :Control ) -> void:
    	of.rect_min_size = to.rect_min_size
    	of.rect_size = to.rect_size
    	of.rect_global_position = to.rect_global_position
    
    
    7 Tage später

    I got too excited at first 😞. There is an issue when the SpineSprite is on the edge of the view port that it breaks face culling and appear 🥲.

    8 Tage später

    Update:

    After more testing, inserting glEnable(GL_CULL_FACE) into RasterizerCanvasGLES3::render_joined_item seem to be a better location than RasterizerCanvasBaseGLES3 _draw_polygon ( https://github.com/godotengine/godot/blob/3.5/drivers/gles3/rasterizer_canvas_gles3.cpp#L1240 ). The editor user interface is not affected. Zooming in or sprites getting close to the view port edge does not break the face culling.

    4 Monate später
    Nate hat das Thema Godot hinzugefügt ().
    2 Monate später

    This is still using Godot 3.5 C++ source for GLES3 rendering. I finally got Godot shader language for 2d to control face culling.

    Source changes:



    Neat! Do you think the core devs would accept a PR from you? Seems like a feature not only valuable for spine-godot.

    I can try to do a pull request.

    ein Jahr später

    Update on Godot 4.3 using Compatibility renderer using GLES3.



    I forgot to add the line

    CullMode cull_mode;

    after the

    enum CullMode{
        CULL_DISABLED,
        CULL_FRONT,
        CULL_BACK
    }

    It would be totally worth it to submit that as a proposal to Godot. Exposing culling in 2D is a good thing for tons of use cases, not only Spine.