Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Duroure <julien.duroure@gmail.com>2018-11-24 18:28:33 +0300
committerJulien Duroure <julien.duroure@gmail.com>2018-11-24 18:28:33 +0300
commitb1f2133fa2849da272e9d8404f371220226ddaf1 (patch)
tree25db56e0f2211bd1059fe0e04e78430a6156e021 /io_scene_gltf2/blender/com
parent8959f1798cfc86924493347304118c61bd5c7f7a (diff)
Initial commit of glTF 2.0 importer/exporter
Official Khronos Group Blender glTF 2.0 importer and exporter. glTF specification: https://github.com/KhronosGroup/glTF The upstream repository can be found here: https://github.com/KhronosGroup/glTF-Blender-IO Reviewed By: Bastien, Campbell Differential Revision: https://developer.blender.org/D3929
Diffstat (limited to 'io_scene_gltf2/blender/com')
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_conversion.py42
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_data_path.py28
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_image.py32
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_image_util.py121
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_json.py38
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_material_helpers.py59
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_math.py159
7 files changed, 479 insertions, 0 deletions
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_conversion.py b/io_scene_gltf2/blender/com/gltf2_blender_conversion.py
new file mode 100755
index 00000000..95fa292d
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_conversion.py
@@ -0,0 +1,42 @@
+# 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.
+
+from mathutils import Matrix, Quaternion
+
+def matrix_gltf_to_blender(mat_input):
+ """Matrix from glTF format to Blender format."""
+ mat = Matrix([mat_input[0:4], mat_input[4:8], mat_input[8:12], mat_input[12:16]])
+ mat.transpose()
+ return mat
+
+def loc_gltf_to_blender(loc):
+ """Location."""
+ return loc
+
+def scale_gltf_to_blender(scale):
+ """Scaling."""
+ return scale
+
+def quaternion_gltf_to_blender(q):
+ """Quaternion from glTF to Blender."""
+ return Quaternion([q[3], q[0], q[1], q[2]])
+
+def scale_to_matrix(scale):
+ """Scale to matrix."""
+ mat = Matrix()
+ for i in range(3):
+ mat[i][i] = scale[i]
+
+ return mat
+
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_data_path.py b/io_scene_gltf2/blender/com/gltf2_blender_data_path.py
new file mode 100755
index 00000000..c5ce4025
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_data_path.py
@@ -0,0 +1,28 @@
+# 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.
+
+
+def get_target_property_name(data_path: str) -> str:
+ """Retrieve target property."""
+ return data_path.rsplit('.', 1)[-1]
+
+
+def get_target_object_path(data_path: str) -> str:
+ """Retrieve target object data path without property"""
+ path_split = data_path.rsplit('.', 1)
+ self_targeting = len(path_split) < 2
+ if self_targeting:
+ return ""
+ return path_split[0]
+
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_image.py b/io_scene_gltf2/blender/com/gltf2_blender_image.py
new file mode 100755
index 00000000..7564070d
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_image.py
@@ -0,0 +1,32 @@
+# 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
+#
+
+from ...io.com.gltf2_io_image import create_img_from_pixels
+
+
+def create_img_from_blender_image(blender_image):
+ """
+ Create a new image object using the given blender image.
+
+ Returns the created image object.
+ """
+ if blender_image is None:
+ return None
+
+ return create_img_from_pixels(blender_image.size[0], blender_image.size[1], blender_image.pixels[:])
+
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_image_util.py b/io_scene_gltf2/blender/com/gltf2_blender_image_util.py
new file mode 100755
index 00000000..e2563a52
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_image_util.py
@@ -0,0 +1,121 @@
+# 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/com/gltf2_blender_json.py b/io_scene_gltf2/blender/com/gltf2_blender_json.py
new file mode 100755
index 00000000..fbf833c1
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_json.py
@@ -0,0 +1,38 @@
+# 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 json
+import bpy
+
+
+class BlenderJSONEncoder(json.JSONEncoder):
+ """Blender JSON Encoder."""
+
+ def default(self, obj):
+ if isinstance(obj, bpy.types.ID):
+ return dict(
+ name=obj.name,
+ type=obj.__class__.__name__
+ )
+ return super(BlenderJSONEncoder, self).default(obj)
+
+
+def is_json_convertible(data):
+ """Test, if a data set can be expressed as JSON."""
+ try:
+ json.dumps(data, cls=BlenderJSONEncoder)
+ return True
+ except:
+ return False
+
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py
new file mode 100755
index 00000000..05f35954
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py
@@ -0,0 +1,59 @@
+# 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.
+
+
+def get_output_node(node_tree):
+ """Retrive output node."""
+ output = [node for node in node_tree.nodes if node.type == 'OUTPUT_MATERIAL'][0]
+ return output
+
+
+def get_output_surface_input(node_tree):
+ """Retrieve surface input of output node."""
+ output_node = get_output_node(node_tree)
+ return output_node.inputs['Surface']
+
+
+def get_diffuse_texture(node_tree):
+ """Retrieve diffuse texture node."""
+ for node in node_tree.nodes:
+ print(node.name)
+ if node.label == 'BASE COLOR':
+ return node
+
+ return None
+
+
+def get_preoutput_node_output(node_tree):
+ """Retrieve node just before output node."""
+ output_node = get_output_node(node_tree)
+ preoutput_node = output_node.inputs['Surface'].links[0].from_node
+
+ # Pre output node is Principled BSDF or any BSDF => BSDF
+ if 'BSDF' in preoutput_node.type:
+ return preoutput_node.outputs['BSDF']
+ elif 'SHADER' in preoutput_node.type:
+ return preoutput_node.outputs['Shader']
+ else:
+ print(preoutput_node.type)
+
+
+def get_base_color_node(node_tree):
+ """Returns the last node of the diffuse block."""
+ for node in node_tree.nodes:
+ if node.label == 'BASE COLOR':
+ return node
+
+ return None
+
diff --git a/io_scene_gltf2/blender/com/gltf2_blender_math.py b/io_scene_gltf2/blender/com/gltf2_blender_math.py
new file mode 100755
index 00000000..26eb6396
--- /dev/null
+++ b/io_scene_gltf2/blender/com/gltf2_blender_math.py
@@ -0,0 +1,159 @@
+# 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
+import math
+from mathutils import Matrix, Vector, Quaternion, Euler
+
+from io_scene_gltf2.blender.com.gltf2_blender_data_path import get_target_property_name
+
+
+def multiply(a, b):
+ """Multiplication."""
+ return a @ b
+
+
+def list_to_mathutils(values: typing.List[float], data_path: str) -> typing.Union[Vector, Quaternion, Euler]:
+ """Transform a list to blender py object."""
+ target = get_target_property_name(data_path)
+
+ if target == 'location':
+ return Vector(values)
+ elif target == 'rotation_axis_angle':
+ angle = values[0]
+ axis = values[1:]
+ return Quaternion(axis, math.radians(angle))
+ elif target == 'rotation_euler':
+ return Euler(values).to_quaternion()
+ elif target == 'rotation_quaternion':
+ return Quaternion(values)
+ elif target == 'scale':
+ return Vector(values)
+ elif target == 'value':
+ return values
+
+ return values
+
+
+def mathutils_to_gltf(x: typing.Union[Vector, Quaternion]) -> typing.List[float]:
+ """Transform a py object to glTF list."""
+ if isinstance(x, Vector):
+ return list(x)
+ if isinstance(x, Quaternion):
+ # Blender has w-first quaternion notation
+ return [x[1], x[2], x[3], x[0]]
+ else:
+ return list(x)
+
+
+def to_yup() -> Matrix:
+ """Transform to Yup."""
+ return Matrix(
+ ((1.0, 0.0, 0.0, 0.0),
+ (0.0, 0.0, 1.0, 0.0),
+ (0.0, -1.0, 0.0, 0.0),
+ (0.0, 0.0, 0.0, 1.0))
+ )
+
+
+to_zup = to_yup
+
+
+def swizzle_yup(v: typing.Union[Vector, Quaternion], data_path: str) -> typing.Union[Vector, Quaternion]:
+ """Manage Yup."""
+ target = get_target_property_name(data_path)
+ swizzle_func = {
+ "location": swizzle_yup_location,
+ "rotation_axis_angle": swizzle_yup_rotation,
+ "rotation_euler": swizzle_yup_rotation,
+ "rotation_quaternion": swizzle_yup_rotation,
+ "scale": swizzle_yup_scale,
+ "value": swizzle_yup_value
+ }.get(target)
+
+ if swizzle_func is None:
+ raise RuntimeError("Cannot transform values at {}".format(data_path))
+
+ return swizzle_func(v)
+
+
+def swizzle_yup_location(loc: Vector) -> Vector:
+ """Manage Yup location."""
+ return Vector((loc[0], loc[2], -loc[1]))
+
+
+def swizzle_yup_rotation(rot: Quaternion) -> Quaternion:
+ """Manage Yup rotation."""
+ return Quaternion((rot[0], rot[1], rot[3], -rot[2]))
+
+
+def swizzle_yup_scale(scale: Vector) -> Vector:
+ """Manage Yup scale."""
+ return Vector((scale[0], scale[2], scale[1]))
+
+
+def swizzle_yup_value(value: typing.Any) -> typing.Any:
+ """Manage Yup value."""
+ return value
+
+
+def transform(v: typing.Union[Vector, Quaternion], data_path: str, transform: Matrix = Matrix.Identity(4)) -> typing \
+ .Union[Vector, Quaternion]:
+ """Manage transformations."""
+ target = get_target_property_name(data_path)
+ transform_func = {
+ "location": transform_location,
+ "rotation_axis_angle": transform_rotation,
+ "rotation_euler": transform_rotation,
+ "rotation_quaternion": transform_rotation,
+ "scale": transform_scale,
+ "value": transform_value
+ }.get(target)
+
+ if transform_func is None:
+ raise RuntimeError("Cannot transform values at {}".format(data_path))
+
+ return transform_func(v, transform)
+
+
+def transform_location(location: Vector, transform: Matrix = Matrix.Identity(4)) -> Vector:
+ """Transform location."""
+ m = Matrix.Translation(location)
+ m = multiply(transform, m)
+ return m.to_translation()
+
+
+def transform_rotation(rotation: Quaternion, transform: Matrix = Matrix.Identity(4)) -> Quaternion:
+ """Transform rotation."""
+ m = rotation.to_matrix().to_4x4()
+ m = multiply(transform, m)
+ return m.to_quaternion()
+
+
+def transform_scale(scale: Vector, transform: Matrix = Matrix.Identity(4)) -> Vector:
+ """Transform scale."""
+ m = Matrix.Identity(4)
+ m[0][0] = scale.x
+ m[1][1] = scale.y
+ m[2][2] = scale.z
+ m = multiply(transform, m)
+
+ return m.to_scale()
+
+
+def transform_value(value: Vector, _: Matrix = Matrix.Identity(4)) -> Vector:
+ """Transform value."""
+ return value
+