1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
# Copyright 2018-2019 The glTF-Blender-IO authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import bpy
from math import sqrt
from mathutils import Quaternion
from .gltf2_blender_node import BlenderNode
from .gltf2_blender_skin import BlenderSkin
from .gltf2_blender_animation import BlenderAnimation
class BlenderScene():
"""Blender Scene."""
def __new__(cls, *args, **kwargs):
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
def create(gltf, scene_idx):
"""Scene creation."""
gltf.blender_active_collection = None
if scene_idx is not None:
pyscene = gltf.data.scenes[scene_idx]
list_nodes = pyscene.nodes
# Create a new scene only if not already exists in .blend file
# TODO : put in current scene instead ?
if pyscene.name not in [scene.name for scene in bpy.data.scenes]:
# TODO: There is a bug in 2.8 alpha that break CLEAR_KEEP_TRANSFORM
# if we are creating a new scene
scene = bpy.context.scene
if bpy.context.collection.name in bpy.data.collections: # avoid master collection
gltf.blender_active_collection = bpy.context.collection.name
if scene.render.engine not in ['CYCLES', 'BLENDER_EEVEE']:
scene.render.engine = "BLENDER_EEVEE"
gltf.blender_scene = scene.name
else:
gltf.blender_scene = pyscene.name
# Switch to newly created main scene
bpy.context.window.scene = bpy.data.scenes[gltf.blender_scene]
if bpy.context.collection.name in bpy.data.collections: # avoid master collection
gltf.blender_active_collection = bpy.context.collection.name
else:
# No scene in glTF file, create all objects in current scene
scene = bpy.context.scene
if scene.render.engine not in ['CYCLES', 'BLENDER_EEVEE']:
scene.render.engine = "BLENDER_EEVEE"
if bpy.context.collection.name in bpy.data.collections: # avoid master collection
gltf.blender_active_collection = bpy.context.collection.name
gltf.blender_scene = scene.name
list_nodes = BlenderScene.get_root_nodes(gltf)
if bpy.app.debug_value != 100:
# Create Yup2Zup empty
obj_rotation = bpy.data.objects.new("Yup2Zup", None)
obj_rotation.rotation_mode = 'QUATERNION'
obj_rotation.rotation_quaternion = Quaternion((sqrt(2) / 2, sqrt(2) / 2, 0.0, 0.0))
if gltf.blender_active_collection is not None:
bpy.data.collections[gltf.blender_active_collection].objects.link(obj_rotation)
else:
bpy.data.scenes[gltf.blender_scene].collection.objects.link(obj_rotation)
if list_nodes is not None:
for node_idx in list_nodes:
BlenderNode.create(gltf, node_idx, None) # None => No parent
# Now that all mesh / bones are created, create vertex groups on mesh
if gltf.data.skins:
for skin_id, skin in enumerate(gltf.data.skins):
if hasattr(skin, "node_ids"):
BlenderSkin.create_vertex_groups(gltf, skin_id)
for skin_id, skin in enumerate(gltf.data.skins):
if hasattr(skin, "node_ids"):
BlenderSkin.assign_vertex_groups(gltf, skin_id)
for skin_id, skin in enumerate(gltf.data.skins):
if hasattr(skin, "node_ids"):
BlenderSkin.create_armature_modifiers(gltf, skin_id)
if gltf.data.animations:
gltf.animation_managed = []
for anim_idx, anim in enumerate(gltf.data.animations):
gltf.current_animation_names = {}
gltf.actions_stashed= {}
if list_nodes is not None:
for node_idx in list_nodes:
BlenderAnimation.anim(gltf, anim_idx, node_idx)
for an in gltf.current_animation_names.values():
gltf.animation_managed.append(an)
for node_idx in list_nodes:
BlenderAnimation.stash_action(gltf, anim_idx, node_idx, an)
for node_idx in list_nodes:
BlenderAnimation.restore_last_action(gltf, node_idx)
if bpy.app.debug_value != 100:
# Parent root node to rotation object
if list_nodes is not None:
exclude_nodes = []
for node_idx in list_nodes:
if gltf.data.nodes[node_idx].is_joint:
# Do not change parent if root node is already parented (can be the case for skinned mesh)
if not bpy.data.objects[gltf.data.nodes[node_idx].blender_armature_name].parent:
bpy.data.objects[gltf.data.nodes[node_idx].blender_armature_name].parent = obj_rotation
else:
exclude_nodes.append(node_idx)
else:
# Do not change parent if root node is already parented (can be the case for skinned mesh)
if not bpy.data.objects[gltf.data.nodes[node_idx].blender_object].parent:
bpy.data.objects[gltf.data.nodes[node_idx].blender_object].parent = obj_rotation
else:
exclude_nodes.append(node_idx)
if gltf.animation_object is False:
# Avoid rotation bug if collection is hidden or disabled
if gltf.blender_active_collection is not None:
gltf.collection_hide_viewport = bpy.data.collections[gltf.blender_active_collection].hide_viewport
bpy.data.collections[gltf.blender_active_collection].hide_viewport = False
# TODO for visibility ... but seems not exposed on bpy for now
for node_idx in list_nodes:
if node_idx in exclude_nodes:
continue # for root node that are parented by the process
# for example skinned meshes
for obj_ in bpy.context.scene.objects:
obj_.select_set(False)
if gltf.data.nodes[node_idx].is_joint:
bpy.data.objects[gltf.data.nodes[node_idx].blender_armature_name].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[gltf.data.nodes[node_idx].blender_armature_name]
else:
bpy.data.objects[gltf.data.nodes[node_idx].blender_object].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[gltf.data.nodes[node_idx].blender_object]
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
# remove object
#bpy.context.scene.collection.objects.unlink(obj_rotation)
bpy.data.objects.remove(obj_rotation)
# Restore collection hiden / disabled values
if gltf.blender_active_collection is not None:
bpy.data.collections[gltf.blender_active_collection].hide_viewport = gltf.collection_hide_viewport
# TODO restore visibility when expose in bpy
# Make first root object the new active one
if list_nodes is not None:
bpy.context.view_layer.objects.active = bpy.data.objects[gltf.data.nodes[list_nodes[0]].blender_object]
@staticmethod
def get_root_nodes(gltf):
if gltf.data.nodes is None:
return None
parents = {}
for idx, node in enumerate(gltf.data.nodes):
pynode = gltf.data.nodes[idx]
if pynode.children:
for child_idx in pynode.children:
parents[child_idx] = idx
roots = []
for idx, node in enumerate(gltf.data.nodes):
if idx not in parents.keys():
roots.append(idx)
return roots
|