diff options
Diffstat (limited to 'io_scene_gltf2/blender')
-rwxr-xr-x | io_scene_gltf2/blender/com/gltf2_blender_image_util.py | 121 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_animate.py | 638 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_filter.py | 455 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_search_scene.py | 89 |
4 files changed, 0 insertions, 1303 deletions
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_image_util.py b/io_scene_gltf2/blender/com/gltf2_blender_image_util.py deleted file mode 100755 index e2563a52..00000000 --- a/io_scene_gltf2/blender/com/gltf2_blender_image_util.py +++ /dev/null @@ -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'')]) - diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_animate.py b/io_scene_gltf2/blender/exp/gltf2_blender_animate.py deleted file mode 100755 index e4b11487..00000000 --- a/io_scene_gltf2/blender/exp/gltf2_blender_animate.py +++ /dev/null @@ -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 - diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_filter.py b/io_scene_gltf2/blender/exp/gltf2_blender_filter.py deleted file mode 100755 index 6a4e18f9..00000000 --- a/io_scene_gltf2/blender/exp/gltf2_blender_filter.py +++ /dev/null @@ -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 - diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_search_scene.py b/io_scene_gltf2/blender/exp/gltf2_blender_search_scene.py deleted file mode 100755 index 0fa7db6e..00000000 --- a/io_scene_gltf2/blender/exp/gltf2_blender_search_scene.py +++ /dev/null @@ -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 - |