""" Exports smaller geometry but is slower. To be replaced with https://github.com/zeux/meshoptimizer """ from mathutils import * import numpy as np import arm.log as log import arm.utils if arm.is_reload(__name__): log = arm.reload_module(log) arm.utils = arm.reload_module(arm.utils) else: arm.enable_reload(__name__) class Vertex: __slots__ = ("co", "normal", "uvs", "col", "loop_indices", "index", "bone_weights", "bone_indices", "bone_count", "vertex_index") def __init__(self, mesh, loop): self.vertex_index = loop.vertex_index loop_idx = loop.index self.co = mesh.vertices[self.vertex_index].co[:] self.normal = loop.normal[:] self.uvs = tuple(layer.data[loop_idx].uv[:] for layer in mesh.uv_layers) self.col = [0.0, 0.0, 0.0] if len(mesh.vertex_colors) > 0: self.col = mesh.vertex_colors[0].data[loop_idx].color[:] self.loop_indices = [loop_idx] self.index = 0 def __hash__(self): return hash((self.co, self.normal, self.uvs)) def __eq__(self, other): eq = ( (self.co == other.co) and (self.normal == other.normal) and (self.uvs == other.uvs) and (self.col == other.col) ) if eq: indices = self.loop_indices + other.loop_indices self.loop_indices = indices other.loop_indices = indices return eq def calc_tangents(posa, nora, uva, ias, scale_pos): num_verts = int(len(posa) / 4) tangents = np.empty(num_verts * 3, dtype=' 0 if self.has_baked_material(bobject, exportMesh.materials): has_tex = True has_tex1 = has_tex == True and num_uv_layers > 1 num_colors = len(exportMesh.vertex_colors) has_col = self.get_export_vcols(exportMesh) == True and num_colors > 0 has_tang = self.has_tangents(exportMesh) pdata = np.empty(num_verts * 4, dtype=' maxdim: maxdim = abs(v.uv[0]) if abs(v.uv[1]) > maxdim: maxdim = abs(v.uv[1]) if maxdim > 1: o['scale_tex'] = maxdim invscale_tex = (1 / o['scale_tex']) * 32767 else: invscale_tex = 1 * 32767 # TODO: handle t1map # Save aabb aabb_center = 0.125 * sum((Vector(b) for b in bobject.bound_box), Vector()) bobject.data.arm_aabb = [ \ abs((bobject.bound_box[6][0] - bobject.bound_box[0][0]) / 2 + abs(aabb_center[0])) * 2, \ abs((bobject.bound_box[6][1] - bobject.bound_box[0][1]) / 2 + abs(aabb_center[1])) * 2, \ abs((bobject.bound_box[6][2] - bobject.bound_box[0][2]) / 2 + abs(aabb_center[2])) * 2 \ ] # Scale for packed coords maxdim = max(bobject.data.arm_aabb[0], max(bobject.data.arm_aabb[1], bobject.data.arm_aabb[2])) if maxdim > 2: o['scale_pos'] = maxdim / 2 else: o['scale_pos'] = 1.0 if has_armature: # Allow up to 2x bigger bounds for skinned mesh o['scale_pos'] *= 2.0 scale_pos = o['scale_pos'] invscale_pos = (1 / scale_pos) * 32767 # Make arrays for i, v in enumerate(vert_list): v.index = i co = v.co normal = v.normal i4 = i * 4 i2 = i * 2 pdata[i4 ] = co[0] pdata[i4 + 1] = co[1] pdata[i4 + 2] = co[2] pdata[i4 + 3] = normal[2] * scale_pos # Cancel scale ndata[i2 ] = normal[0] ndata[i2 + 1] = normal[1] if has_tex: uv = v.uvs[t0map] t0data[i2 ] = uv[0] t0data[i2 + 1] = 1.0 - uv[1] # Reverse Y if has_tex1: uv = v.uvs[t1map] t1data[i2 ] = uv[0] t1data[i2 + 1] = 1.0 - uv[1] if has_col: i3 = i * 3 cdata[i3 ] = v.col[0] cdata[i3 + 1] = v.col[1] cdata[i3 + 2] = v.col[2] # Indices prims = {ma.name if ma else '': [] for ma in exportMesh.materials} if not prims: prims = {'': []} vert_dict = {i : v for v in vert_list for i in v.loop_indices} for poly in exportMesh.polygons: first = poly.loop_start if len(exportMesh.materials) == 0: prim = prims[''] else: mat = exportMesh.materials[min(poly.material_index, len(exportMesh.materials) - 1)] prim = prims[mat.name if mat else ''] indices = [vert_dict[i].index for i in range(first, first+poly.loop_total)] if poly.loop_total == 3: prim += indices elif poly.loop_total > 3: for i in range(poly.loop_total-2): prim += (indices[-1], indices[i], indices[i + 1]) # Write indices o['index_arrays'] = [] for mat, prim in prims.items(): idata = [0] * len(prim) for i, v in enumerate(prim): idata[i] = v if len(idata) == 0: # No face assigned continue ia = {} ia['values'] = idata ia['material'] = 0 # Find material index for multi-mat mesh if len(exportMesh.materials) > 1: for i in range(0, len(exportMesh.materials)): if (exportMesh.materials[i] != None and mat == exportMesh.materials[i].name) or \ (exportMesh.materials[i] == None and mat == ''): # Default material for empty slots ia['material'] = i break o['index_arrays'].append(ia) if has_tang: tangdata = calc_tangents(pdata, ndata, t0data, o['index_arrays'], scale_pos) pdata *= invscale_pos ndata *= 32767 pdata = np.array(pdata, dtype=' max_bones: log.warn(bobject.name + ' - ' + str(bone_count) + ' bones found, exceeds maximum of ' + str(max_bones) + ' bones defined - raise the value in Camera Data - Armory Render Props - Max Bones') for i in range(bone_count): boneRef = self.find_bone(bone_array[i].name) if boneRef: oskin['bone_ref_array'].append(boneRef[1]["structName"]) oskin['bone_len_array'].append(bone_array[i].length) else: oskin['bone_ref_array'].append("") oskin['bone_len_array'].append(0.0) # Write the bind pose transform array oskin['transformsI'] = [] for i in range(bone_count): skeletonI = (armature.matrix_world @ bone_array[i].matrix_local).inverted_safe() skeletonI = (skeletonI @ bobject.matrix_world) oskin['transformsI'].append(self.write_matrix(skeletonI)) # Export the per-vertex bone influence data group_remap = [] for group in bobject.vertex_groups: for i in range(bone_count): if bone_array[i].name == group.name: group_remap.append(i) break else: group_remap.append(-1) bone_count_array = np.empty(len(vert_list), dtype='= 0: #and bone_weight != 0.0: bone_values.append((bone_weight, bone_index)) total_weight += bone_weight bone_count += 1 if bone_count > 4: bone_count = 4 bone_values.sort(reverse=True) bone_values = bone_values[:4] bone_count_array[index] = bone_count for bv in bone_values: bone_weight_array[count] = bv[0] * 32767 bone_index_array[count] = bv[1] count += 1 if total_weight != 0.0 and total_weight != 1.0: normalizer = 1.0 / total_weight for i in range(bone_count): bone_weight_array[count - i - 1] *= normalizer oskin['bone_count_array'] = bone_count_array oskin['bone_index_array'] = bone_index_array[:count] oskin['bone_weight_array'] = bone_weight_array[:count] # Bone constraints for bone in armature.pose.bones: if len(bone.constraints) > 0: if 'constraints' not in oskin: oskin['constraints'] = [] self.add_constraints(bone, oskin, bone=True)