glTF: remove no more needed files
This commit is contained in:
parent
d609009748
commit
6b465fdceb
|
@ -1,121 +0,0 @@
|
|||
# Copyright 2018 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 os
|
||||
import shutil
|
||||
import bpy
|
||||
import zlib
|
||||
import struct
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_get
|
||||
|
||||
|
||||
def create_image_file(context, blender_image, dst_path, file_format):
|
||||
"""Create JPEG or PNG file from a given Blender image."""
|
||||
# Check, if source image exists e.g. does not exist if image is packed.
|
||||
file_exists = 1
|
||||
try:
|
||||
src_path = bpy.path.abspath(blender_image.filepath, library=blender_image.library)
|
||||
file = open(src_path)
|
||||
except IOError:
|
||||
file_exists = 0
|
||||
else:
|
||||
file.close()
|
||||
|
||||
if file_exists == 0:
|
||||
# Image does not exist on disk ...
|
||||
blender_image.filepath = dst_path
|
||||
# ... so save it.
|
||||
blender_image.save()
|
||||
|
||||
elif file_format == blender_image.file_format:
|
||||
# Copy source image to destination, keeping original format.
|
||||
|
||||
src_path = bpy.path.abspath(blender_image.filepath, library=blender_image.library)
|
||||
|
||||
# Required for comapre.
|
||||
src_path = src_path.replace('\\', '/')
|
||||
dst_path = dst_path.replace('\\', '/')
|
||||
|
||||
# Check that source and destination path are not the same using os.path.abspath
|
||||
# because bpy.path.abspath seems to not always return an absolute path
|
||||
if os.path.abspath(dst_path) != os.path.abspath(src_path):
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
|
||||
else:
|
||||
# Render a new image to destination, converting to target format.
|
||||
|
||||
# TODO: Reusing the existing scene means settings like exposure are applied on export,
|
||||
# which we don't want, but I'm not sure how to create a new Scene object through the
|
||||
# Python API. See: https://github.com/KhronosGroup/glTF-Blender-Exporter/issues/184.
|
||||
|
||||
tmp_file_format = context.scene.render.image_settings.file_format
|
||||
tmp_color_depth = context.scene.render.image_settings.color_depth
|
||||
|
||||
context.scene.render.image_settings.file_format = file_format
|
||||
context.scene.render.image_settings.color_depth = '8'
|
||||
blender_image.save_render(dst_path, context.scene)
|
||||
|
||||
context.scene.render.image_settings.file_format = tmp_file_format
|
||||
context.scene.render.image_settings.color_depth = tmp_color_depth
|
||||
|
||||
|
||||
def create_image_data(context, export_settings, blender_image, file_format):
|
||||
"""Create JPEG or PNG byte array from a given Blender image."""
|
||||
if blender_image is None:
|
||||
return None
|
||||
|
||||
if file_format == 'PNG':
|
||||
return _create_png_data(blender_image)
|
||||
else:
|
||||
return _create_jpg_data(context, export_settings, blender_image)
|
||||
|
||||
|
||||
def _create_jpg_data(context, export_settings, blender_image):
|
||||
"""Create a JPEG byte array from a given Blender image."""
|
||||
uri = gltf2_blender_get.get_image_uri(export_settings, blender_image)
|
||||
path = export_settings['gltf_filedirectory'] + uri
|
||||
|
||||
create_image_file(context, blender_image, path, 'JPEG')
|
||||
|
||||
jpg_data = open(path, 'rb').read()
|
||||
os.remove(path)
|
||||
|
||||
return jpg_data
|
||||
|
||||
|
||||
def _create_png_data(blender_image):
|
||||
"""Create a PNG byte array from a given Blender image."""
|
||||
width, height = blender_image.size
|
||||
|
||||
buf = bytearray([int(channel * 255.0) for channel in blender_image.pixels])
|
||||
|
||||
#
|
||||
# Taken from 'blender-thumbnailer.py' in Blender.
|
||||
#
|
||||
|
||||
# reverse the vertical line order and add null bytes at the start
|
||||
width_byte_4 = width * 4
|
||||
raw_data = b"".join(
|
||||
b'\x00' + buf[span:span + width_byte_4] for span in range((height - 1) * width * 4, -1, - width_byte_4))
|
||||
|
||||
def png_pack(png_tag, data):
|
||||
chunk_head = png_tag + data
|
||||
return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))
|
||||
|
||||
return b"".join([
|
||||
b'\x89PNG\r\n\x1a\n',
|
||||
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
|
||||
png_pack(b'IDAT', zlib.compress(raw_data, 9)),
|
||||
png_pack(b'IEND', b'')])
|
||||
|
|
@ -1,638 +0,0 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
#
|
||||
# Imports
|
||||
#
|
||||
|
||||
import bpy
|
||||
from . import gltf2_blender_export_keys
|
||||
from . import gltf2_blender_extract
|
||||
from mathutils import Matrix, Quaternion, Euler
|
||||
|
||||
|
||||
#
|
||||
# Globals
|
||||
#
|
||||
|
||||
JOINT_NODE = 'JOINT'
|
||||
|
||||
NEEDS_CONVERSION = 'CONVERSION_NEEDED'
|
||||
CUBIC_INTERPOLATION = 'CUBICSPLINE'
|
||||
LINEAR_INTERPOLATION = 'LINEAR'
|
||||
STEP_INTERPOLATION = 'STEP'
|
||||
BEZIER_INTERPOLATION = 'BEZIER'
|
||||
CONSTANT_INTERPOLATION = 'CONSTANT'
|
||||
|
||||
|
||||
#
|
||||
# Functions
|
||||
#
|
||||
|
||||
def animate_get_interpolation(export_settings, blender_fcurve_list):
|
||||
"""
|
||||
Retrieve the glTF interpolation, depending on a fcurve list.
|
||||
|
||||
Blender allows mixing and more variations of interpolations.
|
||||
In such a case, a conversion is needed.
|
||||
"""
|
||||
if export_settings[gltf2_blender_export_keys.FORCE_SAMPLING]:
|
||||
return NEEDS_CONVERSION
|
||||
|
||||
#
|
||||
|
||||
interpolation = None
|
||||
|
||||
keyframe_count = None
|
||||
|
||||
for blender_fcurve in blender_fcurve_list:
|
||||
if blender_fcurve is None:
|
||||
continue
|
||||
|
||||
#
|
||||
|
||||
current_keyframe_count = len(blender_fcurve.keyframe_points)
|
||||
|
||||
if keyframe_count is None:
|
||||
keyframe_count = current_keyframe_count
|
||||
|
||||
if current_keyframe_count > 0 > blender_fcurve.keyframe_points[0].co[0]:
|
||||
return NEEDS_CONVERSION
|
||||
|
||||
if keyframe_count != current_keyframe_count:
|
||||
return NEEDS_CONVERSION
|
||||
|
||||
#
|
||||
|
||||
for blender_keyframe in blender_fcurve.keyframe_points:
|
||||
is_bezier = blender_keyframe.interpolation == BEZIER_INTERPOLATION
|
||||
is_linear = blender_keyframe.interpolation == LINEAR_INTERPOLATION
|
||||
is_constant = blender_keyframe.interpolation == CONSTANT_INTERPOLATION
|
||||
|
||||
if interpolation is None:
|
||||
if is_bezier:
|
||||
interpolation = CUBIC_INTERPOLATION
|
||||
elif is_linear:
|
||||
interpolation = LINEAR_INTERPOLATION
|
||||
elif is_constant:
|
||||
interpolation = STEP_INTERPOLATION
|
||||
else:
|
||||
interpolation = NEEDS_CONVERSION
|
||||
return interpolation
|
||||
else:
|
||||
if is_bezier and interpolation != CUBIC_INTERPOLATION:
|
||||
interpolation = NEEDS_CONVERSION
|
||||
return interpolation
|
||||
elif is_linear and interpolation != LINEAR_INTERPOLATION:
|
||||
interpolation = NEEDS_CONVERSION
|
||||
return interpolation
|
||||
elif is_constant and interpolation != STEP_INTERPOLATION:
|
||||
interpolation = NEEDS_CONVERSION
|
||||
return interpolation
|
||||
elif not is_bezier and not is_linear and not is_constant:
|
||||
interpolation = NEEDS_CONVERSION
|
||||
return interpolation
|
||||
|
||||
if interpolation is None:
|
||||
interpolation = NEEDS_CONVERSION
|
||||
|
||||
return interpolation
|
||||
|
||||
|
||||
def animate_convert_rotation_axis_angle(axis_angle):
|
||||
"""Convert an axis angle to a quaternion rotation."""
|
||||
q = Quaternion((axis_angle[1], axis_angle[2], axis_angle[3]), axis_angle[0])
|
||||
|
||||
return [q.x, q.y, q.z, q.w]
|
||||
|
||||
|
||||
def animate_convert_rotation_euler(euler, rotation_mode):
|
||||
"""Convert an euler angle to a quaternion rotation."""
|
||||
rotation = Euler((euler[0], euler[1], euler[2]), rotation_mode).to_quaternion()
|
||||
|
||||
return [rotation.x, rotation.y, rotation.z, rotation.w]
|
||||
|
||||
|
||||
def animate_convert_keys(key_list):
|
||||
"""Convert Blender key frames to glTF time keys depending on the applied frames per second."""
|
||||
times = []
|
||||
|
||||
for key in key_list:
|
||||
times.append(key / bpy.context.scene.render.fps)
|
||||
|
||||
return times
|
||||
|
||||
|
||||
def animate_gather_keys(export_settings, fcurve_list, interpolation):
|
||||
"""
|
||||
Merge and sort several key frames to one set.
|
||||
|
||||
If an interpolation conversion is needed, the sample key frames are created as well.
|
||||
"""
|
||||
keys = []
|
||||
|
||||
frame_start = bpy.context.scene.frame_start
|
||||
frame_end = bpy.context.scene.frame_end
|
||||
|
||||
if interpolation == NEEDS_CONVERSION:
|
||||
start = None
|
||||
end = None
|
||||
|
||||
for blender_fcurve in fcurve_list:
|
||||
if blender_fcurve is None:
|
||||
continue
|
||||
|
||||
if start is None:
|
||||
start = blender_fcurve.range()[0]
|
||||
else:
|
||||
start = min(start, blender_fcurve.range()[0])
|
||||
|
||||
if end is None:
|
||||
end = blender_fcurve.range()[1]
|
||||
else:
|
||||
end = max(end, blender_fcurve.range()[1])
|
||||
|
||||
#
|
||||
|
||||
add_epsilon_keyframe = False
|
||||
for blender_keyframe in blender_fcurve.keyframe_points:
|
||||
if add_epsilon_keyframe:
|
||||
key = blender_keyframe.co[0] - 0.001
|
||||
|
||||
if key not in keys:
|
||||
keys.append(key)
|
||||
|
||||
add_epsilon_keyframe = False
|
||||
|
||||
if blender_keyframe.interpolation == CONSTANT_INTERPOLATION:
|
||||
add_epsilon_keyframe = True
|
||||
|
||||
if add_epsilon_keyframe:
|
||||
key = end - 0.001
|
||||
|
||||
if key not in keys:
|
||||
keys.append(key)
|
||||
|
||||
key = start
|
||||
while key <= end:
|
||||
if not export_settings[gltf2_blender_export_keys.FRAME_RANGE] or (frame_start <= key <= frame_end):
|
||||
keys.append(key)
|
||||
key += export_settings[gltf2_blender_export_keys.FRAME_STEP]
|
||||
|
||||
keys.sort()
|
||||
|
||||
else:
|
||||
for blender_fcurve in fcurve_list:
|
||||
if blender_fcurve is None:
|
||||
continue
|
||||
|
||||
for blender_keyframe in blender_fcurve.keyframe_points:
|
||||
key = blender_keyframe.co[0]
|
||||
if not export_settings[gltf2_blender_export_keys.FRAME_RANGE] or (frame_start <= key <= frame_end):
|
||||
if key not in keys:
|
||||
keys.append(key)
|
||||
|
||||
keys.sort()
|
||||
|
||||
return keys
|
||||
|
||||
|
||||
def animate_location(export_settings, location, interpolation, node_type, node_name, action_name, matrix_correction,
|
||||
matrix_basis):
|
||||
"""Calculate/gather the key value pairs for location transformations."""
|
||||
joint_cache = export_settings[gltf2_blender_export_keys.JOINT_CACHE][action_name]
|
||||
if not joint_cache.get(node_name):
|
||||
joint_cache[node_name] = {}
|
||||
|
||||
keys = animate_gather_keys(export_settings, location, interpolation)
|
||||
|
||||
times = animate_convert_keys(keys)
|
||||
|
||||
result = {}
|
||||
result_in_tangent = {}
|
||||
result_out_tangent = {}
|
||||
|
||||
keyframe_index = 0
|
||||
for timeIndex, time in enumerate(times):
|
||||
translation = [0.0, 0.0, 0.0]
|
||||
in_tangent = [0.0, 0.0, 0.0]
|
||||
out_tangent = [0.0, 0.0, 0.0]
|
||||
|
||||
if node_type == JOINT_NODE:
|
||||
if joint_cache[node_name].get(keys[keyframe_index]):
|
||||
translation, tmp_rotation, tmp_scale = joint_cache[node_name][keys[keyframe_index]]
|
||||
else:
|
||||
bpy.context.scene.frame_set(keys[keyframe_index])
|
||||
|
||||
matrix = matrix_correction * matrix_basis
|
||||
|
||||
translation, tmp_rotation, tmp_scale = matrix.decompose()
|
||||
|
||||
joint_cache[node_name][keys[keyframe_index]] = [translation, tmp_rotation, tmp_scale]
|
||||
else:
|
||||
channel_index = 0
|
||||
for blender_fcurve in location:
|
||||
|
||||
if blender_fcurve is not None:
|
||||
|
||||
if interpolation == CUBIC_INTERPOLATION:
|
||||
blender_key_frame = blender_fcurve.keyframe_points[keyframe_index]
|
||||
|
||||
translation[channel_index] = blender_key_frame.co[1]
|
||||
|
||||
if timeIndex == 0:
|
||||
in_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (time - times[timeIndex - 1])
|
||||
in_tangent_value = (blender_key_frame.co[1] - blender_key_frame.handle_left[1]) * factor
|
||||
|
||||
if timeIndex == len(times) - 1:
|
||||
out_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (times[timeIndex + 1] - time)
|
||||
out_tangent_value = (blender_key_frame.handle_right[1] - blender_key_frame.co[1]) * factor
|
||||
|
||||
in_tangent[channel_index] = in_tangent_value
|
||||
out_tangent[channel_index] = out_tangent_value
|
||||
else:
|
||||
value = blender_fcurve.evaluate(keys[keyframe_index])
|
||||
|
||||
translation[channel_index] = value
|
||||
|
||||
channel_index += 1
|
||||
|
||||
# handle parent inverse
|
||||
matrix = Matrix.Translation(translation)
|
||||
matrix = matrix_correction * matrix
|
||||
translation = matrix.to_translation()
|
||||
|
||||
translation = gltf2_blender_extract.convert_swizzle_location(translation, export_settings)
|
||||
in_tangent = gltf2_blender_extract.convert_swizzle_location(in_tangent, export_settings)
|
||||
out_tangent = gltf2_blender_extract.convert_swizzle_location(out_tangent, export_settings)
|
||||
|
||||
result[time] = translation
|
||||
result_in_tangent[time] = in_tangent
|
||||
result_out_tangent[time] = out_tangent
|
||||
|
||||
keyframe_index += 1
|
||||
|
||||
return result, result_in_tangent, result_out_tangent
|
||||
|
||||
|
||||
def animate_rotation_axis_angle(export_settings, rotation_axis_angle, interpolation, node_type, node_name, action_name,
|
||||
matrix_correction, matrix_basis):
|
||||
"""Calculate/gather the key value pairs for axis angle transformations."""
|
||||
joint_cache = export_settings[gltf2_blender_export_keys.JOINT_CACHE][action_name]
|
||||
if not joint_cache.get(node_name):
|
||||
joint_cache[node_name] = {}
|
||||
|
||||
keys = animate_gather_keys(export_settings, rotation_axis_angle, interpolation)
|
||||
|
||||
times = animate_convert_keys(keys)
|
||||
|
||||
result = {}
|
||||
|
||||
keyframe_index = 0
|
||||
for time in times:
|
||||
axis_angle_rotation = [1.0, 0.0, 0.0, 0.0]
|
||||
|
||||
if node_type == JOINT_NODE:
|
||||
if joint_cache[node_name].get(keys[keyframe_index]):
|
||||
tmp_location, rotation, tmp_scale = joint_cache[node_name][keys[keyframe_index]]
|
||||
else:
|
||||
bpy.context.scene.frame_set(keys[keyframe_index])
|
||||
|
||||
matrix = matrix_correction * matrix_basis
|
||||
|
||||
tmp_location, rotation, tmp_scale = matrix.decompose()
|
||||
|
||||
joint_cache[node_name][keys[keyframe_index]] = [tmp_location, rotation, tmp_scale]
|
||||
else:
|
||||
channel_index = 0
|
||||
for blender_fcurve in rotation_axis_angle:
|
||||
if blender_fcurve is not None:
|
||||
value = blender_fcurve.evaluate(keys[keyframe_index])
|
||||
|
||||
axis_angle_rotation[channel_index] = value
|
||||
|
||||
channel_index += 1
|
||||
|
||||
rotation = animate_convert_rotation_axis_angle(axis_angle_rotation)
|
||||
|
||||
# handle parent inverse
|
||||
rotation = Quaternion((rotation[3], rotation[0], rotation[1], rotation[2]))
|
||||
matrix = rotation.to_matrix().to_4x4()
|
||||
matrix = matrix_correction * matrix
|
||||
rotation = matrix.to_quaternion()
|
||||
|
||||
# Bring back to internal Quaternion notation.
|
||||
rotation = gltf2_blender_extract.convert_swizzle_rotation(
|
||||
[rotation[0], rotation[1], rotation[2], rotation[3]], export_settings)
|
||||
|
||||
# Bring back to glTF Quaternion notation.
|
||||
rotation = [rotation[1], rotation[2], rotation[3], rotation[0]]
|
||||
|
||||
result[time] = rotation
|
||||
|
||||
keyframe_index += 1
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def animate_rotation_euler(export_settings, rotation_euler, rotation_mode, interpolation, node_type, node_name,
|
||||
action_name, matrix_correction, matrix_basis):
|
||||
"""Calculate/gather the key value pairs for euler angle transformations."""
|
||||
joint_cache = export_settings[gltf2_blender_export_keys.JOINT_CACHE][action_name]
|
||||
if not joint_cache.get(node_name):
|
||||
joint_cache[node_name] = {}
|
||||
|
||||
keys = animate_gather_keys(export_settings, rotation_euler, interpolation)
|
||||
|
||||
times = animate_convert_keys(keys)
|
||||
|
||||
result = {}
|
||||
|
||||
keyframe_index = 0
|
||||
for time in times:
|
||||
euler_rotation = [0.0, 0.0, 0.0]
|
||||
|
||||
if node_type == JOINT_NODE:
|
||||
if joint_cache[node_name].get(keys[keyframe_index]):
|
||||
tmp_location, rotation, tmp_scale = joint_cache[node_name][keys[keyframe_index]]
|
||||
else:
|
||||
bpy.context.scene.frame_set(keys[keyframe_index])
|
||||
|
||||
matrix = matrix_correction * matrix_basis
|
||||
|
||||
tmp_location, rotation, tmp_scale = matrix.decompose()
|
||||
|
||||
joint_cache[node_name][keys[keyframe_index]] = [tmp_location, rotation, tmp_scale]
|
||||
else:
|
||||
channel_index = 0
|
||||
for blender_fcurve in rotation_euler:
|
||||
if blender_fcurve is not None:
|
||||
value = blender_fcurve.evaluate(keys[keyframe_index])
|
||||
|
||||
euler_rotation[channel_index] = value
|
||||
|
||||
channel_index += 1
|
||||
|
||||
rotation = animate_convert_rotation_euler(euler_rotation, rotation_mode)
|
||||
|
||||
# handle parent inverse
|
||||
rotation = Quaternion((rotation[3], rotation[0], rotation[1], rotation[2]))
|
||||
matrix = rotation.to_matrix().to_4x4()
|
||||
matrix = matrix_correction * matrix
|
||||
rotation = matrix.to_quaternion()
|
||||
|
||||
# Bring back to internal Quaternion notation.
|
||||
rotation = gltf2_blender_extract.convert_swizzle_rotation(
|
||||
[rotation[0], rotation[1], rotation[2], rotation[3]], export_settings)
|
||||
|
||||
# Bring back to glTF Quaternion notation.
|
||||
rotation = [rotation[1], rotation[2], rotation[3], rotation[0]]
|
||||
|
||||
result[time] = rotation
|
||||
|
||||
keyframe_index += 1
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def animate_rotation_quaternion(export_settings, rotation_quaternion, interpolation, node_type, node_name, action_name,
|
||||
matrix_correction, matrix_basis):
|
||||
"""Calculate/gather the key value pairs for quaternion transformations."""
|
||||
joint_cache = export_settings[gltf2_blender_export_keys.JOINT_CACHE][action_name]
|
||||
if not joint_cache.get(node_name):
|
||||
joint_cache[node_name] = {}
|
||||
|
||||
keys = animate_gather_keys(export_settings, rotation_quaternion, interpolation)
|
||||
|
||||
times = animate_convert_keys(keys)
|
||||
|
||||
result = {}
|
||||
result_in_tangent = {}
|
||||
result_out_tangent = {}
|
||||
|
||||
keyframe_index = 0
|
||||
for timeIndex, time in enumerate(times):
|
||||
rotation = [1.0, 0.0, 0.0, 0.0]
|
||||
in_tangent = [1.0, 0.0, 0.0, 0.0]
|
||||
out_tangent = [1.0, 0.0, 0.0, 0.0]
|
||||
|
||||
if node_type == JOINT_NODE:
|
||||
if joint_cache[node_name].get(keys[keyframe_index]):
|
||||
tmp_location, rotation, tmp_scale = joint_cache[node_name][keys[keyframe_index]]
|
||||
else:
|
||||
bpy.context.scene.frame_set(keys[keyframe_index])
|
||||
|
||||
matrix = matrix_correction * matrix_basis
|
||||
|
||||
tmp_location, rotation, tmp_scale = matrix.decompose()
|
||||
|
||||
joint_cache[node_name][keys[keyframe_index]] = [tmp_location, rotation, tmp_scale]
|
||||
else:
|
||||
channel_index = 0
|
||||
for blender_fcurve in rotation_quaternion:
|
||||
|
||||
if blender_fcurve is not None:
|
||||
if interpolation == CUBIC_INTERPOLATION:
|
||||
blender_key_frame = blender_fcurve.keyframe_points[keyframe_index]
|
||||
|
||||
rotation[channel_index] = blender_key_frame.co[1]
|
||||
|
||||
if timeIndex == 0:
|
||||
in_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (time - times[timeIndex - 1])
|
||||
in_tangent_value = (blender_key_frame.co[1] - blender_key_frame.handle_left[1]) * factor
|
||||
|
||||
if timeIndex == len(times) - 1:
|
||||
out_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (times[timeIndex + 1] - time)
|
||||
out_tangent_value = (blender_key_frame.handle_right[1] - blender_key_frame.co[1]) * factor
|
||||
|
||||
in_tangent[channel_index] = in_tangent_value
|
||||
out_tangent[channel_index] = out_tangent_value
|
||||
else:
|
||||
value = blender_fcurve.evaluate(keys[keyframe_index])
|
||||
|
||||
rotation[channel_index] = value
|
||||
|
||||
channel_index += 1
|
||||
|
||||
rotation = Quaternion((rotation[0], rotation[1], rotation[2], rotation[3]))
|
||||
in_tangent = gltf2_blender_extract.convert_swizzle_rotation(in_tangent, export_settings)
|
||||
out_tangent = gltf2_blender_extract.convert_swizzle_rotation(out_tangent, export_settings)
|
||||
|
||||
# handle parent inverse
|
||||
matrix = rotation.to_matrix().to_4x4()
|
||||
matrix = matrix_correction * matrix
|
||||
rotation = matrix.to_quaternion()
|
||||
|
||||
# Bring back to internal Quaternion notation.
|
||||
rotation = gltf2_blender_extract.convert_swizzle_rotation(
|
||||
[rotation[0], rotation[1], rotation[2], rotation[3]], export_settings)
|
||||
|
||||
# Bring to glTF Quaternion notation.
|
||||
rotation = [rotation[1], rotation[2], rotation[3], rotation[0]]
|
||||
in_tangent = [in_tangent[1], in_tangent[2], in_tangent[3], in_tangent[0]]
|
||||
out_tangent = [out_tangent[1], out_tangent[2], out_tangent[3], out_tangent[0]]
|
||||
|
||||
result[time] = rotation
|
||||
result_in_tangent[time] = in_tangent
|
||||
result_out_tangent[time] = out_tangent
|
||||
|
||||
keyframe_index += 1
|
||||
|
||||
return result, result_in_tangent, result_out_tangent
|
||||
|
||||
|
||||
def animate_scale(export_settings, scale, interpolation, node_type, node_name, action_name, matrix_correction,
|
||||
matrix_basis):
|
||||
"""Calculate/gather the key value pairs for scale transformations."""
|
||||
joint_cache = export_settings[gltf2_blender_export_keys.JOINT_CACHE][action_name]
|
||||
if not joint_cache.get(node_name):
|
||||
joint_cache[node_name] = {}
|
||||
|
||||
keys = animate_gather_keys(export_settings, scale, interpolation)
|
||||
|
||||
times = animate_convert_keys(keys)
|
||||
|
||||
result = {}
|
||||
result_in_tangent = {}
|
||||
result_out_tangent = {}
|
||||
|
||||
keyframe_index = 0
|
||||
for timeIndex, time in enumerate(times):
|
||||
scale_data = [1.0, 1.0, 1.0]
|
||||
in_tangent = [0.0, 0.0, 0.0]
|
||||
out_tangent = [0.0, 0.0, 0.0]
|
||||
|
||||
if node_type == JOINT_NODE:
|
||||
if joint_cache[node_name].get(keys[keyframe_index]):
|
||||
tmp_location, tmp_rotation, scale_data = joint_cache[node_name][keys[keyframe_index]]
|
||||
else:
|
||||
bpy.context.scene.frame_set(keys[keyframe_index])
|
||||
|
||||
matrix = matrix_correction * matrix_basis
|
||||
|
||||
tmp_location, tmp_rotation, scale_data = matrix.decompose()
|
||||
|
||||
joint_cache[node_name][keys[keyframe_index]] = [tmp_location, tmp_rotation, scale_data]
|
||||
else:
|
||||
channel_index = 0
|
||||
for blender_fcurve in scale:
|
||||
|
||||
if blender_fcurve is not None:
|
||||
if interpolation == CUBIC_INTERPOLATION:
|
||||
blender_key_frame = blender_fcurve.keyframe_points[keyframe_index]
|
||||
|
||||
scale_data[channel_index] = blender_key_frame.co[1]
|
||||
|
||||
if timeIndex == 0:
|
||||
in_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (time - times[timeIndex - 1])
|
||||
in_tangent_value = (blender_key_frame.co[1] - blender_key_frame.handle_left[1]) * factor
|
||||
|
||||
if timeIndex == len(times) - 1:
|
||||
out_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (times[timeIndex + 1] - time)
|
||||
out_tangent_value = (blender_key_frame.handle_right[1] - blender_key_frame.co[1]) * factor
|
||||
|
||||
in_tangent[channel_index] = in_tangent_value
|
||||
out_tangent[channel_index] = out_tangent_value
|
||||
else:
|
||||
value = blender_fcurve.evaluate(keys[keyframe_index])
|
||||
|
||||
scale_data[channel_index] = value
|
||||
|
||||
channel_index += 1
|
||||
|
||||
scale_data = gltf2_blender_extract.convert_swizzle_scale(scale_data, export_settings)
|
||||
in_tangent = gltf2_blender_extract.convert_swizzle_scale(in_tangent, export_settings)
|
||||
out_tangent = gltf2_blender_extract.convert_swizzle_scale(out_tangent, export_settings)
|
||||
|
||||
# handle parent inverse
|
||||
matrix = Matrix()
|
||||
matrix[0][0] = scale_data.x
|
||||
matrix[1][1] = scale_data.y
|
||||
matrix[2][2] = scale_data.z
|
||||
matrix = matrix_correction * matrix
|
||||
scale_data = matrix.to_scale()
|
||||
|
||||
result[time] = scale_data
|
||||
result_in_tangent[time] = in_tangent
|
||||
result_out_tangent[time] = out_tangent
|
||||
|
||||
keyframe_index += 1
|
||||
|
||||
return result, result_in_tangent, result_out_tangent
|
||||
|
||||
|
||||
def animate_value(export_settings, value_parameter, interpolation,
|
||||
node_type, node_name, matrix_correction, matrix_basis):
|
||||
"""Calculate/gather the key value pairs for scalar anaimations."""
|
||||
keys = animate_gather_keys(export_settings, value_parameter, interpolation)
|
||||
|
||||
times = animate_convert_keys(keys)
|
||||
|
||||
result = {}
|
||||
result_in_tangent = {}
|
||||
result_out_tangent = {}
|
||||
|
||||
keyframe_index = 0
|
||||
for timeIndex, time in enumerate(times):
|
||||
value_data = []
|
||||
in_tangent = []
|
||||
out_tangent = []
|
||||
|
||||
for blender_fcurve in value_parameter:
|
||||
|
||||
if blender_fcurve is not None:
|
||||
if interpolation == CUBIC_INTERPOLATION:
|
||||
blender_key_frame = blender_fcurve.keyframe_points[keyframe_index]
|
||||
|
||||
value_data.append(blender_key_frame.co[1])
|
||||
|
||||
if timeIndex == 0:
|
||||
in_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (time - times[timeIndex - 1])
|
||||
in_tangent_value = (blender_key_frame.co[1] - blender_key_frame.handle_left[1]) * factor
|
||||
|
||||
if timeIndex == len(times) - 1:
|
||||
out_tangent_value = 0.0
|
||||
else:
|
||||
factor = 3.0 / (times[timeIndex + 1] - time)
|
||||
out_tangent_value = (blender_key_frame.handle_right[1] - blender_key_frame.co[1]) * factor
|
||||
|
||||
in_tangent.append(in_tangent_value)
|
||||
out_tangent.append(out_tangent_value)
|
||||
else:
|
||||
value = blender_fcurve.evaluate(keys[keyframe_index])
|
||||
|
||||
value_data.append(value)
|
||||
|
||||
result[time] = value_data
|
||||
result_in_tangent[time] = in_tangent
|
||||
result_out_tangent[time] = out_tangent
|
||||
|
||||
keyframe_index += 1
|
||||
|
||||
return result, result_in_tangent, result_out_tangent
|
||||
|
|
@ -1,455 +0,0 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
#
|
||||
# Imports
|
||||
#
|
||||
|
||||
import bpy
|
||||
from . import gltf2_blender_export_keys
|
||||
from . import gltf2_blender_get
|
||||
from ...io.com.gltf2_io_debug import print_console
|
||||
from ..com.gltf2_blender_image import create_img_from_blender_image
|
||||
from ...io.com import gltf2_io_image
|
||||
|
||||
#
|
||||
# Globals
|
||||
#
|
||||
|
||||
PREVIEW = 'PREVIEW'
|
||||
GLOSSINESS = 'glTF Specular Glossiness'
|
||||
ROUGHNESS = 'glTF Metallic Roughness'
|
||||
|
||||
|
||||
#
|
||||
# Functions
|
||||
#
|
||||
|
||||
def filter_merge_image(export_settings, blender_image):
|
||||
metallic_channel = gltf2_blender_get.get_image_material_usage_to_socket(blender_image, "Metallic")
|
||||
roughness_channel = gltf2_blender_get.get_image_material_usage_to_socket(blender_image, "Roughness")
|
||||
|
||||
if metallic_channel < 0 and roughness_channel < 0:
|
||||
return False
|
||||
|
||||
output = export_settings[gltf2_blender_export_keys.METALLIC_ROUGHNESS_IMAGE]
|
||||
if export_settings.get(export_keys.METALLIC_ROUGHNESS_IMAGE) is None:
|
||||
width = blender_image.image.size[0]
|
||||
height = blender_image.image.size[1]
|
||||
output = gltf2_io_image.create_img(width, height, r=1.0, g=1.0, b=1.0, a=1.0)
|
||||
|
||||
source = create_img_from_blender_image(blender_image.image)
|
||||
|
||||
if metallic_channel >= 0:
|
||||
gltf2_io_image.copy_img_channel(output, dst_channel=2, src_image=source, src_channel=metallic_channel)
|
||||
output.name = blender_image.image.name + output.name
|
||||
if roughness_channel >= 0:
|
||||
gltf2_io_image.copy_img_channel(output, dst_channel=1, src_image=source, src_channel=roughness_channel)
|
||||
if metallic_channel < 0:
|
||||
output.name = output.name + blender_image.image.name
|
||||
return True
|
||||
|
||||
|
||||
def filter_used_materials():
|
||||
"""Gather and return all unfiltered, valid Blender materials."""
|
||||
materials = []
|
||||
|
||||
for blender_material in bpy.data.materials:
|
||||
if blender_material.node_tree and blender_material.use_nodes:
|
||||
for currentNode in blender_material.node_tree.nodes:
|
||||
if isinstance(currentNode, bpy.types.ShaderNodeGroup):
|
||||
if currentNode.node_tree.name.startswith(ROUGHNESS):
|
||||
materials.append(blender_material)
|
||||
elif currentNode.node_tree.name.startswith(GLOSSINESS):
|
||||
materials.append(blender_material)
|
||||
elif isinstance(currentNode, bpy.types.ShaderNodeBsdfPrincipled):
|
||||
materials.append(blender_material)
|
||||
else:
|
||||
materials.append(blender_material)
|
||||
|
||||
return materials
|
||||
|
||||
|
||||
def filter_apply(export_settings):
|
||||
"""
|
||||
Gathers and filters the objects and assets to export.
|
||||
|
||||
Also filters out invalid, deleted and not exportable elements.
|
||||
"""
|
||||
filtered_objects = []
|
||||
implicit_filtered_objects = []
|
||||
|
||||
for blender_object in bpy.data.objects:
|
||||
|
||||
if blender_object.users == 0:
|
||||
continue
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.SELECTED] and blender_object.select_get() is False:
|
||||
continue
|
||||
|
||||
if not export_settings[gltf2_blender_export_keys.LAYERS] and not blender_object.layers[0]:
|
||||
continue
|
||||
|
||||
filtered_objects.append(blender_object)
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.SELECTED] or not export_settings[gltf2_blender_export_keys.LAYERS]:
|
||||
current_parent = blender_object.parent
|
||||
while current_parent:
|
||||
if current_parent not in implicit_filtered_objects:
|
||||
implicit_filtered_objects.append(current_parent)
|
||||
|
||||
current_parent = current_parent.parent
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_OBJECTS] = filtered_objects
|
||||
|
||||
# Meshes
|
||||
|
||||
filtered_meshes = {}
|
||||
filtered_vertex_groups = {}
|
||||
temporary_meshes = []
|
||||
|
||||
for blender_mesh in bpy.data.meshes:
|
||||
|
||||
if blender_mesh.users == 0:
|
||||
continue
|
||||
|
||||
current_blender_mesh = blender_mesh
|
||||
|
||||
current_blender_object = None
|
||||
|
||||
skip = True
|
||||
|
||||
for blender_object in filtered_objects:
|
||||
|
||||
current_blender_object = blender_object
|
||||
|
||||
if current_blender_object.type != 'MESH':
|
||||
continue
|
||||
|
||||
if current_blender_object.data == current_blender_mesh:
|
||||
|
||||
skip = False
|
||||
|
||||
use_auto_smooth = current_blender_mesh.use_auto_smooth
|
||||
|
||||
if use_auto_smooth:
|
||||
|
||||
if current_blender_mesh.shape_keys is None:
|
||||
current_blender_object = current_blender_object.copy()
|
||||
else:
|
||||
use_auto_smooth = False
|
||||
|
||||
print_console('WARNING',
|
||||
'Auto smooth and shape keys cannot be exported in parallel. '
|
||||
'Falling back to non auto smooth.')
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.APPLY] or use_auto_smooth:
|
||||
# TODO: maybe add to new exporter
|
||||
if not export_settings[gltf2_blender_export_keys.APPLY]:
|
||||
current_blender_object.modifiers.clear()
|
||||
|
||||
if use_auto_smooth:
|
||||
blender_modifier = current_blender_object.modifiers.new('Temporary_Auto_Smooth', 'EDGE_SPLIT')
|
||||
|
||||
blender_modifier.split_angle = current_blender_mesh.auto_smooth_angle
|
||||
blender_modifier.use_edge_angle = not current_blender_mesh.has_custom_normals
|
||||
|
||||
current_blender_mesh = current_blender_object.to_mesh(bpy.context.scene, True, PREVIEW)
|
||||
temporary_meshes.append(current_blender_mesh)
|
||||
|
||||
break
|
||||
|
||||
if skip:
|
||||
continue
|
||||
|
||||
filtered_meshes[blender_mesh.name] = current_blender_mesh
|
||||
filtered_vertex_groups[blender_mesh.name] = current_blender_object.vertex_groups
|
||||
|
||||
# Curves
|
||||
|
||||
for blender_curve in bpy.data.curves:
|
||||
|
||||
if blender_curve.users == 0:
|
||||
continue
|
||||
|
||||
current_blender_curve = blender_curve
|
||||
|
||||
current_blender_mesh = None
|
||||
|
||||
current_blender_object = None
|
||||
|
||||
skip = True
|
||||
|
||||
for blender_object in filtered_objects:
|
||||
|
||||
current_blender_object = blender_object
|
||||
|
||||
if current_blender_object.type not in ('CURVE', 'FONT'):
|
||||
continue
|
||||
|
||||
if current_blender_object.data == current_blender_curve:
|
||||
|
||||
skip = False
|
||||
|
||||
current_blender_object = current_blender_object.copy()
|
||||
|
||||
if not export_settings[gltf2_blender_export_keys.APPLY]:
|
||||
current_blender_object.modifiers.clear()
|
||||
|
||||
current_blender_mesh = current_blender_object.to_mesh(bpy.context.scene, True, PREVIEW)
|
||||
temporary_meshes.append(current_blender_mesh)
|
||||
|
||||
break
|
||||
|
||||
if skip:
|
||||
continue
|
||||
|
||||
filtered_meshes[blender_curve.name] = current_blender_mesh
|
||||
filtered_vertex_groups[blender_curve.name] = current_blender_object.vertex_groups
|
||||
|
||||
#
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_MESHES] = filtered_meshes
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_VERTEX_GROUPS] = filtered_vertex_groups
|
||||
export_settings[gltf2_blender_export_keys.TEMPORARY_MESHES] = temporary_meshes
|
||||
|
||||
#
|
||||
|
||||
filtered_materials = []
|
||||
|
||||
for blender_material in filter_used_materials():
|
||||
|
||||
if blender_material.users == 0:
|
||||
continue
|
||||
|
||||
for mesh_name, blender_mesh in filtered_meshes.items():
|
||||
for compare_blender_material in blender_mesh.materials:
|
||||
if compare_blender_material == blender_material and blender_material not in filtered_materials:
|
||||
filtered_materials.append(blender_material)
|
||||
|
||||
#
|
||||
|
||||
for blender_object in filtered_objects:
|
||||
if blender_object.material_slots:
|
||||
for blender_material_slot in blender_object.material_slots:
|
||||
if blender_material_slot.link == 'DATA':
|
||||
continue
|
||||
|
||||
if blender_material_slot.material not in filtered_materials:
|
||||
filtered_materials.append(blender_material_slot.material)
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_MATERIALS] = filtered_materials
|
||||
|
||||
#
|
||||
|
||||
filtered_textures = []
|
||||
filtered_merged_textures = []
|
||||
|
||||
temp_filtered_texture_names = []
|
||||
|
||||
for blender_material in filtered_materials:
|
||||
if blender_material.node_tree and blender_material.use_nodes:
|
||||
|
||||
per_material_textures = []
|
||||
|
||||
for blender_node in blender_material.node_tree.nodes:
|
||||
|
||||
if is_valid_node(blender_node) and blender_node not in filtered_textures:
|
||||
add_node = False
|
||||
add_merged_node = False
|
||||
for blender_socket in blender_node.outputs:
|
||||
if blender_socket.is_linked:
|
||||
for blender_link in blender_socket.links:
|
||||
if isinstance(blender_link.to_node, bpy.types.ShaderNodeGroup):
|
||||
is_roughness = blender_link.to_node.node_tree.name.startswith(ROUGHNESS)
|
||||
is_glossiness = blender_link.to_node.node_tree.name.startswith(GLOSSINESS)
|
||||
if is_roughness or is_glossiness:
|
||||
add_node = True
|
||||
break
|
||||
elif isinstance(blender_link.to_node, bpy.types.ShaderNodeBsdfPrincipled):
|
||||
add_node = True
|
||||
break
|
||||
elif isinstance(blender_link.to_node, bpy.types.ShaderNodeNormalMap):
|
||||
add_node = True
|
||||
break
|
||||
elif isinstance(blender_link.to_node, bpy.types.ShaderNodeSeparateRGB):
|
||||
add_merged_node = True
|
||||
break
|
||||
|
||||
if add_node or add_merged_node:
|
||||
break
|
||||
|
||||
if add_node:
|
||||
filtered_textures.append(blender_node)
|
||||
# TODO: Add displacement texture, as not stored in node tree.
|
||||
|
||||
if add_merged_node:
|
||||
if len(per_material_textures) == 0:
|
||||
filtered_merged_textures.append(per_material_textures)
|
||||
|
||||
per_material_textures.append(blender_node)
|
||||
|
||||
else:
|
||||
|
||||
for blender_texture_slot in blender_material.texture_slots:
|
||||
|
||||
if is_valid_texture_slot(blender_texture_slot) and \
|
||||
blender_texture_slot not in filtered_textures and \
|
||||
blender_texture_slot.name not in temp_filtered_texture_names:
|
||||
accept = False
|
||||
|
||||
if blender_texture_slot.use_map_color_diffuse:
|
||||
accept = True
|
||||
|
||||
if blender_texture_slot.use_map_ambient:
|
||||
accept = True
|
||||
if blender_texture_slot.use_map_emit:
|
||||
accept = True
|
||||
if blender_texture_slot.use_map_normal:
|
||||
accept = True
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.DISPLACEMENT]:
|
||||
if blender_texture_slot.use_map_displacement:
|
||||
accept = True
|
||||
|
||||
if accept:
|
||||
filtered_textures.append(blender_texture_slot)
|
||||
temp_filtered_texture_names.append(blender_texture_slot.name)
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_TEXTURES] = filtered_textures
|
||||
|
||||
#
|
||||
|
||||
filtered_images = []
|
||||
filtered_merged_images = []
|
||||
filtered_images_use_alpha = {}
|
||||
|
||||
for blender_texture in filtered_textures:
|
||||
|
||||
if isinstance(blender_texture, bpy.types.ShaderNodeTexImage):
|
||||
if is_valid_image(blender_texture.image) and blender_texture.image not in filtered_images:
|
||||
filtered_images.append(blender_texture.image)
|
||||
alpha_socket = blender_texture.outputs.get('Alpha')
|
||||
if alpha_socket is not None and alpha_socket.is_linked:
|
||||
filtered_images_use_alpha[blender_texture.image.name] = True
|
||||
|
||||
else:
|
||||
if is_valid_image(blender_texture.texture.image) and blender_texture.texture.image not in filtered_images:
|
||||
filtered_images.append(blender_texture.texture.image)
|
||||
if blender_texture.use_map_alpha:
|
||||
filtered_images_use_alpha[blender_texture.texture.image.name] = True
|
||||
|
||||
#
|
||||
|
||||
for per_material_textures in filtered_merged_textures:
|
||||
|
||||
export_settings[gltf2_blender_export_keys.METALLIC_ROUGHNESS_IMAGE] = None
|
||||
|
||||
for blender_texture in per_material_textures:
|
||||
|
||||
if isinstance(blender_texture, bpy.types.ShaderNodeTexImage):
|
||||
if is_valid_image(blender_texture.image) and blender_texture.image not in filtered_images:
|
||||
filter_merge_image(export_settings, blender_texture)
|
||||
|
||||
img = export_settings.get(export_keys.METALLIC_ROUGHNESS_IMAGE)
|
||||
if img is not None:
|
||||
filtered_merged_images.append(img)
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_TEXTURES].append(img)
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_MERGED_IMAGES] = filtered_merged_images
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_IMAGES] = filtered_images
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_IMAGES_USE_ALPHA] = filtered_images_use_alpha
|
||||
|
||||
#
|
||||
|
||||
filtered_cameras = []
|
||||
|
||||
for blender_camera in bpy.data.cameras:
|
||||
|
||||
if blender_camera.users == 0:
|
||||
continue
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.SELECTED]:
|
||||
if blender_camera not in filtered_objects:
|
||||
continue
|
||||
|
||||
filtered_cameras.append(blender_camera)
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_CAMERAS] = filtered_cameras
|
||||
|
||||
#
|
||||
#
|
||||
|
||||
filtered_lights = []
|
||||
|
||||
for blender_light in bpy.data.lamps:
|
||||
|
||||
if blender_light.users == 0:
|
||||
continue
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.SELECTED]:
|
||||
if blender_light not in filtered_objects:
|
||||
continue
|
||||
|
||||
if blender_light.type == 'HEMI':
|
||||
continue
|
||||
|
||||
filtered_lights.append(blender_light)
|
||||
|
||||
export_settings[gltf2_blender_export_keys.FILTERED_LIGHTS] = filtered_lights
|
||||
|
||||
#
|
||||
#
|
||||
|
||||
for implicit_object in implicit_filtered_objects:
|
||||
if implicit_object not in filtered_objects:
|
||||
filtered_objects.append(implicit_object)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
group_index = {}
|
||||
|
||||
if export_settings[gltf2_blender_export_keys.SKINS]:
|
||||
for blender_object in filtered_objects:
|
||||
if blender_object.type != 'ARMATURE' or len(blender_object.pose.bones) == 0:
|
||||
continue
|
||||
for blender_bone in blender_object.pose.bones:
|
||||
group_index[blender_bone.name] = len(group_index)
|
||||
|
||||
export_settings[gltf2_blender_export_keys.GROUP_INDEX] = group_index
|
||||
|
||||
|
||||
def is_valid_node(blender_node):
|
||||
return isinstance(blender_node, bpy.types.ShaderNodeTexImage) and is_valid_image(blender_node.image)
|
||||
|
||||
|
||||
def is_valid_image(image):
|
||||
return image is not None and \
|
||||
image.users != 0 and \
|
||||
image.size[0] > 0 and \
|
||||
image.size[1] > 0
|
||||
|
||||
|
||||
def is_valid_texture_slot(blender_texture_slot):
|
||||
return blender_texture_slot is not None and \
|
||||
blender_texture_slot.texture and \
|
||||
blender_texture_slot.texture.users != 0 and \
|
||||
blender_texture_slot.texture.type == 'IMAGE' and \
|
||||
blender_texture_slot.texture.image is not None and \
|
||||
blender_texture_slot.texture.image.users != 0 and \
|
||||
blender_texture_slot.texture.image.size[0] > 0 and \
|
||||
blender_texture_slot.texture.image.size[1] > 0
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
# Copyright 2018 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
|
||||
import typing
|
||||
|
||||
|
||||
class Filter:
|
||||
"""Base class for all node tree filter operations."""
|
||||
|
||||
def __call__(self, obj: bpy.types.Object):
|
||||
return True
|
||||
|
||||
|
||||
class ByName(Filter):
|
||||
"""
|
||||
Filter the objects by name.
|
||||
|
||||
example usage:
|
||||
find_objects(FilterByName("Cube"))
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __call__(self, obj: bpy.types.Object):
|
||||
return obj.name == self.name
|
||||
|
||||
|
||||
class ByDataType(Filter):
|
||||
"""Filter the scene objects by their data type."""
|
||||
|
||||
def __init__(self, data_type: str):
|
||||
self.type = data_type
|
||||
|
||||
def __call__(self, obj: bpy.types.Object):
|
||||
return obj.type == self.type
|
||||
|
||||
|
||||
class ByDataInstance(Filter):
|
||||
"""Filter the scene objects by a specific ID instance."""
|
||||
|
||||
def __init__(self, data_instance: bpy.types.ID):
|
||||
self.data = data_instance
|
||||
|
||||
def __call__(self, obj: bpy.types.Object):
|
||||
return self.data == obj.data
|
||||
|
||||
|
||||
def find_objects(object_filter: typing.Union[Filter, typing.Callable]):
|
||||
"""
|
||||
Find objects in the scene where the filter expression is true.
|
||||
|
||||
:param object_filter: should be a function(x: object) -> bool
|
||||
:return: a list of shader nodes for which filter is true
|
||||
"""
|
||||
results = []
|
||||
for obj in bpy.context.scene.objects:
|
||||
if object_filter(obj):
|
||||
results.append(obj)
|
||||
return results
|
||||
|
||||
|
||||
def find_objects_from(obj: bpy.types.Object, object_filter: typing.Union[Filter, typing.Callable]):
|
||||
"""
|
||||
Search for objects matching a filter function below a specified object.
|
||||
|
||||
:param obj: the starting point of the search
|
||||
:param object_filter: a function(x: object) -> bool
|
||||
:return: a list of objects which passed the filter
|
||||
"""
|
||||
results = []
|
||||
if object_filter(obj):
|
||||
results.append(obj)
|
||||
for child in obj.children:
|
||||
results += find_objects_from(child, object_filter)
|
||||
return results
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# Copyright 2018 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 typing
|
||||
|
||||
|
||||
def chunks(lst: typing.Sequence[typing.Any], n: int) -> typing.List[typing.Any]:
|
||||
"""
|
||||
Generator that yields successive n sized chunks of the list l
|
||||
:param lst: the list to be split
|
||||
:param n: the length of the chunks
|
||||
:return: a sublist of at most length n
|
||||
"""
|
||||
result = []
|
||||
for i in range(0, len(lst), n):
|
||||
result.append(lst[i:i + n])
|
||||
return result
|
||||
|
||||
|
||||
def unzip(*args: typing.Iterable[typing.Any]) -> typing.Iterable[typing.Iterable[typing.Any]]:
|
||||
"""
|
||||
Unzip the list. Inverse of the builtin zip
|
||||
:param args: a list of lists or multiple list arguments
|
||||
:return: a list of unzipped lists
|
||||
"""
|
||||
if len(args) == 1:
|
||||
args = args[0]
|
||||
|
||||
return zip(*args)
|
||||
|
Loading…
Reference in New Issue