Merge branch '29-differential-revision' into 'develop'
various implementation refactoring See merge request slumber/multi-user!24
This commit is contained in:
commit
5574059b46
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
bl_info = {
|
||||
"name": "Multi-User",
|
||||
"author": "Swann Martinez",
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
__all__ = [
|
||||
'bl_object',
|
||||
'bl_mesh',
|
||||
|
@ -1,11 +1,200 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
import copy
|
||||
import numpy as np
|
||||
from enum import Enum
|
||||
|
||||
from .. import utils
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
# WIP
|
||||
|
||||
ENUM_EASING_TYPE = [
|
||||
'AUTO',
|
||||
'EAS_IN',
|
||||
'EASE_OUT',
|
||||
'EASE_IN_OUT']
|
||||
|
||||
|
||||
ENUM_HANDLE_TYPE = [
|
||||
'FREE',
|
||||
'ALIGNED',
|
||||
'VECTOR',
|
||||
'AUTO',
|
||||
'AUTO_CLAMPED']
|
||||
|
||||
|
||||
ENUM_INTERPOLATION_TYPE = [
|
||||
'CONSTANT',
|
||||
'LINEAR',
|
||||
'BEZIER',
|
||||
'SINE',
|
||||
'QUAD',
|
||||
'CUBIC',
|
||||
'QUART',
|
||||
'QUINT',
|
||||
'EXPO',
|
||||
'CIRC',
|
||||
'BACK',
|
||||
'BOUNCE',
|
||||
'ELASTIC']
|
||||
|
||||
|
||||
ENUM_KEY_TYPE = [
|
||||
'KEYFRAME',
|
||||
'BREAKDOWN',
|
||||
'MOVING_HOLD',
|
||||
'EXTREME',
|
||||
'JITTER']
|
||||
|
||||
|
||||
#TODO: Automatic enum and numpy dump and loading
|
||||
|
||||
|
||||
def dump_fcurve(fcurve, use_numpy=True):
|
||||
""" Dump a sigle curve to a dict
|
||||
|
||||
:arg fcurve: fcurve to dump
|
||||
:type fcurve: bpy.types.FCurve
|
||||
:arg use_numpy: use numpy to eccelerate dump
|
||||
:type use_numpy: bool
|
||||
:return: dict
|
||||
"""
|
||||
fcurve_data = {
|
||||
"data_path": fcurve.data_path,
|
||||
"dumped_array_index": fcurve.array_index,
|
||||
"use_numpy": use_numpy
|
||||
}
|
||||
|
||||
if use_numpy:
|
||||
keyframes_count = len(fcurve.keyframe_points)
|
||||
|
||||
k_amplitude = np.empty(keyframes_count, dtype=np.float64)
|
||||
fcurve.keyframe_points.foreach_get('amplitude', k_amplitude)
|
||||
k_co = np.empty(keyframes_count*2, dtype=np.float64)
|
||||
fcurve.keyframe_points.foreach_get('co', k_co)
|
||||
k_back = np.empty(keyframes_count, dtype=np.float64)
|
||||
fcurve.keyframe_points.foreach_get('back', k_back)
|
||||
k_handle_left = np.empty(keyframes_count*2, dtype=np.float64)
|
||||
fcurve.keyframe_points.foreach_get('handle_left', k_handle_left)
|
||||
k_handle_right = np.empty(keyframes_count*2, dtype=np.float64)
|
||||
fcurve.keyframe_points.foreach_get('handle_right', k_handle_right)
|
||||
|
||||
fcurve_data['amplitude'] = k_amplitude.tobytes()
|
||||
fcurve_data['co'] = k_co.tobytes()
|
||||
fcurve_data['back'] = k_back.tobytes()
|
||||
fcurve_data['handle_left'] = k_handle_left.tobytes()
|
||||
fcurve_data['handle_right'] = k_handle_right.tobytes()
|
||||
|
||||
fcurve_data['easing'] = [ENUM_EASING_TYPE.index(p.easing) for p in fcurve.keyframe_points]
|
||||
fcurve_data['handle_left_type'] = [ENUM_HANDLE_TYPE.index(p.handle_left_type) for p in fcurve.keyframe_points]
|
||||
fcurve_data['handle_right_type'] = [ENUM_HANDLE_TYPE.index(p.handle_right_type) for p in fcurve.keyframe_points]
|
||||
fcurve_data['type'] = [ENUM_KEY_TYPE.index(p.type) for p in fcurve.keyframe_points]
|
||||
fcurve_data['interpolation'] = [ENUM_INTERPOLATION_TYPE.index(p.interpolation) for p in fcurve.keyframe_points]
|
||||
|
||||
else: # Legacy method
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
fcurve_data["keyframe_points"] = []
|
||||
|
||||
for k in fcurve.keyframe_points:
|
||||
fcurve_data["keyframe_points"].append(
|
||||
dumper.dump(k)
|
||||
)
|
||||
|
||||
return fcurve_data
|
||||
|
||||
def load_fcurve(fcurve_data, fcurve):
|
||||
""" Load a dumped fcurve
|
||||
|
||||
:arg fcurve_data: a dumped fcurve
|
||||
:type fcurve_data: dict
|
||||
:arg fcurve: fcurve to dump
|
||||
:type fcurve: bpy.types.FCurve
|
||||
"""
|
||||
use_numpy = fcurve_data.get('use_numpy')
|
||||
|
||||
keyframe_points = fcurve.keyframe_points
|
||||
|
||||
# Remove all keyframe points
|
||||
for i in range(len(keyframe_points)):
|
||||
keyframe_points.remove(keyframe_points[0], fast=True)
|
||||
|
||||
if use_numpy:
|
||||
k_amplitude = np.frombuffer(fcurve_data['amplitude'], dtype=np.float64)
|
||||
|
||||
keyframe_count = len(k_amplitude)
|
||||
|
||||
k_co = np.frombuffer(fcurve_data['co'], dtype=np.float64)
|
||||
k_back = np.frombuffer(fcurve_data['back'], dtype=np.float64)
|
||||
k_amplitude = np.frombuffer(fcurve_data['amplitude'], dtype=np.float64)
|
||||
k_handle_left= np.frombuffer(fcurve_data['handle_left'], dtype=np.float64)
|
||||
k_handle_right= np.frombuffer(fcurve_data['handle_right'], dtype=np.float64)
|
||||
|
||||
keyframe_points.add(keyframe_count)
|
||||
|
||||
keyframe_points.foreach_set('co',k_co)
|
||||
keyframe_points.foreach_set('back',k_back)
|
||||
keyframe_points.foreach_set('amplitude',k_amplitude)
|
||||
keyframe_points.foreach_set('handle_left',k_handle_left)
|
||||
keyframe_points.foreach_set('handle_right',k_handle_right)
|
||||
|
||||
for index, point in enumerate(keyframe_points):
|
||||
point.type = ENUM_KEY_TYPE[fcurve_data['type'][index]]
|
||||
point.easing = ENUM_EASING_TYPE[fcurve_data['easing'][index]]
|
||||
point.handle_left_type = ENUM_HANDLE_TYPE[fcurve_data['handle_left_type'][index]]
|
||||
point.handle_right_type = ENUM_HANDLE_TYPE[fcurve_data['handle_right_type'][index]]
|
||||
point.interpolation = ENUM_INTERPOLATION_TYPE[fcurve_data['interpolation'][index]]
|
||||
|
||||
else:
|
||||
# paste dumped keyframes
|
||||
for dumped_keyframe_point in fcurve_data["keyframe_points"]:
|
||||
if dumped_keyframe_point['type'] == '':
|
||||
dumped_keyframe_point['type'] = 'KEYFRAME'
|
||||
|
||||
new_kf = keyframe_points.insert(
|
||||
dumped_keyframe_point["co"][0],
|
||||
dumped_keyframe_point["co"][1],
|
||||
options={'FAST', 'REPLACE'}
|
||||
)
|
||||
|
||||
keycache = copy.copy(dumped_keyframe_point)
|
||||
keycache = utils.dump_anything.remove_items_from_dict(
|
||||
keycache,
|
||||
["co", "handle_left", "handle_right", 'type']
|
||||
)
|
||||
|
||||
utils.dump_anything.load(new_kf, keycache)
|
||||
|
||||
new_kf.type = dumped_keyframe_point['type']
|
||||
new_kf.handle_left = [
|
||||
dumped_keyframe_point["handle_left"][0],
|
||||
dumped_keyframe_point["handle_left"][1]
|
||||
]
|
||||
new_kf.handle_right = [
|
||||
dumped_keyframe_point["handle_right"][0],
|
||||
dumped_keyframe_point["handle_right"][1]
|
||||
]
|
||||
|
||||
fcurve.update()
|
||||
|
||||
|
||||
|
||||
class BlAction(BlDatablock):
|
||||
bl_id = "actions"
|
||||
@ -14,30 +203,11 @@ class BlAction(BlDatablock):
|
||||
bl_delay_apply = 1
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'ACTION_TWEAK'
|
||||
|
||||
def construct(self, data):
|
||||
|
||||
def _construct(self, data):
|
||||
return bpy.data.actions.new(data["name"])
|
||||
|
||||
def load(self, data, target):
|
||||
begin_frame = 100000
|
||||
end_frame = -100000
|
||||
|
||||
for dumped_fcurve in data["fcurves"]:
|
||||
begin_frame = min(
|
||||
begin_frame,
|
||||
min(
|
||||
[begin_frame] + [dkp["co"][0] for dkp in dumped_fcurve["keyframe_points"]]
|
||||
)
|
||||
)
|
||||
end_frame = max(
|
||||
end_frame,
|
||||
max(
|
||||
[end_frame] + [dkp["co"][0] for dkp in dumped_fcurve["keyframe_points"]]
|
||||
)
|
||||
)
|
||||
begin_frame = 0
|
||||
|
||||
loader = utils.dump_anything.Loader()
|
||||
def _load(self, data, target):
|
||||
for dumped_fcurve in data["fcurves"]:
|
||||
dumped_data_path = dumped_fcurve["data_path"]
|
||||
dumped_array_index = dumped_fcurve["dumped_array_index"]
|
||||
@ -47,53 +217,13 @@ class BlAction(BlDatablock):
|
||||
if fcurve is None:
|
||||
fcurve = target.fcurves.new(dumped_data_path, index=dumped_array_index)
|
||||
|
||||
load_fcurve(dumped_fcurve, fcurve)
|
||||
target.id_root = data['id_root']
|
||||
|
||||
# remove keyframes within dumped_action range
|
||||
for keyframe in reversed(fcurve.keyframe_points):
|
||||
if end_frame >= (keyframe.co[0] + begin_frame ) >= begin_frame:
|
||||
fcurve.keyframe_points.remove(keyframe, fast=True)
|
||||
|
||||
# paste dumped keyframes
|
||||
for dumped_keyframe_point in dumped_fcurve["keyframe_points"]:
|
||||
if dumped_keyframe_point['type'] == '':
|
||||
dumped_keyframe_point['type'] = 'KEYFRAME'
|
||||
|
||||
new_kf = fcurve.keyframe_points.insert(
|
||||
dumped_keyframe_point["co"][0] - begin_frame,
|
||||
dumped_keyframe_point["co"][1],
|
||||
options={'FAST', 'REPLACE'}
|
||||
)
|
||||
|
||||
keycache = copy.copy(dumped_keyframe_point)
|
||||
keycache = utils.dump_anything.remove_items_from_dict(
|
||||
keycache,
|
||||
["co", "handle_left", "handle_right",'type']
|
||||
)
|
||||
|
||||
loader.load(
|
||||
new_kf,
|
||||
keycache
|
||||
)
|
||||
|
||||
new_kf.type = dumped_keyframe_point['type']
|
||||
new_kf.handle_left = [
|
||||
dumped_keyframe_point["handle_left"][0] - begin_frame,
|
||||
dumped_keyframe_point["handle_left"][1]
|
||||
]
|
||||
new_kf.handle_right = [
|
||||
dumped_keyframe_point["handle_right"][0] - begin_frame,
|
||||
dumped_keyframe_point["handle_right"][1]
|
||||
]
|
||||
|
||||
# clearing (needed for blender to update well)
|
||||
if len(fcurve.keyframe_points) == 0:
|
||||
target.fcurves.remove(fcurve)
|
||||
target.id_root= data['id_root']
|
||||
|
||||
def dump(self, pointer=None):
|
||||
def _dump(self, pointer=None):
|
||||
assert(pointer)
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
dumper.exclude_filter =[
|
||||
dumper.exclude_filter = [
|
||||
'name_full',
|
||||
'original',
|
||||
'use_fake_user',
|
||||
@ -106,27 +236,13 @@ class BlAction(BlDatablock):
|
||||
'users'
|
||||
]
|
||||
dumper.depth = 1
|
||||
data = dumper.dump(pointer)
|
||||
data = dumper.dump(pointer)
|
||||
|
||||
|
||||
data["fcurves"] = []
|
||||
dumper.depth = 2
|
||||
|
||||
for fcurve in self.pointer.fcurves:
|
||||
fc = {
|
||||
"data_path": fcurve.data_path,
|
||||
"dumped_array_index": fcurve.array_index,
|
||||
"keyframe_points": []
|
||||
}
|
||||
data["fcurves"].append(dump_fcurve(fcurve, use_numpy=True))
|
||||
|
||||
for k in fcurve.keyframe_points:
|
||||
fc["keyframe_points"].append(
|
||||
dumper.dump(k)
|
||||
)
|
||||
|
||||
data["fcurves"].append(fc)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,13 +1,28 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
from ..libs.overrider import Overrider
|
||||
from .. import utils
|
||||
from .. import presence, operators
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
# WIP
|
||||
|
||||
|
||||
class BlArmature(BlDatablock):
|
||||
bl_id = "armatures"
|
||||
@ -17,7 +32,7 @@ class BlArmature(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'ARMATURE_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.armatures.new(data["name"])
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -13,6 +31,10 @@ class BlCamera(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'CAMERA_DATA'
|
||||
|
||||
def _construct(self, data):
|
||||
return bpy.data.cameras.new(data["name"])
|
||||
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
@ -22,12 +44,11 @@ class BlCamera(BlDatablock):
|
||||
if dof_settings:
|
||||
utils.dump_anything.load(target.dof, dof_settings)
|
||||
|
||||
def construct(self, data):
|
||||
return bpy.data.cameras.new(data["name"])
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
|
||||
# TODO: background image support
|
||||
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
dumper.depth = 2
|
||||
dumper.include_filter = [
|
||||
@ -49,6 +70,14 @@ class BlCamera(BlDatablock):
|
||||
'aperture_blades',
|
||||
'aperture_rotation',
|
||||
'aperture_ratio',
|
||||
'display_size',
|
||||
'show_limits',
|
||||
'show_mist',
|
||||
'show_sensor',
|
||||
'show_name',
|
||||
'sensor_fit',
|
||||
'sensor_height',
|
||||
'sensor_width',
|
||||
]
|
||||
return dumper.dump(pointer)
|
||||
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -13,7 +31,7 @@ class BlCollection(BlDatablock):
|
||||
bl_delay_apply = 1
|
||||
bl_automatic_push = True
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
if self.is_library:
|
||||
with bpy.data.libraries.load(filepath=bpy.data.libraries[self.data['library']].filepath, link=True) as (sourceData, targetData):
|
||||
targetData.collections = [
|
||||
@ -28,7 +46,7 @@ class BlCollection(BlDatablock):
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
|
||||
def load(self, data, target):
|
||||
def load_implementation(self, data, target):
|
||||
# Load other meshes metadata
|
||||
# dump_anything.load(target, data)
|
||||
target.name = data["name"]
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import bpy.types as T
|
||||
import mathutils
|
||||
@ -15,7 +33,7 @@ class BlCurve(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'CURVE_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.curves.new(data["name"], data["type"])
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -61,7 +79,6 @@ class BlDatablock(ReplicatedDatablock):
|
||||
bl_automatic_push : boolean
|
||||
bl_icon : type icon (blender icon name)
|
||||
"""
|
||||
bl_id = "scenes"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -77,23 +94,7 @@ class BlDatablock(ReplicatedDatablock):
|
||||
|
||||
self.diff_method = DIFF_BINARY
|
||||
|
||||
def library_apply(self):
|
||||
"""Apply stored data
|
||||
"""
|
||||
# UP in case we want to reset our pointer data
|
||||
self.state = UP
|
||||
|
||||
def bl_diff(self):
|
||||
"""Generic datablock diff"""
|
||||
return self.pointer.name != self.data['name']
|
||||
|
||||
def diff_library(self):
|
||||
return False
|
||||
|
||||
def resolve_deps_library(self):
|
||||
return [self.pointer.library]
|
||||
|
||||
def resolve(self):
|
||||
def _resolve(self):
|
||||
datablock_ref = None
|
||||
datablock_root = getattr(bpy.data, self.bl_id)
|
||||
datablock_ref = utils.find_from_attr('uuid', self.uuid, datablock_root)
|
||||
@ -108,7 +109,7 @@ class BlDatablock(ReplicatedDatablock):
|
||||
|
||||
self.pointer = datablock_ref
|
||||
|
||||
def dump(self, pointer=None):
|
||||
def _dump(self, pointer=None):
|
||||
data = {}
|
||||
# Dump animation data
|
||||
if utils.has_action(pointer):
|
||||
@ -134,7 +135,7 @@ class BlDatablock(ReplicatedDatablock):
|
||||
def dump_implementation(self, data, target):
|
||||
raise NotImplementedError
|
||||
|
||||
def load(self, data, target):
|
||||
def _load(self, data, target):
|
||||
# Load animation data
|
||||
if 'animation_data' in data.keys():
|
||||
if target.animation_data is None:
|
||||
|
@ -1,72 +1,271 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
import numpy as np
|
||||
|
||||
from ..libs import dump_anything
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
# GPencil data api is structured as it follow:
|
||||
# GP-Object --> GP-Layers --> GP-Frames --> GP-Strokes --> GP-Stroke-Points
|
||||
|
||||
def load_gpencil_layer(target=None, data=None, create=False):
|
||||
def dump_stroke(stroke):
|
||||
""" Dump a grease pencil stroke to a dict
|
||||
|
||||
dump_anything.load(target, data)
|
||||
for k,v in target.frames.items():
|
||||
target.frames.remove(v)
|
||||
|
||||
for frame in data["frames"]:
|
||||
|
||||
tframe = target.frames.new(data["frames"][frame]['frame_number'])
|
||||
:param stroke: target grease pencil stroke
|
||||
:type stroke: bpy.types.GPencilStroke
|
||||
:return: dict
|
||||
"""
|
||||
|
||||
for stroke in data["frames"][frame]["strokes"]:
|
||||
try:
|
||||
tstroke = tframe.strokes[stroke]
|
||||
except:
|
||||
tstroke = tframe.strokes.new()
|
||||
dump_anything.load(
|
||||
tstroke, data["frames"][frame]["strokes"][stroke])
|
||||
assert(stroke)
|
||||
|
||||
for point in data["frames"][frame]["strokes"][stroke]["points"]:
|
||||
p = data["frames"][frame]["strokes"][stroke]["points"][point]
|
||||
dumper = dump_anything.Dumper()
|
||||
dumper.include_filter = [
|
||||
"aspect",
|
||||
"display_mode",
|
||||
"draw_cyclic",
|
||||
"end_cap_mode",
|
||||
"hardeness",
|
||||
"line_width",
|
||||
"material_index",
|
||||
"start_cap_mode",
|
||||
"uv_rotation",
|
||||
"uv_scale",
|
||||
"uv_translation",
|
||||
"vertex_color_fill",
|
||||
]
|
||||
dumped_stroke = dumper.dump(stroke)
|
||||
|
||||
tstroke.points.add(1)
|
||||
tpoint = tstroke.points[len(tstroke.points)-1]
|
||||
# Stoke points
|
||||
p_count = len(stroke.points)
|
||||
dumped_stroke['p_count'] = p_count
|
||||
|
||||
p_co = np.empty(p_count*3, dtype=np.float64)
|
||||
stroke.points.foreach_get('co', p_co)
|
||||
dumped_stroke['p_co'] = p_co.tobytes()
|
||||
|
||||
p_pressure = np.empty(p_count, dtype=np.float64)
|
||||
stroke.points.foreach_get('pressure', p_pressure)
|
||||
dumped_stroke['p_pressure'] = p_pressure.tobytes()
|
||||
|
||||
p_strength = np.empty(p_count, dtype=np.float64)
|
||||
stroke.points.foreach_get('strength', p_strength)
|
||||
dumped_stroke['p_strength'] = p_strength.tobytes()
|
||||
|
||||
if bpy.app.version[1] >= 83: # new in blender 2.83
|
||||
p_vertex_color = np.empty(p_count*4, dtype=np.float64)
|
||||
stroke.points.foreach_get('vertex_color', p_vertex_color)
|
||||
dumped_stroke['p_vertex_color'] = p_vertex_color.tobytes()
|
||||
|
||||
# TODO: uv_factor, uv_rotation
|
||||
|
||||
return dumped_stroke
|
||||
|
||||
|
||||
def load_stroke(stroke_data, stroke):
|
||||
""" Load a grease pencil stroke from a dict
|
||||
|
||||
:param stroke_data: dumped grease pencil stroke
|
||||
:type stroke_data: dict
|
||||
:param stroke: target grease pencil stroke
|
||||
:type stroke: bpy.types.GPencilStroke
|
||||
"""
|
||||
assert(stroke and stroke_data)
|
||||
|
||||
dump_anything.load(stroke, stroke_data)
|
||||
|
||||
p_co = np.frombuffer(stroke_data["p_co"], dtype=np.float64)
|
||||
p_pressure = np.frombuffer(stroke_data["p_pressure"], dtype=np.float64)
|
||||
p_strength = np.frombuffer(stroke_data["p_strength"], dtype=np.float64)
|
||||
|
||||
stroke.points.add(stroke_data["p_count"])
|
||||
|
||||
stroke.points.foreach_set('co', p_co)
|
||||
stroke.points.foreach_set('pressure', p_pressure)
|
||||
stroke.points.foreach_set('strength', p_strength)
|
||||
|
||||
if "p_vertex_color" in stroke_data:
|
||||
p_vertex_color = np.frombuffer(stroke_data["p_vertex_color"], dtype=np.float64)
|
||||
stroke.points.foreach_set('vertex_color', p_vertex_color)
|
||||
|
||||
|
||||
def dump_frame(frame):
|
||||
""" Dump a grease pencil frame to a dict
|
||||
|
||||
:param frame: target grease pencil stroke
|
||||
:type frame: bpy.types.GPencilFrame
|
||||
:return: dict
|
||||
"""
|
||||
|
||||
assert(frame)
|
||||
|
||||
dumped_frame = dict()
|
||||
dumped_frame['frame_number'] = frame.frame_number
|
||||
dumped_frame['strokes'] = []
|
||||
|
||||
# TODO: took existing strokes in account
|
||||
for stroke in frame.strokes:
|
||||
dumped_frame['strokes'].append(dump_stroke(stroke))
|
||||
|
||||
return dumped_frame
|
||||
|
||||
|
||||
def load_frame(frame_data, frame):
|
||||
""" Load a grease pencil frame from a dict
|
||||
|
||||
:param frame_data: source grease pencil frame
|
||||
:type frame_data: dict
|
||||
:param frame: target grease pencil stroke
|
||||
:type frame: bpy.types.GPencilFrame
|
||||
"""
|
||||
|
||||
assert(frame and frame_data)
|
||||
|
||||
# frame.frame_number = frame_data['frame_number']
|
||||
|
||||
# TODO: took existing stroke in account
|
||||
|
||||
for stroke_data in frame_data['strokes']:
|
||||
target_stroke = frame.strokes.new()
|
||||
load_stroke(stroke_data, target_stroke)
|
||||
|
||||
|
||||
def dump_layer(layer):
|
||||
""" Dump a grease pencil layer
|
||||
|
||||
:param layer: target grease pencil stroke
|
||||
:type layer: bpy.types.GPencilFrame
|
||||
"""
|
||||
|
||||
assert(layer)
|
||||
|
||||
dumper = dump_anything.Dumper()
|
||||
|
||||
dumper.include_filter = [
|
||||
'info',
|
||||
'opacity',
|
||||
'channel_color',
|
||||
'color',
|
||||
# 'thickness',
|
||||
'tint_color',
|
||||
'tint_factor',
|
||||
'vertex_paint_opacity',
|
||||
'line_change',
|
||||
'use_onion_skinning',
|
||||
# 'use_annotation_onion_skinning',
|
||||
# 'annotation_onion_before_range',
|
||||
# 'annotation_onion_after_range',
|
||||
# 'annotation_onion_before_color',
|
||||
# 'annotation_onion_after_color',
|
||||
'pass_index',
|
||||
# 'viewlayer_render',
|
||||
'blend_mode',
|
||||
'hide',
|
||||
'annotation_hide',
|
||||
'lock',
|
||||
# 'lock_frame',
|
||||
# 'lock_material',
|
||||
# 'use_mask_layer',
|
||||
'use_lights',
|
||||
'use_solo_mode',
|
||||
'select',
|
||||
'show_points',
|
||||
'show_in_front',
|
||||
# 'parent',
|
||||
# 'parent_type',
|
||||
# 'parent_bone',
|
||||
# 'matrix_inverse',
|
||||
]
|
||||
dumped_layer = dumper.dump(layer)
|
||||
|
||||
dumped_layer['frames'] = []
|
||||
|
||||
for frame in layer.frames:
|
||||
dumped_layer['frames'].append(dump_frame(frame))
|
||||
|
||||
return dumped_layer
|
||||
|
||||
|
||||
def load_layer(layer_data, layer):
|
||||
""" Load a grease pencil layer from a dict
|
||||
|
||||
:param layer_data: source grease pencil layer data
|
||||
:type layer_data: dict
|
||||
:param layer: target grease pencil stroke
|
||||
:type layer: bpy.types.GPencilFrame
|
||||
"""
|
||||
# TODO: take existing data in account
|
||||
dump_anything.load(layer, layer_data)
|
||||
|
||||
for frame_data in layer_data["frames"]:
|
||||
target_frame = layer.frames.new(frame_data['frame_number'])
|
||||
|
||||
load_frame(frame_data, target_frame)
|
||||
|
||||
dump_anything.load(tpoint, p)
|
||||
|
||||
|
||||
class BlGpencil(BlDatablock):
|
||||
bl_id = "grease_pencils"
|
||||
bl_class = bpy.types.GreasePencil
|
||||
bl_delay_refresh = 5
|
||||
bl_delay_apply = 5
|
||||
bl_delay_refresh = 2
|
||||
bl_delay_apply = 1
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'GREASEPENCIL'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.grease_pencils.new(data["name"])
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
for layer in target.layers:
|
||||
target.layers.remove(layer)
|
||||
|
||||
if "layers" in data.keys():
|
||||
for layer in data["layers"]:
|
||||
if layer not in target.layers.keys():
|
||||
gp_layer = target.layers.new(data["layers"][layer]["info"])
|
||||
else:
|
||||
gp_layer = target.layers[layer]
|
||||
load_gpencil_layer(
|
||||
target=gp_layer, data=data["layers"][layer], create=True)
|
||||
|
||||
dump_anything.load(target, data)
|
||||
|
||||
target.materials.clear()
|
||||
if "materials" in data.keys():
|
||||
for mat in data['materials']:
|
||||
target.materials.append(bpy.data.materials[mat])
|
||||
|
||||
# TODO: reuse existing layer
|
||||
for layer in target.layers:
|
||||
target.layers.remove(layer)
|
||||
|
||||
if "layers" in data.keys():
|
||||
for layer in data["layers"]:
|
||||
layer_data = data["layers"].get(layer)
|
||||
|
||||
# if layer not in target.layers.keys():
|
||||
target_layer = target.layers.new(data["layers"][layer]["info"])
|
||||
# else:
|
||||
# target_layer = target.layers[layer]
|
||||
# target_layer.clear()
|
||||
|
||||
load_layer(layer_data, target_layer)
|
||||
|
||||
dump_anything.load(target, data)
|
||||
|
||||
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
data = dump_anything.dump(pointer, 2)
|
||||
data['layers'] = dump_anything.dump(pointer.layers, 9)
|
||||
|
||||
data['layers'] = {}
|
||||
|
||||
for layer in pointer.layers:
|
||||
data['layers'][layer.info] = dump_layer(layer)
|
||||
|
||||
return data
|
||||
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
import os
|
||||
@ -10,8 +28,10 @@ def dump_image(image):
|
||||
if image.source == "GENERATED":
|
||||
prefs = utils.get_preferences()
|
||||
img_name = "{}.png".format(image.name)
|
||||
|
||||
|
||||
# Cache the image on the disk
|
||||
image.filepath_raw = os.path.join(prefs.cache_directory, img_name)
|
||||
os.makedirs(prefs.cache_directory, exist_ok=True)
|
||||
image.file_format = "PNG"
|
||||
image.save()
|
||||
|
||||
@ -35,14 +55,14 @@ class BlImage(BlDatablock):
|
||||
bl_automatic_push = False
|
||||
bl_icon = 'IMAGE_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.images.new(
|
||||
name=data['name'],
|
||||
width=data['size'][0],
|
||||
height=data['size'][1]
|
||||
)
|
||||
|
||||
def load(self, data, target):
|
||||
def _load(self, data, target):
|
||||
image = target
|
||||
prefs = utils.get_preferences()
|
||||
|
||||
@ -59,7 +79,7 @@ class BlImage(BlDatablock):
|
||||
image.colorspace_settings.name = data["colorspace_settings"]["name"]
|
||||
|
||||
|
||||
def dump(self, data, pointer=None):
|
||||
def _dump(self, pointer=None):
|
||||
assert(pointer)
|
||||
data = {}
|
||||
data['pixels'] = dump_image(pointer)
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -18,7 +36,7 @@ class BlLattice(BlDatablock):
|
||||
|
||||
for point in data['points']:
|
||||
utils.dump_anything.load(target.points[point], data["points"][point])
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.lattices.new(data["name"])
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -13,11 +31,11 @@ class BlLibrary(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'LIBRARY_DATA_DIRECT'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
with bpy.data.libraries.load(filepath=data["filepath"], link=True) as (sourceData, targetData):
|
||||
targetData = sourceData
|
||||
return sourceData
|
||||
def load(self, data, target):
|
||||
def _load(self, data, target):
|
||||
pass
|
||||
|
||||
def dump(self, pointer=None):
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -13,10 +31,10 @@ class BlLight(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'LIGHT_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.lights.new(data["name"], data["type"])
|
||||
|
||||
def load(self, data, target):
|
||||
def load_implementation(self, data, target):
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
@ -41,7 +59,8 @@ class BlLight(BlDatablock):
|
||||
"contact_shadow_distance",
|
||||
"contact_shadow_soft_size",
|
||||
"contact_shadow_bias",
|
||||
"contact_shadow_thickness"
|
||||
"contact_shadow_thickness",
|
||||
"shape"
|
||||
]
|
||||
data = dumper.dump(pointer)
|
||||
return data
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
import logging
|
||||
@ -15,16 +33,16 @@ class BlLightprobe(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'LIGHTPROBE_GRID'
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type']
|
||||
# See https://developer.blender.org/D6396
|
||||
if bpy.app.version[1] >= 83:
|
||||
return bpy.data.lightprobes.new(data["name"], type)
|
||||
else:
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
import logging
|
||||
@ -7,66 +25,62 @@ from ..libs import dump_anything
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
def clean_color_ramp(target_ramp):
|
||||
# clear existing
|
||||
try:
|
||||
for key in target_ramp.elements:
|
||||
target_ramp.elements.remove(key)
|
||||
except:
|
||||
pass
|
||||
|
||||
def load_mapping(target_apping, source_mapping):
|
||||
# clear existing curves
|
||||
for curve in target_apping.curves:
|
||||
for point in curve.points:
|
||||
try:
|
||||
curve.remove(point)
|
||||
except:
|
||||
continue
|
||||
|
||||
# Load curves
|
||||
for curve in source_mapping['curves']:
|
||||
for point in source_mapping['curves'][curve]['points']:
|
||||
pos = source_mapping['curves'][curve]['points'][point]['location']
|
||||
target_apping.curves[curve].points.new(pos[0],pos[1])
|
||||
|
||||
def load_node(node_data, node_tree):
|
||||
""" Load a node into a node_tree from a dict
|
||||
|
||||
def load_node(target_node_tree, source):
|
||||
target_node = target_node_tree.nodes.get(source["name"])
|
||||
:arg node_data: dumped node data
|
||||
:type node_data: dict
|
||||
:arg node_tree: target node_tree
|
||||
:type node_tree: bpy.types.NodeTree
|
||||
"""
|
||||
target_node = node_tree.nodes.new(type=node_data["bl_idname"])
|
||||
|
||||
if target_node is None:
|
||||
node_type = source["bl_idname"]
|
||||
dump_anything.load(target_node, node_data)
|
||||
|
||||
target_node = target_node_tree.nodes.new(type=node_type)
|
||||
|
||||
# Clean color ramp before loading it
|
||||
if source['type'] == 'VALTORGB':
|
||||
clean_color_ramp(target_node.color_ramp)
|
||||
if source['type'] == 'CURVE_RGB':
|
||||
load_mapping(target_node.mapping, source['mapping'])
|
||||
dump_anything.load(
|
||||
target_node,
|
||||
source)
|
||||
|
||||
if source['type'] == 'TEX_IMAGE':
|
||||
target_node.image = bpy.data.images[source['image']]
|
||||
|
||||
|
||||
for input in source["inputs"]:
|
||||
for input in node_data["inputs"]:
|
||||
if hasattr(target_node.inputs[input], "default_value"):
|
||||
try:
|
||||
target_node.inputs[input].default_value = source["inputs"][input]["default_value"]
|
||||
target_node.inputs[input].default_value = node_data["inputs"][input]["default_value"]
|
||||
except:
|
||||
logger.error("{} not supported, skipping".format(input))
|
||||
|
||||
def load_link(target_node_tree, source):
|
||||
input_socket = target_node_tree.nodes[source['to_node']
|
||||
['name']].inputs[source['to_socket']['name']]
|
||||
output_socket = target_node_tree.nodes[source['from_node']
|
||||
['name']].outputs[source['from_socket']['name']]
|
||||
def load_links(links_data, node_tree):
|
||||
""" Load node_tree links from a list
|
||||
|
||||
:arg links_data: dumped node links
|
||||
:type links_data: list
|
||||
:arg node_tree: node links collection
|
||||
:type node_tree: bpy.types.NodeTree
|
||||
"""
|
||||
|
||||
target_node_tree.links.new(input_socket, output_socket)
|
||||
for link in links_data:
|
||||
input_socket = node_tree.nodes[link['to_node']].inputs[int(link['to_socket'])]
|
||||
output_socket = node_tree.nodes[link['from_node']].outputs[int(link['from_socket'])]
|
||||
|
||||
node_tree.links.new(input_socket, output_socket)
|
||||
|
||||
def dump_links(links):
|
||||
""" Dump node_tree links collection to a list
|
||||
|
||||
:arg links: node links collection
|
||||
:type links: bpy.types.NodeLinks
|
||||
:retrun: list
|
||||
"""
|
||||
|
||||
links_data = []
|
||||
|
||||
for link in links:
|
||||
links_data.append({
|
||||
'to_node':link.to_node.name,
|
||||
'to_socket':link.to_socket.path_from_id()[-2:-1],
|
||||
'from_node':link.from_node.name,
|
||||
'from_socket':link.from_socket.path_from_id()[-2:-1],
|
||||
})
|
||||
|
||||
return links_data
|
||||
|
||||
class BlMaterial(BlDatablock):
|
||||
bl_id = "materials"
|
||||
@ -76,7 +90,7 @@ class BlMaterial(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'MATERIAL_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.materials.new(data["name"])
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
@ -100,19 +114,25 @@ class BlMaterial(BlDatablock):
|
||||
|
||||
# Load nodes
|
||||
for node in data["node_tree"]["nodes"]:
|
||||
load_node(target.node_tree, data["node_tree"]["nodes"][node])
|
||||
load_node(data["node_tree"]["nodes"][node], target.node_tree)
|
||||
|
||||
# Load nodes links
|
||||
target.node_tree.links.clear()
|
||||
|
||||
for link in data["node_tree"]["links"]:
|
||||
load_link(target.node_tree, data["node_tree"]["links"][link])
|
||||
load_links(data["node_tree"]["links"], target.node_tree)
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
mat_dumper = dump_anything.Dumper()
|
||||
mat_dumper.depth = 2
|
||||
mat_dumper.exclude_filter = [
|
||||
"is_embed_data",
|
||||
"is_evaluated",
|
||||
"name_full",
|
||||
"bl_description",
|
||||
"bl_icon",
|
||||
"bl_idname",
|
||||
"bl_label",
|
||||
"preview",
|
||||
"original",
|
||||
"uuid",
|
||||
@ -121,47 +141,44 @@ class BlMaterial(BlDatablock):
|
||||
"line_color",
|
||||
"view_center",
|
||||
]
|
||||
node_dumper = dump_anything.Dumper()
|
||||
node_dumper.depth = 1
|
||||
node_dumper.exclude_filter = [
|
||||
"dimensions",
|
||||
"show_expanded"
|
||||
"select",
|
||||
"bl_height_min",
|
||||
"bl_height_max",
|
||||
"bl_width_min",
|
||||
"bl_width_max",
|
||||
"bl_width_default",
|
||||
"hide",
|
||||
"show_options",
|
||||
"show_tetxures",
|
||||
"show_preview",
|
||||
"outputs",
|
||||
"width_hidden"
|
||||
]
|
||||
input_dumper = dump_anything.Dumper()
|
||||
input_dumper.depth = 2
|
||||
input_dumper.include_filter = ["default_value"]
|
||||
links_dumper = dump_anything.Dumper()
|
||||
links_dumper.depth = 3
|
||||
links_dumper.include_filter = [
|
||||
"name",
|
||||
"to_node",
|
||||
"from_node",
|
||||
"from_socket",
|
||||
"to_socket"]
|
||||
data = mat_dumper.dump(pointer)
|
||||
|
||||
if pointer.use_nodes:
|
||||
nodes = {}
|
||||
|
||||
node_dumper = dump_anything.Dumper()
|
||||
node_dumper.depth = 1
|
||||
node_dumper.exclude_filter = [
|
||||
"dimensions",
|
||||
"show_expanded",
|
||||
"name_full",
|
||||
"select",
|
||||
"bl_height_min",
|
||||
"bl_height_max",
|
||||
"bl_width_min",
|
||||
"bl_width_max",
|
||||
"type",
|
||||
"bl_icon",
|
||||
"bl_width_default",
|
||||
"bl_static_type",
|
||||
"show_tetxure",
|
||||
"hide",
|
||||
"show_options",
|
||||
"show_preview",
|
||||
"outputs",
|
||||
"width_hidden"
|
||||
]
|
||||
for node in pointer.node_tree.nodes:
|
||||
|
||||
nodes[node.name] = node_dumper.dump(node)
|
||||
|
||||
if hasattr(node, 'inputs'):
|
||||
nodes[node.name]['inputs'] = {}
|
||||
|
||||
for i in node.inputs:
|
||||
for i in node.inputs:
|
||||
input_dumper = dump_anything.Dumper()
|
||||
input_dumper.depth = 2
|
||||
input_dumper.include_filter = ["default_value"]
|
||||
|
||||
if hasattr(i, 'default_value'):
|
||||
nodes[node.name]['inputs'][i.name] = input_dumper.dump(
|
||||
i)
|
||||
@ -184,11 +201,39 @@ class BlMaterial(BlDatablock):
|
||||
'location'
|
||||
]
|
||||
nodes[node.name]['mapping'] = curve_dumper.dump(node.mapping)
|
||||
|
||||
data["node_tree"]['nodes'] = nodes
|
||||
data["node_tree"]["links"] = links_dumper.dump(pointer.node_tree.links)
|
||||
|
||||
|
||||
data["node_tree"]["links"] = dump_links(pointer.node_tree.links)
|
||||
|
||||
elif pointer.is_grease_pencil:
|
||||
data['grease_pencil'] = dump_anything.dump(pointer.grease_pencil, 3)
|
||||
gp_mat_dumper = dump_anything.Dumper()
|
||||
gp_mat_dumper.depth = 3
|
||||
|
||||
gp_mat_dumper.include_filter = [
|
||||
'show_stroke',
|
||||
'mode',
|
||||
'stroke_style',
|
||||
'color',
|
||||
'use_overlap_strokes',
|
||||
'show_fill',
|
||||
'fill_style',
|
||||
'fill_color',
|
||||
'pass_index',
|
||||
'alignment_mode',
|
||||
# 'fill_image',
|
||||
'texture_opacity',
|
||||
'mix_factor',
|
||||
'texture_offset',
|
||||
'texture_angle',
|
||||
'texture_scale',
|
||||
'texture_clamp',
|
||||
'gradient_type',
|
||||
'mix_color',
|
||||
'flip'
|
||||
]
|
||||
data['grease_pencil'] = gp_mat_dumper.dump(pointer.grease_pencil)
|
||||
return data
|
||||
|
||||
def resolve_deps_implementation(self):
|
||||
|
@ -1,163 +1,213 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
import mathutils
|
||||
import logging
|
||||
import numpy as np
|
||||
|
||||
from .. import utils
|
||||
from ..libs.replication.replication.constants import DIFF_BINARY
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
|
||||
def dump_mesh(mesh, data={}):
|
||||
import bmesh
|
||||
|
||||
mesh_data = data
|
||||
mesh_buffer = bmesh.new()
|
||||
|
||||
# https://blog.michelanders.nl/2016/02/copying-vertices-to-numpy-arrays-in_4.html
|
||||
mesh_buffer.from_mesh(mesh)
|
||||
|
||||
uv_layer = mesh_buffer.loops.layers.uv.verify()
|
||||
bevel_layer = mesh_buffer.verts.layers.bevel_weight.verify()
|
||||
skin_layer = mesh_buffer.verts.layers.skin.verify()
|
||||
|
||||
verts = {}
|
||||
for vert in mesh_buffer.verts:
|
||||
v = {}
|
||||
v["co"] = list(vert.co)
|
||||
|
||||
# vert metadata
|
||||
v['bevel'] = vert[bevel_layer]
|
||||
v['normal'] = list(vert.normal)
|
||||
# v['skin'] = list(vert[skin_layer])
|
||||
|
||||
verts[str(vert.index)] = v
|
||||
|
||||
mesh_data["verts"] = verts
|
||||
|
||||
edges = {}
|
||||
for edge in mesh_buffer.edges:
|
||||
e = {}
|
||||
e["verts"] = [edge.verts[0].index, edge.verts[1].index]
|
||||
|
||||
# Edge metadata
|
||||
e["smooth"] = edge.smooth
|
||||
|
||||
edges[edge.index] = e
|
||||
mesh_data["edges"] = edges
|
||||
|
||||
faces = {}
|
||||
for face in mesh_buffer.faces:
|
||||
f = {}
|
||||
fverts = []
|
||||
for vert in face.verts:
|
||||
fverts.append(vert.index)
|
||||
|
||||
f["verts"] = fverts
|
||||
f["material_index"] = face.material_index
|
||||
f["smooth"] = face.smooth
|
||||
f["normal"] = list(face.normal)
|
||||
f["index"] = face.index
|
||||
|
||||
uvs = []
|
||||
# Face metadata
|
||||
for loop in face.loops:
|
||||
loop_uv = loop[uv_layer]
|
||||
|
||||
uvs.append(list(loop_uv.uv))
|
||||
|
||||
f["uv"] = uvs
|
||||
faces[face.index] = f
|
||||
|
||||
mesh_data["faces"] = faces
|
||||
|
||||
uv_layers = []
|
||||
for uv_layer in mesh.uv_layers:
|
||||
uv_layers.append(uv_layer.name)
|
||||
|
||||
mesh_data["uv_layers"] = uv_layers
|
||||
# return mesh_data
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BlMesh(BlDatablock):
|
||||
bl_id = "meshes"
|
||||
bl_class = bpy.types.Mesh
|
||||
bl_delay_refresh = 10
|
||||
bl_delay_apply = 10
|
||||
bl_delay_refresh = 2
|
||||
bl_delay_apply = 1
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'MESH_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
instance = bpy.data.meshes.new(data["name"])
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
if not target or not target.is_editmode:
|
||||
# 1 - LOAD MATERIAL SLOTS
|
||||
# SLots
|
||||
i = 0
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
# MATERIAL SLOTS
|
||||
target.materials.clear()
|
||||
|
||||
for m in data["material_list"]:
|
||||
target.materials.append(bpy.data.materials[m])
|
||||
|
||||
# 2 - LOAD GEOMETRY
|
||||
mesh_buffer = bmesh.new()
|
||||
# CLEAR GEOMETRY
|
||||
if target.vertices:
|
||||
target.clear_geometry()
|
||||
|
||||
for i in data["verts"]:
|
||||
v = mesh_buffer.verts.new(data["verts"][i]["co"])
|
||||
v.normal = data["verts"][i]["normal"]
|
||||
mesh_buffer.verts.ensure_lookup_table()
|
||||
# VERTS
|
||||
vertices = np.frombuffer(data["verts_co"], dtype=np.float64)
|
||||
vert_count = int(len(vertices)/3)
|
||||
target.vertices.add(vert_count)
|
||||
|
||||
for i in data["edges"]:
|
||||
verts = mesh_buffer.verts
|
||||
v1 = data["edges"][i]["verts"][0]
|
||||
v2 = data["edges"][i]["verts"][1]
|
||||
edge = mesh_buffer.edges.new([verts[v1], verts[v2]])
|
||||
edge.smooth = data["edges"][i]["smooth"]
|
||||
# EDGES
|
||||
|
||||
egdes_vert = np.frombuffer(data["egdes_vert"], dtype=np.int)
|
||||
|
||||
mesh_buffer.edges.ensure_lookup_table()
|
||||
for p in data["faces"]:
|
||||
verts = []
|
||||
for v in data["faces"][p]["verts"]:
|
||||
verts.append(mesh_buffer.verts[v])
|
||||
edge_count = data["egdes_count"]
|
||||
target.edges.add(edge_count)
|
||||
|
||||
|
||||
|
||||
if len(verts) > 0:
|
||||
f = mesh_buffer.faces.new(verts)
|
||||
# LOOPS
|
||||
loops_count = data["loop_count"]
|
||||
target.loops.add(loops_count)
|
||||
|
||||
uv_layer = mesh_buffer.loops.layers.uv.verify()
|
||||
loop_vertex_index = np.frombuffer(
|
||||
data['loop_vertex_index'], dtype=np.int)
|
||||
loop_normal = np.frombuffer(data['loop_normal'], dtype=np.float64)
|
||||
|
||||
f.smooth = data["faces"][p]["smooth"]
|
||||
f.normal = data["faces"][p]["normal"]
|
||||
f.index = data["faces"][p]["index"]
|
||||
f.material_index = data["faces"][p]['material_index']
|
||||
# UV loading
|
||||
for i, loop in enumerate(f.loops):
|
||||
loop_uv = loop[uv_layer]
|
||||
loop_uv.uv = data["faces"][p]["uv"][i]
|
||||
mesh_buffer.faces.ensure_lookup_table()
|
||||
mesh_buffer.to_mesh(target)
|
||||
# POLY
|
||||
poly_count = data["poly_count"]
|
||||
target.polygons.add(poly_count)
|
||||
|
||||
# 3 - LOAD METADATA
|
||||
# uv's
|
||||
utils.dump_anything.load(target.uv_layers, data['uv_layers'])
|
||||
poly_loop_start = np.frombuffer(
|
||||
data["poly_loop_start"], dtype=np.int)
|
||||
poly_loop_total = np.frombuffer(
|
||||
data["poly_loop_total"], dtype=np.int)
|
||||
poly_smooth = np.frombuffer(data["poly_smooth"], dtype=np.bool)
|
||||
|
||||
bevel_layer = mesh_buffer.verts.layers.bevel_weight.verify()
|
||||
skin_layer = mesh_buffer.verts.layers.skin.verify()
|
||||
poly_mat = np.frombuffer(data["poly_mat"], dtype=np.int)
|
||||
|
||||
utils.dump_anything.load(target, data)
|
||||
# LOADING
|
||||
target.vertices.foreach_set('co', vertices)
|
||||
target.edges.foreach_set("vertices", egdes_vert)
|
||||
|
||||
if data['use_customdata_edge_crease']:
|
||||
edges_crease = np.frombuffer(data["edges_crease"], dtype=np.float64)
|
||||
target.edges.foreach_set("crease", edges_crease)
|
||||
|
||||
if data['use_customdata_edge_bevel']:
|
||||
edges_bevel = np.frombuffer(data["edges_bevel"], dtype=np.float64)
|
||||
target.edges.foreach_set("bevel_weight", edges_bevel)
|
||||
|
||||
target.loops.foreach_set("vertex_index", loop_vertex_index)
|
||||
target.loops.foreach_set("normal", loop_normal)
|
||||
target.polygons.foreach_set("loop_total", poly_loop_total)
|
||||
target.polygons.foreach_set("loop_start", poly_loop_start)
|
||||
target.polygons.foreach_set("use_smooth", poly_smooth)
|
||||
target.polygons.foreach_set("material_index", poly_mat)
|
||||
|
||||
|
||||
# UV Layers
|
||||
for layer in data['uv_layers']:
|
||||
if layer not in target.uv_layers:
|
||||
target.uv_layers.new(name=layer)
|
||||
|
||||
uv_buffer = np.frombuffer(data["uv_layers"][layer]['data'])
|
||||
|
||||
target.uv_layers[layer].data.foreach_set('uv', uv_buffer)
|
||||
|
||||
target.validate()
|
||||
target.update()
|
||||
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
|
||||
mesh = pointer
|
||||
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
dumper.depth = 2
|
||||
dumper.depth = 1
|
||||
dumper.include_filter = [
|
||||
'name',
|
||||
'use_auto_smooth',
|
||||
'auto_smooth_angle'
|
||||
'auto_smooth_angle',
|
||||
'use_customdata_edge_bevel',
|
||||
'use_customdata_edge_crease'
|
||||
]
|
||||
data = dumper.dump(pointer)
|
||||
dump_mesh(pointer, data)
|
||||
|
||||
data = dumper.dump(mesh)
|
||||
|
||||
# TODO: selective dump
|
||||
# VERTICES
|
||||
vert_count = len(mesh.vertices)
|
||||
|
||||
verts_co = np.empty(vert_count*3, dtype=np.float64)
|
||||
mesh.vertices.foreach_get('co', verts_co)
|
||||
data["verts_co"] = verts_co.tobytes()
|
||||
|
||||
# EDGES
|
||||
edge_count = len(mesh.edges)
|
||||
|
||||
edges_vert = np.empty(edge_count*2, dtype=np.int)
|
||||
mesh.edges.foreach_get('vertices', edges_vert)
|
||||
data["egdes_vert"] = edges_vert.tobytes()
|
||||
data["egdes_count"] = len(mesh.edges)
|
||||
|
||||
if mesh.use_customdata_edge_crease:
|
||||
edges_crease = np.empty(edge_count, dtype=np.float64)
|
||||
mesh.edges.foreach_get('crease', edges_crease)
|
||||
data["edges_crease"] = edges_crease.tobytes()
|
||||
|
||||
if mesh.use_customdata_edge_bevel:
|
||||
edges_bevel = np.empty(edge_count, dtype=np.float64)
|
||||
mesh.edges.foreach_get('bevel_weight', edges_bevel)
|
||||
data["edges_bevel"] = edges_bevel.tobytes()
|
||||
|
||||
# POLYGONS
|
||||
poly_count = len(mesh.polygons)
|
||||
data["poly_count"] = poly_count
|
||||
|
||||
poly_mat = np.empty(poly_count, dtype=np.int)
|
||||
mesh.polygons.foreach_get("material_index", poly_mat)
|
||||
data["poly_mat"] = poly_mat.tobytes()
|
||||
|
||||
poly_loop_start = np.empty(poly_count, dtype=np.int)
|
||||
mesh.polygons.foreach_get("loop_start", poly_loop_start)
|
||||
data["poly_loop_start"] = poly_loop_start.tobytes()
|
||||
|
||||
poly_loop_total = np.empty(poly_count, dtype=np.int)
|
||||
mesh.polygons.foreach_get("loop_total", poly_loop_total)
|
||||
data["poly_loop_total"] = poly_loop_total.tobytes()
|
||||
|
||||
poly_smooth = np.empty(poly_count, dtype=np.bool)
|
||||
mesh.polygons.foreach_get("use_smooth", poly_smooth)
|
||||
data["poly_smooth"] = poly_smooth.tobytes()
|
||||
|
||||
# LOOPS
|
||||
loop_count = len(mesh.loops)
|
||||
data["loop_count"] = loop_count
|
||||
|
||||
loop_normal = np.empty(loop_count*3, dtype=np.float64)
|
||||
mesh.loops.foreach_get("normal", loop_normal)
|
||||
data["loop_normal"] = loop_normal.tobytes()
|
||||
|
||||
loop_vertex_index = np.empty(loop_count, dtype=np.int)
|
||||
mesh.loops.foreach_get("vertex_index", loop_vertex_index)
|
||||
data["loop_vertex_index"] = loop_vertex_index.tobytes()
|
||||
|
||||
# UV Layers
|
||||
data['uv_layers'] = {}
|
||||
for layer in mesh.uv_layers:
|
||||
data['uv_layers'][layer.name] = {}
|
||||
|
||||
uv_layer = np.empty(len(layer.data)*2, dtype=np.float64)
|
||||
layer.data.foreach_get("uv", uv_layer)
|
||||
|
||||
data['uv_layers'][layer.name]['data'] = uv_layer.tobytes()
|
||||
|
||||
# Fix material index
|
||||
m_list = []
|
||||
for material in pointer.materials:
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -13,7 +31,7 @@ class BlMetaball(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'META_BALL'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.metaballs.new(data["name"])
|
||||
|
||||
def load(self, data, target):
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
import logging
|
||||
@ -8,22 +26,6 @@ from .bl_datablock import BlDatablock
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_constraints(target, data):
|
||||
for local_constraint in target.constraints:
|
||||
if local_constraint.name not in data:
|
||||
target.constraints.remove(local_constraint)
|
||||
|
||||
for constraint in data:
|
||||
target_constraint = target.constraints.get(constraint)
|
||||
|
||||
if not target_constraint:
|
||||
target_constraint = target.constraints.new(
|
||||
data[constraint]['type'])
|
||||
|
||||
utils.dump_anything.load(
|
||||
target_constraint, data[constraint])
|
||||
|
||||
|
||||
def load_pose(target_bone, data):
|
||||
target_bone.rotation_mode = data['rotation_mode']
|
||||
|
||||
@ -38,7 +40,7 @@ class BlObject(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'OBJECT_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
pointer = None
|
||||
|
||||
if self.is_library:
|
||||
@ -50,7 +52,7 @@ class BlObject(BlDatablock):
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
|
||||
# Object specific constructor...
|
||||
# TODO: refactoring
|
||||
if "data" not in data:
|
||||
pass
|
||||
elif data["data"] in bpy.data.meshes.keys():
|
||||
@ -87,32 +89,7 @@ class BlObject(BlDatablock):
|
||||
|
||||
def load_implementation(self, data, target):
|
||||
# Load transformation data
|
||||
rot_mode = 'rotation_quaternion' if data['rotation_mode'] == 'QUATERNION' else 'rotation_euler'
|
||||
target.rotation_mode = data['rotation_mode']
|
||||
target.location = data['location']
|
||||
setattr(target, rot_mode, data[rot_mode])
|
||||
target.scale = data['scale']
|
||||
|
||||
target.name = data["name"]
|
||||
# Load modifiers
|
||||
if hasattr(target, 'modifiers'):
|
||||
# TODO: smarter selective update
|
||||
target.modifiers.clear()
|
||||
|
||||
for modifier in data['modifiers']:
|
||||
target_modifier = target.modifiers.get(modifier)
|
||||
|
||||
if not target_modifier:
|
||||
target_modifier = target.modifiers.new(
|
||||
data['modifiers'][modifier]['name'], data['modifiers'][modifier]['type'])
|
||||
|
||||
utils.dump_anything.load(
|
||||
target_modifier, data['modifiers'][modifier])
|
||||
|
||||
# Load constraints
|
||||
# Object
|
||||
if hasattr(target, 'constraints') and 'constraints' in data:
|
||||
load_constraints(target, data['constraints'])
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
# Pose
|
||||
if 'pose' in data:
|
||||
@ -135,28 +112,14 @@ class BlObject(BlDatablock):
|
||||
bone_data = data['pose']['bones'].get(bone)
|
||||
|
||||
if 'constraints' in bone_data.keys():
|
||||
load_constraints(
|
||||
target_bone, bone_data['constraints'])
|
||||
utils.dump_anything.load(target_bone, bone_data['constraints'])
|
||||
|
||||
|
||||
load_pose(target_bone, bone_data)
|
||||
|
||||
if 'bone_index' in bone_data.keys():
|
||||
target_bone.bone_group = target.pose.bone_group[bone_data['bone_group_index']]
|
||||
|
||||
# Load relations
|
||||
if 'children' in data.keys():
|
||||
for child in data['children']:
|
||||
bpy.data.objects[child].parent = self.pointer
|
||||
|
||||
# Load empty representation
|
||||
target.empty_display_size = data['empty_display_size']
|
||||
target.empty_display_type = data['empty_display_type']
|
||||
|
||||
# Instancing
|
||||
target.instance_type = data['instance_type']
|
||||
if data['instance_type'] == 'COLLECTION':
|
||||
target.instance_collection = bpy.data.collections[data['instance_collection']]
|
||||
|
||||
# vertex groups
|
||||
if 'vertex_groups' in data:
|
||||
target.vertex_groups.clear()
|
||||
@ -220,7 +183,6 @@ class BlObject(BlDatablock):
|
||||
data["modifiers"] = {}
|
||||
for index, modifier in enumerate(pointer.modifiers):
|
||||
data["modifiers"][modifier.name] = dumper.dump(modifier)
|
||||
data["modifiers"][modifier.name]['m_index'] = index
|
||||
|
||||
# CONSTRAINTS
|
||||
# OBJECT
|
||||
@ -299,18 +261,18 @@ class BlObject(BlDatablock):
|
||||
data['vertex_groups'] = vg_data
|
||||
|
||||
# SHAPE KEYS
|
||||
pointer_data = pointer.data
|
||||
if hasattr(pointer_data, 'shape_keys') and pointer_data.shape_keys:
|
||||
object_data = pointer.data
|
||||
if hasattr(object_data, 'shape_keys') and object_data.shape_keys:
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
dumper.depth = 2
|
||||
dumper.include_filter = [
|
||||
'reference_key',
|
||||
'use_relative'
|
||||
]
|
||||
data['shape_keys'] = dumper.dump(pointer_data.shape_keys)
|
||||
data['shape_keys']['reference_key'] = pointer_data.shape_keys.reference_key.name
|
||||
data['shape_keys'] = dumper.dump(object_data.shape_keys)
|
||||
data['shape_keys']['reference_key'] = object_data.shape_keys.reference_key.name
|
||||
key_blocks = {}
|
||||
for key in pointer_data.shape_keys.key_blocks:
|
||||
for key in object_data.shape_keys.key_blocks:
|
||||
dumper.depth = 3
|
||||
dumper.include_filter = [
|
||||
'name',
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -12,12 +30,12 @@ class BlScene(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'SCENE_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
instance = bpy.data.scenes.new(data["name"])
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
|
||||
def load(self, data, target):
|
||||
def load_implementation(self, data, target):
|
||||
target = self.pointer
|
||||
# Load other meshes metadata
|
||||
utils.dump_anything.load(target, data)
|
||||
@ -55,7 +73,13 @@ class BlScene(BlDatablock):
|
||||
|
||||
scene_dumper = utils.dump_anything.Dumper()
|
||||
scene_dumper.depth = 1
|
||||
scene_dumper.include_filter = ['name','world', 'id', 'camera', 'grease_pencil']
|
||||
scene_dumper.include_filter = [
|
||||
'name',
|
||||
'world',
|
||||
'id',
|
||||
'camera',
|
||||
'grease_pencil'
|
||||
]
|
||||
data = scene_dumper.dump(pointer)
|
||||
|
||||
scene_dumper.depth = 3
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
@ -16,7 +34,7 @@ class BlSpeaker(BlDatablock):
|
||||
def load_implementation(self, data, target):
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.speakers.new(data["name"])
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
|
@ -1,9 +1,27 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
from .. import utils
|
||||
from .bl_datablock import BlDatablock
|
||||
from .bl_material import load_link, load_node
|
||||
from .bl_material import load_links, load_node, dump_links
|
||||
|
||||
|
||||
class BlWorld(BlDatablock):
|
||||
@ -14,10 +32,10 @@ class BlWorld(BlDatablock):
|
||||
bl_automatic_push = True
|
||||
bl_icon = 'WORLD_DATA'
|
||||
|
||||
def construct(self, data):
|
||||
def _construct(self, data):
|
||||
return bpy.data.worlds.new(data["name"])
|
||||
|
||||
def load(self, data, target):
|
||||
def load_implementation(self, data, target):
|
||||
if data["use_nodes"]:
|
||||
if target.node_tree is None:
|
||||
target.use_nodes = True
|
||||
@ -25,13 +43,13 @@ class BlWorld(BlDatablock):
|
||||
target.node_tree.nodes.clear()
|
||||
|
||||
for node in data["node_tree"]["nodes"]:
|
||||
load_node(target.node_tree, data["node_tree"]["nodes"][node])
|
||||
load_node(data["node_tree"]["nodes"][node], target.node_tree)
|
||||
|
||||
# Load nodes links
|
||||
target.node_tree.links.clear()
|
||||
|
||||
for link in data["node_tree"]["links"]:
|
||||
load_link(target.node_tree, data["node_tree"]["links"][link])
|
||||
|
||||
load_links(data["node_tree"]["links"], target.node_tree)
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
@ -86,8 +104,9 @@ class BlWorld(BlDatablock):
|
||||
nodes[node.name]['inputs'][i.name] = input_dumper.dump(
|
||||
i)
|
||||
data["node_tree"]['nodes'] = nodes
|
||||
utils.dump_datablock_attibutes(
|
||||
pointer.node_tree, ["links"], 3, data['node_tree'])
|
||||
|
||||
data["node_tree"]['links'] = dump_links(pointer.node_tree.links)
|
||||
|
||||
return data
|
||||
|
||||
def resolve_deps_implementation(self):
|
||||
@ -101,6 +120,3 @@ class BlWorld(BlDatablock):
|
||||
deps.append(self.pointer.library)
|
||||
return deps
|
||||
|
||||
def is_valid(self):
|
||||
return bpy.data.worlds.get(self.data['name'])
|
||||
|
||||
|
@ -1,3 +1,20 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import logging
|
||||
|
||||
import bpy
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import collections
|
||||
import logging
|
||||
import os
|
||||
|
@ -1,7 +1,27 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import logging
|
||||
|
||||
import bpy
|
||||
import bpy.types as T
|
||||
import mathutils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def remove_items_from_dict(d, keys, recursive=False):
|
||||
copy = dict(d)
|
||||
@ -47,7 +67,7 @@ def _load_filter_type(t, use_bl_rna=True):
|
||||
if use_bl_rna and x.bl_rna_property:
|
||||
return isinstance(x.bl_rna_property, t)
|
||||
else:
|
||||
isinstance(x.read(), t)
|
||||
return isinstance(x.read(), t)
|
||||
return filter_function
|
||||
|
||||
|
||||
@ -73,8 +93,9 @@ def _load_filter_default(default):
|
||||
|
||||
|
||||
class Dumper:
|
||||
# TODO: support occlude readonly
|
||||
def __init__(self):
|
||||
self.verbose = False
|
||||
self.verbose = True
|
||||
self.depth = 1
|
||||
self.keep_compounds_as_leaves = False
|
||||
self.accept_read_only = True
|
||||
@ -83,7 +104,6 @@ class Dumper:
|
||||
self.type_subset = self.match_subset_all
|
||||
self.include_filter = []
|
||||
self.exclude_filter = []
|
||||
# self._atomic_types = [] # TODO future option?
|
||||
|
||||
def dump(self, any):
|
||||
return self._dump_any(any, 0)
|
||||
@ -175,7 +195,8 @@ class Dumper:
|
||||
if (self.include_filter and p not in self.include_filter):
|
||||
return False
|
||||
getattr(default, p)
|
||||
except AttributeError:
|
||||
except AttributeError as err:
|
||||
logger.debug(err)
|
||||
return False
|
||||
if p.startswith("__"):
|
||||
return False
|
||||
@ -238,14 +259,12 @@ class BlenderAPIElement:
|
||||
|
||||
def write(self, value):
|
||||
# take precaution if property is read-only
|
||||
try:
|
||||
if self.sub_element_name:
|
||||
setattr(self.api_element, self.sub_element_name, value)
|
||||
else:
|
||||
self.api_element = value
|
||||
except AttributeError as err:
|
||||
if not self.occlude_read_only:
|
||||
raise err
|
||||
if self.sub_element_name and \
|
||||
not self.api_element.is_property_readonly(self.sub_element_name):
|
||||
|
||||
setattr(self.api_element, self.sub_element_name, value)
|
||||
else:
|
||||
self.api_element = value
|
||||
|
||||
def extend(self, element_name):
|
||||
return BlenderAPIElement(self.read(), element_name)
|
||||
@ -262,7 +281,7 @@ class BlenderAPIElement:
|
||||
class Loader:
|
||||
def __init__(self):
|
||||
self.type_subset = self.match_subset_all
|
||||
self.occlude_read_only = True
|
||||
self.occlude_read_only = False
|
||||
self.order = ['*']
|
||||
|
||||
def load(self, dst_data, src_dumped_data):
|
||||
@ -287,6 +306,7 @@ class Loader:
|
||||
for i in range(len(dump)):
|
||||
element.read()[i] = dump[i]
|
||||
except AttributeError as err:
|
||||
logger.debug(err)
|
||||
if not self.occlude_read_only:
|
||||
raise err
|
||||
|
||||
@ -297,29 +317,77 @@ class Loader:
|
||||
CONSTRUCTOR_NEW = "new"
|
||||
CONSTRUCTOR_ADD = "add"
|
||||
|
||||
DESTRUCTOR_REMOVE = "remove"
|
||||
DESTRUCTOR_CLEAR = "clear"
|
||||
|
||||
constructors = {
|
||||
T.ColorRampElement: (CONSTRUCTOR_NEW, ["position"]),
|
||||
T.ParticleSettingsTextureSlot: (CONSTRUCTOR_ADD, [])
|
||||
T.ParticleSettingsTextureSlot: (CONSTRUCTOR_ADD, []),
|
||||
T.Modifier: (CONSTRUCTOR_NEW, ["name", "type"]),
|
||||
T.Constraint: (CONSTRUCTOR_NEW, ["type"]),
|
||||
# T.VertexGroup: (CONSTRUCTOR_NEW, ["name"], True),
|
||||
}
|
||||
|
||||
destructors = {
|
||||
T.ColorRampElement:DESTRUCTOR_REMOVE,
|
||||
T.Modifier: DESTRUCTOR_CLEAR,
|
||||
T.Constraint: CONSTRUCTOR_NEW,
|
||||
}
|
||||
element_type = element.bl_rna_property.fixed_type
|
||||
|
||||
constructor = constructors.get(type(element_type))
|
||||
|
||||
if constructor is None: # collection type not supported
|
||||
return
|
||||
for dumped_element in dump.values():
|
||||
try:
|
||||
constructor_parameters = [dumped_element[name]
|
||||
for name in constructor[1]]
|
||||
except KeyError:
|
||||
print("Collection load error, missing parameters.")
|
||||
continue # TODO handle error
|
||||
new_element = getattr(element.read(), constructor[0])(
|
||||
*constructor_parameters)
|
||||
|
||||
destructor = destructors.get(type(element_type))
|
||||
|
||||
# Try to clear existing
|
||||
if destructor:
|
||||
if destructor == DESTRUCTOR_REMOVE:
|
||||
collection = element.read()
|
||||
for i in range(len(collection)-1):
|
||||
collection.remove(collection[0])
|
||||
else:
|
||||
getattr(element.read(), DESTRUCTOR_CLEAR)()
|
||||
|
||||
for dump_idx, dumped_element in enumerate(dump.values()):
|
||||
if dump_idx == 0 and len(element.read())>0:
|
||||
new_element = element.read()[0]
|
||||
else:
|
||||
try:
|
||||
constructor_parameters = [dumped_element[name]
|
||||
for name in constructor[1]]
|
||||
except KeyError:
|
||||
logger.debug("Collection load error, missing parameters.")
|
||||
continue # TODO handle error
|
||||
|
||||
new_element = getattr(element.read(), constructor[0])(
|
||||
*constructor_parameters)
|
||||
self._load_any(
|
||||
BlenderAPIElement(
|
||||
new_element, occlude_read_only=self.occlude_read_only),
|
||||
dumped_element
|
||||
)
|
||||
|
||||
def _load_curve_mapping(self, element, dump):
|
||||
mapping = element.read()
|
||||
# cleanup existing curve
|
||||
for curve in mapping.curves:
|
||||
for idx in range(len(curve.points)):
|
||||
if idx == 0:
|
||||
break
|
||||
|
||||
curve.points.remove(curve.points[1])
|
||||
for curve_index, curve in dump['curves'].items():
|
||||
for point_idx, point in curve['points'].items():
|
||||
pos = point['location']
|
||||
|
||||
if len(mapping.curves[curve_index].points) == 1:
|
||||
mapping.curves[curve_index].points[int(point_idx)].location = pos
|
||||
else:
|
||||
mapping.curves[curve_index].points.new(pos[0],pos[1])
|
||||
|
||||
def _load_pointer(self, pointer, dump):
|
||||
rna_property_type = pointer.bl_rna_property.fixed_type
|
||||
if not rna_property_type:
|
||||
@ -336,6 +404,8 @@ class Loader:
|
||||
pointer.write(bpy.data.meshes.get(dump))
|
||||
elif isinstance(rna_property_type, T.Material):
|
||||
pointer.write(bpy.data.materials.get(dump))
|
||||
elif isinstance(rna_property_type, T.Collection):
|
||||
pointer.write(bpy.data.collections.get(dump))
|
||||
|
||||
def _load_matrix(self, matrix, dump):
|
||||
matrix.write(mathutils.Matrix(dump))
|
||||
@ -365,11 +435,11 @@ class Loader:
|
||||
for k in self._ordered_keys(dump.keys()):
|
||||
v = dump[k]
|
||||
if not hasattr(default.read(), k):
|
||||
continue # TODO error handling
|
||||
logger.debug(f"Load default, skipping {default} : {k}")
|
||||
try:
|
||||
self._load_any(default.extend(k), v)
|
||||
except:
|
||||
pass
|
||||
except Exception as err:
|
||||
logger.debug(f"Cannot load {k}: {err}")
|
||||
|
||||
@property
|
||||
def match_subset_all(self):
|
||||
@ -382,6 +452,7 @@ class Loader:
|
||||
(_load_filter_type(mathutils.Vector, use_bl_rna=False), self._load_vector),
|
||||
(_load_filter_type(mathutils.Quaternion, use_bl_rna=False), self._load_quaternion),
|
||||
(_load_filter_type(mathutils.Euler, use_bl_rna=False), self._load_euler),
|
||||
(_load_filter_type(T.CurveMapping, use_bl_rna=False), self._load_curve_mapping),
|
||||
(_load_filter_type(T.FloatProperty), self._load_identity),
|
||||
(_load_filter_type(T.StringProperty), self._load_identity),
|
||||
(_load_filter_type(T.EnumProperty), self._load_identity),
|
||||
|
@ -1,219 +0,0 @@
|
||||
"""
|
||||
Context Manager allowing temporary override of attributes
|
||||
|
||||
````python
|
||||
import bpy
|
||||
from overrider import Overrider
|
||||
|
||||
with Overrider(name='bpy_', parent=bpy) as bpy_:
|
||||
# set preview render settings
|
||||
bpy_.context.scene.render.use_file_extension = False
|
||||
bpy_.context.scene.render.resolution_x = 512
|
||||
bpy_.context.scene.render.resolution_y = 512
|
||||
bpy_.context.scene.render.use_file_extension = False
|
||||
bpy_.context.scene.render.image_settings.file_format = "JPEG"
|
||||
bpy_.context.scene.layers[10] = False
|
||||
|
||||
frame_start = action.frame_range[0]
|
||||
frame_end = action.frame_range[1]
|
||||
if begin_frame is not None:
|
||||
frame_start = begin_frame
|
||||
if end_frame is not None:
|
||||
frame_end = end_frame
|
||||
|
||||
# render
|
||||
window = bpy_.data.window_managers[0].windows[0]
|
||||
screen = bpy_.data.window_managers[0].windows[0].screen
|
||||
area = next(area for area in screen.areas if area.type == 'VIEW_3D')
|
||||
space = next(space for space in area.spaces if space.type == 'VIEW_3D')
|
||||
|
||||
space.viewport_shade = 'MATERIAL'
|
||||
space.region_3d.view_perspective = 'CAMERA'
|
||||
|
||||
override_context = {
|
||||
"window": window._real_value_(),
|
||||
"screen": screen._real_value_()
|
||||
}
|
||||
|
||||
if frame_start == frame_end:
|
||||
bpy.context.scene.frame_set(int(frame_start))
|
||||
bpy_.context.scene.render.filepath = os.path.join(directory, "icon.jpg")
|
||||
bpy.ops.render.opengl(override_context, write_still=True)
|
||||
|
||||
else:
|
||||
for icon_index, frame_number in enumerate(range(int(frame_start), int(frame_end) + 1)):
|
||||
bpy.context.scene.frame_set(frame_number)
|
||||
bpy.context.scene.render.filepath = os.path.join(directory, "icon", "{:04d}.jpg".format(icon_index))
|
||||
bpy.ops.render.opengl(override_context, write_still=True)
|
||||
````
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class OverrideIter:
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.index = -1
|
||||
|
||||
def __next__(self):
|
||||
self.index += 1
|
||||
try:
|
||||
return self.parent[self.index]
|
||||
except IndexError as e:
|
||||
raise StopIteration
|
||||
|
||||
|
||||
class OverrideBase:
|
||||
|
||||
def __init__(self, context_manager, name=None, parent=None):
|
||||
self._name__ = name
|
||||
self._context_manager_ = context_manager
|
||||
self._parent_ = parent
|
||||
self._changed_attributes_ = OrderedDict()
|
||||
self._changed_items_ = OrderedDict()
|
||||
self._children_ = list()
|
||||
self._original_value_ = self._real_value_()
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}({})>".format(self.__class__.__name__, self._path_)
|
||||
|
||||
@property
|
||||
def _name_(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def _path_(self):
|
||||
if isinstance(self._parent_, OverrideBase):
|
||||
return self._parent_._path_ + self._name_
|
||||
|
||||
return self._name_
|
||||
|
||||
def _real_value_(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _restore_(self):
|
||||
for attribute, original_value in reversed(self._changed_attributes_.items()):
|
||||
setattr(self._real_value_(), attribute, original_value)
|
||||
|
||||
for item, original_value in reversed(self._changed_items_.items()):
|
||||
self._real_value_()[item] = original_value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
new_attribute = OverrideAttribute(self._context_manager_, name=attr, parent=self)
|
||||
self._children_.append(new_attribute)
|
||||
return new_attribute
|
||||
|
||||
def __getitem__(self, item):
|
||||
new_item = OverrideItem(self._context_manager_, name=item, parent=self)
|
||||
self._children_.append(new_item)
|
||||
return new_item
|
||||
|
||||
def __iter__(self):
|
||||
return OverrideIter(self)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr in (
|
||||
'_name__',
|
||||
'_context_manager_',
|
||||
'_parent_',
|
||||
'_children_',
|
||||
'_original_value_',
|
||||
'_changed_attributes_',
|
||||
'_changed_items_'
|
||||
):
|
||||
self.__dict__[attr] = value
|
||||
return
|
||||
|
||||
if attr not in self._changed_attributes_.keys():
|
||||
self._changed_attributes_[attr] = getattr(self._real_value_(), attr)
|
||||
self._context_manager_.register_as_changed(self)
|
||||
|
||||
setattr(self._real_value_(), attr, value)
|
||||
|
||||
def __setitem__(self, item, value):
|
||||
if item not in self._changed_items_.keys():
|
||||
self._changed_items_[item] = self._real_value_()[item]
|
||||
self._context_manager_.register_as_changed(self)
|
||||
|
||||
self._real_value_()[item] = value
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._real_value_() == other
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._real_value_() > other
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._real_value_() < other
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._real_value_() >= other
|
||||
|
||||
def __le__(self, other):
|
||||
return self._real_value_() <= other
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
# TODO : surround str value with quotes
|
||||
arguments = list([str(arg) for arg in args]) + ['{}={}'.format(key, value) for key, value in kwargs.items()]
|
||||
arguments = ', '.join(arguments)
|
||||
raise RuntimeError('Overrider does not allow call to {}({})'.format(self._path_, arguments))
|
||||
|
||||
|
||||
class OverrideRoot(OverrideBase):
|
||||
|
||||
@property
|
||||
def _name_(self):
|
||||
return self._name__
|
||||
|
||||
def _real_value_(self):
|
||||
return self._parent_
|
||||
|
||||
|
||||
class OverrideAttribute(OverrideBase):
|
||||
|
||||
@property
|
||||
def _name_(self):
|
||||
return '.{}'.format(self._name__)
|
||||
|
||||
def _real_value_(self):
|
||||
return getattr(self._parent_._real_value_(), self._name__)
|
||||
|
||||
|
||||
class OverrideItem(OverrideBase):
|
||||
|
||||
@property
|
||||
def _name_(self):
|
||||
if isinstance(self._name__, str):
|
||||
return '["{}"]'.format(self._name__)
|
||||
|
||||
return '[{}]'.format(self._name__)
|
||||
|
||||
def _real_value_(self):
|
||||
return self._parent_._real_value_()[self._name__]
|
||||
|
||||
|
||||
class Overrider:
|
||||
def __init__(self, name, parent):
|
||||
self.name = name
|
||||
self.parent = parent
|
||||
self.override = None
|
||||
self.registered_overrides = list()
|
||||
|
||||
def __enter__(self):
|
||||
self.override = OverrideRoot(
|
||||
context_manager=self,
|
||||
parent=self.parent,
|
||||
name=self.name
|
||||
)
|
||||
return self.override
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.restore()
|
||||
|
||||
def register_as_changed(self, override):
|
||||
self.registered_overrides.append(override)
|
||||
|
||||
def restore(self):
|
||||
for override in reversed(self.registered_overrides):
|
||||
override._restore_()
|
@ -1 +1 @@
|
||||
Subproject commit 114b2ebe37ccd10b36544924d23443df0867581b
|
||||
Subproject commit 70b2d24d15690540c0e0bee43bd82bf338f986c9
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
@ -470,7 +488,7 @@ def sanitize_deps_graph(dummy):
|
||||
|
||||
if client and client.state['STATE'] in [STATE_ACTIVE]:
|
||||
for node_key in client.list():
|
||||
client.get(node_key).resolve()
|
||||
client.get(node_key)._resolve()
|
||||
|
||||
|
||||
@persistent
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import logging
|
||||
import bpy
|
||||
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import copy
|
||||
import logging
|
||||
import math
|
||||
|
@ -1,3 +1,21 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import bpy
|
||||
|
||||
from . import operators, utils
|
||||
|
@ -1,9 +1,28 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
from uuid import uuid4
|
||||
from collections.abc import Iterable
|
||||
|
||||
@ -157,4 +176,7 @@ def resolve_from_id(id, optionnal_type=None):
|
||||
|
||||
|
||||
def get_preferences():
|
||||
return bpy.context.preferences.addons[__package__].preferences
|
||||
return bpy.context.preferences.addons[__package__].preferences
|
||||
|
||||
def current_milli_time():
|
||||
return int(round(time.time() * 1000))
|
Loading…
x
Reference in New Issue
Block a user