glTF importer: accessor decoding enhancements

This commit is contained in:
Julien Duroure 2019-11-09 10:52:56 +01:00
parent 5367ebad48
commit eb4085f1dc
2 changed files with 74 additions and 72 deletions

View File

@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
"version": (1, 1, 1),
"version": (1, 1, 2),
'blender': (2, 81, 6),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',

View File

@ -15,17 +15,24 @@
import struct
import base64
from ..com.gltf2_io import Accessor
class BinaryData():
"""Binary reader."""
def __new__(cls, *args, **kwargs):
raise RuntimeError("%s should not be instantiated" % cls)
# Note that this function is not used in Blender importer, but is kept in
# Source code to be used in any pipeline that want to manage gltf/glb file in python
@staticmethod
def get_binary_from_accessor(gltf, accessor_idx):
"""Get binary from accessor."""
accessor = gltf.data.accessors[accessor_idx]
data = BinaryData.get_buffer_view(gltf, accessor.buffer_view) # TODO initialize with 0 when not present!
if accessor.buffer_view is None:
return None
data = BinaryData.get_buffer_view(gltf, accessor.buffer_view)
accessor_offset = accessor.byte_offset
if accessor_offset is None:
@ -58,40 +65,78 @@ class BinaryData():
return gltf.accessor_cache[accessor_idx]
accessor = gltf.data.accessors[accessor_idx]
data = BinaryData.get_data_from_accessor_obj(gltf, accessor)
bufferView = gltf.data.buffer_views[accessor.buffer_view] # TODO initialize with 0 when not present!
buffer_data = BinaryData.get_binary_from_accessor(gltf, accessor_idx)
if cache:
gltf.accessor_cache[accessor_idx] = data
fmt_char = gltf.fmt_char_dict[accessor.component_type]
component_nb = gltf.component_nb_dict[accessor.type]
fmt = '<' + (fmt_char * component_nb)
stride_ = struct.calcsize(fmt)
# TODO data alignment stuff
return data
@staticmethod
def get_data_from_accessor_obj(gltf, accessor):
if accessor.buffer_view is not None:
bufferView = gltf.data.buffer_views[accessor.buffer_view]
buffer_data = BinaryData.get_buffer_view(gltf, accessor.buffer_view)
accessor_offset = accessor.byte_offset or 0
buffer_data = buffer_data[accessor_offset:]
fmt_char = gltf.fmt_char_dict[accessor.component_type]
component_nb = gltf.component_nb_dict[accessor.type]
fmt = '<' + (fmt_char * component_nb)
default_stride = struct.calcsize(fmt)
# Special layouts for certain formats; see the section about
# data alignment in the glTF 2.0 spec.
component_size = struct.calcsize('<' + fmt_char)
if accessor.type == 'MAT2' and component_size == 1:
fmt = '<FFxxFF'.replace('F', fmt_char)
default_stride = 8
elif accessor.type == 'MAT3' and component_size == 1:
fmt = '<FFFxFFFxFFF'.replace('F', fmt_char)
default_stride = 12
elif accessor.type == 'MAT3' and component_size == 2:
fmt = '<FFFxxFFFxxFFF'.replace('F', fmt_char)
default_stride = 24
stride = bufferView.byte_stride or default_stride
# Decode
unpack_from = struct.Struct(fmt).unpack_from
data = [
unpack_from(buffer_data, offset)
for offset in range(0, accessor.count*stride, stride)
]
if bufferView.byte_stride:
stride = bufferView.byte_stride
else:
stride = stride_
unpack_from = struct.Struct(fmt).unpack_from
data = [
unpack_from(buffer_data, offset)
for offset in range(0, accessor.count*stride, stride)
]
# No buffer view; initialize to zeros
component_nb = gltf.component_nb_dict[accessor.type]
data = [
(0,) * component_nb
for i in range(accessor.count)
]
if accessor.sparse:
sparse_indices_data = BinaryData.get_data_from_sparse(gltf, accessor.sparse, "indices")
sparse_values_values = BinaryData.get_data_from_sparse(
gltf,
accessor.sparse,
"values",
accessor.type,
accessor.component_type
)
sparse_indices_obj = Accessor.from_dict({
'count': accessor.sparse.count,
'bufferView': accessor.sparse.indices.buffer_view,
'byteOffset': accessor.sparse.indices.byte_offset or 0,
'componentType': accessor.sparse.indices.component_type,
'type': 'SCALAR',
})
sparse_values_obj = Accessor.from_dict({
'count': accessor.sparse.count,
'bufferView': accessor.sparse.values.buffer_view,
'byteOffset': accessor.sparse.values.byte_offset or 0,
'componentType': accessor.component_type,
'type': accessor.type,
})
sparse_indices = BinaryData.get_data_from_accessor_obj(gltf, sparse_indices_obj)
sparse_values = BinaryData.get_data_from_accessor_obj(gltf, sparse_values_obj)
# apply sparse
for cpt_idx, idx in enumerate(sparse_indices_data):
data[idx[0]] = sparse_values_values[cpt_idx]
# Apply sparse
for i in range(accessor.sparse.count):
data[sparse_indices[i][0]] = sparse_values[i]
# Normalization
if accessor.normalized:
@ -110,49 +155,6 @@ class BinaryData():
new_tuple += (float(i),)
data[idx] = new_tuple
if cache:
gltf.accessor_cache[accessor_idx] = data
return data
@staticmethod
def get_data_from_sparse(gltf, sparse, type_, type_val=None, comp_type=None):
"""Get data from sparse."""
if type_ == "indices":
bufferView = gltf.data.buffer_views[sparse.indices.buffer_view]
offset = sparse.indices.byte_offset
component_nb = gltf.component_nb_dict['SCALAR']
fmt_char = gltf.fmt_char_dict[sparse.indices.component_type]
elif type_ == "values":
bufferView = gltf.data.buffer_views[sparse.values.buffer_view]
offset = sparse.values.byte_offset
component_nb = gltf.component_nb_dict[type_val]
fmt_char = gltf.fmt_char_dict[comp_type]
if bufferView.buffer in gltf.buffers.keys():
buffer = gltf.buffers[bufferView.buffer]
else:
# load buffer
gltf.load_buffer(bufferView.buffer)
buffer = gltf.buffers[bufferView.buffer]
bin_data = buffer[bufferView.byte_offset + offset:bufferView.byte_offset + offset + bufferView.byte_length]
fmt = '<' + (fmt_char * component_nb)
stride_ = struct.calcsize(fmt)
# TODO data alignment stuff ?
if bufferView.byte_stride:
stride = bufferView.byte_stride
else:
stride = stride_
unpack_from = struct.Struct(fmt).unpack_from
data = [
unpack_from(bin_data, offset)
for offset in range(0, sparse.count*stride, stride)
]
return data
@staticmethod