Optimizing concave polygons (3D)

Godot Version

4.2

Question

My goal is to set up a process for generating fairly accurate collision shapes for custom Sprite3Ds (for mouse detection). So far I’ve more or less solved it: I’ve found an amazing plugin for generating meshes from sprites, although it is fairly slow - so I run it in the editor, export the meshes, and then convert them to ConcavePolygon3Ds at runtime using create_trimesh_shape(). However, this feels like an overly complicated process - it beats manually drawing the shapes (since I want my process to work regardless of specific sprites), but since I’m preparing stuff in advance anyway, I might as well skip the conversions and export the collision shapes. Especially since I end up with a 500KB mesh for a 50KB sprite; for a thing that is supposed to be just an array of coordinates, something definitely isn’t right. Is there a better semi-automatic way of generating those polygons? Not necessarily in Godot itself, I’m down to using Blender or whatever - although in that case I’d appreciate some keywords to investigate because I’m only learning the basics of 3D.

1 Like

Well, it took me a while, but I’ve learned how to generate collision polygons from sprites, skipping meshes entirely. Hopefully this will help someone else. Here’s my collision_generator.gd:

class_name CollisionGenerator
extends Resource

# Based on a 2D tool by Kevin Thompson
# https://gist.github.com/kevinthompson/84b2010306817d7845d2b11e96da2c48

func generate_collision_from_sprite(target_parent : Node, source_sprite : Sprite3D, standing_up : bool):
	# Destroy Existing Collision Polygons
	for node in target_parent.get_children():
		node.queue_free()
	
	# Generate Bitmap from Sprite3D
	var image : Image = source_sprite.get_texture().get_image()
	image.decompress()
	var bitmap = BitMap.new()
	bitmap.create_from_image_alpha(image, 0.1) # Opacity treshhold
	
	# Create Collision Polygon from Opaque Pixels
	var scale = source_sprite.pixel_size
	var polys = bitmap.opaque_to_polygons(Rect2(Vector2.ZERO, image.get_size()), 10.0) # Set to 0.0 for pixel perfect
	# Flip and resize for 3D
	var transform = Transform2D.FLIP_Y * Transform2D(0, Vector2(scale, scale), 0, Vector2.ZERO)
	for poly in polys:
		var collision_polygon = CollisionPolygon3D.new()
		collision_polygon.depth = 0.05 # Increase if you want fake depth (e.g. with stacked sprites)
		collision_polygon.polygon = poly * transform
		target_parent.add_child(collision_polygon)
		# Shift the shape to match the sprite (it's usually centered)
		collision_polygon.position.x -= image.get_width() * scale / 2
		if standing_up:
			collision_polygon.position.y += image.get_height() * scale # /2 if the sprite isn't anchored down
		else:
			collision_polygon.set_rotation_degrees(Vector3(-90, 0, 0))
			collision_polygon.position.z -= image.get_height() * scale / 2

With this, I call CollisionGenerator.new().generate_collision_from_sprite($BoundingBox, $Sprite, standing_up) in the “_ready” function of my object, and it works like a charm. Result:
image

1 Like

Nice. Appreciate the followup.

Just an fyi if younever need it:

I have had good success using python scripts in blender to automate some things.

Since both blender and python have a lot of resources, chatgpt gets me close enough that I can tweak and fix without slogging through a bunch of docs.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.