mirror of
https://github.com/godotengine/godot-blender-exporter.git
synced 2026-02-25 02:32:46 +03:00
add animation export option
refactor test scenes, able to run different configuration
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,7 +2,7 @@
|
||||
__pycache__
|
||||
|
||||
*.blend[0-9]
|
||||
tests/godot_project/exports/*.escn
|
||||
tests/godot_project/exports/*
|
||||
|
||||
.import
|
||||
*.import
|
||||
|
||||
@@ -82,7 +82,7 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
|
||||
description="Export only selected objects (and visible in active "
|
||||
"layers if that applies).",
|
||||
default=False,
|
||||
)
|
||||
)
|
||||
use_exclude_ctrl_bone = BoolProperty(
|
||||
name="Exclude Control Bones",
|
||||
description="Do not export control bones (bone.use_deform = false)",
|
||||
@@ -90,28 +90,47 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
|
||||
)
|
||||
use_export_animation = BoolProperty(
|
||||
name="Export Animation",
|
||||
description="Export all the animation actions (include those "
|
||||
"in nla_tracks), notice if an animated object has "
|
||||
"an ancestor also has animated, its animation would "
|
||||
"go into the ancetor's AnimationPlayer",
|
||||
description="Export all the animation actions (include actions "
|
||||
"in nla_tracks), note that by default blender animation "
|
||||
"is exported as actions, so every node would have their "
|
||||
"own AnimationPlayer hold their actions",
|
||||
default=True,
|
||||
)
|
||||
use_seperate_animation_player = BoolProperty(
|
||||
name="Seperate AnimationPlayer For Each Object",
|
||||
description="Create a seperate AnimationPlayer node for every"
|
||||
"blender object which has animtion data",
|
||||
default=False,
|
||||
)
|
||||
use_mesh_modifiers = BoolProperty(
|
||||
name="Apply Modifiers",
|
||||
description="Apply modifiers to mesh objects (on a copy!).",
|
||||
default=True,
|
||||
)
|
||||
)
|
||||
use_active_layers = BoolProperty(
|
||||
name="Active Layers",
|
||||
description="Export only objects on the active layers.",
|
||||
default=True,
|
||||
)
|
||||
animation_modes = EnumProperty(
|
||||
name="Animation Modes",
|
||||
description="Configuration of how blender animation data being "
|
||||
"exported to godot AnimationPlayer as well as the "
|
||||
"placement of AnimationPlayers in the node tree.",
|
||||
default="ACTIONS",
|
||||
items=(
|
||||
(
|
||||
"ACTIONS", "Animation as Actions",
|
||||
"Each animated node would have their own AnimationPlayer"
|
||||
),
|
||||
(
|
||||
"SCENE_ANIMATION", "Scene Animation",
|
||||
"All the animations of the whole scene would be placed "
|
||||
"into one AnimationPlayer at scene root"
|
||||
),
|
||||
(
|
||||
"SQUASHED_ACTIONS", "Animation as Actions with Squash",
|
||||
"Animation is exported as actions of nodes, but instead "
|
||||
"of having an individual AnimationPlayer for each node, "
|
||||
"this configuration would squash children nodes' actions "
|
||||
"to their parents"
|
||||
)
|
||||
)
|
||||
)
|
||||
material_search_paths = EnumProperty(
|
||||
name="Material Search Paths",
|
||||
description="Search for existing godot materials with names that match"
|
||||
|
||||
@@ -229,12 +229,26 @@ def transform_frames_to_keys(frame_list, value_list, interp):
|
||||
|
||||
|
||||
def get_animation_player(escn_file, export_settings, godot_node):
|
||||
"""Get a AnimationPlayer node, if not existed, a new
|
||||
one will be created and returned"""
|
||||
"""Get a AnimationPlayer node, its return value depends
|
||||
on animation exporting settings"""
|
||||
animation_player = None
|
||||
# the parent of AnimationPlayer
|
||||
animation_base = None
|
||||
|
||||
# looking for a existed AnimationPlayer
|
||||
if not export_settings['use_seperate_animation_player']:
|
||||
if export_settings['animation_modes'] == 'ACTIONS':
|
||||
animation_base = godot_node
|
||||
elif export_settings['animation_modes'] == 'SCENE_ANIMATION':
|
||||
node_ptr = godot_node
|
||||
while node_ptr.parent is not None:
|
||||
node_ptr = node_ptr.parent
|
||||
scene_root = node_ptr
|
||||
animation_base = scene_root
|
||||
for child in scene_root.children:
|
||||
if child.get_type() == 'AnimationPlayer':
|
||||
animation_player = child
|
||||
break
|
||||
else: # export_settings['animation_modes'] == 'SQUASHED_ACTIONS':
|
||||
animation_base = godot_node
|
||||
node_ptr = godot_node
|
||||
while node_ptr is not None:
|
||||
for child in node_ptr.children:
|
||||
@@ -248,7 +262,7 @@ def get_animation_player(escn_file, export_settings, godot_node):
|
||||
if animation_player is None:
|
||||
animation_player = AnimationPlayer(
|
||||
name='AnimationPlayer',
|
||||
parent=godot_node,
|
||||
parent=animation_base,
|
||||
)
|
||||
|
||||
escn_file.add_node(animation_player)
|
||||
|
||||
@@ -2,34 +2,56 @@ import bpy
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import json
|
||||
|
||||
sys.path = [os.getcwd()] + sys.path # Ensure exporter from this folder
|
||||
from io_scene_godot import export_godot
|
||||
|
||||
TEST_SCENE_DIR = os.path.join(os.getcwd(), "tests/test_scenes")
|
||||
EXPORTED_DIR = os.path.join(os.getcwd(), "tests/godot_project/exports")
|
||||
|
||||
def export_escn(out_file):
|
||||
def export_escn(out_file, config):
|
||||
"""Fake the export operator call"""
|
||||
import io_scene_godot
|
||||
io_scene_godot.export(out_file, {})
|
||||
io_scene_godot.export(out_file, config)
|
||||
|
||||
|
||||
def main():
|
||||
target_dir = os.path.join(os.getcwd(), "tests/test_scenes")
|
||||
for file_name in os.listdir(target_dir):
|
||||
full_path = os.path.join(target_dir, file_name)
|
||||
if full_path.endswith(".blend"):
|
||||
print("Exporting {}".format(full_path))
|
||||
bpy.ops.wm.open_mainfile(filepath=full_path)
|
||||
dir_queue = list()
|
||||
dir_queue.append('.')
|
||||
while dir_queue:
|
||||
dir_relpath = dir_queue.pop(0)
|
||||
|
||||
out_path, blend_name = os.path.split(full_path)
|
||||
out_path = os.path.join(
|
||||
out_path,
|
||||
'../godot_project/exports/',
|
||||
blend_name.replace('.blend', '.escn')
|
||||
)
|
||||
print(out_path)
|
||||
export_escn(out_path)
|
||||
print("Exported")
|
||||
# read config file if present, otherwise use default
|
||||
src_dir_path = os.path.join(TEST_SCENE_DIR, dir_relpath)
|
||||
if os.path.exists(os.path.join(src_dir_path, "config.json")):
|
||||
with open(os.path.join(src_dir_path, "config.json")) as config_file:
|
||||
config = json.load(config_file)
|
||||
else:
|
||||
config = {}
|
||||
|
||||
# create exported to directory
|
||||
exported_dir_path = os.path.join(EXPORTED_DIR, dir_relpath)
|
||||
if not os.path.exists(exported_dir_path):
|
||||
os.makedirs(exported_dir_path)
|
||||
|
||||
for item in os.listdir(os.path.join(TEST_SCENE_DIR, dir_relpath)):
|
||||
item_abspath = os.path.join(TEST_SCENE_DIR, dir_relpath, item)
|
||||
if os.path.isdir(item_abspath):
|
||||
# push dir into queue for later traversal
|
||||
dir_queue.append(os.path.join(dir_relpath, item))
|
||||
elif item_abspath.endswith('blend'):
|
||||
# export blend file
|
||||
print("---------")
|
||||
print("Exporting {}".format(os.path.abspath(item_abspath)))
|
||||
bpy.ops.wm.open_mainfile(filepath=item_abspath)
|
||||
|
||||
out_path = os.path.join(
|
||||
EXPORTED_DIR,
|
||||
dir_relpath,
|
||||
item.replace('.blend', '.escn')
|
||||
)
|
||||
export_escn(out_path, config)
|
||||
print("Exported to {}".format(os.path.abspath(out_path)))
|
||||
|
||||
|
||||
def run_with_abort(function):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[gd_scene load_steps=1 format=2]
|
||||
[ext_resource id=1 path="../uv_tester_material.tres" type="SpatialMaterial"]
|
||||
[ext_resource id=1 path="../../uv_tester_material.tres" type="SpatialMaterial"]
|
||||
|
||||
[sub_resource id=1 type="ArrayMesh"]
|
||||
|
||||
File diff suppressed because one or more lines are too long
3
tests/test_scenes/scene_animation/config.json
Normal file
3
tests/test_scenes/scene_animation/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"animation_modes": "SCENE_ANIMATION"
|
||||
}
|
||||
Reference in New Issue
Block a user