diff options
author | Julien Duroure <julien.duroure@gmail.com> | 2018-11-24 18:28:33 +0300 |
---|---|---|
committer | Julien Duroure <julien.duroure@gmail.com> | 2018-11-24 18:28:33 +0300 |
commit | b1f2133fa2849da272e9d8404f371220226ddaf1 (patch) | |
tree | 25db56e0f2211bd1059fe0e04e78430a6156e021 /io_scene_gltf2/io/com | |
parent | 8959f1798cfc86924493347304118c61bd5c7f7a (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/io/com')
-rwxr-xr-x | io_scene_gltf2/io/com/gltf2_io.py | 1200 | ||||
-rwxr-xr-x | io_scene_gltf2/io/com/gltf2_io_constants.py | 132 | ||||
-rwxr-xr-x | io_scene_gltf2/io/com/gltf2_io_debug.py | 138 | ||||
-rwxr-xr-x | io_scene_gltf2/io/com/gltf2_io_functional.py | 41 | ||||
-rwxr-xr-x | io_scene_gltf2/io/com/gltf2_io_image.py | 154 | ||||
-rwxr-xr-x | io_scene_gltf2/io/com/gltf2_io_trs.py | 68 |
6 files changed, 1733 insertions, 0 deletions
diff --git a/io_scene_gltf2/io/com/gltf2_io.py b/io_scene_gltf2/io/com/gltf2_io.py new file mode 100755 index 00000000..1332adf6 --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io.py @@ -0,0 +1,1200 @@ +# 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. + +# NOTE: Generated from latest glTF 2.0 JSON Scheme specs using quicktype (https://github.com/quicktype/quicktype) +# command used: +# quicktype --src glTF.schema.json --src-lang schema -t gltf --lang python --python-version 3.5 + +# TODO: add __slots__ to all classes by extending the generator + +# TODO: REMOVE traceback import +import sys +import traceback + +from io_scene_gltf2.io.com import gltf2_io_debug + + +def from_int(x): + assert isinstance(x, int) and not isinstance(x, bool) + return x + + +def from_none(x): + assert x is None + return x + + +def from_union(fs, x): + tracebacks = [] + for f in fs: + try: + return f(x) + except AssertionError: + _, _, tb = sys.exc_info() + tracebacks.append(tb) + for tb in tracebacks: + traceback.print_tb(tb) # Fixed format + tb_info = traceback.extract_tb(tb) + for tbi in tb_info: + filename, line, func, text = tbi + gltf2_io_debug.print_console('ERROR', 'An error occurred on line {} in statement {}'.format(line, text)) + assert False + + +def from_dict(f, x): + assert isinstance(x, dict) + return {k: f(v) for (k, v) in x.items()} + + +def to_class(c, x): + assert isinstance(x, c) + return x.to_dict() + + +def from_list(f, x): + assert isinstance(x, list) + return [f(y) for y in x] + + +def from_float(x): + assert isinstance(x, (float, int)) and not isinstance(x, bool) + return float(x) + + +def from_str(x): + assert isinstance(x, str) + return x + + +def from_bool(x): + assert isinstance(x, bool) + return x + + +def to_float(x): + assert isinstance(x, float) + return x + + +class AccessorSparseIndices: + """Index array of size `count` that points to those accessor attributes that deviate from + their initialization value. Indices must strictly increase. + + Indices of those attributes that deviate from their initialization value. + """ + + def __init__(self, buffer_view, byte_offset, component_type, extensions, extras): + self.buffer_view = buffer_view + self.byte_offset = byte_offset + self.component_type = component_type + self.extensions = extensions + self.extras = extras + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + buffer_view = from_int(obj.get("bufferView")) + byte_offset = from_union([from_int, from_none], obj.get("byteOffset")) + component_type = from_int(obj.get("componentType")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + return AccessorSparseIndices(buffer_view, byte_offset, component_type, extensions, extras) + + def to_dict(self): + result = {} + result["bufferView"] = from_int(self.buffer_view) + result["byteOffset"] = from_union([from_int, from_none], self.byte_offset) + result["componentType"] = from_int(self.component_type) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + return result + + +class AccessorSparseValues: + """Array of size `count` times number of components, storing the displaced accessor + attributes pointed by `indices`. Substituted values must have the same `componentType` + and number of components as the base accessor. + + Array of size `accessor.sparse.count` times number of components storing the displaced + accessor attributes pointed by `accessor.sparse.indices`. + """ + + def __init__(self, buffer_view, byte_offset, extensions, extras): + self.buffer_view = buffer_view + self.byte_offset = byte_offset + self.extensions = extensions + self.extras = extras + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + buffer_view = from_int(obj.get("bufferView")) + byte_offset = from_union([from_int, from_none], obj.get("byteOffset")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + return AccessorSparseValues(buffer_view, byte_offset, extensions, extras) + + def to_dict(self): + result = {} + result["bufferView"] = from_int(self.buffer_view) + result["byteOffset"] = from_union([from_int, from_none], self.byte_offset) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + return result + + +class AccessorSparse: + """Sparse storage of attributes that deviate from their initialization value.""" + + def __init__(self, count, extensions, extras, indices, values): + self.count = count + self.extensions = extensions + self.extras = extras + self.indices = indices + self.values = values + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + count = from_int(obj.get("count")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + indices = AccessorSparseIndices.from_dict(obj.get("indices")) + values = AccessorSparseValues.from_dict(obj.get("values")) + return AccessorSparse(count, extensions, extras, indices, values) + + def to_dict(self): + result = {} + result["count"] = from_int(self.count) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["indices"] = to_class(AccessorSparseIndices, self.indices) + result["values"] = to_class(AccessorSparseValues, self.values) + return result + + +class Accessor: + """A typed view into a bufferView. A bufferView contains raw binary data. An accessor + provides a typed view into a bufferView or a subset of a bufferView similar to how + WebGL's `vertexAttribPointer()` defines an attribute in a buffer. + """ + + def __init__(self, buffer_view, byte_offset, component_type, count, extensions, extras, max, min, name, normalized, + sparse, type): + self.buffer_view = buffer_view + self.byte_offset = byte_offset + self.component_type = component_type + self.count = count + self.extensions = extensions + self.extras = extras + self.max = max + self.min = min + self.name = name + self.normalized = normalized + self.sparse = sparse + self.type = type + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + buffer_view = from_union([from_int, from_none], obj.get("bufferView")) + byte_offset = from_union([from_int, from_none], obj.get("byteOffset")) + component_type = from_int(obj.get("componentType")) + count = from_int(obj.get("count")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + max = from_union([lambda x: from_list(from_float, x), from_none], obj.get("max")) + min = from_union([lambda x: from_list(from_float, x), from_none], obj.get("min")) + name = from_union([from_str, from_none], obj.get("name")) + normalized = from_union([from_bool, from_none], obj.get("normalized")) + sparse = from_union([AccessorSparse.from_dict, from_none], obj.get("sparse")) + type = from_str(obj.get("type")) + return Accessor(buffer_view, byte_offset, component_type, count, extensions, extras, max, min, name, normalized, + sparse, type) + + def to_dict(self): + result = {} + result["bufferView"] = from_union([from_int, from_none], self.buffer_view) + result["byteOffset"] = from_union([from_int, from_none], self.byte_offset) + result["componentType"] = from_int(self.component_type) + result["count"] = from_int(self.count) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["max"] = from_union([lambda x: from_list(to_float, x), from_none], self.max) + result["min"] = from_union([lambda x: from_list(to_float, x), from_none], self.min) + result["name"] = from_union([from_str, from_none], self.name) + result["normalized"] = from_union([from_bool, from_none], self.normalized) + result["sparse"] = from_union([lambda x: to_class(AccessorSparse, x), from_none], self.sparse) + result["type"] = from_str(self.type) + return result + + +class AnimationChannelTarget: + """The index of the node and TRS property to target. + + The index of the node and TRS property that an animation channel targets. + """ + + def __init__(self, extensions, extras, node, path): + self.extensions = extensions + self.extras = extras + self.node = node + self.path = path + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + node = from_union([from_int, from_none], obj.get("node")) + path = from_str(obj.get("path")) + return AnimationChannelTarget(extensions, extras, node, path) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["node"] = from_union([from_int, from_none], self.node) + result["path"] = from_str(self.path) + return result + + +class AnimationChannel: + """Targets an animation's sampler at a node's property.""" + + def __init__(self, extensions, extras, sampler, target): + self.extensions = extensions + self.extras = extras + self.sampler = sampler + self.target = target + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + sampler = from_int(obj.get("sampler")) + target = AnimationChannelTarget.from_dict(obj.get("target")) + return AnimationChannel(extensions, extras, sampler, target) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["sampler"] = from_int(self.sampler) + result["target"] = to_class(AnimationChannelTarget, self.target) + return result + + +class AnimationSampler: + """Combines input and output accessors with an interpolation algorithm to define a keyframe + graph (but not its target). + """ + + def __init__(self, extensions, extras, input, interpolation, output): + self.extensions = extensions + self.extras = extras + self.input = input + self.interpolation = interpolation + self.output = output + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + input = from_int(obj.get("input")) + interpolation = from_union([from_str, from_none], obj.get("interpolation")) + output = from_int(obj.get("output")) + return AnimationSampler(extensions, extras, input, interpolation, output) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["input"] = from_int(self.input) + result["interpolation"] = from_union([from_str, from_none], self.interpolation) + result["output"] = from_int(self.output) + return result + + +class Animation: + """A keyframe animation.""" + + def __init__(self, channels, extensions, extras, name, samplers): + self.channels = channels + self.extensions = extensions + self.extras = extras + self.name = name + self.samplers = samplers + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + channels = from_list(AnimationChannel.from_dict, obj.get("channels")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + samplers = from_list(AnimationSampler.from_dict, obj.get("samplers")) + return Animation(channels, extensions, extras, name, samplers) + + def to_dict(self): + result = {} + result["channels"] = from_list(lambda x: to_class(AnimationChannel, x), self.channels) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["samplers"] = from_list(lambda x: to_class(AnimationSampler, x), self.samplers) + return result + + +class Asset: + """Metadata about the glTF asset.""" + + def __init__(self, copyright, extensions, extras, generator, min_version, version): + self.copyright = copyright + self.extensions = extensions + self.extras = extras + self.generator = generator + self.min_version = min_version + self.version = version + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + copyright = from_union([from_str, from_none], obj.get("copyright")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + generator = from_union([from_str, from_none], obj.get("generator")) + min_version = from_union([from_str, from_none], obj.get("minVersion")) + version = from_str(obj.get("version")) + return Asset(copyright, extensions, extras, generator, min_version, version) + + def to_dict(self): + result = {} + result["copyright"] = from_union([from_str, from_none], self.copyright) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["generator"] = from_union([from_str, from_none], self.generator) + result["minVersion"] = from_union([from_str, from_none], self.min_version) + result["version"] = from_str(self.version) + return result + + +class BufferView: + """A view into a buffer generally representing a subset of the buffer.""" + + def __init__(self, buffer, byte_length, byte_offset, byte_stride, extensions, extras, name, target): + self.buffer = buffer + self.byte_length = byte_length + self.byte_offset = byte_offset + self.byte_stride = byte_stride + self.extensions = extensions + self.extras = extras + self.name = name + self.target = target + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + buffer = from_int(obj.get("buffer")) + byte_length = from_int(obj.get("byteLength")) + byte_offset = from_union([from_int, from_none], obj.get("byteOffset")) + byte_stride = from_union([from_int, from_none], obj.get("byteStride")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + target = from_union([from_int, from_none], obj.get("target")) + return BufferView(buffer, byte_length, byte_offset, byte_stride, extensions, extras, name, target) + + def to_dict(self): + result = {} + result["buffer"] = from_int(self.buffer) + result["byteLength"] = from_int(self.byte_length) + result["byteOffset"] = from_union([from_int, from_none], self.byte_offset) + result["byteStride"] = from_union([from_int, from_none], self.byte_stride) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["target"] = from_union([from_int, from_none], self.target) + return result + + +class Buffer: + """A buffer points to binary geometry, animation, or skins.""" + + def __init__(self, byte_length, extensions, extras, name, uri): + self.byte_length = byte_length + self.extensions = extensions + self.extras = extras + self.name = name + self.uri = uri + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + byte_length = from_int(obj.get("byteLength")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + uri = from_union([from_str, from_none], obj.get("uri")) + return Buffer(byte_length, extensions, extras, name, uri) + + def to_dict(self): + result = {} + result["byteLength"] = from_int(self.byte_length) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["uri"] = from_union([from_str, from_none], self.uri) + return result + + +class CameraOrthographic: + """An orthographic camera containing properties to create an orthographic projection matrix.""" + + def __init__(self, extensions, extras, xmag, ymag, zfar, znear): + self.extensions = extensions + self.extras = extras + self.xmag = xmag + self.ymag = ymag + self.zfar = zfar + self.znear = znear + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + xmag = from_float(obj.get("xmag")) + ymag = from_float(obj.get("ymag")) + zfar = from_float(obj.get("zfar")) + znear = from_float(obj.get("znear")) + return CameraOrthographic(extensions, extras, xmag, ymag, zfar, znear) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["xmag"] = to_float(self.xmag) + result["ymag"] = to_float(self.ymag) + result["zfar"] = to_float(self.zfar) + result["znear"] = to_float(self.znear) + return result + + +class CameraPerspective: + """A perspective camera containing properties to create a perspective projection matrix.""" + + def __init__(self, aspect_ratio, extensions, extras, yfov, zfar, znear): + self.aspect_ratio = aspect_ratio + self.extensions = extensions + self.extras = extras + self.yfov = yfov + self.zfar = zfar + self.znear = znear + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + aspect_ratio = from_union([from_float, from_none], obj.get("aspectRatio")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + yfov = from_float(obj.get("yfov")) + zfar = from_union([from_float, from_none], obj.get("zfar")) + znear = from_float(obj.get("znear")) + return CameraPerspective(aspect_ratio, extensions, extras, yfov, zfar, znear) + + def to_dict(self): + result = {} + result["aspectRatio"] = from_union([to_float, from_none], self.aspect_ratio) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["yfov"] = to_float(self.yfov) + result["zfar"] = from_union([to_float, from_none], self.zfar) + result["znear"] = to_float(self.znear) + return result + + +class Camera: + """A camera's projection. A node can reference a camera to apply a transform to place the + camera in the scene. + """ + + def __init__(self, extensions, extras, name, orthographic, perspective, type): + self.extensions = extensions + self.extras = extras + self.name = name + self.orthographic = orthographic + self.perspective = perspective + self.type = type + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + orthographic = from_union([CameraOrthographic.from_dict, from_none], obj.get("orthographic")) + perspective = from_union([CameraPerspective.from_dict, from_none], obj.get("perspective")) + type = from_str(obj.get("type")) + return Camera(extensions, extras, name, orthographic, perspective, type) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["orthographic"] = from_union([lambda x: to_class(CameraOrthographic, x), from_none], self.orthographic) + result["perspective"] = from_union([lambda x: to_class(CameraPerspective, x), from_none], self.perspective) + result["type"] = from_str(self.type) + return result + + +class Image: + """Image data used to create a texture. Image can be referenced by URI or `bufferView` + index. `mimeType` is required in the latter case. + """ + + def __init__(self, buffer_view, extensions, extras, mime_type, name, uri): + self.buffer_view = buffer_view + self.extensions = extensions + self.extras = extras + self.mime_type = mime_type + self.name = name + self.uri = uri + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + buffer_view = from_union([from_int, from_none], obj.get("bufferView")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + mime_type = from_union([from_str, from_none], obj.get("mimeType")) + name = from_union([from_str, from_none], obj.get("name")) + uri = from_union([from_str, from_none], obj.get("uri")) + return Image(buffer_view, extensions, extras, mime_type, name, uri) + + def to_dict(self): + result = {} + result["bufferView"] = from_union([from_int, from_none], self.buffer_view) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["mimeType"] = from_union([from_str, from_none], self.mime_type) + result["name"] = from_union([from_str, from_none], self.name) + result["uri"] = from_union([from_str, from_none], self.uri) + return result + + +class TextureInfo: + """The emissive map texture. + + The base color texture. + + The metallic-roughness texture. + + Reference to a texture. + """ + + def __init__(self, extensions, extras, index, tex_coord): + self.extensions = extensions + self.extras = extras + self.index = index + self.tex_coord = tex_coord + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + index = from_int(obj.get("index")) + tex_coord = from_union([from_int, from_none], obj.get("texCoord")) + return TextureInfo(extensions, extras, index, tex_coord) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["index"] = from_int(self.index) + result["texCoord"] = from_union([from_int, from_none], self.tex_coord) + return result + + +class MaterialNormalTextureInfoClass: + """The normal map texture. + + Reference to a texture. + """ + + def __init__(self, extensions, extras, index, scale, tex_coord): + self.extensions = extensions + self.extras = extras + self.index = index + self.scale = scale + self.tex_coord = tex_coord + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + index = from_int(obj.get("index")) + scale = from_union([from_float, from_none], obj.get("scale")) + tex_coord = from_union([from_int, from_none], obj.get("texCoord")) + return MaterialNormalTextureInfoClass(extensions, extras, index, scale, tex_coord) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["index"] = from_int(self.index) + result["scale"] = from_union([to_float, from_none], self.scale) + result["texCoord"] = from_union([from_int, from_none], self.tex_coord) + return result + + +class MaterialOcclusionTextureInfoClass: + """The occlusion map texture. + + Reference to a texture. + """ + + def __init__(self, extensions, extras, index, strength, tex_coord): + self.extensions = extensions + self.extras = extras + self.index = index + self.strength = strength + self.tex_coord = tex_coord + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + index = from_int(obj.get("index")) + strength = from_union([from_float, from_none], obj.get("strength")) + tex_coord = from_union([from_int, from_none], obj.get("texCoord")) + return MaterialOcclusionTextureInfoClass(extensions, extras, index, strength, tex_coord) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["index"] = from_int(self.index) + result["strength"] = from_union([to_float, from_none], self.strength) + result["texCoord"] = from_union([from_int, from_none], self.tex_coord) + return result + + +class MaterialPBRMetallicRoughness: + """A set of parameter values that are used to define the metallic-roughness material model + from Physically-Based Rendering (PBR) methodology. When not specified, all the default + values of `pbrMetallicRoughness` apply. + + A set of parameter values that are used to define the metallic-roughness material model + from Physically-Based Rendering (PBR) methodology. + """ + + def __init__(self, base_color_factor, base_color_texture, extensions, extras, metallic_factor, + metallic_roughness_texture, roughness_factor): + self.base_color_factor = base_color_factor + self.base_color_texture = base_color_texture + self.extensions = extensions + self.extras = extras + self.metallic_factor = metallic_factor + self.metallic_roughness_texture = metallic_roughness_texture + self.roughness_factor = roughness_factor + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + base_color_factor = from_union([lambda x: from_list(from_float, x), from_none], obj.get("baseColorFactor")) + base_color_texture = from_union([TextureInfo.from_dict, from_none], obj.get("baseColorTexture")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + metallic_factor = from_union([from_float, from_none], obj.get("metallicFactor")) + metallic_roughness_texture = from_union([TextureInfo.from_dict, from_none], obj.get("metallicRoughnessTexture")) + roughness_factor = from_union([from_float, from_none], obj.get("roughnessFactor")) + return MaterialPBRMetallicRoughness(base_color_factor, base_color_texture, extensions, extras, metallic_factor, + metallic_roughness_texture, roughness_factor) + + def to_dict(self): + result = {} + result["baseColorFactor"] = from_union([lambda x: from_list(to_float, x), from_none], self.base_color_factor) + result["baseColorTexture"] = from_union([lambda x: to_class(TextureInfo, x), from_none], + self.base_color_texture) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["metallicFactor"] = from_union([to_float, from_none], self.metallic_factor) + result["metallicRoughnessTexture"] = from_union([lambda x: to_class(TextureInfo, x), from_none], + self.metallic_roughness_texture) + result["roughnessFactor"] = from_union([to_float, from_none], self.roughness_factor) + return result + + +class Material: + """The material appearance of a primitive.""" + + def __init__(self, alpha_cutoff, alpha_mode, double_sided, emissive_factor, emissive_texture, extensions, extras, + name, normal_texture, occlusion_texture, pbr_metallic_roughness): + self.alpha_cutoff = alpha_cutoff + self.alpha_mode = alpha_mode + self.double_sided = double_sided + self.emissive_factor = emissive_factor + self.emissive_texture = emissive_texture + self.extensions = extensions + self.extras = extras + self.name = name + self.normal_texture = normal_texture + self.occlusion_texture = occlusion_texture + self.pbr_metallic_roughness = pbr_metallic_roughness + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + alpha_cutoff = from_union([from_float, from_none], obj.get("alphaCutoff")) + alpha_mode = from_union([from_str, from_none], obj.get("alphaMode")) + double_sided = from_union([from_bool, from_none], obj.get("doubleSided")) + emissive_factor = from_union([lambda x: from_list(from_float, x), from_none], obj.get("emissiveFactor")) + emissive_texture = from_union([TextureInfo.from_dict, from_none], obj.get("emissiveTexture")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + normal_texture = from_union([MaterialNormalTextureInfoClass.from_dict, from_none], obj.get("normalTexture")) + occlusion_texture = from_union([MaterialOcclusionTextureInfoClass.from_dict, from_none], + obj.get("occlusionTexture")) + pbr_metallic_roughness = from_union([MaterialPBRMetallicRoughness.from_dict, from_none], + obj.get("pbrMetallicRoughness")) + return Material(alpha_cutoff, alpha_mode, double_sided, emissive_factor, emissive_texture, extensions, extras, + name, normal_texture, occlusion_texture, pbr_metallic_roughness) + + def to_dict(self): + result = {} + result["alphaCutoff"] = from_union([to_float, from_none], self.alpha_cutoff) + result["alphaMode"] = from_union([from_str, from_none], self.alpha_mode) + result["doubleSided"] = from_union([from_bool, from_none], self.double_sided) + result["emissiveFactor"] = from_union([lambda x: from_list(to_float, x), from_none], self.emissive_factor) + result["emissiveTexture"] = from_union([lambda x: to_class(TextureInfo, x), from_none], self.emissive_texture) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["normalTexture"] = from_union([lambda x: to_class(MaterialNormalTextureInfoClass, x), from_none], + self.normal_texture) + result["occlusionTexture"] = from_union([lambda x: to_class(MaterialOcclusionTextureInfoClass, x), from_none], + self.occlusion_texture) + result["pbrMetallicRoughness"] = from_union([lambda x: to_class(MaterialPBRMetallicRoughness, x), from_none], + self.pbr_metallic_roughness) + return result + + +class MeshPrimitive: + """Geometry to be rendered with the given material.""" + + def __init__(self, attributes, extensions, extras, indices, material, mode, targets): + self.attributes = attributes + self.extensions = extensions + self.extras = extras + self.indices = indices + self.material = material + self.mode = mode + self.targets = targets + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + attributes = from_dict(from_int, obj.get("attributes")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + indices = from_union([from_int, from_none], obj.get("indices")) + material = from_union([from_int, from_none], obj.get("material")) + mode = from_union([from_int, from_none], obj.get("mode")) + targets = from_union([lambda x: from_list(lambda x: from_dict(from_int, x), x), from_none], obj.get("targets")) + return MeshPrimitive(attributes, extensions, extras, indices, material, mode, targets) + + def to_dict(self): + result = {} + result["attributes"] = from_dict(from_int, self.attributes) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["indices"] = from_union([from_int, from_none], self.indices) + result["material"] = from_union([from_int, from_none], self.material) + result["mode"] = from_union([from_int, from_none], self.mode) + result["targets"] = from_union([lambda x: from_list(lambda x: from_dict(from_int, x), x), from_none], + self.targets) + return result + + +class Mesh: + """A set of primitives to be rendered. A node can contain one mesh. A node's transform + places the mesh in the scene. + """ + + def __init__(self, extensions, extras, name, primitives, weights): + self.extensions = extensions + self.extras = extras + self.name = name + self.primitives = primitives + self.weights = weights + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + primitives = from_list(MeshPrimitive.from_dict, obj.get("primitives")) + weights = from_union([lambda x: from_list(from_float, x), from_none], obj.get("weights")) + return Mesh(extensions, extras, name, primitives, weights) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["primitives"] = from_list(lambda x: to_class(MeshPrimitive, x), self.primitives) + result["weights"] = from_union([lambda x: from_list(to_float, x), from_none], self.weights) + return result + + +class Node: + """A node in the node hierarchy. When the node contains `skin`, all `mesh.primitives` must + contain `JOINTS_0` and `WEIGHTS_0` attributes. A node can have either a `matrix` or any + combination of `translation`/`rotation`/`scale` (TRS) properties. TRS properties are + converted to matrices and postmultiplied in the `T * R * S` order to compose the + transformation matrix; first the scale is applied to the vertices, then the rotation, and + then the translation. If none are provided, the transform is the identity. When a node is + targeted for animation (referenced by an animation.channel.target), only TRS properties + may be present; `matrix` will not be present. + """ + + def __init__(self, camera, children, extensions, extras, matrix, mesh, name, rotation, scale, skin, translation, + weights): + self.camera = camera + self.children = children + self.extensions = extensions + self.extras = extras + self.matrix = matrix + self.mesh = mesh + self.name = name + self.rotation = rotation + self.scale = scale + self.skin = skin + self.translation = translation + self.weights = weights + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + camera = from_union([from_int, from_none], obj.get("camera")) + children = from_union([lambda x: from_list(from_int, x), from_none], obj.get("children")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + matrix = from_union([lambda x: from_list(from_float, x), from_none], obj.get("matrix")) + mesh = from_union([from_int, from_none], obj.get("mesh")) + name = from_union([from_str, from_none], obj.get("name")) + rotation = from_union([lambda x: from_list(from_float, x), from_none], obj.get("rotation")) + scale = from_union([lambda x: from_list(from_float, x), from_none], obj.get("scale")) + skin = from_union([from_int, from_none], obj.get("skin")) + translation = from_union([lambda x: from_list(from_float, x), from_none], obj.get("translation")) + weights = from_union([lambda x: from_list(from_float, x), from_none], obj.get("weights")) + return Node(camera, children, extensions, extras, matrix, mesh, name, rotation, scale, skin, translation, + weights) + + def to_dict(self): + result = {} + result["camera"] = from_union([from_int, from_none], self.camera) + result["children"] = from_union([lambda x: from_list(from_int, x), from_none], self.children) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["matrix"] = from_union([lambda x: from_list(to_float, x), from_none], self.matrix) + result["mesh"] = from_union([from_int, from_none], self.mesh) + result["name"] = from_union([from_str, from_none], self.name) + result["rotation"] = from_union([lambda x: from_list(to_float, x), from_none], self.rotation) + result["scale"] = from_union([lambda x: from_list(to_float, x), from_none], self.scale) + result["skin"] = from_union([from_int, from_none], self.skin) + result["translation"] = from_union([lambda x: from_list(to_float, x), from_none], self.translation) + result["weights"] = from_union([lambda x: from_list(to_float, x), from_none], self.weights) + return result + + +class Sampler: + """Texture sampler properties for filtering and wrapping modes.""" + + def __init__(self, extensions, extras, mag_filter, min_filter, name, wrap_s, wrap_t): + self.extensions = extensions + self.extras = extras + self.mag_filter = mag_filter + self.min_filter = min_filter + self.name = name + self.wrap_s = wrap_s + self.wrap_t = wrap_t + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + mag_filter = from_union([from_int, from_none], obj.get("magFilter")) + min_filter = from_union([from_int, from_none], obj.get("minFilter")) + name = from_union([from_str, from_none], obj.get("name")) + wrap_s = from_union([from_int, from_none], obj.get("wrapS")) + wrap_t = from_union([from_int, from_none], obj.get("wrapT")) + return Sampler(extensions, extras, mag_filter, min_filter, name, wrap_s, wrap_t) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["magFilter"] = from_union([from_int, from_none], self.mag_filter) + result["minFilter"] = from_union([from_int, from_none], self.min_filter) + result["name"] = from_union([from_str, from_none], self.name) + result["wrapS"] = from_union([from_int, from_none], self.wrap_s) + result["wrapT"] = from_union([from_int, from_none], self.wrap_t) + return result + + +class Scene: + """The root nodes of a scene.""" + + def __init__(self, extensions, extras, name, nodes): + self.extensions = extensions + self.extras = extras + self.name = name + self.nodes = nodes + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + nodes = from_union([lambda x: from_list(from_int, x), from_none], obj.get("nodes")) + return Scene(extensions, extras, name, nodes) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["nodes"] = from_union([lambda x: from_list(from_int, x), from_none], self.nodes) + return result + + +class Skin: + """Joints and matrices defining a skin.""" + + def __init__(self, extensions, extras, inverse_bind_matrices, joints, name, skeleton): + self.extensions = extensions + self.extras = extras + self.inverse_bind_matrices = inverse_bind_matrices + self.joints = joints + self.name = name + self.skeleton = skeleton + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + inverse_bind_matrices = from_union([from_int, from_none], obj.get("inverseBindMatrices")) + joints = from_list(from_int, obj.get("joints")) + name = from_union([from_str, from_none], obj.get("name")) + skeleton = from_union([from_int, from_none], obj.get("skeleton")) + return Skin(extensions, extras, inverse_bind_matrices, joints, name, skeleton) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["inverseBindMatrices"] = from_union([from_int, from_none], self.inverse_bind_matrices) + result["joints"] = from_list(from_int, self.joints) + result["name"] = from_union([from_str, from_none], self.name) + result["skeleton"] = from_union([from_int, from_none], self.skeleton) + return result + + +class Texture: + """A texture and its sampler.""" + + def __init__(self, extensions, extras, name, sampler, source): + self.extensions = extensions + self.extras = extras + self.name = name + self.sampler = sampler + self.source = source + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + name = from_union([from_str, from_none], obj.get("name")) + sampler = from_union([from_int, from_none], obj.get("sampler")) + source = from_int(obj.get("source")) + return Texture(extensions, extras, name, sampler, source) + + def to_dict(self): + result = {} + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + result["name"] = from_union([from_str, from_none], self.name) + result["sampler"] = from_union([from_int, from_none], self.sampler) + result["source"] = from_int(self.source) + return result + + +class Gltf: + """The root object for a glTF asset.""" + + def __init__(self, accessors, animations, asset, buffers, buffer_views, cameras, extensions, extensions_required, + extensions_used, extras, images, materials, meshes, nodes, samplers, scene, scenes, skins, textures): + self.accessors = accessors + self.animations = animations + self.asset = asset + self.buffers = buffers + self.buffer_views = buffer_views + self.cameras = cameras + self.extensions = extensions + self.extensions_required = extensions_required + self.extensions_used = extensions_used + self.extras = extras + self.images = images + self.materials = materials + self.meshes = meshes + self.nodes = nodes + self.samplers = samplers + self.scene = scene + self.scenes = scenes + self.skins = skins + self.textures = textures + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + accessors = from_union([lambda x: from_list(Accessor.from_dict, x), from_none], obj.get("accessors")) + animations = from_union([lambda x: from_list(Animation.from_dict, x), from_none], obj.get("animations")) + asset = Asset.from_dict(obj.get("asset")) + buffers = from_union([lambda x: from_list(Buffer.from_dict, x), from_none], obj.get("buffers")) + buffer_views = from_union([lambda x: from_list(BufferView.from_dict, x), from_none], obj.get("bufferViews")) + cameras = from_union([lambda x: from_list(Camera.from_dict, x), from_none], obj.get("cameras")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extensions_required = from_union([lambda x: from_list(from_str, x), from_none], obj.get("extensionsRequired")) + extensions_used = from_union([lambda x: from_list(from_str, x), from_none], obj.get("extensionsUsed")) + extras = obj.get("extras") + images = from_union([lambda x: from_list(Image.from_dict, x), from_none], obj.get("images")) + materials = from_union([lambda x: from_list(Material.from_dict, x), from_none], obj.get("materials")) + meshes = from_union([lambda x: from_list(Mesh.from_dict, x), from_none], obj.get("meshes")) + nodes = from_union([lambda x: from_list(Node.from_dict, x), from_none], obj.get("nodes")) + samplers = from_union([lambda x: from_list(Sampler.from_dict, x), from_none], obj.get("samplers")) + scene = from_union([from_int, from_none], obj.get("scene")) + scenes = from_union([lambda x: from_list(Scene.from_dict, x), from_none], obj.get("scenes")) + skins = from_union([lambda x: from_list(Skin.from_dict, x), from_none], obj.get("skins")) + textures = from_union([lambda x: from_list(Texture.from_dict, x), from_none], obj.get("textures")) + return Gltf(accessors, animations, asset, buffers, buffer_views, cameras, extensions, extensions_required, + extensions_used, extras, images, materials, meshes, nodes, samplers, scene, scenes, skins, textures) + + def to_dict(self): + result = {} + result["accessors"] = from_union([lambda x: from_list(lambda x: to_class(Accessor, x), x), from_none], + self.accessors) + result["animations"] = from_union([lambda x: from_list(lambda x: to_class(Animation, x), x), from_none], + self.animations) + result["asset"] = to_class(Asset, self.asset) + result["buffers"] = from_union([lambda x: from_list(lambda x: to_class(Buffer, x), x), from_none], self.buffers) + result["bufferViews"] = from_union([lambda x: from_list(lambda x: to_class(BufferView, x), x), from_none], + self.buffer_views) + result["cameras"] = from_union([lambda x: from_list(lambda x: to_class(Camera, x), x), from_none], self.cameras) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extensionsRequired"] = from_union([lambda x: from_list(from_str, x), from_none], + self.extensions_required) + result["extensionsUsed"] = from_union([lambda x: from_list(from_str, x), from_none], self.extensions_used) + result["extras"] = self.extras + result["images"] = from_union([lambda x: from_list(lambda x: to_class(Image, x), x), from_none], self.images) + result["materials"] = from_union([lambda x: from_list(lambda x: to_class(Material, x), x), from_none], + self.materials) + result["meshes"] = from_union([lambda x: from_list(lambda x: to_class(Mesh, x), x), from_none], self.meshes) + result["nodes"] = from_union([lambda x: from_list(lambda x: to_class(Node, x), x), from_none], self.nodes) + result["samplers"] = from_union([lambda x: from_list(lambda x: to_class(Sampler, x), x), from_none], + self.samplers) + result["scene"] = from_union([from_int, from_none], self.scene) + result["scenes"] = from_union([lambda x: from_list(lambda x: to_class(Scene, x), x), from_none], self.scenes) + result["skins"] = from_union([lambda x: from_list(lambda x: to_class(Skin, x), x), from_none], self.skins) + result["textures"] = from_union([lambda x: from_list(lambda x: to_class(Texture, x), x), from_none], + self.textures) + return result + + +def gltf_from_dict(s): + return Gltf.from_dict(s) + + +def gltf_to_dict(x): + return to_class(Gltf, x) + diff --git a/io_scene_gltf2/io/com/gltf2_io_constants.py b/io_scene_gltf2/io/com/gltf2_io_constants.py new file mode 100755 index 00000000..c97908cd --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io_constants.py @@ -0,0 +1,132 @@ +# 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 enum import IntEnum + + +class ComponentType(IntEnum): + Byte = 5120 + UnsignedByte = 5121 + Short = 5122 + UnsignedShort = 5123 + UnsignedInt = 5125 + Float = 5126 + + @classmethod + def to_type_code(cls, component_type): + return { + ComponentType.Byte: 'b', + ComponentType.UnsignedByte: 'B', + ComponentType.Short: 'h', + ComponentType.UnsignedShort: 'H', + ComponentType.UnsignedInt: 'I', + ComponentType.Float: 'f' + }[component_type] + + @classmethod + def from_legacy_define(cls, type_define): + return { + GLTF_COMPONENT_TYPE_BYTE: ComponentType.Byte, + GLTF_COMPONENT_TYPE_UNSIGNED_BYTE: ComponentType.UnsignedByte, + GLTF_COMPONENT_TYPE_SHORT: ComponentType.Short, + GLTF_COMPONENT_TYPE_UNSIGNED_SHORT: ComponentType.UnsignedShort, + GLTF_COMPONENT_TYPE_UNSIGNED_INT: ComponentType.UnsignedInt, + GLTF_COMPONENT_TYPE_FLOAT: ComponentType.Float + }[type_define] + + @classmethod + def get_size(cls, component_type): + return { + ComponentType.Byte: 1, + ComponentType.UnsignedByte: 1, + ComponentType.Short: 2, + ComponentType.UnsignedShort: 2, + ComponentType.UnsignedInt: 4, + ComponentType.Float: 4 + }[component_type] + + +class DataType: + Scalar = "SCALAR" + Vec2 = "VEC2" + Vec3 = "VEC3" + Vec4 = "VEC4" + Mat2 = "MAT2" + Mat3 = "MAT3" + Mat4 = "MAT4" + + def __new__(cls, *args, **kwargs): + raise RuntimeError("{} should not be instantiated".format(cls.__name__)) + + @classmethod + def num_elements(cls, data_type): + return { + DataType.Scalar: 1, + DataType.Vec2: 2, + DataType.Vec3: 3, + DataType.Vec4: 4, + DataType.Mat2: 4, + DataType.Mat3: 9, + DataType.Mat4: 16 + }[data_type] + + @classmethod + def vec_type_from_num(cls, num_elems): + if not (0 < num_elems < 5): + raise ValueError("No vector type with {} elements".format(num_elems)) + return { + 1: DataType.Scalar, + 2: DataType.Vec2, + 3: DataType.Vec3, + 4: DataType.Vec4 + }[num_elems] + + @classmethod + def mat_type_from_num(cls, num_elems): + if not (4 <= num_elems <= 16): + raise ValueError("No matrix type with {} elements".format(num_elems)) + return { + 4: DataType.Mat2, + 9: DataType.Mat3, + 16: DataType.Mat4 + }[num_elems] + + +################# +# LEGACY DEFINES + +GLTF_VERSION = "2.0" + +# +# Component Types +# +GLTF_COMPONENT_TYPE_BYTE = "BYTE" +GLTF_COMPONENT_TYPE_UNSIGNED_BYTE = "UNSIGNED_BYTE" +GLTF_COMPONENT_TYPE_SHORT = "SHORT" +GLTF_COMPONENT_TYPE_UNSIGNED_SHORT = "UNSIGNED_SHORT" +GLTF_COMPONENT_TYPE_UNSIGNED_INT = "UNSIGNED_INT" +GLTF_COMPONENT_TYPE_FLOAT = "FLOAT" + + +# +# Data types +# +GLTF_DATA_TYPE_SCALAR = "SCALAR" +GLTF_DATA_TYPE_VEC2 = "VEC2" +GLTF_DATA_TYPE_VEC3 = "VEC3" +GLTF_DATA_TYPE_VEC4 = "VEC4" +GLTF_DATA_TYPE_MAT2 = "MAT2" +GLTF_DATA_TYPE_MAT3 = "MAT3" +GLTF_DATA_TYPE_MAT4 = "MAT4" + diff --git a/io_scene_gltf2/io/com/gltf2_io_debug.py b/io_scene_gltf2/io/com/gltf2_io_debug.py new file mode 100755 index 00000000..a7df8fed --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io_debug.py @@ -0,0 +1,138 @@ +# 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 time +import logging + +# +# Globals +# + +OUTPUT_LEVELS = ['ERROR', 'WARNING', 'INFO', 'PROFILE', 'DEBUG', 'VERBOSE'] + +g_current_output_level = 'DEBUG' +g_profile_started = False +g_profile_start = 0.0 +g_profile_end = 0.0 +g_profile_delta = 0.0 + +# +# Functions +# + + +def set_output_level(level): + """Set an output debug level.""" + global g_current_output_level + + if OUTPUT_LEVELS.index(level) < 0: + return + + g_current_output_level = level + + +def print_console(level, output): + """Print to Blender console with a given header and output.""" + global OUTPUT_LEVELS + global g_current_output_level + + if OUTPUT_LEVELS.index(level) > OUTPUT_LEVELS.index(g_current_output_level): + return + + print(level + ': ' + output) + + +def print_newline(): + """Print a new line to Blender console.""" + print() + + +def print_timestamp(label=None): + """Print a timestamp to Blender console.""" + output = 'Timestamp: ' + str(time.time()) + + if label is not None: + output = output + ' (' + label + ')' + + print_console('PROFILE', output) + + +def profile_start(): + """Start profiling by storing the current time.""" + global g_profile_start + global g_profile_started + + if g_profile_started: + print_console('ERROR', 'Profiling already started') + return + + g_profile_started = True + + g_profile_start = time.time() + + +def profile_end(label=None): + """Stop profiling and printing out the delta time since profile start.""" + global g_profile_end + global g_profile_delta + global g_profile_started + + if not g_profile_started: + print_console('ERROR', 'Profiling not started') + return + + g_profile_started = False + + g_profile_end = time.time() + g_profile_delta = g_profile_end - g_profile_start + + output = 'Delta time: ' + str(g_profile_delta) + + if label is not None: + output = output + ' (' + label + ')' + + print_console('PROFILE', output) + + +# TODO: need to have a unique system for logging importer/exporter +# TODO: this logger is used for importer, but in io and in blender part, but is written here in a _io_ file +class Log: + def __init__(self, loglevel): + self.logger = logging.getLogger('glTFImporter') + self.hdlr = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + self.hdlr.setFormatter(formatter) + self.logger.addHandler(self.hdlr) + self.logger.setLevel(int(loglevel)) + + @staticmethod + def get_levels(): + levels = [ + (str(logging.CRITICAL), "Critical", "", logging.CRITICAL), + (str(logging.ERROR), "Error", "", logging.ERROR), + (str(logging.WARNING), "Warning", "", logging.WARNING), + (str(logging.INFO), "Info", "", logging.INFO), + (str(logging.NOTSET), "NotSet", "", logging.NOTSET) + ] + + return levels + + @staticmethod + def default(): + return str(logging.ERROR) + diff --git a/io_scene_gltf2/io/com/gltf2_io_functional.py b/io_scene_gltf2/io/com/gltf2_io_functional.py new file mode 100755 index 00000000..eb65112f --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io_functional.py @@ -0,0 +1,41 @@ +# 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) + diff --git a/io_scene_gltf2/io/com/gltf2_io_image.py b/io_scene_gltf2/io/com/gltf2_io_image.py new file mode 100755 index 00000000..af86daeb --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io_image.py @@ -0,0 +1,154 @@ +# 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 struct +import zlib + + +class Image: + """ + Image object class to represent a 4-channel RGBA image. + + Pixel values are expected to be floating point in the range of [0.0 to 1.0] + """ + + def __init__(self, width, height, pixels): + self.width = width + self.height = height + self.channels = 4 + self.pixels = pixels + self.name = "" + self.file_format = "PNG" + + def to_png_data(self): + buf = bytearray([int(channel * 255.0) for channel in self.pixels]) + + # + # Taken from 'blender-thumbnailer.py' in Blender. + # + + # reverse the vertical line order and add null bytes at the start + width_byte_4 = self.width * 4 + raw_data = b"".join( + b'\x00' + buf[span:span + width_byte_4] for span in range( + (self.height - 1) * self.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", self.width, self.height, 8, 6, 0, 0, 0)), + png_pack(b'IDAT', zlib.compress(raw_data, 9)), + png_pack(b'IEND', b'')]) + + def to_image_data(self, mime_type): + if mime_type == 'image/png': + return self.to_png_data() + raise ValueError("Unsupported image file type {}".format(mime_type)) + + def save_png(self, dst_path): + data = self.to_png_data() + with open(dst_path, 'wb') as f: + f.write(data) + + +def create_img(width, height, r=0.0, g=0.0, b=0.0, a=1.0): + """ + Create a new image object with 4 channels and initialize it with the given default values. + + (if no arguments are given, these default to R=0, G=0, B=0, A=1.0) + Return the created image object. + """ + return Image(width, height, [r, g, b, a] * (width * height)) + + +def create_img_from_pixels(width, height, pixels): + """ + Create a new image object with 4 channels and initialize it using the given array of pixel data. + + Return the created image object. + """ + if pixels is None or len(pixels) != width * height * 4: + return None + + return Image(width, height, pixels) + + +def copy_img_channel(dst_image, dst_channel, src_image, src_channel): + """ + Copy a single channel (identified by src_channel) from src_image to dst_image (overwriting dst_channel). + + src_image and dst_image are expected to be image objects created using create_img. + Return True on success, False otherwise. + """ + if dst_image is None or src_image is None: + return False + + if dst_channel < 0 or dst_channel >= dst_image.channels or src_channel < 0 or src_channel >= src_image.channels: + return False + + if src_image.width != dst_image.width or \ + src_image.height != dst_image.height or \ + src_image.channels != dst_image.channels: + return False + + for i in range(0, len(dst_image.pixels), dst_image.channels): + dst_image.pixels[i + dst_channel] = src_image.pixels[i + src_channel] + + return True + + +def test_save_img(image, path): + """ + Save the given image to a PNG file (specified by path). + + Return True on success, False otherwise. + """ + if image is None or image.channels != 4: + return False + + width = image.width + height = image.height + + buf = bytearray([int(channel * 255.0) for channel in 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)) + + data = 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'')]) + + with open(path, 'wb') as f: + f.write(data) + return True + diff --git a/io_scene_gltf2/io/com/gltf2_io_trs.py b/io_scene_gltf2/io/com/gltf2_io_trs.py new file mode 100755 index 00000000..59f30830 --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io_trs.py @@ -0,0 +1,68 @@ +# 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. + + +class TRS: + + def __new__(cls, *args, **kwargs): + raise RuntimeError("{} should not be instantiated".format(cls.__name__)) + + @staticmethod + def scale_to_matrix(scale): + # column major ! + return [scale[0], 0, 0, 0, + 0, scale[1], 0, 0, + 0, 0, scale[2], 0, + 0, 0, 0, 1] + + @staticmethod + def quaternion_to_matrix(q): + x, y, z, w = q + # TODO : is q normalized ? --> if not, multiply by 1/(w*w + x*x + y*y + z*z) + # column major ! + return [ + 1 - 2 * y * y - 2 * z * z, 2 * x * y + 2 * w * z, 2 * x * z - 2 * w * y, 0, + 2 * x * y - 2 * w * z, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * w * x, 0, + 2 * x * z + 2 * y * w, 2 * y * z - 2 * w * x, 1 - 2 * x * x - 2 * y * y, 0, + 0, 0, 0, 1] + + @staticmethod + def matrix_multiply(m, n): + # column major ! + + return [ + m[0] * n[0] + m[4] * n[1] + m[8] * n[2] + m[12] * n[3], + m[1] * n[0] + m[5] * n[1] + m[9] * n[2] + m[13] * n[3], + m[2] * n[0] + m[6] * n[1] + m[10] * n[2] + m[14] * n[3], + m[3] * n[0] + m[7] * n[1] + m[11] * n[2] + m[15] * n[3], + m[0] * n[4] + m[4] * n[5] + m[8] * n[6] + m[12] * n[7], + m[1] * n[4] + m[5] * n[5] + m[9] * n[6] + m[13] * n[7], + m[2] * n[4] + m[6] * n[5] + m[10] * n[6] + m[14] * n[7], + m[3] * n[4] + m[7] * n[5] + m[11] * n[6] + m[15] * n[7], + m[0] * n[8] + m[4] * n[9] + m[8] * n[10] + m[12] * n[11], + m[1] * n[8] + m[5] * n[9] + m[9] * n[10] + m[13] * n[11], + m[2] * n[8] + m[6] * n[9] + m[10] * n[10] + m[14] * n[11], + m[3] * n[8] + m[7] * n[9] + m[11] * n[10] + m[15] * n[11], + m[0] * n[12] + m[4] * n[13] + m[8] * n[14] + m[12] * n[15], + m[1] * n[12] + m[5] * n[13] + m[9] * n[14] + m[13] * n[15], + m[2] * n[12] + m[6] * n[13] + m[10] * n[14] + m[14] * n[15], + m[3] * n[12] + m[7] * n[13] + m[11] * n[14] + m[15] * n[15], + ] + + @staticmethod + def translation_to_matrix(translation): + # column major ! + return [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, + translation[0], translation[1], translation[2], 1.0] + |