Blender to Godot Z-Axis Best Practice?

Godot Version

4.4.1

Question

Hello! I’m currently exporting my first 3D model into Godot for use as an animated character.

I was wondering what the best practice was for dealing with the Z-axis discrepancy between Godot and most Modeling Software. That is, that when importing Blender models directly into Godot, that they will be facing “backward” and not “forward”, which is the -Z axis.

From what I can currently gather, my options to deal with this quirk are to:

  1. Rotate the model in advance in Blender before exporting.
  2. Export the model as is, then rotate it in Godot
  3. Export the model as is, and then just use the model-orientation specific arguments for functions such as look_at()

What is best practice and has the least negative side effects?
On-the-face, Option 1 seems most stable but most cumbersome as it requires re-rotating the model before and after every export.
Option 2 seems fine, but I don’t know if this will have side-effects.
Option 3 also seems fine, but error-prone if you constantly mix between models that have a well defined forward face, and models that don’t.

1 Like

In Blender there’s an export option which allows you to export for -Z facing forward. I don’t have time to type up steps for you now. But if you control the model, that’s what I would recommend.

Option number two I would recommend if you don’t control the model, which is to create an import script that just rotates the rig 180 degrees on the Z-Axis for you.

It appears that this option is not available for .glTF which seems to be the format the documentation recommends to use. The only transform option available fot .gltTF is for Y to be up.

Then use option number 2. Here’s the script I use for my rigs. You can extract the rotation part out if you want. To use this or any import script.

  1. Click on the .gltf file in FileSystem.
  2. Click on the Import tab in the upper left-hand corner.
  3. Scroll down to ImportScript.
  4. Click the file icon and browse to the location of the import script.
  5. Click the Reimport button.
@tool
extends EditorScenePostImport


enum LoopMode { LOOP_NONE, LOOP_LINEAR, LOOP_PINGPONG }
const LOOPMODE = ["LOOP_NONE", "[b][color=green]LOOP_LINEAR[/color][/b]", "[b][color=orange]LOOP_PINGPONG[/color][/b]"]
var scene_name: StringName


# Import script for rig to set up everything so little editing is needed once the character is
# instantiated.
func _post_import(scene):
	scene_name = scene.name + " Rig"
	print_rich("\n[b][color=red]Begin Post-import[/color] -> [color=purple]%s[/color] as [color=green]%s[/color][/b]" % [scene_name, scene.get_class()])
	print_rich("[b]Time/Date Stamp: %s[/b]\n" % [Time.get_datetime_string_from_system(false, true)])
	iterate(scene)
	return scene


func iterate(node):
	if node is AnimationPlayer:
		for animation_name in node.get_animation_list():
			if animation_name.contains("Idle") or animation_name.contains("ing"):
				node.get_animation(animation_name).set_loop_mode(LoopMode.LOOP_LINEAR)
			print_rich("Post-import: [b]%s[/b] -> [b]%s[/b]" % [animation_name, LOOPMODE[node.get_animation(animation_name).get_loop_mode()]])
	# Append "_Bone" to BoneAttachment3D nodes just to prevent duplicate names in our scene tree.
	if node is BoneAttachment3D:
		print_rich("Post-import: [b]Renamed [color=green]%s[/color] [color=yellow]%s[/color][/b] -> [color=yellow][b]%s[/b][/color]" % [node.get_class(), node.name, node.name + "_Bone"])
		node.name = node.name + "_Bone"
	# We want to hide everything in the character's hands, and everything they are wearing that is removeable.
	if node is MeshInstance3D and node.get_parent() is BoneAttachment3D:
		node.set_visible(false)
		print_rich("Post-import: [b]Visibile [color=green]%s[/color] [color=yellow]%s[/color][/b] -> [color=yellow][b]%s[/b][/color]" % [node.get_class(), node.name, get_color_string(node.visible)])
	# Rotating the model to face the other direction so it moves in the correct direction when animated.
	if node is Skeleton3D:
		node.rotate_y(deg_to_rad(-180.0))
		print_rich("Post-import: [b]Rotated [color=green]%s[/color] [color=yellow]-180 degrees[/color][/b] on the [b][color=green]y-axis[/color][/b]" % [node.get_class()])
	# Recursively call this function on any child nodes that exist.
	for child in node.get_children():
		iterate(child)


# Return rich text color string for true (green)/red (false).
func get_color_string(value: bool):
	if value:
		return "[color=green]true[/color]"
	else:
		return "[color=red]false[/color]"
2 Likes

In Blender, I always create my models with forward facing the positive Y axis. When I export the gltf, I make sure +Y Up is checked in the Transform setting (that is the default). Godot likes it that way.

1 Like

Oh right. I forgot Y-axis in Blender is Z-axis in Godot.

I recommend dragonforge-dev’s import script. It is the only perfect solution during my test. The other methods I have tried are:

  1. Rotate the model in Blender and apply the rotation → It messed up the control rip created by Mixamo addon.
  2. Export FBX format with Z- Forward option in Blender → Blender packaged duplicated animation for each action.
  3. Simply rotate the Mesh in Blender or in Godot → need extra effort to handle the “look at” or rotation mechanism.

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