glTF importer: big perf improvement

This commit is contained in:
Julien Duroure 2019-09-26 16:35:44 +02:00
parent a89261a31c
commit 06bb353c84
7 changed files with 332 additions and 391 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": (0, 9, 73),
"version": (0, 9, 74),
'blender': (2, 81, 6),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',

View File

@ -70,11 +70,8 @@ class BlenderWeightAnim():
values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output)
# retrieve number of targets
nb_targets = 0
for prim in gltf.data.meshes[gltf.data.nodes[node_idx].mesh].primitives:
if prim.targets:
if len(prim.targets) > nb_targets:
nb_targets = len(prim.targets)
pymesh = gltf.data.meshes[gltf.data.nodes[node_idx].mesh]
nb_targets = len(pymesh.shapekey_names)
if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
offset = nb_targets
@ -92,8 +89,8 @@ class BlenderWeightAnim():
group = action.groups[group_name]
for sk in range(nb_targets):
if gltf.shapekeys[sk] is not None: # Do not animate shapekeys not created
kb_name = obj.data.shape_keys.key_blocks[gltf.shapekeys[sk]].name
if pymesh.shapekey_names[sk] is not None: # Do not animate shapekeys not created
kb_name = pymesh.shapekey_names[sk]
data_path = "key_blocks[" + json.dumps(kb_name) + "].value"
fcurve = action.fcurves.new(data_path=data_path)
fcurve.group = group

View File

@ -109,10 +109,6 @@ class BlenderGlTF():
# Init is to False, and will be set to True during creation
gltf.animation_object = False
# Store shapekeys equivalent between target & shapekey index
# For example when no POSITION on target
gltf.shapekeys = {}
# Blender material
if gltf.data.materials:
for material in gltf.data.materials:
@ -281,6 +277,33 @@ class BlenderGlTF():
mesh.blender_name = None
mesh.is_weight_animated = False
# Calculate names for each mesh's shapekeys
for mesh in gltf.data.meshes:
mesh.shapekey_names = []
used_names = set()
for sk, target in enumerate(mesh.primitives[0].targets or []):
if 'POSITION' not in target:
mesh.shapekey_names.append(None)
continue
# Check if glTF file has some extras with targetNames. Otherwise
# use the name of the POSITION accessor on the first primitive.
shapekey_name = None
if mesh.extras is not None:
if 'targetNames' in mesh.extras and sk < len(mesh.extras['targetNames']):
shapekey_name = mesh.extras['targetNames'][sk]
if shapekey_name is None:
if gltf.data.accessors[target['POSITION']].name is not None:
shapekey_name = gltf.data.accessors[target['POSITION']].name
if shapekey_name is None:
shapekey_name = "target_" + str(sk)
shapekey_name = BlenderGlTF.find_unused_name(used_names, shapekey_name)
used_names.add(shapekey_name)
mesh.shapekey_names.append(shapekey_name)
@staticmethod
def find_unused_name(haystack, desired_name):
"""Finds a name not in haystack and <= 63 UTF-8 bytes.

View File

@ -14,10 +14,11 @@
import bpy
import bmesh
from mathutils import Vector
from .gltf2_blender_material import BlenderMaterial
from .gltf2_blender_primitive import BlenderPrimitive
from ...io.imp.gltf2_io_binary import BinaryData
from ...io.com.gltf2_io_color_management import color_linear_to_srgb
from ..com.gltf2_blender_conversion import loc_gltf_to_blender
@ -31,21 +32,48 @@ class BlenderMesh():
"""Mesh creation."""
pymesh = gltf.data.meshes[mesh_idx]
# Geometry
if pymesh.name:
mesh_name = pymesh.name
else:
mesh_name = "Mesh_" + str(mesh_idx)
# Create one bmesh, add all primitives to it, and then convert it to a
# mesh.
bme = bmesh.new()
mesh = bpy.data.meshes.new(mesh_name)
verts = []
edges = []
faces = []
# List of all the materials this mesh will use. The material each
# primitive uses is set by giving an index into this list.
materials = []
# Process all primitives
for prim in pymesh.primitives:
verts, edges, faces = BlenderPrimitive.create(gltf, prim, verts, edges, faces)
prim.blender_texcoord = {}
mesh.from_pydata(verts, edges, faces)
mesh.validate()
if prim.material is None:
material_idx = None
else:
pymaterial = gltf.data.materials[prim.material]
vertex_color = None
if 'COLOR_0' in prim.attributes:
vertex_color = 'COLOR_0'
# Create Blender material if needed
if vertex_color not in pymaterial.blender_material:
BlenderMaterial.create(gltf, prim.material, vertex_color)
material_name = pymaterial.blender_material[vertex_color]
material = bpy.data.materials[material_name]
try:
material_idx = materials.index(material)
except ValueError:
materials.append(material)
material_idx = len(materials) - 1
BlenderPrimitive.add_primitive_to_bmesh(gltf, bme, pymesh, prim, material_idx)
name = pymesh.name or 'Mesh_' + str(mesh_idx)
mesh = bpy.data.meshes.new(name)
BlenderMesh.bmesh_to_mesh(gltf, pymesh, bme, mesh)
bme.free()
for material in materials:
mesh.materials.append(material)
mesh.update()
pymesh.blender_name = mesh.name
@ -53,31 +81,7 @@ class BlenderMesh():
@staticmethod
def set_mesh(gltf, pymesh, mesh, obj):
"""Set all data after mesh creation."""
# Normals
offset = 0
custom_normals = [[0.0, 0.0, 0.0]] * len(mesh.vertices)
if gltf.import_settings['import_shading'] == "NORMALS":
mesh.create_normals_split()
for prim in pymesh.primitives:
offset = BlenderPrimitive.set_normals(gltf, prim, mesh, offset, custom_normals)
mesh.update()
# manage UV
offset = 0
for prim in pymesh.primitives:
offset = BlenderPrimitive.set_UV(gltf, prim, obj, mesh, offset)
mesh.update()
# Normals, now that every update is done
if gltf.import_settings['import_shading'] == "NORMALS":
mesh.normals_split_custom_set_from_vertices(custom_normals)
mesh.use_auto_smooth = True
"""Sets mesh data after creation."""
# Object and UV are now created, we can set UVMap into material
for prim in pymesh.primitives:
vertex_color = None
@ -85,139 +89,88 @@ class BlenderMesh():
vertex_color = 'COLOR_0'
BlenderPrimitive.set_UV_in_mat(gltf, prim, obj, vertex_color)
# Assign materials to mesh
offset = 0
cpt_index_mat = 0
bm = bmesh.new()
bm.from_mesh(obj.data)
bm.faces.ensure_lookup_table()
for prim in pymesh.primitives:
offset, cpt_index_mat = BlenderPrimitive.assign_material(gltf, prim, obj, bm, offset, cpt_index_mat)
bm.to_mesh(obj.data)
bm.free()
# Create shapekeys if needed
max_shape_to_create = 0
for prim in pymesh.primitives:
if prim.targets:
if len(prim.targets) > max_shape_to_create:
max_shape_to_create = len(prim.targets)
# Create basis shape key
if max_shape_to_create > 0:
obj.shape_key_add(name="Basis")
current_shapekey_index = 0
for sk in range(max_shape_to_create):
# Check if this target has POSITION
if 'POSITION' not in prim.targets[sk].keys():
gltf.shapekeys[sk] = None
continue
# Check if glTF file has some extras with targetNames
shapekey_name = None
if pymesh.extras is not None:
if 'targetNames' in pymesh.extras.keys() and sk < len(pymesh.extras['targetNames']):
shapekey_name = pymesh.extras['targetNames'][sk]
if shapekey_name is None:
shapekey_name = "target_" + str(sk)
obj.shape_key_add(name=shapekey_name)
current_shapekey_index += 1
offset_idx = 0
for prim in pymesh.primitives:
if prim.targets is None:
continue
if sk >= len(prim.targets):
continue
bm = bmesh.new()
bm.from_mesh(mesh)
shape_layer = bm.verts.layers.shape[current_shapekey_index]
gltf.shapekeys[sk] = current_shapekey_index
original_pos = BinaryData.get_data_from_accessor(gltf, prim.targets[sk]['POSITION'])
tmp_indices = {}
tmp_idx = 0
pos = []
for i in prim.tmp_indices:
if i[0] not in tmp_indices.keys():
tmp_indices[i[0]] = tmp_idx
tmp_idx += 1
pos.append(original_pos[i[0]])
for vert in bm.verts:
if vert.index not in range(offset_idx, offset_idx + prim.vertices_length):
continue
shape = vert[shape_layer]
co = loc_gltf_to_blender(list(pos[vert.index - offset_idx]))
shape.x = obj.data.vertices[vert.index].co.x + co[0]
shape.y = obj.data.vertices[vert.index].co.y + co[1]
shape.z = obj.data.vertices[vert.index].co.z + co[2]
bm.to_mesh(obj.data)
bm.free()
offset_idx += prim.vertices_length
# set default weights for shape keys, and names, if not set by convention on extras data
if pymesh.weights is not None:
for i in range(max_shape_to_create):
if i < len(pymesh.weights):
if gltf.shapekeys[i] is None: # No default value if shapekeys was not created
continue
obj.data.shape_keys.key_blocks[gltf.shapekeys[i]].value = pymesh.weights[i]
if shapekey_name is None: # No names set for now
if gltf.data.accessors[pymesh.primitives[0].targets[i]['POSITION']].name is not None:
obj.data.shape_keys.key_blocks[gltf.shapekeys[i]].name = \
gltf.data.accessors[pymesh.primitives[0].targets[i]['POSITION']].name
for i in range(len(pymesh.weights)):
if pymesh.shapekey_names[i] is None: # No default value if shapekeys was not created
continue
obj.data.shape_keys.key_blocks[pymesh.shapekey_names[i]].value = pymesh.weights[i]
# Apply vertex color.
vertex_color = None
offset = 0
@staticmethod
def bmesh_to_mesh(gltf, pymesh, bme, mesh):
bme.to_mesh(mesh)
# Unfortunately need to do shapekeys/normals/smoothing ourselves.
# Shapekeys
if len(bme.verts.layers.shape) != 0:
# The only way I could find to create a shape key was to temporarily
# parent mesh to an object and use obj.shape_key_add.
tmp_ob = None
try:
tmp_ob = bpy.data.objects.new('##gltf-import:tmp-object##', mesh)
tmp_ob.shape_key_add(name='Basis')
mesh.shape_keys.name = mesh.name
for layer_name in bme.verts.layers.shape.keys():
tmp_ob.shape_key_add(name=layer_name)
key_block = mesh.shape_keys.key_blocks[layer_name]
layer = bme.verts.layers.shape[layer_name]
for i, v in enumerate(bme.verts):
key_block.data[i].co = v[layer]
finally:
if tmp_ob:
bpy.data.objects.remove(tmp_ob)
# Normals
mesh.update()
if gltf.import_settings['import_shading'] == "NORMALS":
mesh.create_normals_split()
# use_smooth for faces
face_idx = 0
for prim in pymesh.primitives:
if 'COLOR_0' in prim.attributes.keys():
# Create vertex color, once only per object
if vertex_color is None:
vertex_color = obj.data.vertex_colors.new(name="COLOR_0")
if 'NORMAL' not in prim.attributes:
face_idx += prim.num_faces
continue
original_color_data = BinaryData.get_data_from_accessor(gltf, prim.attributes['COLOR_0'])
tmp_indices = {}
tmp_idx = 0
color_data = []
for i in prim.tmp_indices:
if i[0] not in tmp_indices.keys():
tmp_indices[i[0]] = tmp_idx
tmp_idx += 1
color_data.append(original_color_data[i[0]])
for poly in mesh.polygons:
if gltf.import_settings['import_shading'] == "FLAT":
for fi in range(face_idx, face_idx + prim.num_faces):
mesh.polygons[fi].use_smooth = False
elif gltf.import_settings['import_shading'] == "SMOOTH":
for fi in range(face_idx, face_idx + prim.num_faces):
mesh.polygons[fi].use_smooth = True
elif gltf.import_settings['import_shading'] == "NORMALS":
for fi in range(face_idx, face_idx + prim.num_faces):
poly = mesh.polygons[fi]
calc_norm_vertices = []
for loop_idx in range(poly.loop_start, poly.loop_start + poly.loop_total):
vert_idx = mesh.loops[loop_idx].vertex_index
if vert_idx in range(offset, offset + prim.vertices_length):
cpt_idx = vert_idx - offset
# Need to convert from linear (glTF to sRGB (blender))
# check dimension, and add alpha if needed
if len(color_data[cpt_idx]) == 3:
srgb_color = [
color_linear_to_srgb(color_data[cpt_idx][0]),
color_linear_to_srgb(color_data[cpt_idx][1]),
color_linear_to_srgb(color_data[cpt_idx][2]),
1.0]
else:
srgb_color = [
color_linear_to_srgb(color_data[cpt_idx][0]),
color_linear_to_srgb(color_data[cpt_idx][1]),
color_linear_to_srgb(color_data[cpt_idx][2]),
color_data[cpt_idx][3]]
vertex_color.data[loop_idx].color = srgb_color
offset = offset + prim.vertices_length
calc_norm_vertices.append(vert_idx)
if len(calc_norm_vertices) == 3:
# Calcul normal
vert0 = mesh.vertices[calc_norm_vertices[0]].co
vert1 = mesh.vertices[calc_norm_vertices[1]].co
vert2 = mesh.vertices[calc_norm_vertices[2]].co
calc_normal = (vert1 - vert0).cross(vert2 - vert0).normalized()
# Compare normal to vertex normal
for i in calc_norm_vertices:
vec = Vector(bme.verts[i].normal)
if not calc_normal.dot(vec) > 0.9999999:
poly.use_smooth = True
break
else:
# shouldn't happen
pass
face_idx += prim.num_faces
# Custom normals, now that every update is done
if gltf.import_settings['import_shading'] == "NORMALS":
custom_normals = [v.normal for v in bme.verts]
mesh.normals_split_custom_set_from_vertices(custom_normals)
mesh.use_auto_smooth = True

View File

@ -18,70 +18,201 @@ from mathutils import Vector
from .gltf2_blender_material import BlenderMaterial
from ..com.gltf2_blender_conversion import loc_gltf_to_blender
from ...io.imp.gltf2_io_binary import BinaryData
from ...io.com.gltf2_io_color_management import color_linear_to_srgb
from ...io.com import gltf2_io_debug
MAX_NUM_COLOR_SETS = 8
MAX_NUM_TEXCOORD_SETS = 8
class BlenderPrimitive():
"""Blender Primitive."""
def __new__(cls, *args, **kwargs):
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
def create(gltf, pyprimitive, verts, edges, faces):
"""Primitive creation."""
pyprimitive.blender_texcoord = {}
def get_layer(bme_layers, name):
if name not in bme_layers:
return bme_layers.new(name)
return bme_layers[name]
@staticmethod
def add_primitive_to_bmesh(gltf, bme, pymesh, pyprimitive, material_index):
attributes = pyprimitive.attributes
if 'POSITION' not in attributes:
pyprimitive.num_faces = 0
return
positions = BinaryData.get_data_from_accessor(gltf, attributes['POSITION'])
current_length = len(verts)
pos = BinaryData.get_data_from_accessor(gltf, pyprimitive.attributes['POSITION'])
if pyprimitive.indices is not None:
indices = BinaryData.get_data_from_accessor(gltf, pyprimitive.indices)
indices = [i[0] for i in indices]
else:
indices = [(i,) for i in range(len(pos))]
indices = list(range(len(positions)))
pyprimitive.tmp_indices = indices
bme_verts = bme.verts
bme_edges = bme.edges
bme_faces = bme.faces
# Manage only vertices that are in indices tab
indice_equivalents = {}
new_pos = []
new_pos_idx = 0
for i in indices:
if i[0] not in indice_equivalents.keys():
indice_equivalents[i[0]] = new_pos_idx
new_pos.append(pos[i[0]])
new_pos_idx += 1
# Every vertex has an index into the primitive's attribute arrays and a
# *different* index into the BMesh's list of verts. Call the first one the
# pidx and the second the bidx. Need to keep them straight!
prim_verts = [loc_gltf_to_blender(vert) for vert in new_pos]
# The pidx of all the vertices that are actually used by the primitive (only
# indices that appear in the pyprimitive.indices list are actually used)
used_pidxs = set(indices)
# Contains a pair (bidx, pidx) for every vertex in the primitive
vert_idxs = []
# pidx_to_bidx[pidx] will be the bidx of the vertex with that pidx (or -1 if
# unused)
pidx_to_bidx = [-1] * len(positions)
bidx = len(bme_verts)
for pidx in range(0, len(positions)):
if pidx in used_pidxs:
bme_verts.new(positions[pidx])
vert_idxs.append((bidx, pidx))
pidx_to_bidx[pidx] = bidx
bidx += 1
bme_verts.ensure_lookup_table()
mode = 4 if pyprimitive.mode is None else pyprimitive.mode
prim_edges, prim_faces = BlenderPrimitive.edges_and_faces(mode, indices)
# Add edges/faces to bmesh
mode = pyprimitive.mode or 4
edges, faces = BlenderPrimitive.edges_and_faces(mode, indices)
# NOTE: edges and vertices are in terms of pidxs!
for edge in edges:
try:
bme_edges.new((
bme_verts[pidx_to_bidx[edge[0]]],
bme_verts[pidx_to_bidx[edge[1]]],
))
except ValueError:
# Ignores duplicate/degenerate edges
pass
pyprimitive.num_faces = 0
for face in faces:
try:
face = bme_faces.new(tuple(
bme_verts[pidx_to_bidx[i]]
for i in face
))
verts.extend(prim_verts)
pyprimitive.vertices_length = len(prim_verts)
edges.extend(
tuple(indice_equivalents[y] + current_length for y in e)
for e in prim_edges
)
faces.extend(
tuple(indice_equivalents[y] + current_length for y in f)
for f in prim_faces
)
if material_index is not None:
face.material_index = material_index
# manage material of primitive
if pyprimitive.material is not None:
pyprimitive.num_faces += 1
vertex_color = None
if 'COLOR_0' in pyprimitive.attributes.keys():
vertex_color = 'COLOR_0'
except ValueError:
# Ignores duplicate/degenerate faces
pass
# Create Blender material if needed
if vertex_color is None:
if None not in gltf.data.materials[pyprimitive.material].blender_material.keys():
BlenderMaterial.create(gltf, pyprimitive.material, vertex_color)
else:
if vertex_color not in gltf.data.materials[pyprimitive.material].blender_material.keys():
BlenderMaterial.create(gltf, pyprimitive.material, vertex_color)
# Set normals
if 'NORMAL' in attributes:
normals = BinaryData.get_data_from_accessor(gltf, attributes['NORMAL'])
for bidx, pidx in vert_idxs:
bme_verts[bidx].normal = normals[pidx]
# Set vertex colors. Add them in the order COLOR_0, COLOR_1, etc.
set_num = 0
while 'COLOR_%d' % set_num in attributes:
if set_num >= MAX_NUM_COLOR_SETS:
gltf2_io_debug.print_console("WARNING",
"too many color sets; COLOR_%d will be ignored" % set_num
)
break
return verts, edges, faces
layer_name = 'COLOR_%d' % set_num
layer = BlenderPrimitive.get_layer(bme.loops.layers.color, layer_name)
colors = BinaryData.get_data_from_accessor(gltf, attributes[layer_name])
# Check whether Blender takes RGB or RGBA colors (old versions only take RGB)
num_components = len(colors[0])
blender_num_components = len(bme_verts[0].link_loops[0][layer])
if num_components == 3 and blender_num_components == 4:
# RGB -> RGBA
colors = [color+(1,) for color in colors]
if num_components == 4 and blender_num_components == 3:
# RGBA -> RGB
colors = [color[:3] for color in colors]
gltf2_io_debug.print_console("WARNING",
"this Blender doesn't support RGBA vertex colors; dropping A"
)
for bidx, pidx in vert_idxs:
for loop in bme_verts[bidx].link_loops:
loop[layer] = tuple(
color_linear_to_srgb(c)
for c in colors[pidx]
)
set_num += 1
# Set texcoords
set_num = 0
while 'TEXCOORD_%d' % set_num in attributes:
if set_num >= MAX_NUM_TEXCOORD_SETS:
gltf2_io_debug.print_console("WARNING",
"too many UV sets; TEXCOORD_%d will be ignored" % set_num
)
break
layer_name = 'TEXCOORD_%d' % set_num
layer = BlenderPrimitive.get_layer(bme.loops.layers.uv, layer_name)
pyprimitive.blender_texcoord[set_num] = layer_name
uvs = BinaryData.get_data_from_accessor(gltf, attributes[layer_name])
for bidx, pidx in vert_idxs:
# UV transform
u, v = uvs[pidx]
uv = (u, 1 - v)
for loop in bme_verts[bidx].link_loops:
loop[layer].uv = uv
set_num += 1
# Set joints/weights for skinning (multiple sets allow > 4 influences)
joint_sets = []
weight_sets = []
set_num = 0
while 'JOINTS_%d' % set_num in attributes and 'WEIGHTS_%d' % set_num in attributes:
joint_data = BinaryData.get_data_from_accessor(gltf, attributes['JOINTS_%d' % set_num])
weight_data = BinaryData.get_data_from_accessor(gltf, attributes['WEIGHTS_%d' % set_num])
joint_sets.append(joint_data)
weight_sets.append(weight_data)
set_num += 1
if joint_sets:
layer = BlenderPrimitive.get_layer(bme.verts.layers.deform, 'Vertex Weights')
for joint_set, weight_set in zip(joint_sets, weight_sets):
for bidx, pidx in vert_idxs:
for j in range(0, 4):
weight = weight_set[pidx][j]
if weight != 0.0:
joint = joint_set[pidx][j]
bme_verts[bidx][layer][joint] = weight
# Set morph target positions (no normals/tangents)
for sk, target in enumerate(pyprimitive.targets or []):
if pymesh.shapekey_names[sk] is None:
continue
layer_name = pymesh.shapekey_names[sk]
layer = BlenderPrimitive.get_layer(bme.verts.layers.shape, layer_name)
morph_positions = BinaryData.get_data_from_accessor(gltf, target['POSITION'])
for bidx, pidx in vert_idxs:
bme_verts[bidx][layer] = (
Vector(positions[pidx]) +
Vector(morph_positions[pidx])
)
@staticmethod
def edges_and_faces(mode, indices):
@ -100,7 +231,7 @@ class BlenderPrimitive():
# / /
# 0 2
es = [
(indices[i][0], indices[i + 1][0])
(indices[i], indices[i + 1])
for i in range(0, len(indices), 2)
]
elif mode == 2:
@ -109,17 +240,17 @@ class BlenderPrimitive():
# / \
# 0-------3
es = [
(indices[i][0], indices[i + 1][0])
(indices[i], indices[i + 1])
for i in range(0, len(indices) - 1)
]
es.append((indices[-1][0], indices[0][0]))
es.append((indices[-1], indices[0]))
elif mode == 3:
# LINE STRIP
# 1---2
# / \
# 0 3
es = [
(indices[i][0], indices[i + 1][0])
(indices[i], indices[i + 1])
for i in range(0, len(indices) - 1)
]
elif mode == 4:
@ -128,7 +259,7 @@ class BlenderPrimitive():
# / \ / \
# 0---1 4---5
fs = [
(indices[i][0], indices[i + 1][0], indices[i + 2][0])
(indices[i], indices[i + 1], indices[i + 2])
for i in range(0, len(indices), 3)
]
elif mode == 5:
@ -140,7 +271,7 @@ class BlenderPrimitive():
even = i % 2 == 0
return xs if even else (xs[0], xs[2], xs[1])
fs = [
alternate(i, (indices[i][0], indices[i + 1][0], indices[i + 2][0]))
alternate(i, (indices[i], indices[i + 1], indices[i + 2]))
for i in range(0, len(indices) - 2)
]
elif mode == 6:
@ -149,7 +280,7 @@ class BlenderPrimitive():
# / \ / \
# 4---0---1
fs = [
(indices[0][0], indices[i][0], indices[i + 1][0])
(indices[0], indices[i], indices[i + 1])
for i in range(1, len(indices) - 1)
]
else:
@ -157,86 +288,7 @@ class BlenderPrimitive():
return es, fs
def set_normals(gltf, pyprimitive, mesh, offset, custom_normals):
"""Set Normal."""
if 'NORMAL' in pyprimitive.attributes.keys():
original_normal_data = BinaryData.get_data_from_accessor(gltf, pyprimitive.attributes['NORMAL'])
tmp_indices = {}
tmp_idx = 0
normal_data = []
for i in pyprimitive.tmp_indices:
if i[0] not in tmp_indices.keys():
tmp_indices[i[0]] = tmp_idx
tmp_idx += 1
normal_data.append(original_normal_data[i[0]])
for poly in mesh.polygons:
if gltf.import_settings['import_shading'] == "NORMALS":
calc_norm_vertices = []
for loop_idx in range(poly.loop_start, poly.loop_start + poly.loop_total):
vert_idx = mesh.loops[loop_idx].vertex_index
if vert_idx in range(offset, offset + pyprimitive.vertices_length):
cpt_vert = vert_idx - offset
mesh.vertices[vert_idx].normal = normal_data[cpt_vert]
custom_normals[vert_idx] = list(normal_data[cpt_vert])
calc_norm_vertices.append(vert_idx)
if len(calc_norm_vertices) == 3:
# Calcul normal
vert0 = mesh.vertices[calc_norm_vertices[0]].co
vert1 = mesh.vertices[calc_norm_vertices[1]].co
vert2 = mesh.vertices[calc_norm_vertices[2]].co
calc_normal = (vert1 - vert0).cross(vert2 - vert0).normalized()
# Compare normal to vertex normal
for i in calc_norm_vertices:
cpt_vert = vert_idx - offset
vec = Vector(
(normal_data[cpt_vert][0], normal_data[cpt_vert][1], normal_data[cpt_vert][2])
)
if not calc_normal.dot(vec) > 0.9999999:
poly.use_smooth = True
break
elif gltf.import_settings['import_shading'] == "FLAT":
poly.use_smooth = False
elif gltf.import_settings['import_shading'] == "SMOOTH":
poly.use_smooth = True
else:
pass # Should not happen
offset = offset + pyprimitive.vertices_length
return offset
def set_UV(gltf, pyprimitive, obj, mesh, offset):
"""Set UV Map."""
for texcoord in [attr for attr in pyprimitive.attributes.keys() if attr[:9] == "TEXCOORD_"]:
if texcoord not in mesh.uv_layers:
mesh.uv_layers.new(name=texcoord)
pyprimitive.blender_texcoord[int(texcoord[9:])] = texcoord
original_texcoord_data = BinaryData.get_data_from_accessor(gltf, pyprimitive.attributes[texcoord])
tmp_indices = {}
tmp_idx = 0
texcoord_data = []
for i in pyprimitive.tmp_indices:
if i[0] not in tmp_indices.keys():
tmp_indices[i[0]] = tmp_idx
tmp_idx += 1
texcoord_data.append(original_texcoord_data[i[0]])
for poly in mesh.polygons:
for loop_idx in range(poly.loop_start, poly.loop_start + poly.loop_total):
vert_idx = mesh.loops[loop_idx].vertex_index
if vert_idx in range(offset, offset + pyprimitive.vertices_length):
obj.data.uv_layers[texcoord].data[loop_idx].uv = \
Vector((texcoord_data[vert_idx - offset][0], 1 - texcoord_data[vert_idx - offset][1]))
offset = offset + pyprimitive.vertices_length
return offset
@staticmethod
def set_UV_in_mat(gltf, pyprimitive, obj, vertex_color):
"""After nodetree creation, set UVMap in nodes."""
if pyprimitive.material is None:
@ -267,21 +319,3 @@ class BlenderPrimitive():
[gltf.TEXTURE, gltf.TEXTURE_FACTOR]:
BlenderMaterial.set_uvmap(gltf, pyprimitive.material, pyprimitive, obj, vertex_color)
def assign_material(gltf, pyprimitive, obj, bm, offset, cpt_index_mat):
"""Assign material to faces of primitives."""
if pyprimitive.material is not None:
vertex_color = None
if 'COLOR_0' in pyprimitive.attributes.keys():
vertex_color = 'COLOR_0'
obj.data.materials.append(bpy.data.materials[gltf.data.materials[pyprimitive.material].blender_material[vertex_color]])
for vert in bm.verts:
if vert.index in range(offset, offset + pyprimitive.vertices_length):
for loop in vert.link_loops:
face = loop.face.index
bm.faces[face].material_index = cpt_index_mat
cpt_index_mat += 1
offset = offset + pyprimitive.vertices_length
return offset, cpt_index_mat

View File

@ -85,10 +85,6 @@ class BlenderScene():
if hasattr(skin, "node_ids"):
BlenderSkin.create_vertex_groups(gltf, skin_id)
for skin_id, skin in enumerate(gltf.data.skins):
if hasattr(skin, "node_ids"):
BlenderSkin.assign_vertex_groups(gltf, skin_id)
for skin_id, skin in enumerate(gltf.data.skins):
if hasattr(skin, "node_ids"):
BlenderSkin.create_armature_modifiers(gltf, skin_id)

View File

@ -144,68 +144,6 @@ class BlenderSkin():
for bone in pyskin.joints:
obj.vertex_groups.new(name=gltf.data.nodes[bone].blender_bone_name)
@staticmethod
def assign_vertex_groups(gltf, skin_id):
"""Assign vertex groups to vertices."""
pyskin = gltf.data.skins[skin_id]
for node_id in pyskin.node_ids:
node = gltf.data.nodes[node_id]
obj = bpy.data.objects[node.blender_object]
offset = 0
for prim in gltf.data.meshes[node.mesh].primitives:
idx_already_done = {}
if 'JOINTS_0' in prim.attributes.keys() and 'WEIGHTS_0' in prim.attributes.keys():
original_joint_ = BinaryData.get_data_from_accessor(gltf, prim.attributes['JOINTS_0'])
original_weight_ = BinaryData.get_data_from_accessor(gltf, prim.attributes['WEIGHTS_0'])
tmp_indices = {}
tmp_idx = 0
weight_ = []
for i in prim.tmp_indices:
if i[0] not in tmp_indices.keys():
tmp_indices[i[0]] = tmp_idx
tmp_idx += 1
weight_.append(original_weight_[i[0]])
tmp_indices = {}
tmp_idx = 0
joint_ = []
for i in prim.tmp_indices:
if i[0] not in tmp_indices.keys():
tmp_indices[i[0]] = tmp_idx
tmp_idx += 1
joint_.append(original_joint_[i[0]])
for poly in obj.data.polygons:
for loop_idx in range(poly.loop_start, poly.loop_start + poly.loop_total):
vert_idx = obj.data.loops[loop_idx].vertex_index
if vert_idx in idx_already_done.keys():
continue
idx_already_done[vert_idx] = True
if vert_idx in range(offset, offset + prim.vertices_length):
tab_index = vert_idx - offset
cpt = 0
for joint_idx in joint_[tab_index]:
weight_val = weight_[tab_index][cpt]
if weight_val != 0.0: # It can be a problem to assign weights of 0
# for bone index 0, if there is always 4 indices in joint_
# tuple
group = obj.vertex_groups[gltf.data.nodes[
pyskin.joints[joint_idx]
].blender_bone_name]
group.add([vert_idx], weight_val, 'REPLACE')
cpt += 1
else:
gltf.log.error("No Skinning ?????") # TODO
offset = offset + prim.vertices_length
@staticmethod
def create_armature_modifiers(gltf, skin_id):
"""Create Armature modifier."""