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>2021-01-04 22:55:25 +0300
committerJulien Duroure <julien.duroure@gmail.com>2021-01-04 22:55:25 +0300
commiteb29a12da48e89fa2a3518976e9223f2092ad22d (patch)
tree2a642bd05f5a182f58c7ad96ecc0286b43ed5e1c
parent48ec56bde5fcb7fc18352559d629ef486f1a5640 (diff)
glTF importer/exporter: Draco decoder + encoder fixes
We can now read Draco compressed files. This also fix exporting vertex color Draco compressed files. Fix #T75550
-rwxr-xr-xio_scene_gltf2/__init__.py18
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_export.py2
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_mesh.py6
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_io_draco_compression_extension.py143
-rw-r--r--io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py64
-rw-r--r--io_scene_gltf2/io/exp/gltf2_io_draco_compression_extension.py361
-rwxr-xr-xio_scene_gltf2/io/imp/gltf2_io_gltf.py1
7 files changed, 308 insertions, 287 deletions
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 319c9c17..b8afe70f 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
- "version": (1, 5, 11),
+ "version": (1, 5, 12),
'blender': (2, 91, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
@@ -91,7 +91,7 @@ class ExportGLTF2_Base:
# TODO: refactor to avoid boilerplate
def __init__(self):
- from io_scene_gltf2.io.exp import gltf2_io_draco_compression_extension
+ from io_scene_gltf2.io.com import gltf2_io_draco_compression_extension
self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists()
bl_options = {'PRESET'}
@@ -202,6 +202,14 @@ class ExportGLTF2_Base:
max=30
)
+ export_draco_color_quantization: IntProperty(
+ name='Color quantization bits',
+ description='Quantization bits for color values (0 = no quantization)',
+ default=10,
+ min=0,
+ max=30
+ )
+
export_draco_generic_quantization: IntProperty(
name='Generic quantization bits',
description='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
@@ -473,6 +481,7 @@ class ExportGLTF2_Base:
export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
+ export_settings['gltf_draco_color_quantization'] = self.export_draco_color_quantization
export_settings['gltf_draco_generic_quantization'] = self.export_draco_generic_quantization
else:
export_settings['gltf_draco_mesh_compression'] = False
@@ -692,7 +701,7 @@ class GLTF_PT_export_geometry_compression(bpy.types.Panel):
bl_options = {'DEFAULT_CLOSED'}
def __init__(self):
- from io_scene_gltf2.io.exp import gltf2_io_draco_compression_extension
+ from io_scene_gltf2.io.com import gltf2_io_draco_compression_extension
self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists(quiet=True)
@classmethod
@@ -721,7 +730,8 @@ class GLTF_PT_export_geometry_compression(bpy.types.Panel):
col = layout.column(align=True)
col.prop(operator, 'export_draco_position_quantization', text="Quantize Position")
col.prop(operator, 'export_draco_normal_quantization', text="Normal")
- col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coords")
+ col.prop(operator, 'export_draco_texcoord_quantization', text="Tex Coord")
+ col.prop(operator, 'export_draco_color_quantization', text="Color")
col.prop(operator, 'export_draco_generic_quantization', text="Generic")
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export.py b/io_scene_gltf2/blender/exp/gltf2_blender_export.py
index fd433c7e..a01390c9 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_export.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_export.py
@@ -76,7 +76,7 @@ def __gather_gltf(exporter, export_settings):
active_scene_idx, scenes, animations = plan['active_scene_idx'], plan['scenes'], plan['animations']
if export_settings['gltf_draco_mesh_compression']:
- gltf2_io_draco_compression_extension.compress_scene_primitives(scenes, export_settings)
+ gltf2_io_draco_compression_extension.encode_scene_primitives(scenes, export_settings)
exporter.add_draco_extension()
for idx, scene in enumerate(scenes):
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
index a6896491..328bc1a8 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
@@ -19,6 +19,8 @@ import numpy as np
from ...io.imp.gltf2_io_binary import BinaryData
from ..com.gltf2_blender_extras import set_extras
from .gltf2_blender_material import BlenderMaterial
+from ...io.com.gltf2_io_debug import print_console
+from .gltf2_io_draco_compression_extension import decode_primitive
class BlenderMesh():
@@ -134,6 +136,10 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
vert_index_base = len(vert_locs)
+ if prim.extensions is not None and 'KHR_draco_mesh_compression' in prim.extensions:
+ print_console('INFO', 'Draco Decoder: Decode primitive {}'.format(pymesh.name or '[unnamed]'))
+ decode_primitive(gltf, prim)
+
if prim.indices is not None:
indices = BinaryData.decode_accessor(gltf, prim.indices)
indices = indices.reshape(len(indices))
diff --git a/io_scene_gltf2/blender/imp/gltf2_io_draco_compression_extension.py b/io_scene_gltf2/blender/imp/gltf2_io_draco_compression_extension.py
new file mode 100644
index 00000000..5a93673f
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_io_draco_compression_extension.py
@@ -0,0 +1,143 @@
+# Copyright 2018-2019 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ctypes import *
+
+from io_scene_gltf2.io.com.gltf2_io import BufferView
+from io_scene_gltf2.io.imp.gltf2_io_binary import BinaryData
+from ...io.com.gltf2_io_debug import print_console
+from io_scene_gltf2.io.com.gltf2_io_draco_compression_extension import dll_path
+
+
+def decode_primitive(gltf, prim):
+ """
+ Handles draco compression.
+ Moves decoded data into new buffers and buffer views held by the accessors of the given primitive.
+ """
+
+ # Load DLL and setup function signatures.
+ dll = cdll.LoadLibrary(str(dll_path().resolve()))
+
+ dll.decoderCreate.restype = c_void_p
+ dll.decoderCreate.argtypes = []
+
+ dll.decoderRelease.restype = None
+ dll.decoderRelease.argtypes = [c_void_p]
+
+ dll.decoderDecode.restype = c_bool
+ dll.decoderDecode.argtypes = [c_void_p, c_void_p, c_size_t]
+
+ dll.decoderReadAttribute.restype = c_bool
+ dll.decoderReadAttribute.argtypes = [c_void_p, c_uint32, c_size_t, c_char_p]
+
+ dll.decoderGetVertexCount.restype = c_uint32
+ dll.decoderGetVertexCount.argtypes = [c_void_p]
+
+ dll.decoderGetIndexCount.restype = c_uint32
+ dll.decoderGetIndexCount.argtypes = [c_void_p]
+
+ dll.decoderAttributeIsNormalized.restype = c_bool
+ dll.decoderAttributeIsNormalized.argtypes = [c_void_p, c_uint32]
+
+ dll.decoderGetAttributeByteLength.restype = c_size_t
+ dll.decoderGetAttributeByteLength.argtypes = [c_void_p, c_uint32]
+
+ dll.decoderCopyAttribute.restype = None
+ dll.decoderCopyAttribute.argtypes = [c_void_p, c_uint32, c_void_p]
+
+ dll.decoderReadIndices.restype = c_bool
+ dll.decoderReadIndices.argtypes = [c_void_p, c_size_t]
+
+ dll.decoderGetIndicesByteLength.restype = c_size_t
+ dll.decoderGetIndicesByteLength.argtypes = [c_void_p]
+
+ dll.decoderCopyIndices.restype = None
+ dll.decoderCopyIndices.argtypes = [c_void_p, c_void_p]
+
+ decoder = dll.decoderCreate()
+ extension = prim.extensions['KHR_draco_mesh_compression']
+
+ name = prim.name if hasattr(prim, 'name') else '[unnamed]'
+
+ # Create Draco decoder.
+ draco_buffer = bytes(BinaryData.get_buffer_view(gltf, extension['bufferView']))
+ if not dll.decoderDecode(decoder, draco_buffer, len(draco_buffer)):
+ print_console('ERROR', 'Draco Decoder: Unable to decode. Skipping primitive {}.'.format(name))
+ return
+
+ # Choose a buffer index which does not yet exist, skipping over existing glTF buffers yet to be loaded
+ # and buffers which were generated and did not exist in the initial glTF file, like this decoder does.
+ base_buffer_idx = len(gltf.data.buffers)
+ for existing_buffer_idx in gltf.buffers:
+ if base_buffer_idx <= existing_buffer_idx:
+ base_buffer_idx = existing_buffer_idx + 1
+
+ # Read indices.
+ index_accessor = gltf.data.accessors[prim.indices]
+ if dll.decoderGetIndexCount(decoder) != index_accessor.count:
+ print_console('WARNING', 'Draco Decoder: Index count of accessor and decoded index count does not match. Updating accessor.')
+ index_accessor.count = dll.decoderGetIndexCount(decoder)
+ if not dll.decoderReadIndices(decoder, index_accessor.component_type):
+ print_console('ERROR', 'Draco Decoder: Unable to decode indices. Skipping primitive {}.'.format(name))
+ return
+
+ indices_byte_length = dll.decoderGetIndicesByteLength(decoder)
+ decoded_data = bytes(indices_byte_length)
+ dll.decoderCopyIndices(decoder, decoded_data)
+
+ # Generate a new buffer holding the decoded indices.
+ gltf.buffers[base_buffer_idx] = decoded_data
+
+ # Create a buffer view referencing the new buffer.
+ gltf.data.buffer_views.append(BufferView.from_dict({
+ 'buffer': base_buffer_idx,
+ 'byteLength': indices_byte_length
+ }))
+
+ # Update accessor to point to the new buffer view.
+ index_accessor.buffer_view = len(gltf.data.buffer_views) - 1
+
+ # Read each attribute.
+ for attr_idx, attr in enumerate(extension['attributes']):
+ dracoId = extension['attributes'][attr]
+ if attr not in prim.attributes:
+ print_console('ERROR', 'Draco Decoder: Draco attribute {} not in primitive attributes. Skipping primitive {}.'.format(attr, name))
+ return
+
+ accessor = gltf.data.accessors[prim.attributes[attr]]
+ if dll.decoderGetVertexCount(decoder) != accessor.count:
+ print_console('WARNING', 'Draco Decoder: Vertex count of accessor and decoded vertex count does not match for attribute {}. Updating accessor.'.format(attr, name))
+ accessor.count = dll.decoderGetVertexCount(decoder)
+ if not dll.decoderReadAttribute(decoder, dracoId, accessor.component_type, accessor.type.encode()):
+ print_console('ERROR', 'Draco Decoder: Could not decode attribute {}. Skipping primitive {}.'.format(attr, name))
+ return
+
+ byte_length = dll.decoderGetAttributeByteLength(decoder, dracoId)
+ decoded_data = bytes(byte_length)
+ dll.decoderCopyAttribute(decoder, dracoId, decoded_data)
+
+ # Generate a new buffer holding the decoded vertex data.
+ buffer_idx = base_buffer_idx + 1 + attr_idx
+ gltf.buffers[buffer_idx] = decoded_data
+
+ # Create a buffer view referencing the new buffer.
+ gltf.data.buffer_views.append(BufferView.from_dict({
+ 'buffer': buffer_idx,
+ 'byteLength': byte_length
+ }))
+
+ # Update accessor to point to the new buffer view.
+ accessor.buffer_view = len(gltf.data.buffer_views) - 1
+
+ dll.decoderRelease(decoder)
diff --git a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py
new file mode 100644
index 00000000..51359f82
--- /dev/null
+++ b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py
@@ -0,0 +1,64 @@
+# Copyright 2018-2019 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+from pathlib import Path
+import bpy
+
+from ...io.com.gltf2_io_debug import print_console
+
+
+def dll_path() -> Path:
+ """
+ Get the DLL path depending on the underlying platform.
+ :return: DLL path.
+ """
+ lib_name = 'extern_draco'
+ blender_root = Path(bpy.app.binary_path).parent
+ python_lib = Path('{v[0]}.{v[1]}/python/lib'.format(v=bpy.app.version))
+ python_version = 'python{v[0]}.{v[1]}'.format(v=sys.version_info)
+
+ path = os.environ.get('BLENDER_EXTERN_DRACO_LIBRARY_PATH')
+ if path is None:
+ path = {
+ 'win32': blender_root / python_lib / 'site-packages',
+ 'linux': blender_root / python_lib / python_version / 'site-packages',
+ 'darwin': blender_root.parent / 'Resources' / python_lib / python_version / 'site-packages'
+ }.get(sys.platform)
+ else:
+ path = Path(path)
+
+ library_name = {
+ 'win32': '{}.dll'.format(lib_name),
+ 'linux': 'lib{}.so'.format(lib_name),
+ 'darwin': 'lib{}.dylib'.format(lib_name)
+ }.get(sys.platform)
+
+ if path is None or library_name is None:
+ print_console('WARNING', 'Unsupported platform {}, Draco mesh compression is unavailable'.format(sys.platform))
+
+ return path / library_name
+
+
+def dll_exists(quiet=False) -> bool:
+ """
+ Checks whether the DLL path exists.
+ :return: True if the DLL exists.
+ """
+ exists = dll_path().exists()
+ if quiet is False:
+ print("'{}' ".format(dll_path().absolute()) + ("exists, draco mesh compression is available" if exists else
+ "does not exist, draco mesh compression not available"))
+ return exists
diff --git a/io_scene_gltf2/io/exp/gltf2_io_draco_compression_extension.py b/io_scene_gltf2/io/exp/gltf2_io_draco_compression_extension.py
index 2bb79aa0..613bd231 100644
--- a/io_scene_gltf2/io/exp/gltf2_io_draco_compression_extension.py
+++ b/io_scene_gltf2/io/exp/gltf2_io_draco_compression_extension.py
@@ -12,334 +12,131 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import bpy
-import sys
-from ctypes import c_void_p, c_uint32, c_uint64, c_bool, c_char_p, cdll
+from ctypes import *
from pathlib import Path
-import struct
from io_scene_gltf2.io.exp.gltf2_io_binary_data import BinaryData
from ...io.com.gltf2_io_debug import print_console
+from io_scene_gltf2.io.com.gltf2_io_draco_compression_extension import dll_path
-def dll_path() -> Path:
- """
- Get the DLL path depending on the underlying platform.
- :return: DLL path.
- """
- lib_name = 'extern_draco'
- blender_root = Path(bpy.app.binary_path).parent
- python_lib = Path("{v[0]}.{v[1]}/python/lib".format(v=bpy.app.version))
- python_version = "python{v[0]}.{v[1]}".format(v=sys.version_info)
- paths = {
- 'win32': blender_root/python_lib/'site-packages'/'{}.dll'.format(lib_name),
- 'linux': blender_root/python_lib/python_version/'site-packages'/'lib{}.so'.format(lib_name),
- 'darwin': blender_root.parent/'Resources'/python_lib/python_version/'site-packages'/'lib{}.dylib'.format(lib_name)
- }
-
- path = paths.get(sys.platform)
- return path if path is not None else ''
-
-
-def dll_exists(quiet=False) -> bool:
- """
- Checks whether the DLL path exists.
- :return: True if the DLL exists.
- """
- exists = dll_path().exists()
- if quiet is False:
- print("'{}' ".format(dll_path().absolute()) + ("exists, draco mesh compression is available" if exists else
- "does not exist, draco mesh compression not available"))
- return exists
-
-
-def compress_scene_primitives(scenes, export_settings):
+def encode_scene_primitives(scenes, export_settings):
"""
Handles draco compression.
- Invoked after data has been gathered, but before scenes get traversed.
- Moves position, normal and texture coordinate attributes into a Draco compressed buffer.
+ Moves position, normal and texture coordinate attributes into a Draco encoded buffer.
"""
# Load DLL and setup function signatures.
- # Nearly all functions take the compressor as the first argument.
dll = cdll.LoadLibrary(str(dll_path().resolve()))
- # Initialization:
-
- dll.create_compressor.restype = c_void_p
- dll.create_compressor.argtypes = []
-
- dll.destroy_compressor.restype = None
- dll.destroy_compressor.argtypes = [c_void_p]
-
- # Configuration:
-
- dll.set_compression_level.restype = None
- dll.set_compression_level.argtypes = [c_void_p, c_uint32]
-
- dll.set_position_quantization.restype = None
- dll.set_position_quantization.argtypes = [c_void_p, c_uint32]
-
- dll.set_normal_quantization.restype = None
- dll.set_normal_quantization.argtypes = [c_void_p, c_uint32]
-
- dll.set_uv_quantization.restype = None
- dll.set_uv_quantization.argtypes = [c_void_p, c_uint32]
+ dll.encoderCreate.restype = c_void_p
+ dll.encoderCreate.argtypes = [c_uint32]
- dll.set_generic_quantization.restype = None
- dll.set_generic_quantization.argtypes = [c_void_p, c_uint32]
+ dll.encoderRelease.restype = None
+ dll.encoderRelease.argtypes = [c_void_p]
- # Data transfer:
+ dll.encoderSetCompressionLevel.restype = None
+ dll.encoderSetCompressionLevel.argtypes = [c_void_p, c_uint32]
- dll.set_faces.restype = None
- dll.set_faces.argtypes = [
- c_void_p, # Compressor
- c_uint32, # Index count
- c_uint32, # Index byte length
- c_char_p # Indices
- ]
+ dll.encoderSetQuantizationBits.restype = None
+ dll.encoderSetQuantizationBits.argtypes = [c_void_p, c_uint32, c_uint32, c_uint32, c_uint32, c_uint32]
+
+ dll.encoderSetIndices.restype = None
+ dll.encoderSetIndices.argtypes = [c_void_p, c_size_t, c_uint32, c_void_p]
- add_attribute_fn_restype = c_uint32 # Draco id
- add_attribute_fn_argtypes = [
- c_void_p, # Compressor
- c_uint32, # Attribute count
- c_char_p # Values
- ]
+ dll.encoderSetAttribute.restype = c_uint32
+ dll.encoderSetAttribute.argtypes = [c_void_p, c_char_p, c_size_t, c_char_p, c_void_p]
- dll.add_positions_f32.restype = add_attribute_fn_restype
- dll.add_positions_f32.argtypes = add_attribute_fn_argtypes
+ dll.encoderEncode.restype = c_bool
+ dll.encoderEncode.argtypes = [c_void_p, c_uint8]
- dll.add_normals_f32.restype = add_attribute_fn_restype
- dll.add_normals_f32.argtypes = add_attribute_fn_argtypes
+ dll.encoderGetEncodedVertexCount.restype = c_uint32
+ dll.encoderGetEncodedVertexCount.argtypes = [c_void_p]
- dll.add_uvs_f32.restype = add_attribute_fn_restype
- dll.add_uvs_f32.argtypes = add_attribute_fn_argtypes
+ dll.encoderGetEncodedIndexCount.restype = c_uint32
+ dll.encoderGetEncodedIndexCount.argtypes = [c_void_p]
- dll.add_weights_f32.restype = add_attribute_fn_restype
- dll.add_weights_f32.argtypes = add_attribute_fn_argtypes
+ dll.encoderGetByteLength.restype = c_uint64
+ dll.encoderGetByteLength.argtypes = [c_void_p]
- dll.add_joints_u16.restype = add_attribute_fn_restype
- dll.add_joints_u16.argtypes = add_attribute_fn_argtypes
+ dll.encoderCopy.restype = None
+ dll.encoderCopy.argtypes = [c_void_p, c_void_p]
- # Compression:
-
- dll.compress.restype = c_bool
- dll.compress.argtypes = [
- c_void_p # Compressor
- ]
-
- dll.compress_morphed.restype = c_bool
- dll.compress_morphed.argtypes = [
- c_void_p # Compressor
- ]
-
- dll.get_compressed_size.restype = c_uint64
- dll.get_compressed_size.argtypes = [
- c_void_p # Compressor
- ]
-
- dll.copy_to_bytes.restype = None
- dll.copy_to_bytes.argtypes = [
- c_void_p, # Compressor
- c_char_p # Destination pointer
- ]
-
- # Traverse nodes.
- for scene in scenes:
- for node in scene.nodes:
- __traverse_node(node, lambda node: __compress_node(node, dll, export_settings))
-
- # Cleanup memory.
- # May be shared amongst nodes because of non-unique primitive parents, so memory
- # release happens delayed.
for scene in scenes:
for node in scene.nodes:
- __traverse_node(node, __dispose_memory)
+ __traverse_node(node, lambda node: __encode_node(node, dll, export_settings))
-def __dispose_memory(node):
- """Remove buffers from attribute, since the data now resides inside the compressed Draco buffer."""
- if not (node.mesh is None):
- for primitive in node.mesh.primitives:
-
- # Drop indices.
- primitive.indices.buffer_view = None
-
- # Drop attributes.
- attributes = primitive.attributes
- if 'NORMAL' in attributes:
- attributes['NORMAL'].buffer_view = None
- for attribute in [attributes[attr] for attr in attributes if attr.startswith('TEXCOORD_')]:
- attribute.buffer_view = None
-
-def __compress_node(node, dll, export_settings):
- """Compress a single node."""
- if not (node.mesh is None):
- print_console('INFO', 'Draco exporter: Compressing mesh "%s".' % node.name)
- for primitive in node.mesh.primitives:
- __compress_primitive(primitive, dll, export_settings)
def __traverse_node(node, f):
- """Calls f for each node and all child nodes, recursively."""
f(node)
if not (node.children is None):
for child in node.children:
__traverse_node(child, f)
-def __compress_primitive(primitive, dll, export_settings):
+def __encode_node(node, dll, export_settings):
+ if not (node.mesh is None):
+ print_console('INFO', 'Draco encoder: Encoding mesh {}.'.format(node.name))
+ for primitive in node.mesh.primitives:
+ __encode_primitive(primitive, dll, export_settings)
+
+def __encode_primitive(primitive, dll, export_settings):
attributes = primitive.attributes
indices = primitive.indices
- # Maps component types to their byte length.
- component_type_byte_length = {
- 'Byte': 1,
- 'UnsignedByte': 1,
- 'Short': 2,
- 'UnsignedShort': 2,
- 'UnsignedInt': 4,
- }
-
- # Positions are the only attribute type required to be present.
if 'POSITION' not in attributes:
- print_console('WARNING', 'Draco exporter: Primitive without positions encountered. Skipping.')
+ print_console('WARNING', 'Draco encoder: Primitive without positions encountered. Skipping.')
return
positions = attributes['POSITION']
- # Skip nodes without a position buffer.
- # This happens with Blender instances, i.e. multiple nodes sharing the same mesh.
+ # Skip nodes without a position buffer, e.g. a primitive from a Blender shared instance.
if attributes['POSITION'].buffer_view is None:
return
- normals = attributes['NORMAL'] if 'NORMAL' in attributes else None
- uvs = [attributes[attr] for attr in attributes if attr.startswith('TEXCOORD_')]
- weights = [attributes[attr] for attr in attributes if attr.startswith('WEIGHTS_')]
- joints = [attributes[attr] for attr in attributes if attr.startswith('JOINTS_')]
-
- print_console('INFO', 'Draco exporter: %s normals, %d uvs, %d weights, %d joints' %
- ('without' if normals is None else 'with', len(uvs), len(weights), len(joints)))
-
- # Begin mesh.
- compressor = dll.create_compressor()
-
- # Each attribute must have the same count of elements.
- count = positions.count
-
- # Add attributes to mesh compressor, remembering each attribute's Draco id.
-
- position_id = dll.add_positions_f32(compressor, count, positions.buffer_view.data)
-
- normal_id = None
- if normals is not None:
- if normals.count != count:
- print_console('INFO', 'Draco exporter: Mismatching normal count. Skipping.')
- dll.disposeCompressor(compressor)
- return
- normal_id = dll.add_normals_f32(compressor, normals.count, normals.buffer_view.data)
-
- uv_ids = []
- for uv in uvs:
- if uv.count != count:
- print_console('INFO', 'Draco exporter: Mismatching uv count. Skipping.')
- dll.disposeCompressor(compressor)
- return
- uv_ids.append(dll.add_uvs_f32(compressor, uv.count, uv.buffer_view.data))
-
- weight_ids = []
- for weight in weights:
- if weight.count != count:
- print_console('INFO', 'Draco exporter: Mismatching weight count. Skipping.')
- dll.disposeCompressor(compressor)
- return
- weight_ids.append(dll.add_weights_f32(compressor, weight.count, weight.buffer_view.data))
-
- joint_ids = []
- for joint in joints:
- if joint.count != count:
- print_console('INFO', 'Draco exporter: Mismatching joint count. Skipping.')
- dll.disposeCompressor(compressor)
- return
- joint_ids.append(dll.add_joints_u16(compressor, joint.count, joint.buffer_view.data))
-
- # Add face indices to mesh compressor.
- dll.set_faces(compressor, indices.count, component_type_byte_length[indices.component_type.name], indices.buffer_view.data)
-
- # Set compression parameters.
- dll.set_compression_level(compressor, export_settings['gltf_draco_mesh_compression_level'])
- dll.set_position_quantization(compressor, export_settings['gltf_draco_position_quantization'])
- dll.set_normal_quantization(compressor, export_settings['gltf_draco_normal_quantization'])
- dll.set_uv_quantization(compressor, export_settings['gltf_draco_texcoord_quantization'])
- dll.set_generic_quantization(compressor, export_settings['gltf_draco_generic_quantization'])
-
- compress_fn = dll.compress if not primitive.targets else dll.compress_morphed
-
- # After all point and connectivity data has been written to the compressor,
- # it can finally be compressed.
- if dll.compress(compressor):
-
- # Compression was successful.
- # Move compressed data into a bytes object,
- # which is referenced by a 'gltf2_io_binary_data.BinaryData':
- #
- # "KHR_draco_mesh_compression": {
- # ....
- # "buffer_view": Compressed data inside a 'gltf2_io_binary_data.BinaryData'.
- # }
-
- # Query size necessary to hold all the compressed data.
- compression_size = dll.get_compressed_size(compressor)
-
- # Allocate byte buffer and write compressed data to it.
- compressed_data = bytes(compression_size)
- dll.copy_to_bytes(compressor, compressed_data)
-
- if primitive.extensions is None:
- primitive.extensions = {}
-
- # Write Draco extension into primitive, including attribute ids:
-
- extension = {
- 'bufferView': BinaryData(compressed_data),
- 'attributes': {
- 'POSITION': position_id
- }
- }
-
- if normals is not None:
- extension['attributes']['NORMAL'] = normal_id
-
- for (k, id) in enumerate(uv_ids):
- extension['attributes']['TEXCOORD_' + str(k)] = id
-
- for (k, id) in enumerate(weight_ids):
- extension['attributes']['WEIGHTS_' + str(k)] = id
-
- for (k, id) in enumerate(joint_ids):
- extension['attributes']['JOINTS_' + str(k)] = id
-
- primitive.extensions['KHR_draco_mesh_compression'] = extension
-
- # Remove buffer views from the accessors of the attributes which compressed:
-
- positions.buffer_view = None
-
- if normals is not None:
- normals.buffer_view = None
-
- for uv in uvs:
- uv.buffer_view = None
-
- for weight in weights:
- weight.buffer_view = None
-
- for joint in joints:
- joint.buffer_view = None
+ encoder = dll.encoderCreate(positions.count)
+
+ draco_ids = {}
+ for attr_name in attributes:
+ attr = attributes[attr_name]
+ draco_id = dll.encoderSetAttribute(encoder, attr_name.encode(), attr.component_type, attr.type.encode(), attr.buffer_view.data)
+ draco_ids[attr_name] = draco_id
+ attr.buffer_view = None
+
+ dll.encoderSetIndices(encoder, indices.component_type, indices.count, indices.buffer_view.data)
+ indices.buffer_view = None
+
+ dll.encoderSetCompressionLevel(encoder, export_settings['gltf_draco_mesh_compression_level'])
+ dll.encoderSetQuantizationBits(encoder,
+ export_settings['gltf_draco_position_quantization'],
+ export_settings['gltf_draco_normal_quantization'],
+ export_settings['gltf_draco_texcoord_quantization'],
+ export_settings['gltf_draco_color_quantization'],
+ export_settings['gltf_draco_generic_quantization'])
+
+ if not dll.encoderEncode(encoder, primitive.targets is not None and len(primitive.targets) > 0):
+ print_console('ERROR', 'Could not encode primitive. Skipping primitive.')
+
+ byte_length = dll.encoderGetByteLength(encoder)
+ encoded_data = bytes(byte_length)
+ dll.encoderCopy(encoder, encoded_data)
+
+ if primitive.extensions is None:
+ primitive.extensions = {}
+
+ primitive.extensions['KHR_draco_mesh_compression'] = {
+ 'bufferView': BinaryData(encoded_data),
+ 'attributes': draco_ids
+ }
- # Set to triangle list mode.
- primitive.mode = 4
+ # Set to triangle list mode.
+ primitive.mode = 4
- # Afterwards, the compressor can be released.
- dll.destroy_compressor(compressor)
+ # Update accessors to match encoded data.
+ indices.count = dll.encoderGetEncodedIndexCount(encoder)
+ encoded_vertices = dll.encoderGetEncodedVertexCount(encoder)
+ for attr_name in attributes:
+ attributes[attr_name].count = encoded_vertices
- return
+ dll.encoderRelease(encoder)
diff --git a/io_scene_gltf2/io/imp/gltf2_io_gltf.py b/io_scene_gltf2/io/imp/gltf2_io_gltf.py
index 348f8904..f0e23ea1 100755
--- a/io_scene_gltf2/io/imp/gltf2_io_gltf.py
+++ b/io_scene_gltf2/io/imp/gltf2_io_gltf.py
@@ -54,6 +54,7 @@ class glTFImporter():
'KHR_texture_transform',
'KHR_materials_clearcoat',
'KHR_mesh_quantization',
+ 'KHR_draco_mesh_compression'
]
@staticmethod