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:
Diffstat (limited to 'io_scene_gltf2/blender/exp/gltf2_blender_extract.py')
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_extract.py619
1 files changed, 0 insertions, 619 deletions
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
deleted file mode 100755
index bdea2c6f..00000000
--- a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
+++ /dev/null
@@ -1,619 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0
-# Copyright 2018-2021 The glTF-Blender-IO authors.
-
-import numpy as np
-from mathutils import Vector
-
-from . import gltf2_blender_export_keys
-from ...io.com.gltf2_io_debug import print_console
-from io_scene_gltf2.blender.exp import gltf2_blender_gather_nodes
-
-
-def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups, modifiers, export_settings):
- """Extract primitives from a mesh."""
- print_console('INFO', 'Extracting primitive: ' + blender_mesh.name)
-
- blender_object = None
- if uuid_for_skined_data:
- blender_object = export_settings['vtree'].nodes[uuid_for_skined_data].blender_object
-
- use_normals = export_settings[gltf2_blender_export_keys.NORMALS]
- if use_normals:
- blender_mesh.calc_normals_split()
-
- use_tangents = False
- if use_normals and export_settings[gltf2_blender_export_keys.TANGENTS]:
- if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0:
- try:
- blender_mesh.calc_tangents()
- use_tangents = True
- except Exception:
- print_console('WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.')
-
- tex_coord_max = 0
- if export_settings[gltf2_blender_export_keys.TEX_COORDS]:
- if blender_mesh.uv_layers.active:
- tex_coord_max = len(blender_mesh.uv_layers)
-
- color_max = 0
- if export_settings[gltf2_blender_export_keys.COLORS]:
- color_max = len(blender_mesh.vertex_colors)
-
- colors_attributes = []
- rendered_color_idx = blender_mesh.attributes.render_color_index
-
- if color_max > 0:
- colors_attributes.append(rendered_color_idx)
- # Then find other ones
- colors_attributes.extend([
- i for i in range(len(blender_mesh.color_attributes)) if i != rendered_color_idx \
- and blender_mesh.vertex_colors.find(blender_mesh.color_attributes[i].name) != -1
- ])
-
-
- armature = None
- skin = None
- if blender_vertex_groups and export_settings[gltf2_blender_export_keys.SKINS]:
- if modifiers is not None:
- modifiers_dict = {m.type: m for m in modifiers}
- if "ARMATURE" in modifiers_dict:
- modifier = modifiers_dict["ARMATURE"]
- armature = modifier.object
-
- # Skin must be ignored if the object is parented to a bone of the armature
- # (This creates an infinite recursive error)
- # So ignoring skin in that case
- is_child_of_arma = (
- armature and
- blender_object and
- blender_object.parent_type == "BONE" and
- blender_object.parent.name == armature.name
- )
- if is_child_of_arma:
- armature = None
-
- if armature:
- skin = gltf2_blender_gather_nodes.gather_skin(uuid_for_skined_data, export_settings)
- if not skin:
- armature = None
-
- use_morph_normals = use_normals and export_settings[gltf2_blender_export_keys.MORPH_NORMAL]
- use_morph_tangents = use_morph_normals and use_tangents and export_settings[gltf2_blender_export_keys.MORPH_TANGENT]
-
- key_blocks = []
- # Shape Keys can't be retrieve when using Apply Modifiers (Blender/bpy limitation)
- if export_settings[gltf2_blender_export_keys.APPLY] is False and blender_mesh.shape_keys and export_settings[gltf2_blender_export_keys.MORPH]:
- key_blocks = [
- key_block
- for key_block in blender_mesh.shape_keys.key_blocks
- if not (key_block == key_block.relative_key or key_block.mute)
- ]
-
- use_materials = export_settings[gltf2_blender_export_keys.MATERIALS]
-
- # Fetch vert positions and bone data (joint,weights)
-
- locs, morph_locs = __get_positions(blender_mesh, key_blocks, armature, blender_object, export_settings)
- if skin:
- vert_bones, num_joint_sets, need_neutral_bone = __get_bone_data(blender_mesh, skin, blender_vertex_groups)
- if need_neutral_bone is True:
- # Need to create a fake joint at root of armature
- # In order to assign not assigned vertices to it
- # But for now, this is not yet possible, we need to wait the armature node is created
- # Just store this, to be used later
- armature_uuid = export_settings['vtree'].nodes[uuid_for_skined_data].armature
- export_settings['vtree'].nodes[armature_uuid].need_neutral_bone = True
-
- # In Blender there is both per-vert data, like position, and also per-loop
- # (loop=corner-of-poly) data, like normals or UVs. glTF only has per-vert
- # data, so we need to split Blender verts up into potentially-multiple glTF
- # verts.
- #
- # First, we'll collect a "dot" for every loop: a struct that stores all the
- # attributes at that loop, namely the vertex index (which determines all
- # per-vert data), and all the per-loop data like UVs, etc.
- #
- # Each unique dot will become one unique glTF vert.
-
- # List all fields the dot struct needs.
- dot_fields = [('vertex_index', np.uint32)]
- if use_normals:
- dot_fields += [('nx', np.float32), ('ny', np.float32), ('nz', np.float32)]
- if use_tangents:
- dot_fields += [('tx', np.float32), ('ty', np.float32), ('tz', np.float32), ('tw', np.float32)]
- for uv_i in range(tex_coord_max):
- dot_fields += [('uv%dx' % uv_i, np.float32), ('uv%dy' % uv_i, np.float32)]
- for col_i, _ in enumerate(colors_attributes):
- dot_fields += [
- ('color%dr' % col_i, np.float32),
- ('color%dg' % col_i, np.float32),
- ('color%db' % col_i, np.float32),
- ('color%da' % col_i, np.float32),
- ]
- if use_morph_normals:
- for morph_i, _ in enumerate(key_blocks):
- dot_fields += [
- ('morph%dnx' % morph_i, np.float32),
- ('morph%dny' % morph_i, np.float32),
- ('morph%dnz' % morph_i, np.float32),
- ]
-
- dots = np.empty(len(blender_mesh.loops), dtype=np.dtype(dot_fields))
-
- vidxs = np.empty(len(blender_mesh.loops))
- blender_mesh.loops.foreach_get('vertex_index', vidxs)
- dots['vertex_index'] = vidxs
- del vidxs
-
- if use_normals:
- kbs = key_blocks if use_morph_normals else []
- normals, morph_normals = __get_normals(
- blender_mesh, kbs, armature, blender_object, export_settings
- )
- dots['nx'] = normals[:, 0]
- dots['ny'] = normals[:, 1]
- dots['nz'] = normals[:, 2]
- del normals
- for morph_i, ns in enumerate(morph_normals):
- dots['morph%dnx' % morph_i] = ns[:, 0]
- dots['morph%dny' % morph_i] = ns[:, 1]
- dots['morph%dnz' % morph_i] = ns[:, 2]
- del morph_normals
-
- if use_tangents:
- tangents = __get_tangents(blender_mesh, armature, blender_object, export_settings)
- dots['tx'] = tangents[:, 0]
- dots['ty'] = tangents[:, 1]
- dots['tz'] = tangents[:, 2]
- del tangents
- signs = __get_bitangent_signs(blender_mesh, armature, blender_object, export_settings)
- dots['tw'] = signs
- del signs
-
- for uv_i in range(tex_coord_max):
- uvs = __get_uvs(blender_mesh, uv_i)
- dots['uv%dx' % uv_i] = uvs[:, 0]
- dots['uv%dy' % uv_i] = uvs[:, 1]
- del uvs
-
- colors_types = []
- for col_i, blender_col_i in enumerate(colors_attributes):
- colors, colors_type, domain = __get_colors(blender_mesh, col_i, blender_col_i)
- if domain == "POINT":
- colors = colors[dots['vertex_index']]
- colors_types.append(colors_type)
- dots['color%dr' % col_i] = colors[:, 0]
- dots['color%dg' % col_i] = colors[:, 1]
- dots['color%db' % col_i] = colors[:, 2]
- dots['color%da' % col_i] = colors[:, 3]
- del colors
-
- # Calculate triangles and sort them into primitives.
-
- blender_mesh.calc_loop_triangles()
- loop_indices = np.empty(len(blender_mesh.loop_triangles) * 3, dtype=np.uint32)
- blender_mesh.loop_triangles.foreach_get('loops', loop_indices)
-
- prim_indices = {} # maps material index to TRIANGLES-style indices into dots
-
- if use_materials == "NONE": # Only for None. For placeholder and export, keep primitives
- # Put all vertices into one primitive
- prim_indices[-1] = loop_indices
-
- else:
- # Bucket by material index.
-
- tri_material_idxs = np.empty(len(blender_mesh.loop_triangles), dtype=np.uint32)
- blender_mesh.loop_triangles.foreach_get('material_index', tri_material_idxs)
- loop_material_idxs = np.repeat(tri_material_idxs, 3) # material index for every loop
- unique_material_idxs = np.unique(tri_material_idxs)
- del tri_material_idxs
-
- for material_idx in unique_material_idxs:
- prim_indices[material_idx] = loop_indices[loop_material_idxs == material_idx]
-
- # Create all the primitives.
-
- primitives = []
-
- for material_idx, dot_indices in prim_indices.items():
- # Extract just dots used by this primitive, deduplicate them, and
- # calculate indices into this deduplicated list.
- prim_dots = dots[dot_indices]
- prim_dots, indices = np.unique(prim_dots, return_inverse=True)
-
- if len(prim_dots) == 0:
- continue
-
- # Now just move all the data for prim_dots into attribute arrays
-
- attributes = {}
-
- blender_idxs = prim_dots['vertex_index']
-
- attributes['POSITION'] = locs[blender_idxs]
-
- for morph_i, vs in enumerate(morph_locs):
- attributes['MORPH_POSITION_%d' % morph_i] = vs[blender_idxs]
-
- if use_normals:
- normals = np.empty((len(prim_dots), 3), dtype=np.float32)
- normals[:, 0] = prim_dots['nx']
- normals[:, 1] = prim_dots['ny']
- normals[:, 2] = prim_dots['nz']
- attributes['NORMAL'] = normals
-
- if use_tangents:
- tangents = np.empty((len(prim_dots), 4), dtype=np.float32)
- tangents[:, 0] = prim_dots['tx']
- tangents[:, 1] = prim_dots['ty']
- tangents[:, 2] = prim_dots['tz']
- tangents[:, 3] = prim_dots['tw']
- attributes['TANGENT'] = tangents
-
- if use_morph_normals:
- for morph_i, _ in enumerate(key_blocks):
- ns = np.empty((len(prim_dots), 3), dtype=np.float32)
- ns[:, 0] = prim_dots['morph%dnx' % morph_i]
- ns[:, 1] = prim_dots['morph%dny' % morph_i]
- ns[:, 2] = prim_dots['morph%dnz' % morph_i]
- attributes['MORPH_NORMAL_%d' % morph_i] = ns
-
- if use_morph_tangents:
- attributes['MORPH_TANGENT_%d' % morph_i] = __calc_morph_tangents(normals, ns, tangents)
-
- for tex_coord_i in range(tex_coord_max):
- uvs = np.empty((len(prim_dots), 2), dtype=np.float32)
- uvs[:, 0] = prim_dots['uv%dx' % tex_coord_i]
- uvs[:, 1] = prim_dots['uv%dy' % tex_coord_i]
- attributes['TEXCOORD_%d' % tex_coord_i] = uvs
-
- for color_i, _ in enumerate(colors_attributes):
- colors = np.empty((len(prim_dots), 4), dtype=np.float32)
- colors[:, 0] = prim_dots['color%dr' % color_i]
- colors[:, 1] = prim_dots['color%dg' % color_i]
- colors[:, 2] = prim_dots['color%db' % color_i]
- colors[:, 3] = prim_dots['color%da' % color_i]
- attributes['COLOR_%d' % color_i] = {}
- attributes['COLOR_%d' % color_i]["data"] = colors
-
- attributes['COLOR_%d' % color_i]["norm"] = colors_types[color_i] == "BYTE_COLOR"
-
- if skin:
- joints = [[] for _ in range(num_joint_sets)]
- weights = [[] for _ in range(num_joint_sets)]
-
- for vi in blender_idxs:
- bones = vert_bones[vi]
- for j in range(0, 4 * num_joint_sets):
- if j < len(bones):
- joint, weight = bones[j]
- else:
- joint, weight = 0, 0.0
- joints[j//4].append(joint)
- weights[j//4].append(weight)
-
- for i, (js, ws) in enumerate(zip(joints, weights)):
- attributes['JOINTS_%d' % i] = js
- attributes['WEIGHTS_%d' % i] = ws
-
- primitives.append({
- 'attributes': attributes,
- 'indices': indices,
- 'material': material_idx,
- })
-
- if export_settings['gltf_loose_edges']:
- # Find loose edges
- loose_edges = [e for e in blender_mesh.edges if e.is_loose]
- blender_idxs = [vi for e in loose_edges for vi in e.vertices]
-
- if blender_idxs:
- # Export one glTF vert per unique Blender vert in a loose edge
- blender_idxs = np.array(blender_idxs, dtype=np.uint32)
- blender_idxs, indices = np.unique(blender_idxs, return_inverse=True)
-
- attributes = {}
-
- attributes['POSITION'] = locs[blender_idxs]
-
- for morph_i, vs in enumerate(morph_locs):
- attributes['MORPH_POSITION_%d' % morph_i] = vs[blender_idxs]
-
- if skin:
- joints = [[] for _ in range(num_joint_sets)]
- weights = [[] for _ in range(num_joint_sets)]
-
- for vi in blender_idxs:
- bones = vert_bones[vi]
- for j in range(0, 4 * num_joint_sets):
- if j < len(bones):
- joint, weight = bones[j]
- else:
- joint, weight = 0, 0.0
- joints[j//4].append(joint)
- weights[j//4].append(weight)
-
- for i, (js, ws) in enumerate(zip(joints, weights)):
- attributes['JOINTS_%d' % i] = js
- attributes['WEIGHTS_%d' % i] = ws
-
- primitives.append({
- 'attributes': attributes,
- 'indices': indices,
- 'mode': 1, # LINES
- 'material': 0,
- })
-
- if export_settings['gltf_loose_points']:
- # Find loose points
- verts_in_edge = set(vi for e in blender_mesh.edges for vi in e.vertices)
- blender_idxs = [
- vi for vi, _ in enumerate(blender_mesh.vertices)
- if vi not in verts_in_edge
- ]
-
- if blender_idxs:
- blender_idxs = np.array(blender_idxs, dtype=np.uint32)
-
- attributes = {}
-
- attributes['POSITION'] = locs[blender_idxs]
-
- for morph_i, vs in enumerate(morph_locs):
- attributes['MORPH_POSITION_%d' % morph_i] = vs[blender_idxs]
-
- if skin:
- joints = [[] for _ in range(num_joint_sets)]
- weights = [[] for _ in range(num_joint_sets)]
-
- for vi in blender_idxs:
- bones = vert_bones[vi]
- for j in range(0, 4 * num_joint_sets):
- if j < len(bones):
- joint, weight = bones[j]
- else:
- joint, weight = 0, 0.0
- joints[j//4].append(joint)
- weights[j//4].append(weight)
-
- for i, (js, ws) in enumerate(zip(joints, weights)):
- attributes['JOINTS_%d' % i] = js
- attributes['WEIGHTS_%d' % i] = ws
-
- primitives.append({
- 'attributes': attributes,
- 'mode': 0, # POINTS
- 'material': 0,
- })
-
- print_console('INFO', 'Primitives created: %d' % len(primitives))
-
- return primitives
-
-
-def __get_positions(blender_mesh, key_blocks, armature, blender_object, export_settings):
- locs = np.empty(len(blender_mesh.vertices) * 3, dtype=np.float32)
- source = key_blocks[0].relative_key.data if key_blocks else blender_mesh.vertices
- source.foreach_get('co', locs)
- locs = locs.reshape(len(blender_mesh.vertices), 3)
-
- morph_locs = []
- for key_block in key_blocks:
- vs = np.empty(len(blender_mesh.vertices) * 3, dtype=np.float32)
- key_block.data.foreach_get('co', vs)
- vs = vs.reshape(len(blender_mesh.vertices), 3)
- morph_locs.append(vs)
-
- # Transform for skinning
- if armature and blender_object:
- # apply_matrix = armature.matrix_world.inverted_safe() @ blender_object.matrix_world
- # loc_transform = armature.matrix_world @ apply_matrix
-
- loc_transform = blender_object.matrix_world
- locs[:] = __apply_mat_to_all(loc_transform, locs)
- for vs in morph_locs:
- vs[:] = __apply_mat_to_all(loc_transform, vs)
-
- # glTF stores deltas in morph targets
- for vs in morph_locs:
- vs -= locs
-
- if export_settings[gltf2_blender_export_keys.YUP]:
- __zup2yup(locs)
- for vs in morph_locs:
- __zup2yup(vs)
-
- return locs, morph_locs
-
-
-def __get_normals(blender_mesh, key_blocks, armature, blender_object, export_settings):
- """Get normal for each loop."""
- if key_blocks:
- normals = key_blocks[0].relative_key.normals_split_get()
- normals = np.array(normals, dtype=np.float32)
- else:
- normals = np.empty(len(blender_mesh.loops) * 3, dtype=np.float32)
- blender_mesh.calc_normals_split()
- blender_mesh.loops.foreach_get('normal', normals)
-
- normals = normals.reshape(len(blender_mesh.loops), 3)
-
- morph_normals = []
- for key_block in key_blocks:
- ns = np.array(key_block.normals_split_get(), dtype=np.float32)
- ns = ns.reshape(len(blender_mesh.loops), 3)
- morph_normals.append(ns)
-
- # Transform for skinning
- if armature and blender_object:
- apply_matrix = (armature.matrix_world.inverted_safe() @ blender_object.matrix_world)
- apply_matrix = apply_matrix.to_3x3().inverted_safe().transposed()
- normal_transform = armature.matrix_world.to_3x3() @ apply_matrix
-
- normals[:] = __apply_mat_to_all(normal_transform, normals)
- __normalize_vecs(normals)
- for ns in morph_normals:
- ns[:] = __apply_mat_to_all(normal_transform, ns)
- __normalize_vecs(ns)
-
- for ns in [normals, *morph_normals]:
- # Replace zero normals with the unit UP vector.
- # Seems to happen sometimes with degenerate tris?
- is_zero = ~ns.any(axis=1)
- ns[is_zero, 2] = 1
-
- # glTF stores deltas in morph targets
- for ns in morph_normals:
- ns -= normals
-
- if export_settings[gltf2_blender_export_keys.YUP]:
- __zup2yup(normals)
- for ns in morph_normals:
- __zup2yup(ns)
-
- return normals, morph_normals
-
-
-def __get_tangents(blender_mesh, armature, blender_object, export_settings):
- """Get an array of the tangent for each loop."""
- tangents = np.empty(len(blender_mesh.loops) * 3, dtype=np.float32)
- blender_mesh.loops.foreach_get('tangent', tangents)
- tangents = tangents.reshape(len(blender_mesh.loops), 3)
-
- # Transform for skinning
- if armature and blender_object:
- apply_matrix = armature.matrix_world.inverted_safe() @ blender_object.matrix_world
- tangent_transform = apply_matrix.to_quaternion().to_matrix()
- tangents = __apply_mat_to_all(tangent_transform, tangents)
- __normalize_vecs(tangents)
-
- if export_settings[gltf2_blender_export_keys.YUP]:
- __zup2yup(tangents)
-
- return tangents
-
-
-def __get_bitangent_signs(blender_mesh, armature, blender_object, export_settings):
- signs = np.empty(len(blender_mesh.loops), dtype=np.float32)
- blender_mesh.loops.foreach_get('bitangent_sign', signs)
-
- # Transform for skinning
- if armature and blender_object:
- # Bitangent signs should flip when handedness changes
- # TODO: confirm
- apply_matrix = armature.matrix_world.inverted_safe() @ blender_object.matrix_world
- tangent_transform = apply_matrix.to_quaternion().to_matrix()
- flipped = tangent_transform.determinant() < 0
- if flipped:
- signs *= -1
-
- # No change for Zup -> Yup
-
- return signs
-
-
-def __calc_morph_tangents(normals, morph_normal_deltas, tangents):
- # TODO: check if this works
- morph_tangent_deltas = np.empty((len(normals), 3), dtype=np.float32)
-
- for i in range(len(normals)):
- n = Vector(normals[i])
- morph_n = n + Vector(morph_normal_deltas[i]) # convert back to non-delta
- t = Vector(tangents[i, :3])
-
- rotation = morph_n.rotation_difference(n)
-
- t_morph = Vector(t)
- t_morph.rotate(rotation)
- morph_tangent_deltas[i] = t_morph - t # back to delta
-
- return morph_tangent_deltas
-
-
-def __get_uvs(blender_mesh, uv_i):
- layer = blender_mesh.uv_layers[uv_i]
- uvs = np.empty(len(blender_mesh.loops) * 2, dtype=np.float32)
- layer.data.foreach_get('uv', uvs)
- uvs = uvs.reshape(len(blender_mesh.loops), 2)
-
- # Blender UV space -> glTF UV space
- # u,v -> u,1-v
- uvs[:, 1] *= -1
- uvs[:, 1] += 1
-
- return uvs
-
-
-def __get_colors(blender_mesh, color_i, blender_color_i):
- if blender_mesh.color_attributes[blender_color_i].domain == "POINT":
- colors = np.empty(len(blender_mesh.vertices) * 4, dtype=np.float32) #POINT
- else:
- colors = np.empty(len(blender_mesh.loops) * 4, dtype=np.float32) #CORNER
- blender_mesh.color_attributes[blender_color_i].data.foreach_get('color', colors)
- colors = colors.reshape(-1, 4)
- # colors are already linear, no need to switch color space
- return colors, blender_mesh.color_attributes[blender_color_i].data_type, blender_mesh.color_attributes[blender_color_i].domain
-
-
-def __get_bone_data(blender_mesh, skin, blender_vertex_groups):
-
- need_neutral_bone = False
- min_influence = 0.0001
-
- joint_name_to_index = {joint.name: index for index, joint in enumerate(skin.joints)}
- group_to_joint = [joint_name_to_index.get(g.name) for g in blender_vertex_groups]
-
- # List of (joint, weight) pairs for each vert
- vert_bones = []
- max_num_influences = 0
-
- for vertex in blender_mesh.vertices:
- bones = []
- if vertex.groups:
- for group_element in vertex.groups:
- weight = group_element.weight
- if weight <= min_influence:
- continue
- try:
- joint = group_to_joint[group_element.group]
- except Exception:
- continue
- if joint is None:
- continue
- bones.append((joint, weight))
- bones.sort(key=lambda x: x[1], reverse=True)
- if not bones:
- # Is not assign to any bone
- bones = ((len(skin.joints), 1.0),) # Assign to a joint that will be created later
- need_neutral_bone = True
- vert_bones.append(bones)
- if len(bones) > max_num_influences:
- max_num_influences = len(bones)
-
- # How many joint sets do we need? 1 set = 4 influences
- num_joint_sets = (max_num_influences + 3) // 4
-
- return vert_bones, num_joint_sets, need_neutral_bone
-
-
-def __zup2yup(array):
- # x,y,z -> x,z,-y
- array[:, [1,2]] = array[:, [2,1]] # x,z,y
- array[:, 2] *= -1 # x,z,-y
-
-
-def __apply_mat_to_all(matrix, vectors):
- """Given matrix m and vectors [v1,v2,...], computes [m@v1,m@v2,...]"""
- # Linear part
- m = matrix.to_3x3() if len(matrix) == 4 else matrix
- res = np.matmul(vectors, np.array(m.transposed()))
- # Translation part
- if len(matrix) == 4:
- res += np.array(matrix.translation)
- return res
-
-
-def __normalize_vecs(vectors):
- norms = np.linalg.norm(vectors, axis=1, keepdims=True)
- np.divide(vectors, norms, out=vectors, where=norms != 0)