Initial commit for WYSIWYG PSK mesh export. This will remove the need for applying mesh modifers before export.
This commit is contained in:
@@ -2,6 +2,8 @@ from collections import OrderedDict
|
|||||||
|
|
||||||
from .data import *
|
from .data import *
|
||||||
from ..helpers import *
|
from ..helpers import *
|
||||||
|
import bmesh
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
class PskInputObjects(object):
|
class PskInputObjects(object):
|
||||||
@@ -23,30 +25,30 @@ class PskBuilder(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_input_objects(context) -> PskInputObjects:
|
def get_input_objects(context) -> PskInputObjects:
|
||||||
input_objects = PskInputObjects()
|
input_objects = PskInputObjects()
|
||||||
for obj in context.view_layer.objects.selected:
|
for selected_object in context.view_layer.objects.selected:
|
||||||
if obj.type != 'MESH':
|
if selected_object.type != 'MESH':
|
||||||
raise RuntimeError(f'Selected object "{obj.name}" is not a mesh')
|
raise RuntimeError(f'Selected object "{selected_object.name}" is not a mesh')
|
||||||
|
|
||||||
input_objects.mesh_objects = context.view_layer.objects.selected
|
input_objects.mesh_objects = context.view_layer.objects.selected
|
||||||
|
|
||||||
if len(input_objects.mesh_objects) == 0:
|
if len(input_objects.mesh_objects) == 0:
|
||||||
raise RuntimeError('At least one mesh must be selected')
|
raise RuntimeError('At least one mesh must be selected')
|
||||||
|
|
||||||
for obj in input_objects.mesh_objects:
|
for mesh_object in input_objects.mesh_objects:
|
||||||
if len(obj.data.materials) == 0:
|
if len(mesh_object.data.materials) == 0:
|
||||||
raise RuntimeError(f'Mesh "{obj.name}" must have at least one material')
|
raise RuntimeError(f'Mesh "{mesh_object.name}" must have at least one material')
|
||||||
|
|
||||||
# Ensure that there are either no armature modifiers (static mesh)
|
# Ensure that there are either no armature modifiers (static mesh)
|
||||||
# or that there is exactly one armature modifier object shared between
|
# or that there is exactly one armature modifier object shared between
|
||||||
# all selected meshes
|
# all selected meshes
|
||||||
armature_modifier_objects = set()
|
armature_modifier_objects = set()
|
||||||
|
|
||||||
for obj in input_objects.mesh_objects:
|
for mesh_object in input_objects.mesh_objects:
|
||||||
modifiers = [x for x in obj.modifiers if x.type == 'ARMATURE']
|
modifiers = [x for x in mesh_object.modifiers if x.type == 'ARMATURE']
|
||||||
if len(modifiers) == 0:
|
if len(modifiers) == 0:
|
||||||
continue
|
continue
|
||||||
elif len(modifiers) > 1:
|
elif len(modifiers) > 1:
|
||||||
raise RuntimeError(f'Mesh "{obj.name}" must have only one armature modifier')
|
raise RuntimeError(f'Mesh "{mesh_object.name}" must have only one armature modifier')
|
||||||
armature_modifier_objects.add(modifiers[0].object)
|
armature_modifier_objects.add(modifiers[0].object)
|
||||||
|
|
||||||
if len(armature_modifier_objects) > 1:
|
if len(armature_modifier_objects) > 1:
|
||||||
@@ -118,44 +120,60 @@ class PskBuilder(object):
|
|||||||
|
|
||||||
psk.bones.append(psk_bone)
|
psk.bones.append(psk_bone)
|
||||||
|
|
||||||
for object in input_objects.mesh_objects:
|
for mesh_object in input_objects.mesh_objects:
|
||||||
|
|
||||||
|
# MATERIALS
|
||||||
|
material_indices = []
|
||||||
|
for i, material in enumerate(mesh_object.data.materials):
|
||||||
|
if material is None:
|
||||||
|
raise RuntimeError('Material cannot be empty (index ' + str(i) + ')')
|
||||||
|
if material.name in materials:
|
||||||
|
# Material already evaluated, just get its index.
|
||||||
|
material_index = list(materials.keys()).index(material.name)
|
||||||
|
else:
|
||||||
|
# New material.
|
||||||
|
psk_material = Psk.Material()
|
||||||
|
psk_material.name = bytes(material.name, encoding='utf-8')
|
||||||
|
psk_material.texture_index = len(psk.materials)
|
||||||
|
psk.materials.append(psk_material)
|
||||||
|
materials[material.name] = material
|
||||||
|
material_index = psk_material.texture_index
|
||||||
|
material_indices.append(material_index)
|
||||||
|
|
||||||
|
depsgraph = context.evaluated_depsgraph_get()
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_object(mesh_object, depsgraph)
|
||||||
|
|
||||||
|
# TODO: make a new temporary mesh then delete it!
|
||||||
|
mesh_data = bpy.data.meshes.new('')
|
||||||
|
bm.to_mesh(mesh_data)
|
||||||
|
del bm
|
||||||
|
|
||||||
|
# Create a copy of the mesh object
|
||||||
|
mesh_object_copy = bpy.data.objects.new('', mesh_data)
|
||||||
|
# Copy the vertex groups
|
||||||
|
for vertex_group in mesh_object.vertex_groups:
|
||||||
|
mesh_object_copy.vertex_groups.new(name=vertex_group.name)
|
||||||
|
|
||||||
vertex_offset = len(psk.points)
|
vertex_offset = len(psk.points)
|
||||||
|
|
||||||
# VERTICES
|
# VERTICES
|
||||||
for vertex in object.data.vertices:
|
for vertex in mesh_data.vertices:
|
||||||
point = Vector3()
|
point = Vector3()
|
||||||
v = object.matrix_world @ vertex.co
|
v = mesh_object.matrix_world @ vertex.co
|
||||||
point.x = v.x
|
point.x = v.x
|
||||||
point.y = v.y
|
point.y = v.y
|
||||||
point.z = v.z
|
point.z = v.z
|
||||||
psk.points.append(point)
|
psk.points.append(point)
|
||||||
|
|
||||||
uv_layer = object.data.uv_layers.active.data
|
uv_layer = mesh_data.uv_layers.active.data
|
||||||
|
|
||||||
# MATERIALS
|
|
||||||
material_indices = []
|
|
||||||
for i, m in enumerate(object.data.materials):
|
|
||||||
if m is None:
|
|
||||||
raise RuntimeError('Material cannot be empty (index ' + str(i) + ')')
|
|
||||||
if m.name in materials:
|
|
||||||
# Material already evaluated, just get its index.
|
|
||||||
material_index = list(materials.keys()).index(m.name)
|
|
||||||
else:
|
|
||||||
# New material.
|
|
||||||
material = Psk.Material()
|
|
||||||
material.name = bytes(m.name, encoding='utf-8')
|
|
||||||
material.texture_index = len(psk.materials)
|
|
||||||
psk.materials.append(material)
|
|
||||||
materials[m.name] = m
|
|
||||||
material_index = material.texture_index
|
|
||||||
material_indices.append(material_index)
|
|
||||||
|
|
||||||
# WEDGES
|
# WEDGES
|
||||||
object.data.calc_loop_triangles()
|
mesh_data.calc_loop_triangles()
|
||||||
|
|
||||||
# Build a list of non-unique wedges.
|
# Build a list of non-unique wedges.
|
||||||
wedges = []
|
wedges = []
|
||||||
for loop_index, loop in enumerate(object.data.loops):
|
for loop_index, loop in enumerate(mesh_data.loops):
|
||||||
wedge = Psk.Wedge()
|
wedge = Psk.Wedge()
|
||||||
wedge.point_index = loop.vertex_index + vertex_offset
|
wedge.point_index = loop.vertex_index + vertex_offset
|
||||||
wedge.u, wedge.v = uv_layer[loop_index].uv
|
wedge.u, wedge.v = uv_layer[loop_index].uv
|
||||||
@@ -163,13 +181,13 @@ class PskBuilder(object):
|
|||||||
wedges.append(wedge)
|
wedges.append(wedge)
|
||||||
|
|
||||||
# Assign material indices to the wedges.
|
# Assign material indices to the wedges.
|
||||||
for triangle in object.data.loop_triangles:
|
for triangle in mesh_data.loop_triangles:
|
||||||
for loop_index in triangle.loops:
|
for loop_index in triangle.loops:
|
||||||
wedges[loop_index].material_index = material_indices[triangle.material_index]
|
wedges[loop_index].material_index = material_indices[triangle.material_index]
|
||||||
|
|
||||||
# Populate the list of wedges with unique wedges & build a look-up table of loop indices to wedge indices
|
# Populate the list of wedges with unique wedges & build a look-up table of loop indices to wedge indices
|
||||||
wedge_indices = {}
|
wedge_indices = {}
|
||||||
loop_wedge_indices = [-1] * len(object.data.loops)
|
loop_wedge_indices = [-1] * len(mesh_data.loops)
|
||||||
for loop_index, wedge in enumerate(wedges):
|
for loop_index, wedge in enumerate(wedges):
|
||||||
wedge_hash = hash(wedge)
|
wedge_hash = hash(wedge)
|
||||||
if wedge_hash in wedge_indices:
|
if wedge_hash in wedge_indices:
|
||||||
@@ -181,8 +199,8 @@ class PskBuilder(object):
|
|||||||
loop_wedge_indices[loop_index] = wedge_index
|
loop_wedge_indices[loop_index] = wedge_index
|
||||||
|
|
||||||
# FACES
|
# FACES
|
||||||
poly_groups, groups = object.data.calc_smooth_groups(use_bitflags=True)
|
poly_groups, groups = mesh_data.calc_smooth_groups(use_bitflags=True)
|
||||||
for f in object.data.loop_triangles:
|
for f in mesh_data.loop_triangles:
|
||||||
face = Psk.Face()
|
face = Psk.Face()
|
||||||
face.material_index = material_indices[f.material_index]
|
face.material_index = material_indices[f.material_index]
|
||||||
face.wedge_indices[0] = loop_wedge_indices[f.loops[2]]
|
face.wedge_indices[0] = loop_wedge_indices[f.loops[2]]
|
||||||
@@ -196,7 +214,7 @@ class PskBuilder(object):
|
|||||||
# Because the vertex groups may contain entries for which there is no matching bone in the armature,
|
# Because the vertex groups may contain entries for which there is no matching bone in the armature,
|
||||||
# we must filter them out and not export any weights for these vertex groups.
|
# we must filter them out and not export any weights for these vertex groups.
|
||||||
bone_names = [x.name for x in bones]
|
bone_names = [x.name for x in bones]
|
||||||
vertex_group_names = [x.name for x in object.vertex_groups]
|
vertex_group_names = [x.name for x in mesh_object_copy.vertex_groups]
|
||||||
vertex_group_bone_indices = dict()
|
vertex_group_bone_indices = dict()
|
||||||
for vertex_group_index, vertex_group_name in enumerate(vertex_group_names):
|
for vertex_group_index, vertex_group_name in enumerate(vertex_group_names):
|
||||||
try:
|
try:
|
||||||
@@ -215,12 +233,12 @@ class PskBuilder(object):
|
|||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
bone = bone.parent
|
bone = bone.parent
|
||||||
for vertex_group_index, vertex_group in enumerate(object.vertex_groups):
|
for vertex_group_index, vertex_group in enumerate(mesh_object_copy.vertex_groups):
|
||||||
if vertex_group_index not in vertex_group_bone_indices:
|
if vertex_group_index not in vertex_group_bone_indices:
|
||||||
# Vertex group has no associated bone, skip it.
|
# Vertex group has no associated bone, skip it.
|
||||||
continue
|
continue
|
||||||
bone_index = vertex_group_bone_indices[vertex_group_index]
|
bone_index = vertex_group_bone_indices[vertex_group_index]
|
||||||
for vertex_index in range(len(object.data.vertices)):
|
for vertex_index in range(len(mesh_data.vertices)):
|
||||||
try:
|
try:
|
||||||
weight = vertex_group.weight(vertex_index)
|
weight = vertex_group.weight(vertex_index)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
@@ -233,4 +251,8 @@ class PskBuilder(object):
|
|||||||
w.weight = weight
|
w.weight = weight
|
||||||
psk.weights.append(w)
|
psk.weights.append(w)
|
||||||
|
|
||||||
|
bpy.data.objects.remove(mesh_object_copy)
|
||||||
|
bpy.data.meshes.remove(mesh_data)
|
||||||
|
del mesh_data
|
||||||
|
|
||||||
return psk
|
return psk
|
||||||
|
|||||||
Reference in New Issue
Block a user