From b7941d2fa8ce3ddd44bf303d2c557b8835c6aa43 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 14 Oct 2021 21:19:18 +0200 Subject: [PATCH 01/36] export shape keys as texture --- blender/arm/exporter.py | 187 ++++++++++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 45 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 325a06e9..56d27299 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -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() From 10d41e5eca14a217afe6ecaee5bb00504e1dcf3e Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 14 Oct 2021 21:19:36 +0200 Subject: [PATCH 02/36] init make morph target --- blender/arm/make_morph_target.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 blender/arm/make_morph_target.py diff --git a/blender/arm/make_morph_target.py b/blender/arm/make_morph_target.py new file mode 100644 index 00000000..c1070bf0 --- /dev/null +++ b/blender/arm/make_morph_target.py @@ -0,0 +1,20 @@ +import arm.utils + +if arm.is_reload(__name__): + arm.utils = arm.reload_module(arm.utils) +else: + arm.enable_reload(__name__) + + + + +def write(vert): + vert.add_include('compiled.inc') + vert.add_include('std/morph_target.glsl') + + vert.add_in('vec2 tex1') + vert.write_attrib('texCoord1 = tex1 * texUnpack;') + + vert.add_uniform('float posUnpack', link='_posUnpack') + vert.add_uniform('sampler2D morphData', link='_morphData') + vert.add_uniform('vec4 morphPosition', link='_morphPosition', included=True) \ No newline at end of file From 09433f2a19b6f9d6d493156d18f48d3ba3e9e66a Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 14 Oct 2021 21:19:51 +0200 Subject: [PATCH 03/36] init morph target glsl --- Shaders/std/morph_target.glsl | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Shaders/std/morph_target.glsl diff --git a/Shaders/std/morph_target.glsl b/Shaders/std/morph_target.glsl new file mode 100644 index 00000000..6fdf4b2f --- /dev/null +++ b/Shaders/std/morph_target.glsl @@ -0,0 +1,6 @@ +#version 450 + +uniform sampler2D morphData; +uniform float morphWeights[maxMorphTargets]; + +in vec2 texCoord; From 867100d1516699b468d1314fd15deb59b5915935 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:42:46 +0200 Subject: [PATCH 04/36] make shader attribute for morph targets --- blender/arm/material/make_attrib.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/blender/arm/material/make_attrib.py b/blender/arm/material/make_attrib.py index b792bad3..49e42675 100644 --- a/blender/arm/material/make_attrib.py +++ b/blender/arm/material/make_attrib.py @@ -6,6 +6,7 @@ import arm.material.make_skin as make_skin import arm.material.make_particle as make_particle import arm.material.make_inst as make_inst import arm.material.make_tess as make_tess +import arm.material.make_morph_target as make_morph_target from arm.material.shader import Shader, ShaderContext import arm.utils @@ -16,6 +17,7 @@ if arm.is_reload(__name__): make_particle = arm.reload_module(make_particle) make_inst = arm.reload_module(make_inst) make_tess = arm.reload_module(make_tess) + make_morph_target = arm.reload_module(make_morph_target) arm.material.shader = arm.reload_module(arm.material.shader) from arm.material.shader import Shader, ShaderContext arm.utils = arm.reload_module(arm.utils) @@ -50,13 +52,19 @@ def write_vertpos(vert): def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor=True): + print('writing nor pos') is_bone = con_mesh.is_elem('bone') + is_morph = con_mesh.is_elem('morph') + if is_morph: + make_morph_target.morph_pos(vert) if is_bone: make_skin.skin_pos(vert) if write_nor: prep = 'vec3 ' if declare else '' + if is_morph: + make_morph_target.morph_nor(vert, is_bone, prep) if is_bone: - make_skin.skin_nor(vert, prep) + make_skin.skin_nor(vert, is_morph, prep) else: vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));') if con_mesh.is_elem('ipos'): From fb004f5dcca117f35d99a2b2bb320c7d6d593e0b Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:44:10 +0200 Subject: [PATCH 05/36] make attributes for shadow maps and depth --- blender/arm/material/make_depth.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blender/arm/material/make_depth.py b/blender/arm/material/make_depth.py index 75ec922c..8273953a 100644 --- a/blender/arm/material/make_depth.py +++ b/blender/arm/material/make_depth.py @@ -8,6 +8,7 @@ import arm.material.make_inst as make_inst import arm.material.make_tess as make_tess import arm.material.make_particle as make_particle import arm.material.make_finalize as make_finalize +import arm.material.make_morph_target as make_morph_target import arm.assets as assets import arm.utils @@ -50,6 +51,9 @@ def make(context_id, rpasses, shadowmap=False): if parse_opacity: frag.write('float opacity;') + + if(con_depth).is_elem('morph'): + make_morph_target.morph_pos(vert) if con_depth.is_elem('bone'): make_skin.skin_pos(vert) From 34243ed74c4c72f166cb7fc2f88bc7f8a0ddbe08 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:46:03 +0200 Subject: [PATCH 06/36] implement make_morph_target --- blender/arm/material/make_morph_target.py | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 blender/arm/material/make_morph_target.py diff --git a/blender/arm/material/make_morph_target.py b/blender/arm/material/make_morph_target.py new file mode 100644 index 00000000..6436221e --- /dev/null +++ b/blender/arm/material/make_morph_target.py @@ -0,0 +1,24 @@ +import arm.utils + +if arm.is_reload(__name__): + arm.utils = arm.reload_module(arm.utils) +else: + arm.enable_reload(__name__) + +def morph_pos(vert): + rpdat = arm.utils.get_rp() + vert.add_include('compiled.inc') + vert.add_include('std/morph_target.glsl') + vert.add_uniform('sampler2D morphDataPos', link='_morphDataPos', included=True) + vert.add_uniform('sampler2D morphDataNor', link='_morphDataNor', included=True) + vert.add_uniform('float morphWeights[32]', link='_morphWeights', included=True) + vert.add_uniform('vec2 morphScaleOffset', link='_morphScaleOffset', included=True) + vert.add_uniform('float texUnpack', link='_texUnpack') + vert.write_attrib('vec2 texCoordMorph = morph * texUnpack;') + vert.write_attrib('getMorphedVertex(texCoordMorph, spos.xyz);') + +def morph_nor(vert, is_bone, prep): + vert.write_attrib('vec3 morphNor;') + vert.write_attrib('getMorphedNormal(texCoordMorph, vec3(nor.xy, pos.w), morphNor);') + if not is_bone: + vert.write_attrib(prep + 'wnormal = normalize(N * morphNor);') From 6f9b51a57a43407700be8f37120c1520d456ad8b Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:48:58 +0200 Subject: [PATCH 07/36] move make_morph_target --- blender/arm/make_morph_target.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 blender/arm/make_morph_target.py diff --git a/blender/arm/make_morph_target.py b/blender/arm/make_morph_target.py deleted file mode 100644 index c1070bf0..00000000 --- a/blender/arm/make_morph_target.py +++ /dev/null @@ -1,20 +0,0 @@ -import arm.utils - -if arm.is_reload(__name__): - arm.utils = arm.reload_module(arm.utils) -else: - arm.enable_reload(__name__) - - - - -def write(vert): - vert.add_include('compiled.inc') - vert.add_include('std/morph_target.glsl') - - vert.add_in('vec2 tex1') - vert.write_attrib('texCoord1 = tex1 * texUnpack;') - - vert.add_uniform('float posUnpack', link='_posUnpack') - vert.add_uniform('sampler2D morphData', link='_morphData') - vert.add_uniform('vec4 morphPosition', link='_morphPosition', included=True) \ No newline at end of file From 8c034655be48f2722bc5dc7ada06591ab91f05b9 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:49:25 +0200 Subject: [PATCH 08/36] implement morph_target.glsl --- Shaders/std/morph_target.glsl | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Shaders/std/morph_target.glsl b/Shaders/std/morph_target.glsl index 6fdf4b2f..616acb20 100644 --- a/Shaders/std/morph_target.glsl +++ b/Shaders/std/morph_target.glsl @@ -1,6 +1,27 @@ -#version 450 +uniform sampler2D morphDataPos; +uniform sampler2D morphDataNor; +uniform vec2 morphScaleOffset; +uniform float morphWeights[32]; -uniform sampler2D morphData; -uniform float morphWeights[maxMorphTargets]; +void getMorphedVertex(vec2 uvCoord, inout vec3 A){ + for(int i = 0; i<32; i++ ) + { + vec2 tempCoord = uvCoord; + tempCoord.y *= i; + vec3 morph = texture(morphDataPos, tempCoord).rgb * morphScaleOffset.x + morphScaleOffset.y; + A += morphWeights[i] * morph; + } +} -in vec2 texCoord; +void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){ + for(int i = 0; i<32; i++ ) + { + vec2 tempCoord = uvCoord; + tempCoord.y *= i; + vec3 norm = morphWeights[i] + (texture(morphDataNor, tempCoord).rgb * 2.0 - 1.0); + morphNor += norm; + } + + morphNor += oldNor; + morphNor = normalize(morphNor); +} \ No newline at end of file From 26a0fb8c5a6f439c819c9cfb500bc8745f1298e4 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:52:16 +0200 Subject: [PATCH 09/36] add morph target to make_shader.py --- blender/arm/material/make_shader.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blender/arm/material/make_shader.py b/blender/arm/material/make_shader.py index 8e95fa5c..ac541ef3 100644 --- a/blender/arm/material/make_shader.py +++ b/blender/arm/material/make_shader.py @@ -187,6 +187,9 @@ def make_instancing_and_skinning(mat: Material, mat_users: Dict[Material, List[O for bo in mat_users[mat]: if mat.arm_custom_material == '': + # Morph Targets + if arm.utils.export_morph_targets(bo) and not arm.utils.export_vcols(bo): + global_elems.append({'name': 'morph', 'data': 'short2norm'}) # GPU Skinning if arm.utils.export_bone_data(bo): global_elems.append({'name': 'bone', 'data': 'short4norm'}) From 2f77c5b06086c54c641563c28cc1dec443d141b6 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:53:23 +0200 Subject: [PATCH 10/36] modify normals calculation in skinning for morphing --- blender/arm/material/make_skin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blender/arm/material/make_skin.py b/blender/arm/material/make_skin.py index aaaff3ce..1d4244fd 100644 --- a/blender/arm/material/make_skin.py +++ b/blender/arm/material/make_skin.py @@ -22,6 +22,9 @@ def skin_pos(vert): vert.write_attrib('spos.xyz /= posUnpack;') -def skin_nor(vert, prep): +def skin_nor(vert, is_morph, prep): rpdat = arm.utils.get_rp() - vert.write_attrib(prep + 'wnormal = normalize(N * (vec3(nor.xy, pos.w) + 2.0 * cross(skinA.xyz, cross(skinA.xyz, vec3(nor.xy, pos.w)) + skinA.w * vec3(nor.xy, pos.w))));') + if(is_morph): + vert.write_attrib(prep + 'wnormal = normalize(N * morphNor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, morphNor) + skinA.w * morphNor)));') + else: + vert.write_attrib(prep + 'wnormal = normalize(N * (vec3(nor.xy, pos.w) + 2.0 * cross(skinA.xyz, cross(skinA.xyz, vec3(nor.xy, pos.w)) + skinA.w * vec3(nor.xy, pos.w))));') From 0636eb85c66ca1b35b657fa566da0dd0d3433b6f Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:54:00 +0200 Subject: [PATCH 11/36] add morph attribute to shaders --- blender/arm/material/shader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/material/shader.py b/blender/arm/material/shader.py index ada7534e..ec091f4e 100644 --- a/blender/arm/material/shader.py +++ b/blender/arm/material/shader.py @@ -92,7 +92,7 @@ class ShaderContext: def sort_vs(self): vs = [] - ar = ['pos', 'nor', 'tex', 'tex1', 'col', 'tang', 'bone', 'weight', 'ipos', 'irot', 'iscl'] + ar = ['pos', 'nor', 'tex', 'tex1', 'morph', 'col', 'tang', 'bone', 'weight', 'ipos', 'irot', 'iscl'] for ename in ar: elem = self.get_elem(ename) if elem != None: From 3814cd9da7d68239cb59924898e252ec381bcee8 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 19:55:20 +0200 Subject: [PATCH 12/36] methods to get if vertex colors and if shapekey present --- blender/arm/utils.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/blender/arm/utils.py b/blender/arm/utils.py index 6d00775e..7abf3524 100755 --- a/blender/arm/utils.py +++ b/blender/arm/utils.py @@ -770,6 +770,20 @@ def export_bone_data(bobject: bpy.types.Object) -> bool: """Returns whether the bone data of the given object should be exported.""" return bobject.find_armature() and is_bone_animation_enabled(bobject) and get_rp().arm_skin == 'On' +def export_morph_targets(bobject: bpy.types.Object) -> bool: + if not hasattr(bobject.data, 'shape_keys'): + return False + + shape_keys = bobject.data.shape_keys + if shape_keys and len(shape_keys.key_blocks) > 1: + return shape_keys + return None + +def export_vcols(bobject: bpy.types.Object) -> bool: + for material in bobject.data.materials: + if material is not None and material.export_vcols: + return True + return False def open_editor(hx_path=None): ide_bin = get_ide_bin() From d01c55fcd4ce0e7027212bfb2cc594a1e85b2b5a Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 18 Oct 2021 20:00:06 +0200 Subject: [PATCH 13/36] mordify to export shape keys --- blender/arm/exporter.py | 139 ++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 56d27299..6750fcfa 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -207,6 +207,14 @@ class ArmoryExporter: return shape_keys return None + @staticmethod + def get_morph_uv_index(mesh): + i = 0 + for uv_layer in mesh.uv_layers: + if uv_layer.name == 'UVMap_shape_key': + return i + i +=1 + def find_bone(self, name: str) -> Optional[Tuple[bpy.types.Bone, Dict]]: """Finds the bone reference (a tuple containing the bone object and its data) by the given name and returns it.""" @@ -1125,24 +1133,27 @@ class ArmoryExporter: vert_pos = [] vert_nor = [] names = [] - default_values = [] + default_values = [0] * max_shape_keys + + shape_key_base = bobject.data.shape_keys.key_blocks[0] count = 0 - for shape_key in bobject.data.shape_keys.key_blocks: + for shape_key in bobject.data.shape_keys.key_blocks[1:]: if(count > max_shape_keys): break - vert_data = self.get_vertex_data_from_shape_key(shape_key) + vert_data = self.get_vertex_data_from_shape_key(shape_key_base, 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) + default_values[count] = shape_key.value count += 1 min, max = self.bake_to_image(vert_pos, vert_nor, name, output_dir) morph_target = {} + morph_target['morph_target_data_file'] = name morph_target['morph_target_ref'] = names morph_target['morph_target_defaults'] = default_values morph_target['num_morph_targets'] = count @@ -1150,14 +1161,14 @@ class ArmoryExporter: 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): + def get_vertex_data_from_shape_key(self, shape_key_base, shape_key_data): + base_vert_pos = shape_key_base.data.values() + base_vert_nor = shape_key_base.normals_split_get() vert_pos = shape_key_data.data.values() - vert_nor = shape_key_data.normals_vertex_get() + vert_nor = shape_key_data.normals_split_get() num_verts = len(vert_pos) @@ -1165,10 +1176,10 @@ class ArmoryExporter: nor = [] for i in range(num_verts): - pos.append(list(vert_pos[i].co)) + pos.append(list(vert_pos[i].co - base_vert_pos[i].co)) temp = [] for j in range(3): - temp.append(vert_nor[j + i * 3]) + temp.append(vert_nor[j + i * 3] - base_vert_nor[j + i * 3]) nor.append(temp) return {'pos': pos, 'nor': nor} @@ -1257,50 +1268,59 @@ class ArmoryExporter: num_verts = len(loops) num_uv_layers = len(exportMesh.uv_layers) is_baked = self.has_baked_material(bobject, exportMesh.materials) - 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 + # Check if shape keys were exported + has_morph_target = self.get_shape_keys(bobject.data) and not has_col + if(has_morph_target): + num_uv_layers -= 1 + morph_uv_index = self.get_morph_uv_index(bobject.data) + has_tex = (self.get_export_uvs(bobject.data) and num_uv_layers > 0) or is_baked + has_tex1 = has_tex and num_uv_layers > 1 has_tang = self.has_tangents(bobject.data) 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 has_tex1: - lay1 = uv_layers[t1map] - for v in lay1.data: + if has_tex: + t0map = 0 # Get active uvmap + t0data = np.empty(num_verts * 2, dtype=' maxdim: + maxdim = abs(v.uv[0]) + if abs(v.uv[1]) > maxdim: + maxdim = abs(v.uv[1]) + if has_tex1: + lay1 = uv_layers[t1map] + for v in lay1.data: + if abs(v.uv[0]) > maxdim: + maxdim = abs(v.uv[0]) + if abs(v.uv[1]) > maxdim: + maxdim = abs(v.uv[1]) + if has_morph_target: + morph_data = np.empty(num_verts * 2, dtype=' maxdim: maxdim = abs(v.uv[0]) if abs(v.uv[1]) > maxdim: @@ -1342,6 +1362,8 @@ Make sure the mesh only has tris/quads.""") lay0 = exportMesh.uv_layers[t0map] if has_tex1: lay1 = exportMesh.uv_layers[t1map] + if has_morph_target: + lay2 = exportMesh.uv_layers[morph_uv_index] if has_col: vcol0 = exportMesh.vertex_colors[0].data @@ -1372,6 +1394,10 @@ Make sure the mesh only has tris/quads.""") tangdata[i3 ] = tang[0] tangdata[i3 + 1] = tang[1] tangdata[i3 + 2] = tang[2] + if has_morph_target: + uv = lay2.data[loop.index].uv + morph_data[i2 ] = uv[0] + morph_data[i2 + 1] = 1.0 - uv[1] if has_col: col = vcol0[loop.index].color i3 = i * 3 @@ -1432,6 +1458,9 @@ Make sure the mesh only has tris/quads.""") if has_tex1: t1data *= invscale_tex t1data = np.array(t1data, dtype=' 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) + self.export_shape_keys(bobject, export_mesh, out_mesh) if export_mesh is None: log.warn(oid + ' was not exported') From 6e153569dcfd545c7b83fa94f84260b7ec4ce603 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 16:23:06 +0200 Subject: [PATCH 14/36] make attrib skin fix --- blender/arm/material/make_attrib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blender/arm/material/make_attrib.py b/blender/arm/material/make_attrib.py index 49e42675..2f1e167c 100644 --- a/blender/arm/material/make_attrib.py +++ b/blender/arm/material/make_attrib.py @@ -56,6 +56,7 @@ def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor is_bone = con_mesh.is_elem('bone') is_morph = con_mesh.is_elem('morph') if is_morph: + print('is_morph') make_morph_target.morph_pos(vert) if is_bone: make_skin.skin_pos(vert) @@ -63,7 +64,7 @@ def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor prep = 'vec3 ' if declare else '' if is_morph: make_morph_target.morph_nor(vert, is_bone, prep) - if is_bone: + elif is_bone: make_skin.skin_nor(vert, is_morph, prep) else: vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));') From 8e7d8a4f56bf31135fc6d72eedbb30e7504b268f Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 16:23:39 +0200 Subject: [PATCH 15/36] additional uniforms --- blender/arm/material/make_morph_target.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blender/arm/material/make_morph_target.py b/blender/arm/material/make_morph_target.py index 6436221e..f6866655 100644 --- a/blender/arm/material/make_morph_target.py +++ b/blender/arm/material/make_morph_target.py @@ -11,8 +11,9 @@ def morph_pos(vert): vert.add_include('std/morph_target.glsl') vert.add_uniform('sampler2D morphDataPos', link='_morphDataPos', included=True) vert.add_uniform('sampler2D morphDataNor', link='_morphDataNor', included=True) - vert.add_uniform('float morphWeights[32]', link='_morphWeights', included=True) + vert.add_uniform('vec4 morphWeights[8]', link='_morphWeights', included=True) vert.add_uniform('vec2 morphScaleOffset', link='_morphScaleOffset', included=True) + vert.add_uniform('vec2 morphDataDim', link='_morphDataDim', included=True) vert.add_uniform('float texUnpack', link='_texUnpack') vert.write_attrib('vec2 texCoordMorph = morph * texUnpack;') vert.write_attrib('getMorphedVertex(texCoordMorph, spos.xyz);') From e562d2515ef45739651d86d1bde81566b85ff69f Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 16:24:33 +0200 Subject: [PATCH 16/36] export with optimized texture size --- blender/arm/exporter.py | 100 +++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 27 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 6750fcfa..def22fac 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1149,8 +1149,24 @@ class ArmoryExporter: default_values[count] = shape_key.value count += 1 + + pos_array = np.array(vert_pos) + nor_array = np.array(vert_nor) + max = np.amax(pos_array) + min = np.amin(pos_array) + + array_size = len(pos_array[0]), len(pos_array) + + img_size, extra_zeros, block_size = self.get_best_image_size(array_size) + + if(img_size < 1): + log.error(f"""object {bobject.name} contains too many vertices or shape keys to support shape keys export""") + self.remove_morph_uv_set(bobject) + return - min, max = self.bake_to_image(vert_pos, vert_nor, name, output_dir) + self.bake_to_image(pos_array, nor_array, max, min, extra_zeros, img_size, name, output_dir) + + self.create_morph_uv_set(bobject, img_size) morph_target = {} morph_target['morph_target_data_file'] = name @@ -1159,6 +1175,8 @@ class ArmoryExporter: morph_target['num_morph_targets'] = count morph_target['morph_scale'] = max - min morph_target['morph_offset'] = min + morph_target['morph_img_size'] = img_size + morph_target['morph_block_size'] = block_size out_mesh['morph_target'] = morph_target return @@ -1184,44 +1202,67 @@ class ArmoryExporter: 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) + def bake_to_image(self, pos_array, nor_array, pos_max, pos_min, extra_x, img_size, name, output_dir): pos_array_scaled = np.interp(pos_array, (pos_min, pos_max), (0, 1)) - self.write_output_image(pos_array_scaled, name + '_pos', output_dir) + self.write_output_image(pos_array_scaled, extra_x, img_size, name + '_morph_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 + self.write_output_image(nor_array_scaled, extra_x, img_size, name + '_morph_nor', output_dir) - def write_output_image(self, data, name, output_dir): + def write_output_image(self, data, extra_x, img_size, name, output_dir): size = len(data[0]), len(data) + print("size 0 =") + print(size[0]) + print("size 1 =") + print(size[1]) + print(len(data)) + print(len(data[0])) + print(len(data[0][0])) + data = np.pad(data, ((0, 0), (0, extra_x), (0, 0)), 'minimum') + print(len(data)) + print(len(data[0])) + print(len(data[0][0])) + + print("img_size = ") + pixel_list = [] - for y in range(size[1]): - for x in range(size[0]): + for y in range(len(data)): + for x in range(len(data[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 = bpy.data.images.new(name, width = img_size, height = img_size, is_data = True) image.pixels = pixel_list image.save_render(output_dir + name + ".png", scene= bpy.context.scene) bpy.data.images.remove(image) + + def get_best_image_size(self, size): + + for i in range(1, 12): + block_len = pow(2, i) + block_height = np.ceil(size[0]/block_len) + if(block_height * size[1] <= block_len): + extra_zeros_x = block_height * block_len - size[0] + return pow(2,i), round(extra_zeros_x), block_height + + return 0, 0, 0 - def create_morph_uv_set(self, obj): - + def remove_morph_uv_set(self, obj): + layer = obj.data.uv_layers.get('UVMap_shape_key') + if(layer is not None): + obj.data.uv_layers.remove(layer) + + def create_morph_uv_set(self, obj, img_size): + if(obj.data.uv_layers.get('UVMap_shape_key') is None): obj.data.uv_layers.new(name = 'UVMap_shape_key') @@ -1229,14 +1270,18 @@ class ArmoryExporter: bm.from_mesh(obj.data) uv_layer = bm.loops.layers.uv.get('UVMap_shape_key') - pixel_size = 1.0 / len(bm.verts) + pixel_size = 1.0 / img_size - i= 0 + i = 0 + j = 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)) + uv_data.uv = Vector(((i + 0.5) * pixel_size, (j + 0.5) * pixel_size)) i += 1 + if(i > img_size - 1): + j += 1 + i = 0 bm.to_mesh(obj.data) bm.free() @@ -1295,12 +1340,12 @@ class ArmoryExporter: break else: for i in range(0, len(uv_layers)): - if uv_layers[i].active_render and uv_layers[i].name is not 'UVMap_shape_key': + if uv_layers[i].active_render and uv_layers[i].name != 'UVMap_shape_key': t0map = i break if has_tex1: for i in range(0, len(uv_layers)): - if i is not t0map and uv_layers.name is not 'UVMap_shape_key': + if i != t0map and uv_layers.name != 'UVMap_shape_key': t1map = i t1data = np.empty(num_verts * 2, dtype=' Date: Wed, 27 Oct 2021 16:25:11 +0200 Subject: [PATCH 17/36] debug prints --- blender/arm/material/make_shader.py | 1 + blender/arm/utils.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/blender/arm/material/make_shader.py b/blender/arm/material/make_shader.py index ac541ef3..026f1628 100644 --- a/blender/arm/material/make_shader.py +++ b/blender/arm/material/make_shader.py @@ -189,6 +189,7 @@ def make_instancing_and_skinning(mat: Material, mat_users: Dict[Material, List[O if mat.arm_custom_material == '': # Morph Targets if arm.utils.export_morph_targets(bo) and not arm.utils.export_vcols(bo): + print("sk and no vcol") global_elems.append({'name': 'morph', 'data': 'short2norm'}) # GPU Skinning if arm.utils.export_bone_data(bo): diff --git a/blender/arm/utils.py b/blender/arm/utils.py index 7abf3524..e6bb544d 100755 --- a/blender/arm/utils.py +++ b/blender/arm/utils.py @@ -772,17 +772,22 @@ def export_bone_data(bobject: bpy.types.Object) -> bool: def export_morph_targets(bobject: bpy.types.Object) -> bool: if not hasattr(bobject.data, 'shape_keys'): + print('A') return False shape_keys = bobject.data.shape_keys if shape_keys and len(shape_keys.key_blocks) > 1: - return shape_keys - return None + print('B') + return True + print('C') + return False def export_vcols(bobject: bpy.types.Object) -> bool: for material in bobject.data.materials: if material is not None and material.export_vcols: + print('D') return True + print('E') return False def open_editor(hx_path=None): From 4522055facedb58fcb331f64f6ae5e2ffa110df4 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 16:25:49 +0200 Subject: [PATCH 18/36] use vec4 uniform array instead of float array --- Shaders/std/morph_target.glsl | 52 +++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/Shaders/std/morph_target.glsl b/Shaders/std/morph_target.glsl index 616acb20..d9096ef6 100644 --- a/Shaders/std/morph_target.glsl +++ b/Shaders/std/morph_target.glsl @@ -1,27 +1,55 @@ uniform sampler2D morphDataPos; uniform sampler2D morphDataNor; uniform vec2 morphScaleOffset; -uniform float morphWeights[32]; +uniform vec2 morphDataDim; +uniform vec4 morphWeights[8]; void getMorphedVertex(vec2 uvCoord, inout vec3 A){ - for(int i = 0; i<32; i++ ) - { - vec2 tempCoord = uvCoord; - tempCoord.y *= i; - vec3 morph = texture(morphDataPos, tempCoord).rgb * morphScaleOffset.x + morphScaleOffset.y; - A += morphWeights[i] * morph; + for(int i = 0; i<8; i++ ) + { + vec4 tempCoordX = vec4( uvCoord.x); + vec4 tempCoordY = vec4( uvCoord.y - (i) * morphDataDim.y, + uvCoord.y - (i + 1) * morphDataDim.y, + uvCoord.y - (i + 2) * morphDataDim.y, + uvCoord.y - (i + 3) * morphDataDim.y); + + vec3 morph = texture(morphDataPos, vec2(tempCoordX.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y; + A += morphWeights[i].x * morph; + + morph = texture(morphDataPos, vec2(tempCoordX.y, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y; + A += morphWeights[i].y * morph; + + morph = texture(morphDataPos, vec2(tempCoordX.z, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y; + A += morphWeights[i].z * morph; + + morph = texture(morphDataPos, vec2(tempCoordX.w, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y; + A += morphWeights[i].w * morph; } } void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){ - for(int i = 0; i<32; i++ ) + + for(int i = 0; i<8; i++ ) { - vec2 tempCoord = uvCoord; - tempCoord.y *= i; - vec3 norm = morphWeights[i] + (texture(morphDataNor, tempCoord).rgb * 2.0 - 1.0); + vec4 tempCoordX = vec4( uvCoord.x); + vec4 tempCoordY = vec4( uvCoord.y - (i) * morphDataDim.y, + uvCoord.y - (i + 1) * morphDataDim.y, + uvCoord.y - (i + 2) * morphDataDim.y, + uvCoord.y - (i + 3) * morphDataDim.y); + + vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(tempCoordX.x, tempCoordY.x)).rgb * 2.0 - 1.0); morphNor += norm; + + norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(tempCoordX.y, tempCoordY.y)).rgb * 2.0 - 1.0); + morphNor += norm; + + norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(tempCoordX.z, tempCoordY.z)).rgb * 2.0 - 1.0); + morphNor += norm; + + norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(tempCoordX.w, tempCoordY.w)).rgb * 2.0 - 1.0); + morphNor += norm; + } - morphNor += oldNor; morphNor = normalize(morphNor); } \ No newline at end of file From cb2cf0c9b4219d3b7247bfa09eeae1d487f84fba Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 16:41:50 +0200 Subject: [PATCH 19/36] keep both vert colors and shape keys --- blender/arm/exporter.py | 5 +---- blender/arm/material/make_shader.py | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index def22fac..38f56057 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1316,7 +1316,7 @@ class ArmoryExporter: num_colors = len(exportMesh.vertex_colors) has_col = self.get_export_vcols(bobject.data) and num_colors > 0 # Check if shape keys were exported - has_morph_target = self.get_shape_keys(bobject.data) and not has_col + has_morph_target = self.get_shape_keys(bobject.data) if(has_morph_target): num_uv_layers -= 1 morph_uv_index = self.get_morph_uv_index(bobject.data) @@ -1614,9 +1614,6 @@ Make sure the mesh only has tris/quads.""") shape_keys = ArmoryExporter.get_shape_keys(mesh) if shape_keys: - if self.get_export_vcols(bobject.data): - log.warn(oid + ' has vertex colors. Shape keys are not supported for objects with vertex colors') - shape_keys = False # Save the morph state active_shape_key_index = bobject.active_shape_key_index show_only_shape_key = bobject.show_only_shape_key diff --git a/blender/arm/material/make_shader.py b/blender/arm/material/make_shader.py index 026f1628..ccc0899c 100644 --- a/blender/arm/material/make_shader.py +++ b/blender/arm/material/make_shader.py @@ -188,8 +188,7 @@ def make_instancing_and_skinning(mat: Material, mat_users: Dict[Material, List[O for bo in mat_users[mat]: if mat.arm_custom_material == '': # Morph Targets - if arm.utils.export_morph_targets(bo) and not arm.utils.export_vcols(bo): - print("sk and no vcol") + if arm.utils.export_morph_targets(bo): global_elems.append({'name': 'morph', 'data': 'short2norm'}) # GPU Skinning if arm.utils.export_bone_data(bo): From edeaf488b92af295770d6cedb1bc47fa005c9d89 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 16:42:08 +0200 Subject: [PATCH 20/36] remove debug prints --- blender/arm/exporter.py | 15 --------------- blender/arm/material/make_attrib.py | 2 -- blender/arm/utils.py | 5 ----- 3 files changed, 22 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 38f56057..f44c249c 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1214,22 +1214,7 @@ class ArmoryExporter: def write_output_image(self, data, extra_x, img_size, name, output_dir): - size = len(data[0]), len(data) - - print("size 0 =") - print(size[0]) - print("size 1 =") - print(size[1]) - print(len(data)) - print(len(data[0])) - print(len(data[0][0])) data = np.pad(data, ((0, 0), (0, extra_x), (0, 0)), 'minimum') - print(len(data)) - print(len(data[0])) - print(len(data[0][0])) - - print("img_size = ") - pixel_list = [] for y in range(len(data)): diff --git a/blender/arm/material/make_attrib.py b/blender/arm/material/make_attrib.py index 2f1e167c..7c80cb3b 100644 --- a/blender/arm/material/make_attrib.py +++ b/blender/arm/material/make_attrib.py @@ -52,11 +52,9 @@ def write_vertpos(vert): def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor=True): - print('writing nor pos') is_bone = con_mesh.is_elem('bone') is_morph = con_mesh.is_elem('morph') if is_morph: - print('is_morph') make_morph_target.morph_pos(vert) if is_bone: make_skin.skin_pos(vert) diff --git a/blender/arm/utils.py b/blender/arm/utils.py index e6bb544d..cba60d8c 100755 --- a/blender/arm/utils.py +++ b/blender/arm/utils.py @@ -772,22 +772,17 @@ def export_bone_data(bobject: bpy.types.Object) -> bool: def export_morph_targets(bobject: bpy.types.Object) -> bool: if not hasattr(bobject.data, 'shape_keys'): - print('A') return False shape_keys = bobject.data.shape_keys if shape_keys and len(shape_keys.key_blocks) > 1: - print('B') return True - print('C') return False def export_vcols(bobject: bpy.types.Object) -> bool: for material in bobject.data.materials: if material is not None and material.export_vcols: - print('D') return True - print('E') return False def open_editor(hx_path=None): From 8df6fc9176dd43ab178c658dc93d86219e6b217d Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 19:50:35 +0200 Subject: [PATCH 21/36] fix shape key with skinning --- blender/arm/material/make_attrib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blender/arm/material/make_attrib.py b/blender/arm/material/make_attrib.py index 7c80cb3b..8aaaaf24 100644 --- a/blender/arm/material/make_attrib.py +++ b/blender/arm/material/make_attrib.py @@ -62,9 +62,9 @@ def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor prep = 'vec3 ' if declare else '' if is_morph: make_morph_target.morph_nor(vert, is_bone, prep) - elif is_bone: + if is_bone: make_skin.skin_nor(vert, is_morph, prep) - else: + if not is_morph and not is_bone: vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));') if con_mesh.is_elem('ipos'): make_inst.inst_pos(con_mesh, vert) From 669898fae1d7755538577937ac8da21d6aea8a89 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 19:51:13 +0200 Subject: [PATCH 22/36] fix normals with shape key and skin --- blender/arm/material/make_skin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/material/make_skin.py b/blender/arm/material/make_skin.py index 1d4244fd..9df79c85 100644 --- a/blender/arm/material/make_skin.py +++ b/blender/arm/material/make_skin.py @@ -25,6 +25,6 @@ def skin_pos(vert): def skin_nor(vert, is_morph, prep): rpdat = arm.utils.get_rp() if(is_morph): - vert.write_attrib(prep + 'wnormal = normalize(N * morphNor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, morphNor) + skinA.w * morphNor)));') + vert.write_attrib(prep + 'wnormal = normalize(N * (morphNor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, morphNor) + skinA.w * morphNor)));') else: vert.write_attrib(prep + 'wnormal = normalize(N * (vec3(nor.xy, pos.w) + 2.0 * cross(skinA.xyz, cross(skinA.xyz, vec3(nor.xy, pos.w)) + skinA.w * vec3(nor.xy, pos.w))));') From 8e69bea14eeeb6dedb2cb660c8fd0489a6518ba1 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 19:53:08 +0200 Subject: [PATCH 23/36] appy position scale --- blender/arm/material/make_morph_target.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blender/arm/material/make_morph_target.py b/blender/arm/material/make_morph_target.py index f6866655..fc978b36 100644 --- a/blender/arm/material/make_morph_target.py +++ b/blender/arm/material/make_morph_target.py @@ -15,8 +15,11 @@ def morph_pos(vert): vert.add_uniform('vec2 morphScaleOffset', link='_morphScaleOffset', included=True) vert.add_uniform('vec2 morphDataDim', link='_morphDataDim', included=True) vert.add_uniform('float texUnpack', link='_texUnpack') + vert.add_uniform('float posUnpack', link='_posUnpack') vert.write_attrib('vec2 texCoordMorph = morph * texUnpack;') + vert.write_attrib('spos.xyz *= posUnpack;') vert.write_attrib('getMorphedVertex(texCoordMorph, spos.xyz);') + vert.write_attrib('spos.xyz /= posUnpack;') def morph_nor(vert, is_bone, prep): vert.write_attrib('vec3 morphNor;') From d4f99827686563f2b90a203b504a997cbbb98aa9 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 20:18:25 +0200 Subject: [PATCH 24/36] documentation 1 --- blender/arm/exporter.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index f44c249c..3ebbd00f 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1127,21 +1127,25 @@ class ArmoryExporter: def export_shape_keys(self, bobject: bpy.types.Object, export_mesh: bpy.types.Mesh, out_mesh): + # Max shape keys supported max_shape_keys = 32 + # Path to store shape key textures output_dir = bpy.path.abspath('//') + "Bundled\\" name = bobject.data.name vert_pos = [] vert_nor = [] names = [] default_values = [0] * max_shape_keys - + # Shape key base mesh shape_key_base = bobject.data.shape_keys.key_blocks[0] count = 0 + # Loop through all shape keys for shape_key in bobject.data.shape_keys.key_blocks[1:]: if(count > max_shape_keys): break + # get vertex data from shape key vert_data = self.get_vertex_data_from_shape_key(shape_key_base, shape_key) vert_pos.append(vert_data['pos']) vert_nor.append(vert_data['nor']) @@ -1150,24 +1154,32 @@ class ArmoryExporter: count += 1 + # Convert to array for easy manipulation pos_array = np.array(vert_pos) nor_array = np.array(vert_nor) + + # Min and Max values of shape key displacements max = np.amax(pos_array) min = np.amin(pos_array) array_size = len(pos_array[0]), len(pos_array) + # Get best 2^n image size to fit shape key data (min = 2 X 2, max = 4096 X 4096) img_size, extra_zeros, block_size = self.get_best_image_size(array_size) + # Image size required is too large. Skip export if(img_size < 1): log.error(f"""object {bobject.name} contains too many vertices or shape keys to support shape keys export""") self.remove_morph_uv_set(bobject) return + # Write data to image self.bake_to_image(pos_array, nor_array, max, min, extra_zeros, img_size, name, output_dir) + # Create a new UV set for shape keys self.create_morph_uv_set(bobject, img_size) + # Export Shape Key names, defaults, etc.. morph_target = {} morph_target['morph_target_data_file'] = name morph_target['morph_target_ref'] = names @@ -1193,27 +1205,31 @@ class ArmoryExporter: pos = [] nor = [] + # Loop through all vertices for i in range(num_verts): + # Vertex position relative to base vertex pos.append(list(vert_pos[i].co - base_vert_pos[i].co)) temp = [] for j in range(3): + # Vertex normal relative to base vertex temp.append(vert_nor[j + i * 3] - base_vert_nor[j + i * 3]) nor.append(temp) return {'pos': pos, 'nor': nor} def bake_to_image(self, pos_array, nor_array, pos_max, pos_min, extra_x, img_size, name, output_dir): - + # Scale position data between [0, 1] to bake to image pos_array_scaled = np.interp(pos_array, (pos_min, pos_max), (0, 1)) - + # Write positions to image self.write_output_image(pos_array_scaled, extra_x, img_size, name + '_morph_pos', output_dir) - + # Scale normal data between [0, 1] to bake to image nor_array_scaled = np.interp(nor_array, (-1, 1), (0, 1)) - + # Write normals to image self.write_output_image(nor_array_scaled, extra_x, img_size, name + '_morph_nor', output_dir) def write_output_image(self, data, extra_x, img_size, name, output_dir): - + + # Pad data with zeros to make up for required number of pixels of 2^n format data = np.pad(data, ((0, 0), (0, extra_x), (0, 0)), 'minimum') pixel_list = [] @@ -1247,7 +1263,7 @@ class ArmoryExporter: obj.data.uv_layers.remove(layer) def create_morph_uv_set(self, obj, img_size): - + # Get/ create morph UV set if(obj.data.uv_layers.get('UVMap_shape_key') is None): obj.data.uv_layers.new(name = 'UVMap_shape_key') @@ -1259,6 +1275,7 @@ class ArmoryExporter: i = 0 j = 0 + # Arrange UVs to match exported image pixels for v in bm.verts: for l in v.link_loops: uv_data = l[uv_layer] @@ -1303,6 +1320,7 @@ class ArmoryExporter: # Check if shape keys were exported has_morph_target = self.get_shape_keys(bobject.data) if(has_morph_target): + # Shape keys UV are exported separately, so reduce UV count by 1 num_uv_layers -= 1 morph_uv_index = self.get_morph_uv_index(bobject.data) has_tex = (self.get_export_uvs(bobject.data) and num_uv_layers > 0) or is_baked @@ -1614,8 +1632,10 @@ 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() + # Export shape keys here if shape_keys: self.export_shape_keys(bobject, export_mesh, out_mesh) + # Update dependancy after new UV layer was added self.depsgraph.update() bobject_eval = bobject.evaluated_get(self.depsgraph) if apply_modifiers else bobject export_mesh = bobject_eval.to_mesh() @@ -1640,7 +1660,7 @@ Make sure the mesh only has tris/quads.""") if armature: self.export_skin(bobject, armature, export_mesh, out_mesh) - # Restore the morph state + # Restore the morph state after mesh export if shape_keys: bobject.active_shape_key_index = active_shape_key_index bobject.show_only_shape_key = show_only_shape_key From b7cafbb4e3f679673b65a2f3faf60fff15115014 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 27 Oct 2021 20:49:59 +0200 Subject: [PATCH 25/36] add node to set shape key value --- .../armory/logicnode/SetObjectShapeKeyNode.hx | 23 +++++++++++++++++++ .../object/LN_set_object_shape_key.py | 16 +++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 Sources/armory/logicnode/SetObjectShapeKeyNode.hx create mode 100644 blender/arm/logicnode/object/LN_set_object_shape_key.py diff --git a/Sources/armory/logicnode/SetObjectShapeKeyNode.hx b/Sources/armory/logicnode/SetObjectShapeKeyNode.hx new file mode 100644 index 00000000..6df0760f --- /dev/null +++ b/Sources/armory/logicnode/SetObjectShapeKeyNode.hx @@ -0,0 +1,23 @@ +package armory.logicnode; + +import iron.object.MeshObject; + +class SetObjectShapeKeyNode extends LogicNode { + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + var object: Dynamic = inputs[1].get(); + var shapeKey: String = inputs[2].get(); + var value: Dynamic = inputs[3].get(); + + if (object == null) return; + var sk = cast(object, MeshObject).morphTarget; + if(sk == null) return; + + sk.setMorphValue(shapeKey, value); + runOutput(0); + } +} diff --git a/blender/arm/logicnode/object/LN_set_object_shape_key.py b/blender/arm/logicnode/object/LN_set_object_shape_key.py new file mode 100644 index 00000000..87888cdf --- /dev/null +++ b/blender/arm/logicnode/object/LN_set_object_shape_key.py @@ -0,0 +1,16 @@ +from arm.logicnode.arm_nodes import * + +class SetObjectShapeKeyNode(ArmLogicTreeNode): + """Sets shape key value of the object""" + bl_idname = 'LNSetObjectShapeKeyNode' + bl_label = 'Set Object Shape Key' + arm_section = 'props' + arm_version = 1 + + def arm_init(self, context): + self.add_input('ArmNodeSocketAction', 'In') + self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('ArmStringSocket', 'Shape Key') + self.add_input('ArmFloatSocket', 'Value') + + self.add_output('ArmNodeSocketAction', 'Out') From 13a2f4538ad30c4e75716bc584efcf03d7e5fa39 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 28 Oct 2021 15:47:49 +0200 Subject: [PATCH 26/36] move shape key textures to dedicated directory --- blender/arm/exporter.py | 2 +- blender/arm/write_data.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 3ebbd00f..d3cb3c8b 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1130,7 +1130,7 @@ class ArmoryExporter: # Max shape keys supported max_shape_keys = 32 # Path to store shape key textures - output_dir = bpy.path.abspath('//') + "Bundled\\" + output_dir = bpy.path.abspath('//') + "MorphTargets\\" name = bobject.data.name vert_pos = [] vert_nor = [] diff --git a/blender/arm/write_data.py b/blender/arm/write_data.py index 85f03577..924b54d1 100755 --- a/blender/arm/write_data.py +++ b/blender/arm/write_data.py @@ -87,6 +87,12 @@ project.addSources('Sources'); for file in glob.glob("Bundled/**", recursive=True): if os.path.isfile(file): assets.add(file) + + # Auto-add shape key textures if exists + if os.path.exists('MorphTargets'): + for file in glob.glob("MorphTargets/**", recursive=True): + if os.path.isfile(file): + assets.add(file) # Add project shaders if os.path.exists('Shaders'): From 4b65d8d68cbfea3489e12827a5325ad695eb3bd2 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 28 Oct 2021 15:48:08 +0200 Subject: [PATCH 27/36] do not export muted shape keys --- blender/arm/exporter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index d3cb3c8b..269e59d5 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1146,6 +1146,8 @@ class ArmoryExporter: if(count > max_shape_keys): break # get vertex data from shape key + if shape_key.mute: + continue vert_data = self.get_vertex_data_from_shape_key(shape_key_base, shape_key) vert_pos.append(vert_data['pos']) vert_nor.append(vert_data['nor']) From 8f733222a9232aaef160c6b9995b19db2902d91f Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 28 Oct 2021 15:48:26 +0200 Subject: [PATCH 28/36] fix shape keys and multiple uv --- blender/arm/exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 269e59d5..3e959906 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1350,7 +1350,7 @@ class ArmoryExporter: break if has_tex1: for i in range(0, len(uv_layers)): - if i != t0map and uv_layers.name != 'UVMap_shape_key': + if i != t0map and uv_layers[i].name != 'UVMap_shape_key': t1map = i t1data = np.empty(num_verts * 2, dtype=' Date: Thu, 28 Oct 2021 17:18:02 +0200 Subject: [PATCH 29/36] fix error when all shape keys muted --- blender/arm/exporter.py | 13 +++++++++++-- blender/arm/utils.py | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 3e959906..76beceec 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -203,8 +203,13 @@ class ArmoryExporter: return None shape_keys = mesh.shape_keys - if shape_keys and len(shape_keys.key_blocks) > 1: - return shape_keys + if not shape_keys: + return None + if len(shape_keys.key_blocks) < 2: + return None + for shape_key in shape_keys.key_blocks[1:]: + if(not shape_key.mute): + return shape_keys return None @staticmethod @@ -1156,6 +1161,10 @@ class ArmoryExporter: count += 1 + # No shape keys present or all shape keys are muted + if (count < 1): + return + # Convert to array for easy manipulation pos_array = np.array(vert_pos) nor_array = np.array(vert_nor) diff --git a/blender/arm/utils.py b/blender/arm/utils.py index cba60d8c..eb184860 100755 --- a/blender/arm/utils.py +++ b/blender/arm/utils.py @@ -775,8 +775,13 @@ def export_morph_targets(bobject: bpy.types.Object) -> bool: return False shape_keys = bobject.data.shape_keys - if shape_keys and len(shape_keys.key_blocks) > 1: - return True + if not shape_keys: + return False + if len(shape_keys.key_blocks) < 2: + return False + for shape_key in shape_keys.key_blocks[1:]: + if(not shape_key.mute): + return True return False def export_vcols(bobject: bpy.types.Object) -> bool: From 8116729822e16e6093fea5a9bd1599ddafb1dab2 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sun, 31 Oct 2021 18:42:23 +0100 Subject: [PATCH 30/36] fix pixel count for images, fix max shape keys --- blender/arm/exporter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 76beceec..219cd8a6 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -1148,7 +1148,7 @@ class ArmoryExporter: # Loop through all shape keys for shape_key in bobject.data.shape_keys.key_blocks[1:]: - if(count > max_shape_keys): + if(count > max_shape_keys - 1): break # get vertex data from shape key if shape_key.mute: @@ -1251,6 +1251,8 @@ class ArmoryExporter: pixel_list.append(data[y, x, 1]) pixel_list.append(data[y, x, 2]) pixel_list.append(1.0) + + pixel_list = (pixel_list + [0] * (img_size * img_size * 4 - len(pixel_list))) image = bpy.data.images.new(name, width = img_size, height = img_size, is_data = True) image.pixels = pixel_list From 3c0f580f69d86e26a173f8c24a3849bfe87822c1 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sun, 31 Oct 2021 18:43:09 +0100 Subject: [PATCH 31/36] fix morph weights lookup --- Shaders/std/morph_target.glsl | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/Shaders/std/morph_target.glsl b/Shaders/std/morph_target.glsl index d9096ef6..b4746238 100644 --- a/Shaders/std/morph_target.glsl +++ b/Shaders/std/morph_target.glsl @@ -7,22 +7,21 @@ uniform vec4 morphWeights[8]; void getMorphedVertex(vec2 uvCoord, inout vec3 A){ for(int i = 0; i<8; i++ ) { - vec4 tempCoordX = vec4( uvCoord.x); - vec4 tempCoordY = vec4( uvCoord.y - (i) * morphDataDim.y, - uvCoord.y - (i + 1) * morphDataDim.y, - uvCoord.y - (i + 2) * morphDataDim.y, - uvCoord.y - (i + 3) * morphDataDim.y); + vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y, + uvCoord.y - (i * 4 + 1) * morphDataDim.y, + uvCoord.y - (i * 4 + 2) * morphDataDim.y, + uvCoord.y - (i * 4 + 3) * morphDataDim.y); - vec3 morph = texture(morphDataPos, vec2(tempCoordX.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y; + vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y; A += morphWeights[i].x * morph; - morph = texture(morphDataPos, vec2(tempCoordX.y, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y; + morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y; A += morphWeights[i].y * morph; - morph = texture(morphDataPos, vec2(tempCoordX.z, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y; + morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y; A += morphWeights[i].z * morph; - morph = texture(morphDataPos, vec2(tempCoordX.w, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y; + morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y; A += morphWeights[i].w * morph; } } @@ -31,22 +30,21 @@ void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){ for(int i = 0; i<8; i++ ) { - vec4 tempCoordX = vec4( uvCoord.x); - vec4 tempCoordY = vec4( uvCoord.y - (i) * morphDataDim.y, - uvCoord.y - (i + 1) * morphDataDim.y, - uvCoord.y - (i + 2) * morphDataDim.y, - uvCoord.y - (i + 3) * morphDataDim.y); + vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y, + uvCoord.y - (i * 4 + 1) * morphDataDim.y, + uvCoord.y - (i * 4 + 2) * morphDataDim.y, + uvCoord.y - (i * 4 + 3) * morphDataDim.y); - vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(tempCoordX.x, tempCoordY.x)).rgb * 2.0 - 1.0); + vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0); morphNor += norm; - norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(tempCoordX.y, tempCoordY.y)).rgb * 2.0 - 1.0); + norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0); morphNor += norm; - norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(tempCoordX.z, tempCoordY.z)).rgb * 2.0 - 1.0); + norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0); morphNor += norm; - norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(tempCoordX.w, tempCoordY.w)).rgb * 2.0 - 1.0); + norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0); morphNor += norm; } From a1951bb66e812ceea814215dbdb599e3a55ef478 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Tue, 2 Nov 2021 15:56:24 +0100 Subject: [PATCH 32/36] clean up export --- blender/arm/exporter.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index 219cd8a6..da454883 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -198,19 +198,22 @@ class ArmoryExporter: @staticmethod def get_shape_keys(mesh): + rpdat = arm.utils.get_rp() + if(rpdat.arm_morph_target != 'On'): + return False # Metaball if not hasattr(mesh, 'shape_keys'): - return None + return False shape_keys = mesh.shape_keys if not shape_keys: - return None + return False if len(shape_keys.key_blocks) < 2: - return None + return False for shape_key in shape_keys.key_blocks[1:]: if(not shape_key.mute): - return shape_keys - return None + return True + return False @staticmethod def get_morph_uv_index(mesh): @@ -1332,7 +1335,7 @@ class ArmoryExporter: has_col = self.get_export_vcols(bobject.data) and num_colors > 0 # Check if shape keys were exported has_morph_target = self.get_shape_keys(bobject.data) - if(has_morph_target): + if has_morph_target: # Shape keys UV are exported separately, so reduce UV count by 1 num_uv_layers -= 1 morph_uv_index = self.get_morph_uv_index(bobject.data) @@ -1361,7 +1364,12 @@ class ArmoryExporter: break if has_tex1: for i in range(0, len(uv_layers)): - if i != t0map and uv_layers[i].name != 'UVMap_shape_key': + # Not UVMap 0 + if i != t0map: + # Not Shape Key UVMap + if has_morph_target and uv_layers[i].name == 'UVMap_shape_key': + continue + # Neither UVMap 0 Nor Shape Key Map t1map = i t1data = np.empty(num_verts * 2, dtype=' Date: Tue, 2 Nov 2021 15:56:46 +0100 Subject: [PATCH 33/36] remove shape key textures on clean --- blender/arm/make.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blender/arm/make.py b/blender/arm/make.py index 3e354e14..a9cce99f 100755 --- a/blender/arm/make.py +++ b/blender/arm/make.py @@ -900,6 +900,10 @@ def clean(): shutil.rmtree('Sources/' + pkg_dir, onerror=remove_readonly) if os.path.exists('Sources') and os.listdir('Sources') == []: shutil.rmtree('Sources/', onerror=remove_readonly) + + # Remove Shape key Textures + if os.path.exists('MorphTargets/'): + shutil.rmtree('MorphTargets/', onerror=remove_readonly) # To recache signatures for batched materials for mat in bpy.data.materials: From 603d23fbfdf1266cc03d2fbc1527de8cc089eea6 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Tue, 2 Nov 2021 15:58:38 +0100 Subject: [PATCH 34/36] Add shape key export option --- blender/arm/props_renderpath.py | 4 ++++ blender/arm/props_ui.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/blender/arm/props_renderpath.py b/blender/arm/props_renderpath.py index 2f2b4274..b8932e4a 100644 --- a/blender/arm/props_renderpath.py +++ b/blender/arm/props_renderpath.py @@ -567,6 +567,10 @@ class ArmRPListItem(bpy.types.PropertyGroup): name='Skinning', description='Enable skinning', default='On', update=assets.invalidate_shader_cache) arm_skin_max_bones_auto: BoolProperty(name="Auto Bones", description="Calculate amount of maximum bones based on armatures", default=True, update=assets.invalidate_compiled_data) arm_skin_max_bones: IntProperty(name="Max Bones", default=50, min=1, max=3000, update=assets.invalidate_shader_cache) + arm_morph_target: EnumProperty( + items=[('On', 'On', 'On'), + ('Off', 'Off', 'Off')], + name='Shape key', description='Enable shape keys', default='On', update=assets.invalidate_shader_cache) arm_particles: EnumProperty( items=[('On', 'On', 'On'), ('Off', 'Off', 'Off')], diff --git a/blender/arm/props_ui.py b/blender/arm/props_ui.py index 72ecbfdf..76aff787 100644 --- a/blender/arm/props_ui.py +++ b/blender/arm/props_ui.py @@ -1343,6 +1343,12 @@ class ARM_PT_RenderPathRendererPanel(bpy.types.Panel): row.enabled = not rpdat.arm_skin_max_bones_auto row.prop(rpdat, 'arm_skin_max_bones') layout.separator(factor=0.1) + + col = layout.column() + col.prop(rpdat, 'arm_morph_target') + col = col.column() + col.enabled = rpdat.arm_morph_target == 'On' + layout.separator(factor=0.1) col = layout.column() col.prop(rpdat, "rp_hdr") From 83a60e746203b4ad4058a4b9e71b51aa25d819c9 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Tue, 2 Nov 2021 15:59:15 +0100 Subject: [PATCH 35/36] Implement shape key conditions --- blender/arm/utils.py | 5 ++++- blender/arm/write_data.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/blender/arm/utils.py b/blender/arm/utils.py index eb184860..1deb9798 100755 --- a/blender/arm/utils.py +++ b/blender/arm/utils.py @@ -771,8 +771,11 @@ def export_bone_data(bobject: bpy.types.Object) -> bool: return bobject.find_armature() and is_bone_animation_enabled(bobject) and get_rp().arm_skin == 'On' def export_morph_targets(bobject: bpy.types.Object) -> bool: + if get_rp().arm_morph_target != 'On': + return False + if not hasattr(bobject.data, 'shape_keys'): - return False + return False shape_keys = bobject.data.shape_keys if not shape_keys: diff --git a/blender/arm/write_data.py b/blender/arm/write_data.py index 924b54d1..3074de7e 100755 --- a/blender/arm/write_data.py +++ b/blender/arm/write_data.py @@ -303,6 +303,9 @@ project.addSources('Sources'); rpdat = arm.utils.get_rp() if rpdat.arm_skin != 'Off': assets.add_khafile_def('arm_skin') + + if rpdat.arm_morph_target != 'Off': + assets.add_khafile_def('arm_morph_target') if rpdat.arm_particles != 'Off': assets.add_khafile_def('arm_particles') From 1cec287891669ffc4e3f84a6116a18e689195ade Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Tue, 2 Nov 2021 15:59:40 +0100 Subject: [PATCH 36/36] Conditional compilation for shape key node --- Sources/armory/logicnode/SetObjectShapeKeyNode.hx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/armory/logicnode/SetObjectShapeKeyNode.hx b/Sources/armory/logicnode/SetObjectShapeKeyNode.hx index 6df0760f..f0a22603 100644 --- a/Sources/armory/logicnode/SetObjectShapeKeyNode.hx +++ b/Sources/armory/logicnode/SetObjectShapeKeyNode.hx @@ -9,15 +9,17 @@ class SetObjectShapeKeyNode extends LogicNode { } override function run(from: Int) { + #if arm_morph_target var object: Dynamic = inputs[1].get(); var shapeKey: String = inputs[2].get(); var value: Dynamic = inputs[3].get(); - if (object == null) return; - var sk = cast(object, MeshObject).morphTarget; - if(sk == null) return; + assert(Error, object != null, "Object should not be null"); + var morph = cast(object, MeshObject).morphTarget; - sk.setMorphValue(shapeKey, value); + assert(Error, morph != null, "Object does not have shape keys"); + morph.setMorphValue(shapeKey, value); + #end runOutput(0); } }