export shape keys as texture
This commit is contained in:
parent
93563dbba5
commit
b7941d2fa8
|
@ -22,6 +22,8 @@ import numpy as np
|
||||||
import bpy
|
import bpy
|
||||||
from mathutils import *
|
from mathutils import *
|
||||||
|
|
||||||
|
import bmesh
|
||||||
|
|
||||||
import arm.assets as assets
|
import arm.assets as assets
|
||||||
import arm.exporter_opt as exporter_opt
|
import arm.exporter_opt as exporter_opt
|
||||||
import arm.log as log
|
import arm.log as log
|
||||||
|
@ -1114,6 +1116,119 @@ class ArmoryExporter:
|
||||||
if 'constraints' not in oskin:
|
if 'constraints' not in oskin:
|
||||||
oskin['constraints'] = []
|
oskin['constraints'] = []
|
||||||
self.add_constraints(bone, oskin, bone=True)
|
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):
|
def write_mesh(self, bobject: bpy.types.Object, fp, out_mesh):
|
||||||
if bpy.data.worlds['Arm'].arm_single_data_file:
|
if bpy.data.worlds['Arm'].arm_single_data_file:
|
||||||
|
@ -1142,8 +1257,15 @@ class ArmoryExporter:
|
||||||
num_verts = len(loops)
|
num_verts = len(loops)
|
||||||
num_uv_layers = len(exportMesh.uv_layers)
|
num_uv_layers = len(exportMesh.uv_layers)
|
||||||
is_baked = self.has_baked_material(bobject, exportMesh.materials)
|
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
|
has_tex1 = has_tex and num_uv_layers > 1
|
||||||
|
print(has_tex)
|
||||||
|
print(has_tex1)
|
||||||
num_colors = len(exportMesh.vertex_colors)
|
num_colors = len(exportMesh.vertex_colors)
|
||||||
has_col = self.get_export_vcols(bobject.data) and num_colors > 0
|
has_col = self.get_export_vcols(bobject.data) and num_colors > 0
|
||||||
has_tang = self.has_tangents(bobject.data)
|
has_tang = self.has_tangents(bobject.data)
|
||||||
|
@ -1410,52 +1532,19 @@ Make sure the mesh only has tris/quads.""")
|
||||||
struct_flag = False
|
struct_flag = False
|
||||||
|
|
||||||
# Save the morph state if necessary
|
# Save the morph state if necessary
|
||||||
active_shape_key_index = bobject.active_shape_key_index
|
active_shape_key_index = 0
|
||||||
show_only_shape_key = bobject.show_only_shape_key
|
show_only_shape_key = False
|
||||||
current_morph_value = []
|
current_morph_value = 0
|
||||||
|
|
||||||
shape_keys = ArmoryExporter.get_shape_keys(mesh)
|
shape_keys = ArmoryExporter.get_shape_keys(mesh)
|
||||||
if shape_keys:
|
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.active_shape_key_index = 0
|
||||||
bobject.show_only_shape_key = True
|
bobject.show_only_shape_key = True
|
||||||
|
self.depsgraph.update()
|
||||||
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()
|
|
||||||
|
|
||||||
armature = bobject.find_armature()
|
armature = bobject.find_armature()
|
||||||
apply_modifiers = not 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
|
bobject_eval = bobject.evaluated_get(self.depsgraph) if apply_modifiers else bobject
|
||||||
export_mesh = bobject_eval.to_mesh()
|
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:
|
if export_mesh is None:
|
||||||
log.warn(oid + ' was not exported')
|
log.warn(oid + ' was not exported')
|
||||||
return
|
return
|
||||||
|
@ -1487,9 +1585,8 @@ Make sure the mesh only has tris/quads.""")
|
||||||
if shape_keys:
|
if shape_keys:
|
||||||
bobject.active_shape_key_index = active_shape_key_index
|
bobject.active_shape_key_index = active_shape_key_index
|
||||||
bobject.show_only_shape_key = show_only_shape_key
|
bobject.show_only_shape_key = show_only_shape_key
|
||||||
|
bobject.active_shape_key.value = current_morph_value
|
||||||
for m in range(len(current_morph_value)):
|
self.depsgraph.update()
|
||||||
shape_keys.key_blocks[m].value = current_morph_value[m]
|
|
||||||
|
|
||||||
mesh.update()
|
mesh.update()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue