export shape keys as texture

This commit is contained in:
QuantumCoderQC 2021-10-14 21:19:18 +02:00
parent 93563dbba5
commit b7941d2fa8
1 changed files with 142 additions and 45 deletions

View File

@ -22,6 +22,8 @@ import numpy as np
import bpy
from mathutils import *
import bmesh
import arm.assets as assets
import arm.exporter_opt as exporter_opt
import arm.log as log
@ -1114,6 +1116,119 @@ class ArmoryExporter:
if 'constraints' not in oskin:
oskin['constraints'] = []
self.add_constraints(bone, oskin, bone=True)
def export_shape_keys(self, bobject: bpy.types.Object, export_mesh: bpy.types.Mesh, out_mesh):
max_shape_keys = 32
output_dir = bpy.path.abspath('//') + "Bundled\\"
name = bobject.data.name
vert_pos = []
vert_nor = []
names = []
default_values = []
count = 0
for shape_key in bobject.data.shape_keys.key_blocks:
if(count > max_shape_keys):
break
vert_data = self.get_vertex_data_from_shape_key(shape_key)
vert_pos.append(vert_data['pos'])
vert_nor.append(vert_data['nor'])
names.append(shape_key.name)
default_values.append(shape_key.value)
count += 1
min, max = self.bake_to_image(vert_pos, vert_nor, name, output_dir)
morph_target = {}
morph_target['morph_target_ref'] = names
morph_target['morph_target_defaults'] = default_values
morph_target['num_morph_targets'] = count
morph_target['morph_scale'] = max - min
morph_target['morph_offset'] = min
out_mesh['morph_target'] = morph_target
self.create_morph_uv_set(bobject)
return
def get_vertex_data_from_shape_key(self, shape_key_data):
vert_pos = shape_key_data.data.values()
vert_nor = shape_key_data.normals_vertex_get()
num_verts = len(vert_pos)
pos = []
nor = []
for i in range(num_verts):
pos.append(list(vert_pos[i].co))
temp = []
for j in range(3):
temp.append(vert_nor[j + i * 3])
nor.append(temp)
return {'pos': pos, 'nor': nor}
def bake_to_image(self, vert_pos, vert_nor, name, output_dir):
pos_array = np.array(vert_pos)
nor_array = np.array(vert_nor)
pos_max = np.amax(pos_array)
pos_min = np.amin(pos_array)
pos_array_scaled = np.interp(pos_array, (pos_min, pos_max), (0, 1))
self.write_output_image(pos_array_scaled, name + '_pos', output_dir)
nor_array_scaled = np.interp(nor_array, (-1, 1), (0, 1))
self.write_output_image(nor_array_scaled, name + '_nor', output_dir)
return pos_min, pos_max
def write_output_image(self, data, name, output_dir):
size = len(data[0]), len(data)
pixel_list = []
for y in range(size[1]):
for x in range(size[0]):
# assign RGBA
pixel_list.append(data[y, x, 0])
pixel_list.append(data[y, x, 1])
pixel_list.append(data[y, x, 2])
pixel_list.append(1.0)
image = bpy.data.images.new(name, width = size[0], height = size[1], is_data = True)
image.pixels = pixel_list
image.save_render(output_dir + name + ".png", scene= bpy.context.scene)
bpy.data.images.remove(image)
def create_morph_uv_set(self, obj):
if(obj.data.uv_layers.get('UVMap_shape_key') is None):
obj.data.uv_layers.new(name = 'UVMap_shape_key')
bm = bmesh.new()
bm.from_mesh(obj.data)
uv_layer = bm.loops.layers.uv.get('UVMap_shape_key')
pixel_size = 1.0 / len(bm.verts)
i= 0
for v in bm.verts:
for l in v.link_loops:
uv_data = l[uv_layer]
uv_data.uv = Vector(((i + 0.5) * pixel_size, 0.0))
i += 1
bm.to_mesh(obj.data)
bm.free()
def write_mesh(self, bobject: bpy.types.Object, fp, out_mesh):
if bpy.data.worlds['Arm'].arm_single_data_file:
@ -1142,8 +1257,15 @@ class ArmoryExporter:
num_verts = len(loops)
num_uv_layers = len(exportMesh.uv_layers)
is_baked = self.has_baked_material(bobject, exportMesh.materials)
has_tex = (self.get_export_uvs(bobject.data) and num_uv_layers > 0) or is_baked
self.has_shape_key = False
if('morph_target' in o):
self.has_shape_key = True
print(bobject.name)
print(self.has_shape_key)
has_tex = (self.get_export_uvs(bobject.data) and num_uv_layers > 0) or is_baked or self.has_shape_key
has_tex1 = has_tex and num_uv_layers > 1
print(has_tex)
print(has_tex1)
num_colors = len(exportMesh.vertex_colors)
has_col = self.get_export_vcols(bobject.data) and num_colors > 0
has_tang = self.has_tangents(bobject.data)
@ -1410,52 +1532,19 @@ Make sure the mesh only has tris/quads.""")
struct_flag = False
# Save the morph state if necessary
active_shape_key_index = bobject.active_shape_key_index
show_only_shape_key = bobject.show_only_shape_key
current_morph_value = []
active_shape_key_index = 0
show_only_shape_key = False
current_morph_value = 0
shape_keys = ArmoryExporter.get_shape_keys(mesh)
if shape_keys:
active_shape_key_index = bobject.active_shape_key_index
show_only_shape_key = bobject.show_only_shape_key
current_morph_value = bobject.active_shape_key.value
bobject.active_shape_key_index = 0
bobject.show_only_shape_key = True
base_index = 0
relative = shape_keys.use_relative
if relative:
morph_count = 0
base_name = shape_keys.reference_key.name
for block in shape_keys.key_blocks:
if block.name == base_name:
base_index = morph_count
break
morph_count += 1
morph_count = 0
for block in shape_keys.key_blocks:
current_morph_value.append(block.value)
block.value = 0.0
if block.name != "":
# self.IndentWrite(B"Morph (index = ", 0, struct_flag)
# self.WriteInt(morph_count)
# if (relative) and (morph_count != base_index):
# self.Write(B", base = ")
# self.WriteInt(base_index)
# self.Write(B")\n")
# self.IndentWrite(B"{\n")
# self.IndentWrite(B"Name {string {\"", 1)
# self.Write(bytes(block.name, "UTF-8"))
# self.Write(B"\"}}\n")
# self.IndentWrite(B"}\n")
# TODO
struct_flag = True
morph_count += 1
shape_keys.key_blocks[0].value = 1.0
mesh.update()
self.depsgraph.update()
armature = bobject.find_armature()
apply_modifiers = not armature
@ -1463,6 +1552,15 @@ Make sure the mesh only has tris/quads.""")
bobject_eval = bobject.evaluated_get(self.depsgraph) if apply_modifiers else bobject
export_mesh = bobject_eval.to_mesh()
if shape_keys:
if(len(bobject.data.uv_layers) > 2):
if(bobject.data.uv_layers.get('UVMap_shape_key') is not None):
self.export_shape_keys(bobject, export_mesh, out_mesh)
else:
log.warn(oid + ' has 2 or more UV Maps. Shape keys are not supported for objects with 2 or more UV maps')
else:
self.export_shape_keys(bobject, export_mesh, out_mesh)
if export_mesh is None:
log.warn(oid + ' was not exported')
return
@ -1487,9 +1585,8 @@ Make sure the mesh only has tris/quads.""")
if shape_keys:
bobject.active_shape_key_index = active_shape_key_index
bobject.show_only_shape_key = show_only_shape_key
for m in range(len(current_morph_value)):
shape_keys.key_blocks[m].value = current_morph_value[m]
bobject.active_shape_key.value = current_morph_value
self.depsgraph.update()
mesh.update()