SWBF-msh-Blender-IO/addons/io_scene_swbf_msh/msh_mesh_to_blend.py

192 lines
6.2 KiB
Python

""" Converts msh meshes to Blender counterparts """
import bpy
import bmesh
import math
from enum import Enum
from typing import List, Set, Dict, Tuple
from .msh_scene import Scene
from .msh_material_to_blend import *
from .msh_model import *
from .msh_skeleton_utilities import *
from .msh_model_gather import get_is_model_hidden
from .crc import *
import os
def validate_segment_geometry(segment : GeometrySegment):
if not segment.positions:
return False
if not segment.triangles and not segment.triangle_strips and not segment.polygons:
return False
if not segment.material_name:
return False
if not segment.normals:
return False
return True
def model_to_mesh_object(model: Model, scene : Scene, materials_map : Dict[str, bpy.types.Material]) -> bpy.types.Object:
blender_mesh = bpy.data.meshes.new(model.name)
# Per vertex data which will eventually be remapped to loops
vertex_positions = []
vertex_uvs = []
vertex_normals = []
# Keeps track of which vertices each group of weights affects
# i.e. maps offset of vertices -> weights that affect them
vertex_weights_offsets = {}
# Since polygons in a msh segment index into the segment's verts,
# we must keep an offset to index them into the verts of the whole mesh
polygon_index_offset = 0
# List of tuples of face indices
polygons = []
# Each polygon has an index into the mesh's material list
current_material_index = 0
polygon_material_indices = []
if model.geometry:
for segment in model.geometry:
if not validate_segment_geometry(segment):
continue
blender_mesh.materials.append(materials_map[segment.material_name])
vertex_positions += [tuple(convert_vector_space(p)) for p in segment.positions]
if segment.texcoords:
vertex_uvs += [tuple(texcoord) for texcoord in segment.texcoords]
else:
vertex_uvs += [(0.0,0.0) for _ in range(len(segment.positions))]
if segment.normals:
vertex_normals += [tuple(convert_vector_space(n)) for n in segment.normals]
if segment.weights:
vertex_weights_offsets[polygon_index_offset] = segment.weights
segment_polygons = []
if segment.triangles:
segment_polygons = [tuple([ind + polygon_index_offset for ind in tri]) for tri in segment.triangles]
elif segment.triangle_strips:
winding = [0,1,2]
rwinding = [1,0,2]
for strip in segment.triangle_strips:
for i in range(len(strip) - 2):
strip_tri = tuple([polygon_index_offset + strip[i+j] for j in (winding if i % 2 == 0 else rwinding)])
segment_polygons.append(strip_tri)
elif segment.polygons:
segment_polygons = [tuple([ind + polygon_index_offset for ind in polygon]) for polygon in segment.polygons]
polygon_index_offset += len(segment.positions)
polygons += segment_polygons
polygon_material_indices += [current_material_index for _ in segment_polygons]
current_material_index += 1
'''
Start building the blender mesh
'''
# VERTICES
# This is all we have to do for vertices, other attributes are done per-loop
blender_mesh.vertices.add(len(vertex_positions))
blender_mesh.vertices.foreach_set("co", [component for vertex_position in vertex_positions for component in vertex_position])
# LOOPS
flat_indices = [index for polygon in polygons for index in polygon]
blender_mesh.loops.add(len(flat_indices))
# Position indices
blender_mesh.loops.foreach_set("vertex_index", flat_indices)
# Normals
blender_mesh.create_normals_split()
blender_mesh.loops.foreach_set("normal", [component for i in flat_indices for component in vertex_normals[i]])
# UVs
blender_mesh.uv_layers.new(do_init=False)
blender_mesh.uv_layers[0].data.foreach_set("uv", [component for i in flat_indices for component in vertex_uvs[i]])
# POLYGONS/FACES
blender_mesh.polygons.add(len(polygons))
# Indices of starting loop for each polygon
polygon_loop_start_indices = []
current_polygon_start_index = 0
# Number of loops in this polygon. Polygon i will use
# loops from polygon_loop_start_indices[i] to
# polygon_loop_start_indices[i] + polygon_loop_totals[i]
polygon_loop_totals = []
for polygon in polygons:
polygon_loop_start_indices.append(current_polygon_start_index)
current_polygon_length = len(polygon)
current_polygon_start_index += current_polygon_length
polygon_loop_totals.append(current_polygon_length)
blender_mesh.polygons.foreach_set("loop_start", polygon_loop_start_indices)
blender_mesh.polygons.foreach_set("loop_total", polygon_loop_totals)
blender_mesh.polygons.foreach_set("material_index", polygon_material_indices)
blender_mesh.polygons.foreach_set("use_smooth", [True for _ in polygons])
blender_mesh.validate(clean_customdata=False)
blender_mesh.update()
# Reset custom normals after calling update/validate
reset_normals = [0.0] * (len(blender_mesh.loops) * 3)
blender_mesh.loops.foreach_get("normal", reset_normals)
blender_mesh.normals_split_custom_set(tuple(zip(*(iter(reset_normals),) * 3)))
blender_mesh.use_auto_smooth = True
blender_mesh_object = bpy.data.objects.new(model.name, blender_mesh)
# VERTEX GROUPS
vertex_groups_indicies = {}
for offset in vertex_weights_offsets:
for i, weight_set in enumerate(vertex_weights_offsets[offset]):
for weight in weight_set:
index = weight.bone
if index not in vertex_groups_indicies:
model_name = scene.models[index].name
vertex_groups_indicies[index] = blender_mesh_object.vertex_groups.new(name=model_name)
vertex_groups_indicies[index].add([offset + i], weight.weight, 'ADD')
return blender_mesh_object