mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +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):
|
||||
# Define extract output file path
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.fbx"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
context = bpy.context
|
||||
scene = context.scene
|
||||
view_layer = context.view_layer
|
||||
|
||||
# Perform 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 " \
|
||||
"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
|
||||
|
||||
# We set the scale of the scene for the export
|
||||
|
|
@ -59,6 +39,15 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
|||
armatures = [
|
||||
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 = []
|
||||
original_actions = []
|
||||
|
||||
|
|
@ -66,23 +55,23 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
|||
ending_frames = []
|
||||
|
||||
# For each armature, we make a copy of the current action
|
||||
for obj in armatures:
|
||||
curr_action = None
|
||||
copy_action = None
|
||||
|
||||
curr_action = None
|
||||
copy_action = None
|
||||
if armature.animation_data and armature.animation_data.action:
|
||||
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
|
||||
copy_action = curr_action.copy()
|
||||
starting_frames.append(curr_frame_range[0])
|
||||
ending_frames.append(curr_frame_range[1])
|
||||
else:
|
||||
self.log.info("Object have no animation.")
|
||||
return
|
||||
|
||||
curr_frame_range = curr_action.frame_range
|
||||
|
||||
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)
|
||||
object_action_pairs.append((armature, copy_action))
|
||||
original_actions.append(curr_action)
|
||||
|
||||
# We compute the starting and ending frames
|
||||
max_frame = min(starting_frames)
|
||||
|
|
@ -96,44 +85,52 @@ class ExtractAnimationFBX(pype.api.Extractor):
|
|||
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(
|
||||
override,
|
||||
filepath=filepath,
|
||||
use_active_collection=True,
|
||||
use_selection=True,
|
||||
bake_anim_use_nla_strips=False,
|
||||
bake_anim_use_all_actions=False,
|
||||
add_leaf_bones=False
|
||||
add_leaf_bones=False,
|
||||
armature_nodetype='ROOT',
|
||||
object_types={'ARMATURE'}
|
||||
)
|
||||
|
||||
view_layer.active_layer_collection = old_active_layer_collection
|
||||
armature.name = armature_name
|
||||
armature.select_set(False)
|
||||
|
||||
scene.unit_settings.scale_length = old_scale
|
||||
|
||||
# We delete the baked action and set the original one back
|
||||
for i in range(0, len(object_action_pairs)):
|
||||
|
||||
pair = object_action_pairs[i]
|
||||
action = original_actions[i]
|
||||
|
||||
if action:
|
||||
|
||||
pair[0].animation_data.action = action
|
||||
|
||||
if pair[1]:
|
||||
|
||||
pair[1].user_clear()
|
||||
bpy.data.actions.remove(pair[1])
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
fbx_representation = {
|
||||
'name': 'fbx',
|
||||
'ext': 'fbx',
|
||||
'files': filename,
|
||||
'files': fbx_filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
instance.data["representations"].append(fbx_representation)
|
||||
|
||||
self.log.info("Extracted instance '%s' to: %s",
|
||||
instance.name, representation)
|
||||
self.log.info("Extracted instance '{}' to: {}".format(
|
||||
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