Skip to content

Instantly share code, notes, and snippets.

@HungryProton
Created October 10, 2025 10:33
Show Gist options
  • Save HungryProton/4d33370d5cea40d73656fd1bccc7444a to your computer and use it in GitHub Desktop.
Save HungryProton/4d33370d5cea40d73656fd1bccc7444a to your computer and use it in GitHub Desktop.
Import script example
@tool
extends EditorScenePostImport
## Import script to automate various parts of the pipeline. Features:
## + Primitive collision shapes support
## + Shadow only meshes
##
## COLLISIONS:
## + Append '-static' to the node name to mark it as a static body
## + Add child empties to create the collision shapes
## - Supported shapes: -collider_box, -collider_sphere, -collider_cylinder
##
## SHADOW ONLY MESHES
## + Append '-shadow_only' to the mesh name to only cast shadows
func _post_import(scene: Node) -> Node:
print(scene.name)
scene = _process_colliders(scene)
_process_shadow_meshes(scene)
_fix_owner(scene, scene)
return scene
const COLLISION_STATIC := &"static"
const COLLISION_ANIMATABLE := &"animatable"
const COLLISION_KINEMATIC := &"kinematic"
const COLLISION_RIGID := &"rigid"
const COLLIDER_ROOT := &"-collider_"
const COLLIDER_BOX := &"_box"
const COLLIDER_SPHERE := &"_sphere"
const COLLIDER_CYLINDER := &"_cylinder"
const COLLISIONS := [COLLISION_STATIC, COLLISION_ANIMATABLE, COLLISION_KINEMATIC, COLLISION_RIGID]
func _process_colliders(root: Node) -> Node:
if root is Node3D:
root = _process_colliders_for_node(root as Node3D)
for child in root.get_children():
@warning_ignore("return_value_discarded")
_process_colliders(child)
return root
# TODO: Add support for animatable and character body
func _process_colliders_for_node(node: Node3D) -> Node3D:
var splits := node.name.split("-")
var type: String = splits[-1]
if not type in COLLISIONS:
return node
node.name = node.name.trim_suffix("-" + type)
var body: PhysicsBody3D
match type:
COLLISION_STATIC:
body = StaticBody3D.new()
COLLISION_ANIMATABLE:
body = AnimatableBody3D.new()
COLLISION_KINEMATIC:
body = CharacterBody3D.new()
COLLISION_RIGID:
body = RigidBody3D.new()
body.name = node.name + "Collider"
body.scale = Vector3.ONE
for child: Node in node.get_children():
if not child is Node3D:
continue
var ref: Node3D = child as Node3D
var shape := CollisionShape3D.new()
shape.transform = ref.transform
shape.scale = Vector3.ONE
if ref.name.ends_with(COLLIDER_BOX):
var box_shape := BoxShape3D.new()
box_shape.size = ref.scale * 2.0
shape.shape = box_shape
elif ref.name.ends_with(COLLIDER_SPHERE):
var sphere_shape := SphereShape3D.new()
# TODO: Check is the scale is the same on all 3 axis
sphere_shape.radius = (ref.scale.x + ref.scale.y + ref.scale.z) / 3.0 # HACK
shape.shape = sphere_shape
elif ref.name.ends_with(COLLIDER_CYLINDER):
var cylinder_shape := CylinderShape3D.new()
cylinder_shape.height = ref.scale.y
cylinder_shape.radius = (ref.scale.x + ref.scale.y) / 4.0 # HACK
if shape.shape:
# Rename the collision shape and reparent the child nodes if any
var strip_from: int = ref.name.rfind(&"-")
shape.name = ref.name.left(strip_from)
body.add_child(shape, true)
for ref_child: Node in ref.get_children():
ref_child.reparent(shape)
ref.queue_free()
else:
shape.queue_free()
if body.get_child_count() > 0:
node.add_child(body, true)
else:
body.queue_free() # No collision shape references found.
return node
const SHADOW_MESH := &"-shadows_only"
func _process_shadow_meshes(root: Node) -> void:
if root is GeometryInstance3D and root.name.ends_with(SHADOW_MESH):
(root as GeometryInstance3D).cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_SHADOWS_ONLY
root.name = root.name.trim_suffix(SHADOW_MESH)
for child in root.get_children():
_process_shadow_meshes(child)
func _fix_owner(root: Node, owner: Node) -> void:
root.owner = owner
for child: Node in root.get_children():
_fix_owner(child, owner)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment