glTF importer: add option to glue pieces of a mesh together

Thanks scurest!
This commit is contained in:
Julien Duroure 2020-07-21 21:18:35 +02:00
parent 9313b3a155
commit c7eda7cb49
2 changed files with 111 additions and 1 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, 3, 39),
"version": (1, 3, 40),
'blender': (2, 90, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
@ -862,6 +862,18 @@ class ImportGLTF2(Operator, ImportHelper):
default=True
)
merge_vertices: BoolProperty(
name='Merge Vertices',
description=(
'The glTF format requires discontinuous normals, UVs, and '
'other vertex attributes to be stored as separate vertices, '
'as required for rendering on typical graphics hardware.\n'
'This option attempts to combine co-located vertices where possible.\n'
'Currently cannot combine verts with different normals'
),
default=False,
)
import_shading: EnumProperty(
name="Shading",
items=(("NORMALS", "Use Normal Data", ""),
@ -906,6 +918,7 @@ class ImportGLTF2(Operator, ImportHelper):
layout.use_property_decorate = False # No animation.
layout.prop(self, 'import_pack_images')
layout.prop(self, 'merge_vertices')
layout.prop(self, 'import_shading')
layout.prop(self, 'guess_original_bind_pose')
layout.prop(self, 'bone_heuristic')

View File

@ -217,6 +217,14 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
# the cache now that all prims are done.
gltf.decode_accessor_cache = {}
if gltf.import_settings['merge_vertices']:
vert_locs, vert_normals, vert_joints, vert_weights, \
sk_vert_locs, loop_vidxs, edge_vidxs = \
merge_duplicate_verts(
vert_locs, vert_normals, vert_joints, vert_weights, \
sk_vert_locs, loop_vidxs, edge_vidxs\
)
# ---------------
# Convert all the arrays glTF -> Blender
@ -537,3 +545,92 @@ def skin_into_bind_pose(gltf, skin_idx, vert_joints, vert_weights, locs, vert_no
def mul_mats_vecs(mats, vecs):
"""Given [m1,m2,...] and [v1,v2,...], returns [m1@v1,m2@v2,...]. 3D only."""
return np.matmul(mats, vecs.reshape(len(vecs), 3, 1)).reshape(len(vecs), 3)
def merge_duplicate_verts(vert_locs, vert_normals, vert_joints, vert_weights, sk_vert_locs, loop_vidxs, edge_vidxs):
# This function attempts to invert the splitting done when exporting to
# glTF. Welds together verts with the same per-vert data (but possibly
# different per-loop data).
#
# Ideally normals would be treated as per-loop data, but that has problems,
# so we currently treat the normal as per-vert.
#
# Strategy is simple: put all the per-vert data into an array of structs
# ("dots"), dedupe with np.unique, then take all the data back out.
# Very often two verts that "morally" should be merged will have normals
# with very small differences. Round off the normals to smooth this over.
if len(vert_normals) != 0:
vert_normals *= 50000
vert_normals[:] = np.trunc(vert_normals)
vert_normals *= (1/50000)
dot_fields = [('x', np.float32), ('y', np.float32), ('z', np.float32)]
if len(vert_normals) != 0:
dot_fields += [('nx', np.float32), ('ny', np.float32), ('nz', np.float32)]
for i, _ in enumerate(vert_joints):
dot_fields += [
('joint%dx' % i, np.uint32), ('joint%dy' % i, np.uint32),
('joint%dz' % i, np.uint32), ('joint%dw' % i, np.uint32),
('weight%dx' % i, np.float32), ('weight%dy' % i, np.float32),
('weight%dz' % i, np.float32), ('weight%dw' % i, np.float32),
]
for i, _ in enumerate(sk_vert_locs):
dot_fields += [
('sk%dx' % i, np.float32), ('sk%dy' % i, np.float32), ('sk%dz' % i, np.float32),
]
dots = np.empty(len(vert_locs), dtype=np.dtype(dot_fields))
dots['x'] = vert_locs[:, 0]
dots['y'] = vert_locs[:, 1]
dots['z'] = vert_locs[:, 2]
if len(vert_normals) != 0:
dots['nx'] = vert_normals[:, 0]
dots['ny'] = vert_normals[:, 1]
dots['nz'] = vert_normals[:, 2]
for i, (joints, weights) in enumerate(zip(vert_joints, vert_weights)):
dots['joint%dx' % i] = joints[:, 0]
dots['joint%dy' % i] = joints[:, 1]
dots['joint%dz' % i] = joints[:, 2]
dots['joint%dw' % i] = joints[:, 3]
dots['weight%dx' % i] = weights[:, 0]
dots['weight%dy' % i] = weights[:, 1]
dots['weight%dz' % i] = weights[:, 2]
dots['weight%dw' % i] = weights[:, 3]
for i, locs in enumerate(sk_vert_locs):
dots['sk%dx' % i] = locs[:, 0]
dots['sk%dy' % i] = locs[:, 1]
dots['sk%dz' % i] = locs[:, 2]
unique_dots, inv_indices = np.unique(dots, return_inverse=True)
loop_vidxs = inv_indices[loop_vidxs]
edge_vidxs = inv_indices[edge_vidxs]
vert_locs = np.empty((len(unique_dots), 3), dtype=np.float32)
vert_locs[:, 0] = unique_dots['x']
vert_locs[:, 1] = unique_dots['y']
vert_locs[:, 2] = unique_dots['z']
if len(vert_normals) != 0:
vert_normals = np.empty((len(unique_dots), 3), dtype=np.float32)
vert_normals[:, 0] = unique_dots['nx']
vert_normals[:, 1] = unique_dots['ny']
vert_normals[:, 2] = unique_dots['nz']
for i in range(len(vert_joints)):
vert_joints[i] = np.empty((len(unique_dots), 4), dtype=np.uint32)
vert_joints[i][:, 0] = unique_dots['joint%dx' % i]
vert_joints[i][:, 1] = unique_dots['joint%dy' % i]
vert_joints[i][:, 2] = unique_dots['joint%dz' % i]
vert_joints[i][:, 3] = unique_dots['joint%dw' % i]
vert_weights[i] = np.empty((len(unique_dots), 4), dtype=np.float32)
vert_weights[i][:, 0] = unique_dots['weight%dx' % i]
vert_weights[i][:, 1] = unique_dots['weight%dy' % i]
vert_weights[i][:, 2] = unique_dots['weight%dz' % i]
vert_weights[i][:, 3] = unique_dots['weight%dw' % i]
for i in range(len(sk_vert_locs)):
sk_vert_locs[i] = np.empty((len(unique_dots), 3), dtype=np.float32)
sk_vert_locs[i][:, 0] = unique_dots['sk%dx' % i]
sk_vert_locs[i][:, 1] = unique_dots['sk%dy' % i]
sk_vert_locs[i][:, 2] = unique_dots['sk%dz' % i]
return vert_locs, vert_normals, vert_joints, vert_weights, sk_vert_locs, loop_vidxs, edge_vidxs