mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Implemented extraction of the animation and setdress
This commit is contained in:
parent
525c4fe2f8
commit
bce22acf35
3 changed files with 148 additions and 46 deletions
56
pype/plugins/blender/publish/extract_animation_collection.py
Normal file
56
pype/plugins/blender/publish/extract_animation_collection.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pype.api
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
class ExtractSetDress(pype.api.Extractor):
|
||||||
|
"""Extract setdress."""
|
||||||
|
|
||||||
|
label = "Extract SetDress"
|
||||||
|
hosts = ["blender"]
|
||||||
|
families = ["setdress"]
|
||||||
|
optional = True
|
||||||
|
order = pyblish.api.ExtractorOrder + 0.1
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
stagingdir = self.staging_dir(instance)
|
||||||
|
|
||||||
|
json_data = []
|
||||||
|
|
||||||
|
for i in instance.context:
|
||||||
|
collection = i.data.get('name')
|
||||||
|
container = None
|
||||||
|
for obj in bpy.data.collections[collection].objects:
|
||||||
|
if obj.type == 'ARMATURE':
|
||||||
|
container_name = obj.get('avalon').get('container_name')
|
||||||
|
container = bpy.data.collections[container_name]
|
||||||
|
if container:
|
||||||
|
json_dict = {}
|
||||||
|
json_dict['subset'] = i.data.get('subset')
|
||||||
|
json_dict['container'] = container.name
|
||||||
|
json_dict['instance_name'] = container.get('avalon').get('instance_name')
|
||||||
|
json_data.append(json_dict)
|
||||||
|
|
||||||
|
if "representations" not in instance.data:
|
||||||
|
instance.data["representations"] = []
|
||||||
|
|
||||||
|
json_filename = f"{instance.name}.json"
|
||||||
|
json_path = os.path.join(stagingdir, json_filename)
|
||||||
|
|
||||||
|
with open(json_path, "w+") as file:
|
||||||
|
json.dump(json_data, fp=file, indent=2)
|
||||||
|
|
||||||
|
json_representation = {
|
||||||
|
'name': 'json',
|
||||||
|
'ext': 'json',
|
||||||
|
'files': json_filename,
|
||||||
|
"stagingDir": stagingdir,
|
||||||
|
}
|
||||||
|
instance.data["representations"].append(json_representation)
|
||||||
|
|
||||||
|
self.log.info("Extracted instance '{}' to: {}".format(
|
||||||
|
instance.name, json_representation))
|
||||||
|
|
||||||
|
|
@ -17,14 +17,10 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
|
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
filename = f"{instance.name}.fbx"
|
|
||||||
filepath = os.path.join(stagingdir, filename)
|
|
||||||
|
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
view_layer = context.view_layer
|
|
||||||
|
|
||||||
# Perform extraction
|
# Perform extraction
|
||||||
self.log.info("Performing extraction..")
|
self.log.info("Performing extraction..")
|
||||||
|
|
@ -35,22 +31,6 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
||||||
assert len(collections) == 1, "There should be one and only one " \
|
assert len(collections) == 1, "There should be one and only one " \
|
||||||
"collection collected for this asset"
|
"collection collected for this asset"
|
||||||
|
|
||||||
old_active_layer_collection = view_layer.active_layer_collection
|
|
||||||
|
|
||||||
layers = view_layer.layer_collection.children
|
|
||||||
|
|
||||||
# Get the layer collection from the collection we need to export.
|
|
||||||
# This is needed because in Blender you can only set the active
|
|
||||||
# collection with the layer collection, and there is no way to get
|
|
||||||
# the layer collection from the collection
|
|
||||||
# (but there is the vice versa).
|
|
||||||
layer_collections = [
|
|
||||||
layer for layer in layers if layer.collection == collections[0]]
|
|
||||||
|
|
||||||
assert len(layer_collections) == 1
|
|
||||||
|
|
||||||
view_layer.active_layer_collection = layer_collections[0]
|
|
||||||
|
|
||||||
old_scale = scene.unit_settings.scale_length
|
old_scale = scene.unit_settings.scale_length
|
||||||
|
|
||||||
# We set the scale of the scene for the export
|
# We set the scale of the scene for the export
|
||||||
|
|
@ -59,6 +39,15 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
||||||
armatures = [
|
armatures = [
|
||||||
obj for obj in collections[0].objects if obj.type == 'ARMATURE']
|
obj for obj in collections[0].objects if obj.type == 'ARMATURE']
|
||||||
|
|
||||||
|
assert len(collections) == 1, "There should be one and only one " \
|
||||||
|
"armature collected for this asset"
|
||||||
|
|
||||||
|
armature = armatures[0]
|
||||||
|
|
||||||
|
armature_name = armature.name
|
||||||
|
original_name = armature_name.split(':')[0]
|
||||||
|
armature.name = original_name
|
||||||
|
|
||||||
object_action_pairs = []
|
object_action_pairs = []
|
||||||
original_actions = []
|
original_actions = []
|
||||||
|
|
||||||
|
|
@ -66,23 +55,23 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
||||||
ending_frames = []
|
ending_frames = []
|
||||||
|
|
||||||
# For each armature, we make a copy of the current action
|
# For each armature, we make a copy of the current action
|
||||||
for obj in armatures:
|
curr_action = None
|
||||||
|
copy_action = None
|
||||||
|
|
||||||
curr_action = None
|
if armature.animation_data and armature.animation_data.action:
|
||||||
copy_action = None
|
curr_action = armature.animation_data.action
|
||||||
|
copy_action = curr_action.copy()
|
||||||
|
|
||||||
if obj.animation_data and obj.animation_data.action:
|
curr_frame_range = curr_action.frame_range
|
||||||
|
|
||||||
curr_action = obj.animation_data.action
|
starting_frames.append(curr_frame_range[0])
|
||||||
copy_action = curr_action.copy()
|
ending_frames.append(curr_frame_range[1])
|
||||||
|
else:
|
||||||
|
self.log.info("Object have no animation.")
|
||||||
|
return
|
||||||
|
|
||||||
curr_frame_range = curr_action.frame_range
|
object_action_pairs.append((armature, copy_action))
|
||||||
|
original_actions.append(curr_action)
|
||||||
starting_frames.append(curr_frame_range[0])
|
|
||||||
ending_frames.append(curr_frame_range[1])
|
|
||||||
|
|
||||||
object_action_pairs.append((obj, copy_action))
|
|
||||||
original_actions.append(curr_action)
|
|
||||||
|
|
||||||
# We compute the starting and ending frames
|
# We compute the starting and ending frames
|
||||||
max_frame = min(starting_frames)
|
max_frame = min(starting_frames)
|
||||||
|
|
@ -96,44 +85,52 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
||||||
do_clean=False
|
do_clean=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# We export the fbx
|
for obj in bpy.data.objects:
|
||||||
|
obj.select_set(False)
|
||||||
|
|
||||||
|
armature.select_set(True)
|
||||||
|
fbx_filename = f"{instance.name}_{armature.name}.fbx"
|
||||||
|
filepath = os.path.join(stagingdir, fbx_filename)
|
||||||
|
|
||||||
|
override = bpy.context.copy()
|
||||||
|
override['selected_objects'] = [armature]
|
||||||
bpy.ops.export_scene.fbx(
|
bpy.ops.export_scene.fbx(
|
||||||
|
override,
|
||||||
filepath=filepath,
|
filepath=filepath,
|
||||||
use_active_collection=True,
|
use_selection=True,
|
||||||
bake_anim_use_nla_strips=False,
|
bake_anim_use_nla_strips=False,
|
||||||
bake_anim_use_all_actions=False,
|
bake_anim_use_all_actions=False,
|
||||||
add_leaf_bones=False
|
add_leaf_bones=False,
|
||||||
|
armature_nodetype='ROOT',
|
||||||
|
object_types={'ARMATURE'}
|
||||||
)
|
)
|
||||||
|
armature.name = armature_name
|
||||||
view_layer.active_layer_collection = old_active_layer_collection
|
armature.select_set(False)
|
||||||
|
|
||||||
scene.unit_settings.scale_length = old_scale
|
scene.unit_settings.scale_length = old_scale
|
||||||
|
|
||||||
# We delete the baked action and set the original one back
|
# We delete the baked action and set the original one back
|
||||||
for i in range(0, len(object_action_pairs)):
|
for i in range(0, len(object_action_pairs)):
|
||||||
|
|
||||||
pair = object_action_pairs[i]
|
pair = object_action_pairs[i]
|
||||||
action = original_actions[i]
|
action = original_actions[i]
|
||||||
|
|
||||||
if action:
|
if action:
|
||||||
|
|
||||||
pair[0].animation_data.action = action
|
pair[0].animation_data.action = action
|
||||||
|
|
||||||
if pair[1]:
|
if pair[1]:
|
||||||
|
|
||||||
pair[1].user_clear()
|
pair[1].user_clear()
|
||||||
bpy.data.actions.remove(pair[1])
|
bpy.data.actions.remove(pair[1])
|
||||||
|
|
||||||
if "representations" not in instance.data:
|
if "representations" not in instance.data:
|
||||||
instance.data["representations"] = []
|
instance.data["representations"] = []
|
||||||
|
|
||||||
representation = {
|
fbx_representation = {
|
||||||
'name': 'fbx',
|
'name': 'fbx',
|
||||||
'ext': 'fbx',
|
'ext': 'fbx',
|
||||||
'files': filename,
|
'files': fbx_filename,
|
||||||
"stagingDir": stagingdir,
|
"stagingDir": stagingdir,
|
||||||
}
|
}
|
||||||
instance.data["representations"].append(representation)
|
instance.data["representations"].append(fbx_representation)
|
||||||
|
|
||||||
self.log.info("Extracted instance '%s' to: %s",
|
self.log.info("Extracted instance '{}' to: {}".format(
|
||||||
instance.name, representation)
|
instance.name, fbx_representation))
|
||||||
|
|
|
||||||
49
pype/plugins/blender/publish/integrate_animation.py
Normal file
49
pype/plugins/blender/publish/integrate_animation.py
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from avalon import io
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
|
||||||
|
class IntegrateAnimation(pyblish.api.InstancePlugin):
|
||||||
|
"""Generate a JSON file for animation."""
|
||||||
|
|
||||||
|
label = "Integrate Animation"
|
||||||
|
order = pyblish.api.IntegratorOrder + 0.1
|
||||||
|
optional = True
|
||||||
|
hosts = ["blender"]
|
||||||
|
families = ["setdress"]
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
self.log.info("Integrate Animation")
|
||||||
|
|
||||||
|
representation = instance.data.get('representations')[0]
|
||||||
|
json_path = representation.get('publishedFiles')[0]
|
||||||
|
|
||||||
|
with open(json_path, "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
# Update the json file for the setdress to add the published
|
||||||
|
# representations of the animations
|
||||||
|
for json_dict in data:
|
||||||
|
i = None
|
||||||
|
for elem in instance.context:
|
||||||
|
if elem.data.get('subset') == json_dict['subset']:
|
||||||
|
i = elem
|
||||||
|
break
|
||||||
|
if not i:
|
||||||
|
continue
|
||||||
|
rep = None
|
||||||
|
pub_repr = i.data.get('published_representations')
|
||||||
|
for elem in pub_repr:
|
||||||
|
if pub_repr.get(elem).get('representation').get('name') == "fbx":
|
||||||
|
rep = pub_repr.get(elem)
|
||||||
|
break
|
||||||
|
if not rep:
|
||||||
|
continue
|
||||||
|
obj_id = rep.get('representation').get('_id')
|
||||||
|
|
||||||
|
if obj_id:
|
||||||
|
json_dict['_id'] = str(obj_id)
|
||||||
|
|
||||||
|
with open(json_path, "w") as file:
|
||||||
|
json.dump(data, fp=file, indent=2)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue