glTF importer: big perf improvement
This commit is contained in:
parent
a89261a31c
commit
06bb353c84
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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."""
|
||||
|
|
Loading…
Reference in New Issue