From 4adf11f35dc05119a12ae82a0b643a7d1d891604 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Apr 2020 19:23:30 -0600 Subject: [PATCH 001/100] MechBumpSpecColorize shader Contains diffuse, normal and specular colour textures --- io_alamo_tools/settings.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index 74dc494..f2c147b 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -38,7 +38,9 @@ "TerrainMeshBump.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture", "NormalTexture"], "TerrainMeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], "Tree.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BendScale", "BaseTexture", "NormalTexture"], - "LightProxy.fx": ["Diffuse"] + "LightProxy.fx": ["Diffuse"], + "MeshBumpSpecColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture", "GlossTexture"] } vertex_format_dict = { @@ -70,12 +72,13 @@ "TerrainMeshBumb.fx" : "alD3dVertNU2U3U3", "TerrainMeshGloss.fx" : "alD3dVertNU2", "Tree.fx" : "alD3dVertNU2", - "LightProxy.fx" : "alD3dVertNU2" + "LightProxy.fx" : "alD3dVertNU2", + "MeshBumpSpecColorize.fx" : "alD3dVertNU2U3U3" } billboard_array = {"Disable":0, "Parallel":1, "Face":2, "ZAxis View": 3, "ZAxis Light":4, "ZAxis Wind":5, "Sunlight Glow":6, "Sun":7} -bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx"] +bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx", "MeshBumpSpecColorize.fx" ] rotation_curve_name = ['].rotation_quaternion', '].rotation_euler'] From c3746a439302322b620045f2943d8c5c2e6ba950 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 13 Apr 2020 19:23:56 -0600 Subject: [PATCH 002/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be9faf2..7b23166 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Blender-ALAMAO-Plugin +# Blender-ALAMO-Plugin A plugin that allow reading and writing of ALAMO-Engine model(.alo) and animation(.ala) files. Specifically designed to work with Empire at War: Forces of Corruption. From b6076ac4e32f748aa29854a28da07d7465ea829d Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 26 May 2020 20:51:41 -0700 Subject: [PATCH 003/100] Better materials first commit --- io_alamo_tools/import_alo.py | 137 ++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 43 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index aed447b..27bd6bd 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -414,6 +414,93 @@ def read_animation_mapping(currentSubMesh): counter += 1 return animation_mapping + def material_group_basic(context, operator, group_name, material): + node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') + + node = node_group.nodes.new + link = node_group.links.new + + group_in = node('NodeGroupInput') + group_in.location.x -= 700 + node_group.inputs.new('NodeSocketColor', 'Team Color') + spec = node_group.inputs.new('NodeSocketFloat', 'Specular Intensity') + spec.default_value = 0.1 + + group_out = node('NodeGroupOutput') + group_out.location.x += 300.0 + node_group.outputs.new('NodeSocketShader', 'Surface') + + # if (material.shaderList.shaderList == "MeshAdditive.fx") + # continue + # else + # bsdf = nodes.new("ShaderNodeBsdfPrincipled") + + bsdf = node("ShaderNodeBsdfPrincipled") + + base_image_node = node("ShaderNodeTexImage") + base_image_node.location.x -= 500 + + mix_node = node("ShaderNodeMixRGB") + mix_node.blend_type = 'COLOR' + mix_node.location.x -= 200 + + link(base_image_node.outputs['Color'], mix_node.inputs['Color1']) + link(base_image_node.outputs['Alpha'], mix_node.inputs['Fac']) + link(mix_node.outputs['Color'], bsdf.inputs['Base Color']) + + normal_image_node = node("ShaderNodeTexImage") + normal_image_node.location.x -= 1100.0 + normal_image_node.location.y -= 300.0 + + normal_map_node = node("ShaderNodeNormalMap") + normal_map_node.space = 'TANGENT' + normal_map_node.location.x -= 800.0 + normal_map_node.location.y -= 300.0 + + normal_split = node("ShaderNodeSeparateRGB") + normal_split.location.x -= 600 + normal_split.location.y -= 300 + normal_invert = node("ShaderNodeInvert") + normal_invert.location.x -= 400 + normal_invert.location.y -= 300 + normal_combine = node("ShaderNodeCombineRGB") + normal_combine.location.x -= 200 + normal_combine.location.y -= 300 + + specular_multiply = node("ShaderNodeMath") + specular_multiply.operation = 'MULTIPLY' + specular_multiply.location.x -= 800 + specular_multiply.location.y -= 100 + + link(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) + link(normal_map_node.outputs['Normal'], normal_split.inputs['Image']) + link(normal_split.outputs['R'], normal_combine.inputs['R']) + link(normal_split.outputs['G'], normal_invert.inputs['Color']) + link(normal_invert.outputs['Color'], normal_combine.inputs['G']) + link(normal_split.outputs['B'], normal_combine.inputs['B']) + link(normal_combine.outputs[0], bsdf.inputs['Normal']) + + link(normal_image_node.outputs['Alpha'], specular_multiply.inputs[0]) + + link(group_in.outputs['Team Color'], mix_node.inputs['Color2']) + link(group_in.outputs['Specular Intensity'], specular_multiply.inputs[1]) + link(specular_multiply.outputs[0], bsdf.inputs['Specular']) + + if (material.BaseTexture != 'None'): + if (material.BaseTexture in bpy.data.images): + diffuse_texture = bpy.data.images[material.BaseTexture] + base_image_node.image = diffuse_texture + + if (material.NormalTexture != 'None'): + if (material.NormalTexture in bpy.data.images): + normal_texture = bpy.data.images[material.NormalTexture] + normal_image_node.image = normal_texture + normal_image_node.image.colorspace_settings.name = 'Raw' + + link(bsdf.outputs[0], group_out.inputs[0]) + + return node_group + def set_up_textures(material): material.use_nodes = True @@ -423,53 +510,17 @@ def set_up_textures(material): #clean up while(nodes): nodes.remove(nodes[0]) - + output = nodes.new("ShaderNodeOutputMaterial") - bsdf = nodes.new("ShaderNodeBsdfPrincipled") - - base_image_node = nodes.new("ShaderNodeTexImage") - normal_image_node = nodes.new("ShaderNodeTexImage") - - normal_map_node = nodes.new("ShaderNodeNormalMap") - normal_map_node.space = 'TANGENT' - normal_map_node.uv_map = 'MainUV' - - uvmap = nodes.new("ShaderNodeUVMap") - uvmap.uv_map = "MainUV" - - if material.BaseTexture != 'None': - - links.new(output.inputs['Surface'], bsdf.outputs['BSDF']) - links.new(bsdf.inputs['Base Color'], base_image_node.outputs['Color']) - links.new(bsdf.inputs['Alpha'], base_image_node.outputs['Alpha']) - links.new(base_image_node.inputs['Vector'], uvmap.outputs['UV']) - - if material.BaseTexture in bpy.data.images: - diffuse_texture = bpy.data.images[material.BaseTexture] - base_image_node.image = diffuse_texture - - if material.NormalTexture != 'None': - links.new(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) - links.new(normal_map_node.outputs['Normal'], bsdf.inputs['Normal']) - links.new(normal_image_node.inputs['Vector'], uvmap.outputs['UV']) - - if material.NormalTexture in bpy.data.images: - normal_texture = bpy.data.images[material.NormalTexture] - normal_image_node.image = normal_texture - normal_image_node.image.colorspace_settings.name = 'Raw' - - # distribute nodes along the x axis - for index, node in enumerate((uvmap, base_image_node, bsdf, output)): - node.location.x = 200.0 * index - normal_map_node.location = bsdf.location - normal_map_node.location.y += 300.0 + custom_node_name = material.name + "Group" + my_group = material_group_basic(self, context, custom_node_name, material) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 200.0 - output.location.x += 200.0 - bsdf.location.x += 200.0 + links.new(mat_group.outputs['Surface'], output.inputs['Surface']) - normal_image_node.location = base_image_node.location - normal_image_node.location.y += 300.0 def create_object(currentMesh): global mesh From 86c481d56a6226962c27f427ba4ca30d95b59269 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 26 May 2020 21:06:00 -0700 Subject: [PATCH 004/100] Better materials first pass --- io_alamo_tools/import_alo.py | 137 ++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 43 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index aed447b..27bd6bd 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -414,6 +414,93 @@ def read_animation_mapping(currentSubMesh): counter += 1 return animation_mapping + def material_group_basic(context, operator, group_name, material): + node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') + + node = node_group.nodes.new + link = node_group.links.new + + group_in = node('NodeGroupInput') + group_in.location.x -= 700 + node_group.inputs.new('NodeSocketColor', 'Team Color') + spec = node_group.inputs.new('NodeSocketFloat', 'Specular Intensity') + spec.default_value = 0.1 + + group_out = node('NodeGroupOutput') + group_out.location.x += 300.0 + node_group.outputs.new('NodeSocketShader', 'Surface') + + # if (material.shaderList.shaderList == "MeshAdditive.fx") + # continue + # else + # bsdf = nodes.new("ShaderNodeBsdfPrincipled") + + bsdf = node("ShaderNodeBsdfPrincipled") + + base_image_node = node("ShaderNodeTexImage") + base_image_node.location.x -= 500 + + mix_node = node("ShaderNodeMixRGB") + mix_node.blend_type = 'COLOR' + mix_node.location.x -= 200 + + link(base_image_node.outputs['Color'], mix_node.inputs['Color1']) + link(base_image_node.outputs['Alpha'], mix_node.inputs['Fac']) + link(mix_node.outputs['Color'], bsdf.inputs['Base Color']) + + normal_image_node = node("ShaderNodeTexImage") + normal_image_node.location.x -= 1100.0 + normal_image_node.location.y -= 300.0 + + normal_map_node = node("ShaderNodeNormalMap") + normal_map_node.space = 'TANGENT' + normal_map_node.location.x -= 800.0 + normal_map_node.location.y -= 300.0 + + normal_split = node("ShaderNodeSeparateRGB") + normal_split.location.x -= 600 + normal_split.location.y -= 300 + normal_invert = node("ShaderNodeInvert") + normal_invert.location.x -= 400 + normal_invert.location.y -= 300 + normal_combine = node("ShaderNodeCombineRGB") + normal_combine.location.x -= 200 + normal_combine.location.y -= 300 + + specular_multiply = node("ShaderNodeMath") + specular_multiply.operation = 'MULTIPLY' + specular_multiply.location.x -= 800 + specular_multiply.location.y -= 100 + + link(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) + link(normal_map_node.outputs['Normal'], normal_split.inputs['Image']) + link(normal_split.outputs['R'], normal_combine.inputs['R']) + link(normal_split.outputs['G'], normal_invert.inputs['Color']) + link(normal_invert.outputs['Color'], normal_combine.inputs['G']) + link(normal_split.outputs['B'], normal_combine.inputs['B']) + link(normal_combine.outputs[0], bsdf.inputs['Normal']) + + link(normal_image_node.outputs['Alpha'], specular_multiply.inputs[0]) + + link(group_in.outputs['Team Color'], mix_node.inputs['Color2']) + link(group_in.outputs['Specular Intensity'], specular_multiply.inputs[1]) + link(specular_multiply.outputs[0], bsdf.inputs['Specular']) + + if (material.BaseTexture != 'None'): + if (material.BaseTexture in bpy.data.images): + diffuse_texture = bpy.data.images[material.BaseTexture] + base_image_node.image = diffuse_texture + + if (material.NormalTexture != 'None'): + if (material.NormalTexture in bpy.data.images): + normal_texture = bpy.data.images[material.NormalTexture] + normal_image_node.image = normal_texture + normal_image_node.image.colorspace_settings.name = 'Raw' + + link(bsdf.outputs[0], group_out.inputs[0]) + + return node_group + def set_up_textures(material): material.use_nodes = True @@ -423,53 +510,17 @@ def set_up_textures(material): #clean up while(nodes): nodes.remove(nodes[0]) - + output = nodes.new("ShaderNodeOutputMaterial") - bsdf = nodes.new("ShaderNodeBsdfPrincipled") - - base_image_node = nodes.new("ShaderNodeTexImage") - normal_image_node = nodes.new("ShaderNodeTexImage") - - normal_map_node = nodes.new("ShaderNodeNormalMap") - normal_map_node.space = 'TANGENT' - normal_map_node.uv_map = 'MainUV' - - uvmap = nodes.new("ShaderNodeUVMap") - uvmap.uv_map = "MainUV" - - if material.BaseTexture != 'None': - - links.new(output.inputs['Surface'], bsdf.outputs['BSDF']) - links.new(bsdf.inputs['Base Color'], base_image_node.outputs['Color']) - links.new(bsdf.inputs['Alpha'], base_image_node.outputs['Alpha']) - links.new(base_image_node.inputs['Vector'], uvmap.outputs['UV']) - - if material.BaseTexture in bpy.data.images: - diffuse_texture = bpy.data.images[material.BaseTexture] - base_image_node.image = diffuse_texture - - if material.NormalTexture != 'None': - links.new(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) - links.new(normal_map_node.outputs['Normal'], bsdf.inputs['Normal']) - links.new(normal_image_node.inputs['Vector'], uvmap.outputs['UV']) - - if material.NormalTexture in bpy.data.images: - normal_texture = bpy.data.images[material.NormalTexture] - normal_image_node.image = normal_texture - normal_image_node.image.colorspace_settings.name = 'Raw' - - # distribute nodes along the x axis - for index, node in enumerate((uvmap, base_image_node, bsdf, output)): - node.location.x = 200.0 * index - normal_map_node.location = bsdf.location - normal_map_node.location.y += 300.0 + custom_node_name = material.name + "Group" + my_group = material_group_basic(self, context, custom_node_name, material) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 200.0 - output.location.x += 200.0 - bsdf.location.x += 200.0 + links.new(mat_group.outputs['Surface'], output.inputs['Surface']) - normal_image_node.location = base_image_node.location - normal_image_node.location.y += 300.0 def create_object(currentMesh): global mesh From d48e913116ba32a94a6968b6610ee4d52d6bd2de Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 27 May 2020 16:56:38 -0700 Subject: [PATCH 005/100] Improvements, explicit additive material support --- io_alamo_tools/import_alo.py | 94 +++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 27bd6bd..40b7709 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -414,6 +414,50 @@ def read_animation_mapping(currentSubMesh): counter += 1 return animation_mapping + def material_group_additive(context, operator, group_name, material): + node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') + + node = node_group.nodes.new + link = node_group.links.new + + group_in = node('NodeGroupInput') + group_in.location.x -= 700 + emissive = node_group.inputs.new('NodeSocketFloat', 'Emissive Strength') + emissive.default_value = 1.0 + + group_out = node('NodeGroupOutput') + group_out.location.x += 200.0 + node_group.outputs.new('NodeSocketShader', 'Surface') + + mix_shader = node("ShaderNodeMixShader") + + transparent = node("ShaderNodeBsdfTransparent") + transparent.location.x -= 200 + transparent.location.y -= 50 + + emission = node("ShaderNodeEmission") + emission.location.x -= 200 + emission.location.y -= 150 + + base_image_node = node("ShaderNodeTexImage") + base_image_node.location.x -= 500 + + link(base_image_node.outputs['Color'], mix_shader.inputs['Fac']) + link(base_image_node.outputs['Color'], emission.inputs[0]) + link(transparent.outputs[0], mix_shader.inputs[1]) + link(emission.outputs[0], mix_shader.inputs[2]) + + link(group_in.outputs[0], emission.inputs[1]) + + if (material.BaseTexture != 'None'): + if (material.BaseTexture in bpy.data.images): + diffuse_texture = bpy.data.images[material.BaseTexture] + base_image_node.image = diffuse_texture + + link(mix_shader.outputs[0], group_out.inputs[0]) + + return node_group + def material_group_basic(context, operator, group_name, material): node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') @@ -427,15 +471,9 @@ def material_group_basic(context, operator, group_name, material): spec.default_value = 0.1 group_out = node('NodeGroupOutput') - group_out.location.x += 300.0 - node_group.outputs.new('NodeSocketShader', 'Surface') - - # if (material.shaderList.shaderList == "MeshAdditive.fx") - # continue - # else - # bsdf = nodes.new("ShaderNodeBsdfPrincipled") - - bsdf = node("ShaderNodeBsdfPrincipled") + node_group.outputs.new('NodeSocketColor', 'Base Color') + node_group.outputs.new('NodeSocketValue', 'Specular') + node_group.outputs.new('NodeSocketColor', 'Normal') base_image_node = node("ShaderNodeTexImage") base_image_node.location.x -= 500 @@ -446,14 +484,14 @@ def material_group_basic(context, operator, group_name, material): link(base_image_node.outputs['Color'], mix_node.inputs['Color1']) link(base_image_node.outputs['Alpha'], mix_node.inputs['Fac']) - link(mix_node.outputs['Color'], bsdf.inputs['Base Color']) + link(mix_node.outputs['Color'], group_out.inputs['Base Color']) normal_image_node = node("ShaderNodeTexImage") normal_image_node.location.x -= 1100.0 normal_image_node.location.y -= 300.0 normal_map_node = node("ShaderNodeNormalMap") - normal_map_node.space = 'TANGENT' + normal_map_node.space = 'OBJECT' normal_map_node.location.x -= 800.0 normal_map_node.location.y -= 300.0 @@ -478,13 +516,13 @@ def material_group_basic(context, operator, group_name, material): link(normal_split.outputs['G'], normal_invert.inputs['Color']) link(normal_invert.outputs['Color'], normal_combine.inputs['G']) link(normal_split.outputs['B'], normal_combine.inputs['B']) - link(normal_combine.outputs[0], bsdf.inputs['Normal']) + link(normal_combine.outputs[0], group_out.inputs[2]) link(normal_image_node.outputs['Alpha'], specular_multiply.inputs[0]) link(group_in.outputs['Team Color'], mix_node.inputs['Color2']) link(group_in.outputs['Specular Intensity'], specular_multiply.inputs[1]) - link(specular_multiply.outputs[0], bsdf.inputs['Specular']) + link(specular_multiply.outputs[0], group_out.inputs[1]) if (material.BaseTexture != 'None'): if (material.BaseTexture in bpy.data.images): @@ -496,8 +534,6 @@ def material_group_basic(context, operator, group_name, material): normal_texture = bpy.data.images[material.NormalTexture] normal_image_node.image = normal_texture normal_image_node.image.colorspace_settings.name = 'Raw' - - link(bsdf.outputs[0], group_out.inputs[0]) return node_group @@ -512,14 +548,28 @@ def set_up_textures(material): while(nodes): nodes.remove(nodes[0]) output = nodes.new("ShaderNodeOutputMaterial") - custom_node_name = material.name + "Group" - my_group = material_group_basic(self, context, custom_node_name, material) - mat_group = nt.nodes.new("ShaderNodeGroup") - mat_group.node_tree = bpy.data.node_groups[my_group.name] - mat_group.location.x -= 200.0 - - links.new(mat_group.outputs['Surface'], output.inputs['Surface']) + my_group = 'null' + + if ("Additive" in material.shaderList.shaderList or "Alpha" in material.shaderList.shaderList): + material.blend_method = "BLEND" + my_group = material_group_additive(self, context, custom_node_name, material) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 500.0 + links.new(mat_group.outputs[0], output.inputs['Surface']) + else: + bsdf = nodes.new("ShaderNodeBsdfPrincipled") + bsdf.inputs[7].default_value = 1 # Set roughness to 1 + bsdf.location.x -= 300.0 + links.new(bsdf.outputs['BSDF'], output.inputs['Surface']) + my_group = material_group_basic(self, context, custom_node_name, material) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 500.0 + links.new(mat_group.outputs[0], bsdf.inputs['Base Color']) + links.new(mat_group.outputs[1], bsdf.inputs[5]) + links.new(mat_group.outputs[2], bsdf.inputs['Normal']) def create_object(currentMesh): From 402644e3328148f3e393a5634771c5122fdc4e9b Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 27 May 2020 19:07:47 -0700 Subject: [PATCH 006/100] Normal space fix --- io_alamo_tools/import_alo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 40b7709..c425e50 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -491,7 +491,7 @@ def material_group_basic(context, operator, group_name, material): normal_image_node.location.y -= 300.0 normal_map_node = node("ShaderNodeNormalMap") - normal_map_node.space = 'OBJECT' + normal_map_node.space = 'TANGENT' normal_map_node.location.x -= 800.0 normal_map_node.location.y -= 300.0 From 52c7eda83c75ebd6427c963182c4437bdb9eca19 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 28 May 2020 11:14:47 -0700 Subject: [PATCH 007/100] Non-emissive alpha support, various fixes --- io_alamo_tools/import_alo.py | 81 ++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index c425e50..04bf8f8 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -414,16 +414,11 @@ def read_animation_mapping(currentSubMesh): counter += 1 return animation_mapping - def material_group_additive(context, operator, group_name, material): + def material_group_additive(context, operator, group_name, material, is_emissive): node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') node = node_group.nodes.new link = node_group.links.new - - group_in = node('NodeGroupInput') - group_in.location.x -= 700 - emissive = node_group.inputs.new('NodeSocketFloat', 'Emissive Strength') - emissive.default_value = 1.0 group_out = node('NodeGroupOutput') group_out.location.x += 200.0 @@ -435,19 +430,32 @@ def material_group_additive(context, operator, group_name, material): transparent.location.x -= 200 transparent.location.y -= 50 - emission = node("ShaderNodeEmission") - emission.location.x -= 200 - emission.location.y -= 150 + if is_emissive: + group_in = node('NodeGroupInput') + group_in.location.x -= 700 + emissive = node_group.inputs.new('NodeSocketFloat', 'Emissive Strength') + emissive.default_value = 1.0 + color = node("ShaderNodeEmission") + link(group_in.outputs[0], color.inputs[1]) + + else: + color = node("ShaderNodeBsdfDiffuse") + + color.location.x -= 200 + color.location.y -= 150 base_image_node = node("ShaderNodeTexImage") base_image_node.location.x -= 500 + eevee_alpha_fix = node("ShaderNodeInvert") + eevee_alpha_fix.location.x -= 500 + eevee_alpha_fix.location.y += 300 + link(base_image_node.outputs['Color'], mix_shader.inputs['Fac']) - link(base_image_node.outputs['Color'], emission.inputs[0]) + link(base_image_node.outputs['Color'], color.inputs[0]) + link(base_image_node.outputs[1], eevee_alpha_fix.inputs[1]) # Fix for obnoxious transparency bug in Eevee link(transparent.outputs[0], mix_shader.inputs[1]) - link(emission.outputs[0], mix_shader.inputs[2]) - - link(group_in.outputs[0], emission.inputs[1]) + link(color.outputs[0], mix_shader.inputs[2]) if (material.BaseTexture != 'None'): if (material.BaseTexture in bpy.data.images): @@ -472,8 +480,8 @@ def material_group_basic(context, operator, group_name, material): group_out = node('NodeGroupOutput') node_group.outputs.new('NodeSocketColor', 'Base Color') - node_group.outputs.new('NodeSocketValue', 'Specular') - node_group.outputs.new('NodeSocketColor', 'Normal') + node_group.outputs.new('NodeSocketFloat', 'Specular') + node_group.outputs.new('NodeSocketVector', 'Normal') base_image_node = node("ShaderNodeTexImage") base_image_node.location.x -= 500 @@ -490,33 +498,35 @@ def material_group_basic(context, operator, group_name, material): normal_image_node.location.x -= 1100.0 normal_image_node.location.y -= 300.0 - normal_map_node = node("ShaderNodeNormalMap") - normal_map_node.space = 'TANGENT' - normal_map_node.location.x -= 800.0 - normal_map_node.location.y -= 300.0 - normal_split = node("ShaderNodeSeparateRGB") - normal_split.location.x -= 600 + normal_split.location.x -= 800 normal_split.location.y -= 300 - normal_invert = node("ShaderNodeInvert") - normal_invert.location.x -= 400 + normal_invert = node("ShaderNodeMath") + normal_invert.operation = 'SUBTRACT' + normal_invert.inputs[0].default_value = 1 + normal_invert.location.x -= 600 normal_invert.location.y -= 300 normal_combine = node("ShaderNodeCombineRGB") - normal_combine.location.x -= 200 + normal_combine.location.x -= 400 normal_combine.location.y -= 300 + normal_map_node = node("ShaderNodeNormalMap") + normal_map_node.space = 'TANGENT' + normal_map_node.location.x -= 200.0 + normal_map_node.location.y -= 300.0 + specular_multiply = node("ShaderNodeMath") specular_multiply.operation = 'MULTIPLY' specular_multiply.location.x -= 800 specular_multiply.location.y -= 100 - link(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) - link(normal_map_node.outputs['Normal'], normal_split.inputs['Image']) + link(normal_image_node.outputs['Color'], normal_split.inputs['Image']) link(normal_split.outputs['R'], normal_combine.inputs['R']) - link(normal_split.outputs['G'], normal_invert.inputs['Color']) - link(normal_invert.outputs['Color'], normal_combine.inputs['G']) + link(normal_split.outputs['G'], normal_invert.inputs[1]) + link(normal_invert.outputs[0], normal_combine.inputs['G']) link(normal_split.outputs['B'], normal_combine.inputs['B']) - link(normal_combine.outputs[0], group_out.inputs[2]) + link(normal_combine.outputs[0], normal_map_node.inputs[1]) + link(normal_map_node.outputs[0], group_out.inputs[2]) link(normal_image_node.outputs['Alpha'], specular_multiply.inputs[0]) @@ -551,12 +561,19 @@ def set_up_textures(material): custom_node_name = material.name + "Group" my_group = 'null' - if ("Additive" in material.shaderList.shaderList or "Alpha" in material.shaderList.shaderList): + if ("Additive" in material.shaderList.shaderList): material.blend_method = "BLEND" - my_group = material_group_additive(self, context, custom_node_name, material) + my_group = material_group_additive(self, context, custom_node_name, material, True) mat_group = nt.nodes.new("ShaderNodeGroup") mat_group.node_tree = bpy.data.node_groups[my_group.name] - mat_group.location.x -= 500.0 + mat_group.location.x -= 200.0 + links.new(mat_group.outputs[0], output.inputs['Surface']) + elif ("Alpha" in material.shaderList.shaderList): + material.blend_method = "BLEND" + my_group = material_group_additive(self, context, custom_node_name, material, False) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 200.0 links.new(mat_group.outputs[0], output.inputs['Surface']) else: bsdf = nodes.new("ShaderNodeBsdfPrincipled") From dc63dbbca1bf4e98551c9bae6abea10b44df16fc Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 28 May 2020 12:05:30 -0700 Subject: [PATCH 008/100] Even more fixes, because I can't think more than 30 seconds ahead. --- io_alamo_tools/import_alo.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 04bf8f8..1159e06 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -430,6 +430,9 @@ def material_group_additive(context, operator, group_name, material, is_emissive transparent.location.x -= 200 transparent.location.y -= 50 + base_image_node = node("ShaderNodeTexImage") + base_image_node.location.x -= 500 + if is_emissive: group_in = node('NodeGroupInput') group_in.location.x -= 700 @@ -437,23 +440,20 @@ def material_group_additive(context, operator, group_name, material, is_emissive emissive.default_value = 1.0 color = node("ShaderNodeEmission") link(group_in.outputs[0], color.inputs[1]) + eevee_alpha_fix = node("ShaderNodeInvert") + eevee_alpha_fix.location.x -= 500 + eevee_alpha_fix.location.y += 300 + link(base_image_node.outputs[1], eevee_alpha_fix.inputs[1]) # Fix for obnoxious transparency bug in Eevee + link(base_image_node.outputs['Color'], mix_shader.inputs['Fac']) else: color = node("ShaderNodeBsdfDiffuse") + link(base_image_node.outputs['Alpha'], mix_shader.inputs['Fac']) color.location.x -= 200 color.location.y -= 150 - base_image_node = node("ShaderNodeTexImage") - base_image_node.location.x -= 500 - - eevee_alpha_fix = node("ShaderNodeInvert") - eevee_alpha_fix.location.x -= 500 - eevee_alpha_fix.location.y += 300 - - link(base_image_node.outputs['Color'], mix_shader.inputs['Fac']) link(base_image_node.outputs['Color'], color.inputs[0]) - link(base_image_node.outputs[1], eevee_alpha_fix.inputs[1]) # Fix for obnoxious transparency bug in Eevee link(transparent.outputs[0], mix_shader.inputs[1]) link(color.outputs[0], mix_shader.inputs[2]) From fbdf43eaac1b260253424871015303bc354a27ae Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 31 May 2020 17:07:32 -0700 Subject: [PATCH 009/100] Hidden objects no longer render. --- io_alamo_tools/import_alo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 1159e06..38510e8 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -857,6 +857,7 @@ def hideObject(object): bpy.ops.object.select_all(context_override, action='DESELECT') object.select_set(True) bpy.ops.object.hide_view_set(context_override) + object.hide_render = True def hideLODs(): #hides all but the most detailed LOD in Blender From 95e6dfca6331c7c01035925ada371b29b7b5effb Mon Sep 17 00:00:00 2001 From: Andrew Fullard Date: Tue, 3 Nov 2020 16:40:47 -0500 Subject: [PATCH 010/100] Typo fix --- io_alamo_tools/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index f2c147b..0505a38 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -69,7 +69,7 @@ "RSkinGlossColorize.fx" : "alD3dVertRSkinNU2", "RSkinShadowVolume.fx" : "alD3dVertRSkinNU2", "Skydome.fx" : "alD3dVertNU2", - "TerrainMeshBumb.fx" : "alD3dVertNU2U3U3", + "TerrainMeshBump.fx" : "alD3dVertNU2U3U3", "TerrainMeshGloss.fx" : "alD3dVertNU2", "Tree.fx" : "alD3dVertNU2", "LightProxy.fx" : "alD3dVertNU2", From c4c9778019c732ad1ff8622ca69e7b2a39743904 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Thu, 5 Nov 2020 20:17:47 +0100 Subject: [PATCH 011/100] Update export_alo.py Create welding faces only on normal difference. --- io_alamo_tools/export_alo.py | 102 ++++++++++++++++------------------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 71635bb..786e4bc 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -535,61 +535,53 @@ def shadow_vertex_face_data(bm, mesh, object): face1 = edge.link_faces[0] face2 = edge.link_faces[1] - f1v1 = per_face_vertex_id[face1.index][edge.verts[0].index] - f1v2 = per_face_vertex_id[face1.index][edge.verts[1].index] - - f2v1 = per_face_vertex_id[face2.index][edge.verts[0].index] - f2v2 = per_face_vertex_id[face2.index][edge.verts[1].index] - - mid1 = mathutils.Vector((0, 0, 0)) - for vert in face1.verts: - mid1 += vert.co - mid1 /= 3 - - mid2 = mathutils.Vector((0, 0, 0)) - for vert in face2.verts: - mid2 += vert.co - mid2 /= 3 - - - face1v1 = edge.verts[0].co * 0.75 + mid1 * 0.25 - face1v2 = edge.verts[1].co * 0.75 + mid1 * 0.25 - face2v1 = edge.verts[0].co * 0.75 + mid2 * 0.25 - face2v2 = edge.verts[1].co * 0.75 + mid2 * 0.25 - - out = face1.normal + face2.normal - - #first face - edge1 = face1v1 - face2v1 - edge2 = face1v2 - face2v1 - - cross = mathutils.Vector.cross(edge1, edge2) - dot = mathutils.Vector.dot(out, cross) - - if dot < 0: - face_indices.append(f1v1) - face_indices.append(f2v1) - face_indices.append(f1v2) - else: - face_indices.append(f1v1) - face_indices.append(f1v2) - face_indices.append(f2v1) - - #second face - edge1 = face1v1 - face2v1 - edge2 = face1v2 - face2v1 - - cross = mathutils.Vector.cross(edge1, edge2) - dot = mathutils.Vector.dot(out, cross) - - if dot < 0: - face_indices.append(f2v2) - face_indices.append(f1v2) - face_indices.append(f2v1) - else: - face_indices.append(f2v2) - face_indices.append(f2v1) - face_indices.append(f1v2) + if face1.normal != face2.normal: + f1v1 = per_face_vertex_id[face1.index][edge.verts[0].index] + f1v2 = per_face_vertex_id[face1.index][edge.verts[1].index] + + f2v1 = per_face_vertex_id[face2.index][edge.verts[0].index] + f2v2 = per_face_vertex_id[face2.index][edge.verts[1].index] + + mid1 = mathutils.Vector((0, 0, 0)) + for vert in face1.verts: + mid1 += vert.co + mid1 /= 3 + + mid2 = mathutils.Vector((0, 0, 0)) + for vert in face2.verts: + mid2 += vert.co + mid2 /= 3 + + + face1v1 = edge.verts[0].co * 0.75 + mid1 * 0.25 + face1v2 = edge.verts[1].co * 0.75 + mid1 * 0.25 + face2v1 = edge.verts[0].co * 0.75 + mid2 * 0.25 + face2v2 = edge.verts[1].co * 0.75 + mid2 * 0.25 + + out = face1.normal + face2.normal + + #first face + edge1 = face1v1 - face2v1 + edge2 = face1v2 - face2v1 + + cross = mathutils.Vector.cross(edge1, edge2) + dot = mathutils.Vector.dot(out, cross) + + if dot < 0: + # print("dot " + str(dot)) + face_indices.append(f1v1) + face_indices.append(f2v1) + face_indices.append(f1v2) + face_indices.append(f2v2) + face_indices.append(f1v2) + face_indices.append(f2v1) + else: + face_indices.append(f1v1) + face_indices.append(f1v2) + face_indices.append(f2v1) + face_indices.append(f2v2) + face_indices.append(f2v1) + face_indices.append(f1v2) return [vertices, face_indices] From f556d1df26ee4ccf28cf51c5d9b3d8d933a65d18 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Thu, 5 Nov 2020 20:55:41 +0100 Subject: [PATCH 012/100] Update export_alo.py Limit duplicated vertex amount. --- io_alamo_tools/export_alo.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 786e4bc..5ccf19a 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -503,31 +503,35 @@ def shadow_vertex_face_data(bm, mesh, object): # list of all faces using the current material per_face_vertex_id = {} - vertices = [] face_indices = [] - + vertex_index_map = {} alo_index = 0 + for face in bm.faces: indexArray = {} + for vert in face.verts: vertex = vertexData() + vertex.co = vert.co vertex.normal = face.normal + key = (vert.index, face.normal.x, face.normal.y, face.normal.z) + if key in vertex_index_map: + indexArray[vert.index] = vertex_index_map[key] + face_indices.append(vertex_index_map[key]) + else: + vertex_index_map[key] = alo_index + vertex.uv = mathutils.Vector((0, 0)) + meshVertex = mesh.vertices[vert.index] + vertex.bone_index = getMaxWeightGroupIndex(meshVertex) + if(vertex.bone_index == None): + vertex.bone_index = 0 + vertices.append(vertex) + face_indices.append(alo_index) + indexArray[vert.index] = alo_index + alo_index += 1 - vertex.uv = mathutils.Vector((0, 0)) - - meshVertex = mesh.vertices[vert.index] - vertex.bone_index = getMaxWeightGroupIndex(meshVertex) - if(vertex.bone_index == None): - vertex.bone_index = 0 - - - vertices.append(vertex) - face_indices.append(alo_index) - - indexArray[vert.index] = alo_index - alo_index += 1 per_face_vertex_id[face.index] = indexArray From e7999b6d22fd02307eacac3b0906614f82d9635c Mon Sep 17 00:00:00 2001 From: Andrew Fullard Date: Sun, 22 Nov 2020 15:51:10 -0500 Subject: [PATCH 013/100] Fix UVAlphaScroll shader settings --- io_alamo_tools/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index 0505a38..ba334a5 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -7,7 +7,7 @@ "Grass.fx": ["Emissive", "Diffuse", "Diffuse1", "BendScale", "BaseTexture"], "MeshAdditive.fx": ["BaseTexture", "UVScrollRate", "Color"], "MeshAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "MeshAlphaScroll.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "MeshAlphaScroll.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "UVScrollRate", "BaseTexture"], "MeshBumpColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", "BaseTexture", "NormalTexture"], "MeshBumpColorizeVertex.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", From 90da80302ebabc1941899481dfb560e6b0854a91 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Thu, 4 Feb 2021 21:12:39 +0100 Subject: [PATCH 014/100] Update export_alo.py --- io_alamo_tools/export_alo.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 5ccf19a..b9f301e 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -449,6 +449,7 @@ def submesh_vertex_face_data(bm, object, material, uses_bump, mesh): uv_layer = mesh.uv_layers.active.data loop_to_alo_index = {} #dictionary that maps blender vertex indices to the corresponding alo index #only smooth shaded faces are saved here, as flat shaded faces need duplicate vertices anyway + vertex_index_map = {} alo_index = 0 for face in faces: @@ -457,6 +458,8 @@ def submesh_vertex_face_data(bm, object, material, uses_bump, mesh): vert = loop.vert store_vertex = True + key = (vert.index, face.normal.x, face.normal.y, face.normal.z, uv_layer[loop.index].uv.x, uv_layer[loop.index].uv.y) + if face.smooth: for adjacent_loop in vert.link_loops: if adjacent_loop != loop and uv_layer[loop.index].uv == uv_layer[adjacent_loop.index].uv: @@ -465,7 +468,9 @@ def submesh_vertex_face_data(bm, object, material, uses_bump, mesh): face_indices.append(loop_to_alo_index[adjacent_loop.index]) store_vertex = False break - + elif key in vertex_index_map: + face_indices.append(vertex_index_map[key]) + store_vertex = False if store_vertex: vertex = vertexData() @@ -488,6 +493,8 @@ def submesh_vertex_face_data(bm, object, material, uses_bump, mesh): if face.smooth: loop_to_alo_index[loop.index] = alo_index + else: + vertex_index_map[key] = alo_index vertex.face_index = face.index From 5012a01151dfa91ab1556bd41170ea9b0cb4c2e4 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Mon, 15 Feb 2021 23:44:49 +0100 Subject: [PATCH 015/100] Update export_ala.py --- io_alamo_tools/export_ala.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index ca5f791..f9a5949 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -383,9 +383,11 @@ def create_visibility_chunk(armature, bone): dataExists = False for curve in armature.animation_data.action.fcurves: # by spliting and comparing the data path we know which bone has rotation/location keyframes - if curve.data_path.split('"')[1] == bone.name and curve.data_path.split('"')[2] == '].proxyIsHiddenAnimation': - dataExists = True - break + parts = curve.data_path.split('"'); + if (parts[2] == '].proxyIsHiddenAnimation'): + if parts[1] == bone.name or (bone.parent != None and bone.parent.name == parts[1]): + dataExists = True + break if not dataExists: return b'' @@ -394,13 +396,19 @@ def create_visibility_chunk(armature, bone): binary = '' pose = armature.pose.bones[bone.name] + + parentPose = {} + if bone.parent != None: + parentPose = armature.pose.bones[bone.parent.name] + else: + parentPose = None action = utils.getCurrentAction() animLength = action.AnimationEndFrame scene.frame_set(0) while scene.frame_current <= animLength: - if pose.proxyIsHiddenAnimation == True: + if pose.proxyIsHiddenAnimation == True or (parentPose != None and parentPose.proxyIsHiddenAnimation): binary += '0' else: binary += '1' From c51578706199cb38dc528c60cf87a8a61720f3b3 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:24:32 -0700 Subject: [PATCH 016/100] Set textures to channel packed --- io_alamo_tools/import_alo.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 38510e8..f5b2582 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -460,6 +460,7 @@ def material_group_additive(context, operator, group_name, material, is_emissive if (material.BaseTexture != 'None'): if (material.BaseTexture in bpy.data.images): diffuse_texture = bpy.data.images[material.BaseTexture] + diffuse_texture.alpha_mode = 'CHANNEL_PACKED' base_image_node.image = diffuse_texture link(mix_shader.outputs[0], group_out.inputs[0]) @@ -537,11 +538,13 @@ def material_group_basic(context, operator, group_name, material): if (material.BaseTexture != 'None'): if (material.BaseTexture in bpy.data.images): diffuse_texture = bpy.data.images[material.BaseTexture] + diffuse_texture.alpha_mode = 'CHANNEL_PACKED' base_image_node.image = diffuse_texture if (material.NormalTexture != 'None'): if (material.NormalTexture in bpy.data.images): normal_texture = bpy.data.images[material.NormalTexture] + normal_texture.alpha_mode = 'CHANNEL_PACKED' normal_image_node.image = normal_texture normal_image_node.image.colorspace_settings.name = 'Raw' From 7c8e619f17a9095455229867000ea9dc6118d19f Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:29:05 -0700 Subject: [PATCH 017/100] Update metallic and roughness defaults --- io_alamo_tools/import_alo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index f5b2582..ffd740c 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -580,7 +580,8 @@ def set_up_textures(material): links.new(mat_group.outputs[0], output.inputs['Surface']) else: bsdf = nodes.new("ShaderNodeBsdfPrincipled") - bsdf.inputs[7].default_value = 1 # Set roughness to 1 + bsdf.inputs[4].default_value = 0.1 # Set metallic to 0.1 + bsdf.inputs[7].default_value = 0.2 # Set roughness to 0.2 bsdf.location.x -= 300.0 links.new(bsdf.outputs['BSDF'], output.inputs['Surface']) my_group = material_group_basic(self, context, custom_node_name, material) From b63b45a182a03401c110fda27a8eedaf4356e0a1 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:44:38 -0700 Subject: [PATCH 018/100] Stop forcing renderer to Eevee --- io_alamo_tools/import_alo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index ffd740c..2a4a59b 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1034,7 +1034,7 @@ def loadAnimations(filePath): global file filepath = self.properties.filepath file = open(filepath, 'rb') #open file in read binary mode - setRenderToEevee() + # setRenderToEevee() process_active_junk() removeShadowDoubles() hideLODs() From e5a775741eef386361544a8d4bff8f324d2f46e9 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 18 Aug 2021 21:13:48 -0700 Subject: [PATCH 019/100] Material consolidation, first functional draft --- io_alamo_tools/import_alo.py | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 2a4a59b..e34f685 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -391,7 +391,7 @@ def read_material_info_chunk(currentSubMesh): while (file.tell() < currentPosition + materialChunkSize): active_chunk = file.read(4) if active_chunk == b"\x01\x01\x01\00": - create_material(currentSubMesh) + set_alamo_shader(currentSubMesh) elif active_chunk == b"\x02\x01\x01\00": read_int(currentSubMesh.material) elif active_chunk == b"\x03\x01\x01\00": @@ -402,6 +402,7 @@ def read_material_info_chunk(currentSubMesh): process_texture_chunk(currentSubMesh.material) elif active_chunk == b"\x06\x01\x01\00": read_float4(currentSubMesh.material) + create_material(currentSubMesh) set_up_textures(currentSubMesh.material) def read_animation_mapping(currentSubMesh): @@ -591,7 +592,37 @@ def set_up_textures(material): links.new(mat_group.outputs[0], bsdf.inputs['Base Color']) links.new(mat_group.outputs[1], bsdf.inputs[5]) links.new(mat_group.outputs[2], bsdf.inputs['Normal']) + + def create_material(currentSubMesh): + obj = bpy.context.object + + # Ugly. Should be able to use set_alamo_shader's shader finder instead? + material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular","Shininess","Colorization" \ + ,"DebugColor","UVOffset","Color","UVScrollRate","DiffuseColor","EdgeBrightness","BaseUVScale","WaveUVScale","DistortUVScale","BaseUVScrollRate","WaveUVScrollRate","DistortUVScrollRate","BendScale" \ + , "Diffuse1","CloudScrollRate","CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower"] + + if currentSubMesh.material.name == "DUMMYMATERIAL": + oldMat = currentSubMesh.material + texName = currentSubMesh.material.BaseTexture + texName = texName[0:len(texName) - 4] + mat = assign_material(texName) + + mat.shaderList.shaderList = oldMat.shaderList.shaderList + for texture in material_props: + if texture in oldMat: + mat[texture] = oldMat[texture] + + obj.data.materials.clear() + obj.data.materials.append(mat) + currentSubMesh.material = mat + + def assign_material(name): + if name in bpy.data.materials: + mat = bpy.data.materials.get(name) + else: + mat = bpy.data.materials.new(name) + return mat def create_object(currentMesh): global mesh @@ -665,6 +696,7 @@ def process_texture_chunk(material): file.seek(1, 1) # skip string end byte file.seek(1, 1) # skip child header length = struct.unpack("H", file.read(1) + b'\x00') # get string length + print(texture_function_name) texture_name = "" counter = 0 while counter < length[0] - 1: @@ -685,10 +717,18 @@ def createUVLayer(layerName, uv_coordinates): mesh.uv_layers.new(name = layerName) mesh.uv_layers[-1].data.foreach_set("uv", [uv for pair in [vert_uvs[l.vertex_index] for l in mesh.loops] for uv in pair]) - def create_material(currentSubMesh): # create material and assign + def set_alamo_shader(currentSubMesh): # create material and assign shaderName = read_string() obj = bpy.context.object - mat = bpy.data.materials.new(obj.name + "Material") + + if shaderName == 'MeshCollision.fx': + mat = assign_material("COLLISION") + elif (shaderName == 'RSkinShadowVolume.fx' or shaderName == 'MeshShadowVolume.fx'): + mat = assign_material("SHADOW") + else: + mat = assign_material("DUMMYMATERIAL") + # DUMMYMATERIAL is a temporary material to allow Alamo shader properties to be assigned. + # Can't assign final material because material names are now based on BaseTexture, and textures aren't known yet. Probably a better way to do this. #find shader, ignoring case currentKey = None From 7282be947abf52557a3a39d0d976f8992d31ff55 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 19 Aug 2021 10:53:50 -0700 Subject: [PATCH 020/100] linting and minor refactors --- io_alamo_tools/import_alo.py | 399 +++++++++++++++++++---------------- 1 file changed, 216 insertions(+), 183 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index e34f685..a0a22db 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -24,6 +24,7 @@ from os import listdir import bmesh + def boneEnumCallback(scene, context): bones = [('None', 'None', '', '', 0)] counter = 1 @@ -32,32 +33,33 @@ def boneEnumCallback(scene, context): for bone in armature.data.bones: bones.append((bone.name, bone.name, '', '', counter)) counter += 1 - bones.sort(key=lambda tup : tup[0]) + bones.sort(key=lambda tup: tup[0]) return bones + class ALO_Importer(bpy.types.Operator): """ALO Importer""" # blender will use this as a tooltip for menu items and buttons. bl_idname = "import.alo" # unique identifier for buttons and menu items to reference. bl_label = "Import ALO File" # display name in the interface. bl_options = {'REGISTER', 'UNDO'} # enable undo for the operator. filename_ext = ".alo" - filter_glob : StringProperty(default="*.alo", options={'HIDDEN'}) + filter_glob: StringProperty(default="*.alo", options={'HIDDEN'}) bl_info = { "name": "ALO Importer", "category": "Import", } - parentName : EnumProperty( + parentName: EnumProperty( name='Attachment Bone', - description = "Bone that imported models are attached to", - items = boneEnumCallback + description="Bone that imported models are attached to", + items=boneEnumCallback ) - importAnimations : BoolProperty( - name="Import Animations", - description="Import the model's animations from the same path", - default=True, - ) + importAnimations: BoolProperty( + name="Import Animations", + description="Import the model's animations from the same path", + default=True, + ) def draw(self, context): layout = self.layout @@ -65,28 +67,31 @@ def draw(self, context): layout.prop(self, "importAnimations") layout.prop(self, "parentName") - filepath : StringProperty(name="File Path", description="Filepath used for importing the ALO file", maxlen=1024, default="") + filepath: StringProperty( + name="File Path", description="Filepath used for importing the ALO file", maxlen=1024, default="") - def execute(self, context): # execute() is called by blender when running the operator. + # execute() is called by blender when running the operator. + def execute(self, context): - #main structure + # main structure def process_active_junk(): meshNameList = [] - #loop over file until end is reached + # loop over file until end is reached while(file.tell() < os.path.getsize(self.properties.filepath)): active_chunk = file.read(4) - #print(active_chunk) + # print(active_chunk) if active_chunk == b"\x00\x02\x00\00": armatureData = createArmature() elif active_chunk == b"\x00\x04\x00\00": - file.seek(4, 1) #skip size + file.seek(4, 1) # skip size meshName = processMeshChunk() meshNameList.append(meshName) - elif active_chunk == b"\x00\x13\x00\00": #light chunk is irrelevant - print('WARNING: file contains light objects, these are not supported and might cause minor issues') + elif active_chunk == b"\x00\x13\x00\00": # light chunk is irrelevant + print( + 'WARNING: file contains light objects, these are not supported and might cause minor issues') size = read_chunk_length() - file.seek(size, 1) #skip to next chunk + file.seek(size, 1) # skip to next chunk elif active_chunk == b"\x00\x06\x00\00": file.seek(8, 1) # skip size and next header n_objects_proxies = get_n_objects_n_proxies() @@ -117,7 +122,8 @@ def __init__(self): def removeShadowDoubles(): for object in bpy.data.objects: if(object.type == 'MESH'): - if(len(object.material_slots) <= 0): continue; #already existing objects might not have a material + if(len(object.material_slots) <= 0): + continue # already existing objects might not have a material shader = object.material_slots[0].material.shaderList.shaderList if (shader == 'MeshCollision.fx' or shader == 'RSkinShadowVolume.fx' or shader == 'MeshShadowVolume.fx'): bpy.ops.object.select_all(action='DESELECT') @@ -133,18 +139,19 @@ def createArmature(): global fileName - #create armature + # create armature armatureBlender = bpy.data.armatures.new(fileName + "Armature") - #create object - armatureObj = bpy.data.objects.new(fileName + "Rig", object_data=armatureBlender) + # create object + armatureObj = bpy.data.objects.new( + fileName + "Rig", object_data=armatureBlender) # Link object to collection importCollection.objects.link(armatureObj) bpy.context.view_layer.objects.active = armatureObj bpy.context.view_layer.update() - #adjust settings and enter edit-mode + # adjust settings and enter edit-mode armatureObj = bpy.context.object armatureObj.show_in_front = True utils.setModeToEdit() @@ -163,25 +170,25 @@ def createArmature(): for bone in armatureData.bones: createBone(bone, armatureBlender, armatureData) - bpy.ops.object.mode_set(mode = 'OBJECT') + bpy.ops.object.mode_set(mode='OBJECT') bpy.context.scene.ActiveSkeleton.skeletonEnum = armatureObj.name return armatureData def get_bone_count(armatureData): - file.seek(8,1) #skip header and size + file.seek(8, 1) # skip header and size bone_count = struct.unpack(" 1: currentSubMeshMaxFaceIndex = currentMesh.subMeshList[0].nFaces subMeshCounter = 0 @@ -314,7 +324,7 @@ def readMeshInfo(currentMesh): create_object(currentMesh) def get_mesh_name(): - file.seek(4, 1) #skip header + file.seek(4, 1) # skip header length = read_chunk_length() counter = 0 mesh_name = "" @@ -333,7 +343,7 @@ def get_n_vertices_n_primitives(currentSubMesh): file.seek(120, 1) def processMeshChunk(): - #name chunk + # name chunk currentMesh = meshClass() meshList.append(currentMesh) currentMesh.name = get_mesh_name() @@ -357,7 +367,7 @@ def processMeshChunk(): return name def read_mesh_data(currentSubMesh): - file.seek(4,1) #skip header + file.seek(4, 1) # skip header meshDataChunkSize = read_chunk_length() currentPosition = file.tell() while (file.tell() < currentPosition + meshDataChunkSize): @@ -375,7 +385,8 @@ def read_mesh_data(currentSubMesh): read_animation_mapping(currentSubMesh) elif active_chunk == b"\x07\x00\x01\00": file.seek(4, 1) # skip size - vertex_data = process_vertex_buffer_2(False, currentSubMesh) + vertex_data = process_vertex_buffer_2( + False, currentSubMesh) elif active_chunk == b"\x05\x00\x01\00": file.seek(4, 1) # skip size # old version of the chunk @@ -385,7 +396,7 @@ def read_mesh_data(currentSubMesh): file.seek(size, 1) # skip to next chunk def read_material_info_chunk(currentSubMesh): - file.seek(4,1) #skip header + file.seek(4, 1) # skip header materialChunkSize = read_chunk_length() currentPosition = file.tell() while (file.tell() < currentPosition + materialChunkSize): @@ -411,16 +422,17 @@ def read_animation_mapping(currentSubMesh): counter = 0 animation_mapping = [] while counter < read_counter: - currentSubMesh.animationMapping.append(struct.unpack("I", file.read(4))[0]) + currentSubMesh.animationMapping.append( + struct.unpack("I", file.read(4))[0]) counter += 1 return animation_mapping - def material_group_additive(context, operator, group_name, material, is_emissive): + def material_group_additive(context, operator, group_name, material, is_emissive): node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') node = node_group.nodes.new link = node_group.links.new - + group_out = node('NodeGroupOutput') group_out.location.x += 200.0 node_group.outputs.new('NodeSocketShader', 'Surface') @@ -437,19 +449,23 @@ def material_group_additive(context, operator, group_name, material, is_emissive if is_emissive: group_in = node('NodeGroupInput') group_in.location.x -= 700 - emissive = node_group.inputs.new('NodeSocketFloat', 'Emissive Strength') + emissive = node_group.inputs.new( + 'NodeSocketFloat', 'Emissive Strength') emissive.default_value = 1.0 color = node("ShaderNodeEmission") link(group_in.outputs[0], color.inputs[1]) eevee_alpha_fix = node("ShaderNodeInvert") eevee_alpha_fix.location.x -= 500 eevee_alpha_fix.location.y += 300 - link(base_image_node.outputs[1], eevee_alpha_fix.inputs[1]) # Fix for obnoxious transparency bug in Eevee - link(base_image_node.outputs['Color'], mix_shader.inputs['Fac']) + # Fix for obnoxious transparency bug in Eevee + link(base_image_node.outputs[1], eevee_alpha_fix.inputs[1]) + link(base_image_node.outputs['Color'], + mix_shader.inputs['Fac']) else: color = node("ShaderNodeBsdfDiffuse") - link(base_image_node.outputs['Alpha'], mix_shader.inputs['Fac']) + link(base_image_node.outputs['Alpha'], + mix_shader.inputs['Fac']) color.location.x -= 200 color.location.y -= 150 @@ -457,18 +473,17 @@ def material_group_additive(context, operator, group_name, material, is_emissive link(base_image_node.outputs['Color'], color.inputs[0]) link(transparent.outputs[0], mix_shader.inputs[1]) link(color.outputs[0], mix_shader.inputs[2]) - - if (material.BaseTexture != 'None'): - if (material.BaseTexture in bpy.data.images): - diffuse_texture = bpy.data.images[material.BaseTexture] - diffuse_texture.alpha_mode = 'CHANNEL_PACKED' - base_image_node.image = diffuse_texture - + + if material.BaseTexture != 'None' and material.BaseTexture in bpy.data.images: + diffuse_texture = bpy.data.images[material.BaseTexture] + diffuse_texture.alpha_mode = 'CHANNEL_PACKED' + base_image_node.image = diffuse_texture + link(mix_shader.outputs[0], group_out.inputs[0]) return node_group - def material_group_basic(context, operator, group_name, material): + def material_group_basic(context, operator, group_name, material): node_group = bpy.data.node_groups.new(group_name, 'ShaderNodeTree') node = node_group.nodes.new @@ -477,9 +492,10 @@ def material_group_basic(context, operator, group_name, material): group_in = node('NodeGroupInput') group_in.location.x -= 700 node_group.inputs.new('NodeSocketColor', 'Team Color') - spec = node_group.inputs.new('NodeSocketFloat', 'Specular Intensity') - spec.default_value = 0.1 - + spec = node_group.inputs.new( + 'NodeSocketFloat', 'Specular Intensity') + spec.default_value = 0.1 + group_out = node('NodeGroupOutput') node_group.outputs.new('NodeSocketColor', 'Base Color') node_group.outputs.new('NodeSocketFloat', 'Specular') @@ -521,8 +537,9 @@ def material_group_basic(context, operator, group_name, material): specular_multiply.operation = 'MULTIPLY' specular_multiply.location.x -= 800 specular_multiply.location.y -= 100 - - link(normal_image_node.outputs['Color'], normal_split.inputs['Image']) + + link(normal_image_node.outputs['Color'], + normal_split.inputs['Image']) link(normal_split.outputs['R'], normal_combine.inputs['R']) link(normal_split.outputs['G'], normal_invert.inputs[1]) link(normal_invert.outputs[0], normal_combine.inputs['G']) @@ -530,24 +547,24 @@ def material_group_basic(context, operator, group_name, material): link(normal_combine.outputs[0], normal_map_node.inputs[1]) link(normal_map_node.outputs[0], group_out.inputs[2]) - link(normal_image_node.outputs['Alpha'], specular_multiply.inputs[0]) + link(normal_image_node.outputs['Alpha'], + specular_multiply.inputs[0]) link(group_in.outputs['Team Color'], mix_node.inputs['Color2']) - link(group_in.outputs['Specular Intensity'], specular_multiply.inputs[1]) + link(group_in.outputs['Specular Intensity'], + specular_multiply.inputs[1]) link(specular_multiply.outputs[0], group_out.inputs[1]) - - if (material.BaseTexture != 'None'): - if (material.BaseTexture in bpy.data.images): - diffuse_texture = bpy.data.images[material.BaseTexture] - diffuse_texture.alpha_mode = 'CHANNEL_PACKED' - base_image_node.image = diffuse_texture - - if (material.NormalTexture != 'None'): - if (material.NormalTexture in bpy.data.images): - normal_texture = bpy.data.images[material.NormalTexture] - normal_texture.alpha_mode = 'CHANNEL_PACKED' - normal_image_node.image = normal_texture - normal_image_node.image.colorspace_settings.name = 'Raw' + + if material.BaseTexture != 'None' and material.BaseTexture in bpy.data.images: + diffuse_texture = bpy.data.images[material.BaseTexture] + diffuse_texture.alpha_mode = 'CHANNEL_PACKED' + base_image_node.image = diffuse_texture + + if material.NormalTexture != 'None' and material.NormalTexture in bpy.data.images: + normal_texture = bpy.data.images[material.NormalTexture] + normal_texture.alpha_mode = 'CHANNEL_PACKED' + normal_image_node.image = normal_texture + normal_image_node.image.colorspace_settings.name = 'Raw' return node_group @@ -558,71 +575,76 @@ def set_up_textures(material): nodes = nt.nodes links = nt.links - #clean up - while(nodes): nodes.remove(nodes[0]) - - output = nodes.new("ShaderNodeOutputMaterial") + # clean up + while(nodes): + nodes.remove(nodes[0]) + + output = nodes.new("ShaderNodeOutputMaterial") custom_node_name = material.name + "Group" my_group = 'null' if ("Additive" in material.shaderList.shaderList): material.blend_method = "BLEND" - my_group = material_group_additive(self, context, custom_node_name, material, True) + my_group = material_group_additive( + self, context, custom_node_name, material, True) mat_group = nt.nodes.new("ShaderNodeGroup") mat_group.node_tree = bpy.data.node_groups[my_group.name] mat_group.location.x -= 200.0 links.new(mat_group.outputs[0], output.inputs['Surface']) elif ("Alpha" in material.shaderList.shaderList): material.blend_method = "BLEND" - my_group = material_group_additive(self, context, custom_node_name, material, False) + my_group = material_group_additive( + self, context, custom_node_name, material, False) mat_group = nt.nodes.new("ShaderNodeGroup") mat_group.node_tree = bpy.data.node_groups[my_group.name] mat_group.location.x -= 200.0 links.new(mat_group.outputs[0], output.inputs['Surface']) else: bsdf = nodes.new("ShaderNodeBsdfPrincipled") - bsdf.inputs[4].default_value = 0.1 # Set metallic to 0.1 - bsdf.inputs[7].default_value = 0.2 # Set roughness to 0.2 + bsdf.inputs[4].default_value = 0.1 # Set metallic to 0.1 + bsdf.inputs[7].default_value = 0.2 # Set roughness to 0.2 bsdf.location.x -= 300.0 links.new(bsdf.outputs['BSDF'], output.inputs['Surface']) - my_group = material_group_basic(self, context, custom_node_name, material) + my_group = material_group_basic( + self, context, custom_node_name, material) mat_group = nt.nodes.new("ShaderNodeGroup") mat_group.node_tree = bpy.data.node_groups[my_group.name] mat_group.location.x -= 500.0 links.new(mat_group.outputs[0], bsdf.inputs['Base Color']) links.new(mat_group.outputs[1], bsdf.inputs[5]) links.new(mat_group.outputs[2], bsdf.inputs['Normal']) - + def create_material(currentSubMesh): - obj = bpy.context.object + if currentSubMesh.material.name != "DUMMYMATERIAL": + return + + oldMat = currentSubMesh.material + + texName = currentSubMesh.material.BaseTexture + texName = texName[0:len(texName) - 4] + " Material" + mat = assign_material(texName) + + mat.shaderList.shaderList = oldMat.shaderList.shaderList # Ugly. Should be able to use set_alamo_shader's shader finder instead? - material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular","Shininess","Colorization" \ - ,"DebugColor","UVOffset","Color","UVScrollRate","DiffuseColor","EdgeBrightness","BaseUVScale","WaveUVScale","DistortUVScale","BaseUVScrollRate","WaveUVScrollRate","DistortUVScrollRate","BendScale" \ - , "Diffuse1","CloudScrollRate","CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower"] - - if currentSubMesh.material.name == "DUMMYMATERIAL": - oldMat = currentSubMesh.material - - texName = currentSubMesh.material.BaseTexture - texName = texName[0:len(texName) - 4] - mat = assign_material(texName) - - mat.shaderList.shaderList = oldMat.shaderList.shaderList - for texture in material_props: - if texture in oldMat: - mat[texture] = oldMat[texture] - - obj.data.materials.clear() - obj.data.materials.append(mat) - currentSubMesh.material = mat - + material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "DebugColor", "UVOffset", "Color", "UVScrollRate", "DiffuseColor", + "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale", "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower"] + + for texture in material_props: + if texture in oldMat: + mat[texture] = oldMat[texture] + + obj = bpy.context.object + + obj.data.materials.clear() + obj.data.materials.append(mat) + currentSubMesh.material = mat + def assign_material(name): if name in bpy.data.materials: - mat = bpy.data.materials.get(name) + return bpy.data.materials.get(name) else: - mat = bpy.data.materials.new(name) - return mat + return bpy.data.materials.new(name) def create_object(currentMesh): global mesh @@ -645,7 +667,7 @@ def create_object(currentMesh): if (currentMesh.collision == 1): object.HasCollision = True - #create vertex groups + # create vertex groups armature = utils.findArmature() for bone in armature.data.bones: vertgroup = object.vertex_groups.new(name=bone.name) @@ -658,7 +680,8 @@ def process_vertex_buffer_2(legacy, currentSubMesh): coX = f.unpack(file.read(4))[0] coY = f.unpack(file.read(4))[0] coZ = f.unpack(file.read(4))[0] - currentSubMesh.vertices.append(mathutils.Vector((coX, coY, coZ))) + currentSubMesh.vertices.append( + mathutils.Vector((coX, coY, coZ))) file.seek(12, 1) UV = [] UV.append(f.unpack(file.read(4))[0]) @@ -676,46 +699,52 @@ def process_index_buffer(currentSubMesh): counter = 0 while counter < currentSubMesh.nFaces: face = [] - face.append(h.unpack(file.read(2))[0] + currentSubMesh.faceOffset) - face.append(h.unpack(file.read(2))[0] + currentSubMesh.faceOffset) - face.append(h.unpack(file.read(2))[0] + currentSubMesh.faceOffset) + face.append(h.unpack(file.read(2))[ + 0] + currentSubMesh.faceOffset) + face.append(h.unpack(file.read(2))[ + 0] + currentSubMesh.faceOffset) + face.append(h.unpack(file.read(2))[ + 0] + currentSubMesh.faceOffset) currentSubMesh.faces.append(face) counter += 1 def process_texture_chunk(material): - file.seek(5, 1) # skip chunk size and child header - length = struct.unpack("H", file.read(1) + b'\x00') # get string length - global texture_function_name - texture_function_name = "" - counter = 0 - while counter < length[0] - 1: - letter = str(file.read(1)) - letter = letter[2:len(letter) - 1] - texture_function_name = texture_function_name + letter - counter += 1 - file.seek(1, 1) # skip string end byte - file.seek(1, 1) # skip child header - length = struct.unpack("H", file.read(1) + b'\x00') # get string length - print(texture_function_name) - texture_name = "" - counter = 0 - while counter < length[0] - 1: - letter = str(file.read(1)) - letter = letter[2:len(letter) - 1] - texture_name = texture_name + letter - counter += 1 - # replace texture format with .dds - if texture_name != "None": - texture_name = texture_name[0:len(texture_name) - 4] + ".dds" - file.seek(1, 1) # skip string end byte + file.seek(5, 1) # skip chunk size and child header + length = struct.unpack("H", file.read( + 1) + b'\x00') # get string length + global texture_function_name + texture_function_name = "" + counter = 0 + while counter < length[0] - 1: + letter = str(file.read(1)) + letter = letter[2:len(letter) - 1] + texture_function_name = texture_function_name + letter + counter += 1 + file.seek(1, 1) # skip string end byte + file.seek(1, 1) # skip child header + length = struct.unpack("H", file.read( + 1) + b'\x00') # get string length + print(texture_function_name) + texture_name = "" + counter = 0 + while counter < length[0] - 1: + letter = str(file.read(1)) + letter = letter[2:len(letter) - 1] + texture_name = texture_name + letter + counter += 1 + # replace texture format with .dds + if texture_name != "None": + texture_name = texture_name[0:len(texture_name) - 4] + ".dds" + file.seek(1, 1) # skip string end byte - load_image(texture_name) - exec('material.' + texture_function_name + '= texture_name') + load_image(texture_name) + exec('material.' + texture_function_name + '= texture_name') def createUVLayer(layerName, uv_coordinates): vert_uvs = uv_coordinates - mesh.uv_layers.new(name = layerName) - mesh.uv_layers[-1].data.foreach_set("uv", [uv for pair in [vert_uvs[l.vertex_index] for l in mesh.loops] for uv in pair]) + mesh.uv_layers.new(name=layerName) + mesh.uv_layers[-1].data.foreach_set( + "uv", [uv for pair in [vert_uvs[l.vertex_index] for l in mesh.loops] for uv in pair]) def set_alamo_shader(currentSubMesh): # create material and assign shaderName = read_string() @@ -723,22 +752,23 @@ def set_alamo_shader(currentSubMesh): # create material and assign if shaderName == 'MeshCollision.fx': mat = assign_material("COLLISION") - elif (shaderName == 'RSkinShadowVolume.fx' or shaderName == 'MeshShadowVolume.fx'): + elif shaderName in ['RSkinShadowVolume.fx', 'MeshShadowVolume.fx']: mat = assign_material("SHADOW") else: mat = assign_material("DUMMYMATERIAL") - # DUMMYMATERIAL is a temporary material to allow Alamo shader properties to be assigned. + # DUMMYMATERIAL is a temporary material to allow Alamo shader properties to be assigned. # Can't assign final material because material names are now based on BaseTexture, and textures aren't known yet. Probably a better way to do this. - #find shader, ignoring case + # find shader, ignoring case currentKey = None for key in settings.material_parameter_dict: if(key.lower() == shaderName.lower()): currentKey = key break - if currentKey == None: - print("Warning: unknown shader: " + shaderName + " setting shader to alDefault.fx") + if currentKey is None: + print("Warning: unknown shader: " + shaderName + + " setting shader to alDefault.fx") currentKey = "alDefault.fx" mat.shaderList.shaderList = currentKey @@ -765,10 +795,11 @@ def assign_vertex_groups(animation_mapping, currentMesh): mod.use_vertex_groups = True while counter < n_vertices: - object.vertex_groups[animation_mapping[bone_indices[counter]]].add([counter], 1, 'ADD') + object.vertex_groups[animation_mapping[bone_indices[counter]]].add([ + counter], 1, 'ADD') counter += 1 - #proxy and connection functions + # proxy and connection functions def get_n_objects_n_proxies(): size = read_chunk_length() @@ -776,10 +807,11 @@ def get_n_objects_n_proxies(): n_objects = struct.unpack("l", file.read(4)) file.seek(2, 1) n_proxies = struct.unpack("l", file.read(4)) - n_objects_proxies = {"n_objects": n_objects[0], "n_proxies": n_proxies[0]} + n_objects_proxies = { + "n_objects": n_objects[0], "n_proxies": n_proxies[0]} - #some .alo formats have an additional unspecified value at this position - #to read the rest correctly this code checks if this is the case here and skips appropriately + # some .alo formats have an additional unspecified value at this position + # to read the rest correctly this code checks if this is the case here and skips appropriately size -= 12 file.seek(size, 1) @@ -792,9 +824,9 @@ def read_conncetion(armatureData, meshNameList): bone_index = struct.unpack("I", file.read(4))[0] armatureBlender = utils.findArmature() - #set connection of object to bone and move object to bone + # set connection of object to bone and move object to bone obj = None - if mesh_index < len(meshNameList): #light objects can mess this up + if mesh_index < len(meshNameList): # light objects can mess this up obj = bpy.data.objects[meshNameList[mesh_index]] bone = armatureBlender.data.bones[bone_index] if obj != None: @@ -842,24 +874,24 @@ def read_proxy(): bone.altDecreaseStayHidden = altDecreaseStayHidden bpy.ops.object.mode_set(mode='OBJECT') # go to Edit mode - #Utility functions + # Utility functions def read_chunk_length(): - #the hight bit is used to tell if chunk holds data or chunks, so if it is set it has to be ignored when calculating length + # the hight bit is used to tell if chunk holds data or chunks, so if it is set it has to be ignored when calculating length length = struct.unpack("= 2147483648: length -= 2147483648 return length def cut_string(string): - #bones have a 63 character limit, this function cuts longer strings with space for .xyz end used by blender to distinguish double name - if(len(string)> 63): + # bones have a 63 character limit, this function cuts longer strings with space for .xyz end used by blender to distinguish double name + if(len(string) > 63): return string[0:59] else: return string def read_string(): - #reads string out of chunk containing only a string + # reads string out of chunk containing only a string length = struct.unpack("I", file.read(4)) # get string length string = "" counter = 0 @@ -872,7 +904,7 @@ def read_string(): return string def read_string_mini_chunk(): - file.seek(1,1)#skip chunk header + file.seek(1, 1) # skip chunk header size = length = struct.unpack(" Date: Thu, 19 Aug 2021 11:15:14 -0700 Subject: [PATCH 021/100] Updated authors, incremented version number --- io_alamo_tools/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 0fd34f1..92600a2 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,8 +1,8 @@ bl_info = { "name": "ALAMO Tools", - "author": "Gaukler", - "version": (0, 0, 1, 0), - "blender": (2, 82, 0), + "author": "Gaukler, evilbobthebob, inertial", + "version": (0, 0, 2, 0), + "blender": (2, 93, 0), "category": "Import-Export" } From 41a5713f0433c515e8b351afc6b4ab496d86a898 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 25 Aug 2021 15:13:14 -0700 Subject: [PATCH 022/100] Submod texture override actually works now --- io_alamo_tools/import_alo.py | 43 +++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index a0a22db..a08112e 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -61,18 +61,32 @@ class ALO_Importer(bpy.types.Operator): default=True, ) + textureOverride: EnumProperty( + name = "Submod Texture Override", + description = "Try to import textures from a different submod", + items=( + ("NONE", "None", ""), + ('CoreSaga', "Core Saga", ""), + ('FotR', "Fall of the Republic", ""), + ('GCW', "Imperial Reign", ""), + ('Rev', "Revan's Revenge", ""), + ('TR', "Thrawn's Revenge", ""), + ), + default="NONE", + ) + def draw(self, context): layout = self.layout layout.prop(self, "importAnimations") layout.prop(self, "parentName") + layout.prop(self, "textureOverride") filepath: StringProperty( name="File Path", description="Filepath used for importing the ALO file", maxlen=1024, default="") # execute() is called by blender when running the operator. def execute(self, context): - # main structure def process_active_junk(): @@ -626,7 +640,7 @@ def create_material(currentSubMesh): mat.shaderList.shaderList = oldMat.shaderList.shaderList - # Ugly. Should be able to use set_alamo_shader's shader finder instead? + # TODO: Extract set_alamo_shader's shader finder to new function, use that here. material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "DebugColor", "UVOffset", "Color", "UVScrollRate", "DiffuseColor", "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale", "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower"] @@ -722,9 +736,7 @@ def process_texture_chunk(material): counter += 1 file.seek(1, 1) # skip string end byte file.seek(1, 1) # skip child header - length = struct.unpack("H", file.read( - 1) + b'\x00') # get string length - print(texture_function_name) + length = struct.unpack("H", file.read(1) + b'\x00') # get string length texture_name = "" counter = 0 while counter < length[0] - 1: @@ -736,7 +748,7 @@ def process_texture_chunk(material): if texture_name != "None": texture_name = texture_name[0:len(texture_name) - 4] + ".dds" file.seek(1, 1) # skip string end byte - + load_image(texture_name) exec('material.' + texture_function_name + '= texture_name') @@ -980,6 +992,21 @@ def deleteRoot(): # material utility functions + def textureOverride(path, submod, texture_name): + submodEnd = path.find("\\Data") + submodStart = -1 + if path.find(submod): + submodStart = path.find(submod) + if submodStart == -1: + submodStart = submodEnd + 1 + newPath = path[:submodStart] + submod + path[submodEnd:] + + if os.path.isfile(newPath): + return newPath + else: + print(f'{texture_name} not found in {submod}, falling back to default') + return path + def load_image(texture_name): if texture_name == 'None': return @@ -989,10 +1016,14 @@ def load_image(texture_name): path = file.name path = os.path.split(path)[0] path = os.path.split(path)[0] + "/TEXTURES/" + texture_name + if self.properties.textureOverride != "NONE": + path = textureOverride(path, self.properties.textureOverride, texture_name) + if os.path.isfile(path): img = bpy.data.images.load(path) else: print("Couldn't find texture: " + texture_name) + self.report({"WARNING"}, "Couldn't find texture: " + texture_name) return def validate_material_prop(name): From 29705253afb82f57e8caa9727bea1ee7daa720f4 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 25 Aug 2021 15:59:07 -0700 Subject: [PATCH 023/100] Fixed case when additive materials use the same texture as basic materials --- io_alamo_tools/import_alo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index a08112e..23ce144 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -636,6 +636,8 @@ def create_material(currentSubMesh): texName = currentSubMesh.material.BaseTexture texName = texName[0:len(texName) - 4] + " Material" + if texName in bpy.data.materials and oldMat.shaderList.shaderList != bpy.data.materials.get(texName).shaderList.shaderList: + texName += "1" mat = assign_material(texName) mat.shaderList.shaderList = oldMat.shaderList.shaderList From 43ae86a15a88838eeddd028e290c6daaa307393f Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 25 Aug 2021 15:59:32 -0700 Subject: [PATCH 024/100] Version update --- io_alamo_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 92600a2..37a417f 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 2, 0), + "version": (0, 0, 2, 1), "blender": (2, 93, 0), "category": "Import-Export" } From b229ddc4bdf6ba42052355a4c48b7b612e1e7c95 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 25 Aug 2021 20:58:18 -0700 Subject: [PATCH 025/100] Bulk proxyIsHiddenAnimation UI, first draft --- io_alamo_tools/__init__.py | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 37a417f..fadeb1a 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -61,6 +61,41 @@ def execute(self, context): bpy.context.view_layer.objects.active = object return {'FINISHED'} +class keyframeProxyShow(bpy.types.Operator): + bl_idname = "alamo.show_keyframe_proxy" + bl_label = "Show" + bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" + + def execute(self, context): + bones = bpy.context.selected_pose_bones + for bone in bones: + bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + bone.proxyIsHiddenAnimation = False + return {'FINISHED'} + +class keyframeProxyHide(bpy.types.Operator): + bl_idname = "alamo.hide_keyframe_proxy" + bl_label = "Hide" + bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" + + def execute(self, context): + bones = bpy.context.selected_pose_bones + for bone in bones: + bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + bone.proxyIsHiddenAnimation = True + return {'FINISHED'} + +class keyframeProxyRemove(bpy.types.Operator): + bl_idname = "alamo.remove_keyframe_proxy" + bl_label = "Remove" + bl_description = "Remove keyframes from all selected bones" + + def execute(self, context): + bones = bpy.context.selected_pose_bones + for bone in bones: + bone.keyframe_delete(data_path="proxyIsHiddenAnimation") + return {'FINISHED'} + def skeletonEnumCallback(scene, context): armatures = [('None', 'None', '', '', 0)] counter = 1 @@ -114,6 +149,17 @@ def draw(self, context): bone = bpy.context.active_bone + bones = bpy.context.selected_pose_bones + if len(bones) > 1 and bpy.context.mode == 'POSE': + col = layout.column(align=True) + col.operator('alamo.show_keyframe_proxy', text = "Show", + icon="HIDE_OFF") + col.operator('alamo.hide_keyframe_proxy', text = "Hide", + icon="HIDE_ON") + col.operator('alamo.remove_keyframe_proxy', text = "Remove", + icon="X") + + if type(bone) != type(None): if(type(bpy.context.active_bone ) is bpy.types.EditBone): c.prop(bone.billboardMode, "billboardMode") @@ -234,6 +280,9 @@ def menu_func_export(self, context): ALA_Exporter, ALAMO_PT_materialPropertyPanel, createConstraintBoneButton, + keyframeProxyShow, + keyframeProxyHide, + keyframeProxyRemove, ALAMO_PT_ToolsPanel ) From 442152b58e0f66af8ce1e4f592c26e96ef7dc7ef Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 29 Aug 2021 17:29:15 -0700 Subject: [PATCH 026/100] Error Checking --- io_alamo_tools/__init__.py | 53 ++++++++++++++----- io_alamo_tools/export_ala.py | 2 +- io_alamo_tools/export_alo.py | 99 ++++++++++++++++++++++-------------- io_alamo_tools/import_ala.py | 2 +- io_alamo_tools/import_alo.py | 12 ++--- 5 files changed, 106 insertions(+), 62 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index fadeb1a..7b62587 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 2, 1), + "version": (0, 0, 2, 4), "blender": (2, 93, 0), "category": "Import-Export" } @@ -61,6 +61,30 @@ def execute(self, context): bpy.context.view_layer.objects.active = object return {'FINISHED'} +# class ProxyShow(bpy.types.Operator): +# bl_idname = "alamo.show_proxy" +# bl_label = "Show" +# bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" + +# def execute(self, context): +# bones = bpy.context.selected_pose_bones +# for bone in bones: +# bone.proxyIsHiddenAnimation = False +# bone.keyframe_insert(data_path="proxyIsHiddenAnimation") +# return {'FINISHED'} + +# class ProxyHide(bpy.types.Operator): +# bl_idname = "alamo.hide_proxy" +# bl_label = "Hide" +# bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" + +# def execute(self, context): +# bones = bpy.context.selected_pose_bones +# for bone in bones: +# bone.proxyIsHiddenAnimation = True +# bone.keyframe_insert(data_path="proxyIsHiddenAnimation") +# return {'FINISHED'} + class keyframeProxyShow(bpy.types.Operator): bl_idname = "alamo.show_keyframe_proxy" bl_label = "Show" @@ -69,8 +93,10 @@ class keyframeProxyShow(bpy.types.Operator): def execute(self, context): bones = bpy.context.selected_pose_bones for bone in bones: - bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + if bone.parent is not None: + print(f'{bone.parent.name = }') bone.proxyIsHiddenAnimation = False + bone.keyframe_insert(data_path="proxyIsHiddenAnimation") return {'FINISHED'} class keyframeProxyHide(bpy.types.Operator): @@ -81,8 +107,8 @@ class keyframeProxyHide(bpy.types.Operator): def execute(self, context): bones = bpy.context.selected_pose_bones for bone in bones: - bone.keyframe_insert(data_path="proxyIsHiddenAnimation") bone.proxyIsHiddenAnimation = True + bone.keyframe_insert(data_path="proxyIsHiddenAnimation") return {'FINISHED'} class keyframeProxyRemove(bpy.types.Operator): @@ -148,18 +174,17 @@ def draw(self, context): c.prop(action, "AnimationEndFrame") - bone = bpy.context.active_bone - bones = bpy.context.selected_pose_bones - if len(bones) > 1 and bpy.context.mode == 'POSE': - col = layout.column(align=True) - col.operator('alamo.show_keyframe_proxy', text = "Show", - icon="HIDE_OFF") - col.operator('alamo.hide_keyframe_proxy', text = "Hide", - icon="HIDE_ON") - col.operator('alamo.remove_keyframe_proxy', text = "Remove", - icon="X") - + # bones = bpy.context.selected_pose_bones + # if bpy.context.mode == 'POSE' and len(bones) > 0: + # col = layout.column(align=True) + # col.operator('alamo.show_keyframe_proxy', text = "Show", + # icon="HIDE_OFF") + # col.operator('alamo.hide_keyframe_proxy', text = "Hide", + # icon="HIDE_ON") + # col.operator('alamo.remove_keyframe_proxy', text = "Remove", + # icon="X") + bone = bpy.context.active_bone if type(bone) != type(None): if(type(bpy.context.active_bone ) is bpy.types.EditBone): c.prop(bone.billboardMode, "billboardMode") diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index ca5f791..e733b9e 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -182,7 +182,7 @@ def create_animation(): # get armature name armature = utils.findArmature() if armature == None: - print("Warning: No armature found!") + self.report({"WARNING"}, "No armature found!") return b'' chunk += create_anim_info_chunk(armature) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 71635bb..42e4fc0 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -23,6 +23,19 @@ import os import bmesh import copy +from contextlib import contextmanager + +@contextmanager +def disable_exception_traceback(): + """ + All traceback information is suppressed and only the exception type and value are printed + Used to make user friendly errors + """ + default_value = getattr(sys, "tracebacklimit", 1000) # `1000` is a Python's default value + sys.tracebacklimit = 0 + yield + sys.tracebacklimit = default_value # revert changes + class ALO_Exporter(bpy.types.Operator): @@ -408,7 +421,7 @@ def create_animation_mapping(mesh, object, material, bone_name_per_alo_index): for index in group_index_list: if index == None: cleanUpModifiers(object) - raise RuntimeError('Missing vertex group on object: ' + object.name) + self.report({"ERROR"}, f'ALAMO - Missing vertex group on object: {object.name}') group_index_list.sort() group_to_alo_index = {} @@ -1300,27 +1313,28 @@ def selectNonManifoldVertices(object): def checkShadowMesh(mesh_list): #checks if shadow meshes are correct and checks if material is missing for object in mesh_list: - if len(object.data.materials) == 0: - raise RuntimeError('Missing material on object: ' + object.name) - shader = object.data.materials[0].shaderList.shaderList - if shader == 'MeshShadowVolume.fx' or shader == 'RSkinShadowVolume.fx': - bm = bmesh.new() # create an empty BMesh - bm.from_mesh(object.data) # fill it in from a Mesh - bm.verts.ensure_lookup_table() - - for vertex in bm.verts: - if not vertex.is_manifold: - bm.free() - selectNonManifoldVertices(object) - raise RuntimeError('Non manifold geometry shadow mesh: ' + object.name) - - for edge in bm.edges: - if len(edge.link_faces) < 2 : - bm.free() - selectNonManifoldVertices(object) - raise RuntimeError('Non manifold geometry shadow mesh: ' + object.name) - - bm.free() + if len(object.data.materials) > 0: + shader = object.data.materials[0].shaderList.shaderList + if shader in ['MeshShadowVolume.fx', 'RSkinShadowVolume.fx']: + bm = bmesh.new() # create an empty BMesh + bm.from_mesh(object.data) # fill it in from a Mesh + bm.verts.ensure_lookup_table() + + for vertex in bm.verts: + if not vertex.is_manifold: + bm.free() + selectNonManifoldVertices(object) + self.report({"ERROR"}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}') + + for edge in bm.edges: + if len(edge.link_faces) < 2 : + bm.free() + selectNonManifoldVertices(object) + self.report({"ERROR"}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}') + + bm.free() + else: + self.report({"ERROR"}, f'ALAMO - Missing material on object: {object.name}') def checkUV(mesh_list): #throws error if object lacks UVs @@ -1328,16 +1342,16 @@ def checkUV(mesh_list): #throws error if object lacks UVs for material in object.data.materials: if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': if len(object.data.materials) > 1: - raise RuntimeError('Multiple materials on shadow volume: ' + object.name + ' , remove additional materials') + self.report({"ERROR"}, f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials') else: return if object.HasCollision: if len(object.data.materials) > 1: - raise RuntimeError('Multiple submeshes/materials on collision mesh: ' + object.name + ' , remove additional materials') + self.report({"ERROR"}, f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials') if object.data.uv_layers: #or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows continue else: - raise RuntimeError('Missing UV: ' + object.name) + self.report({"ERROR"}, f'ALAMO - Missing UV: {object.name}') def checkInvalidArmatureModifier(mesh_list): #throws error if armature modifier lacks rig, this would crash the exporter later and checks if skeleton in modifier doesn't match active skeleton activeSkeleton = bpy.context.scene.ActiveSkeleton.skeletonEnum @@ -1345,41 +1359,41 @@ def checkInvalidArmatureModifier(mesh_list): #throws error if armature modifier for modifier in object.modifiers: if modifier.type == "ARMATURE": if modifier.object == None: - raise RuntimeError('Armature modifier without selected skeleton on: ' + object.name) + self.report({"ERROR"}, f'ALAMO - Armature modifier without selected skeleton on: {object.name}') return True elif modifier.object.type != 'NoneType': if modifier.object.name != activeSkeleton: - raise RuntimeError('Armature modifier skeleton doesnt match active skeleton on: ' + object.name) + self.report({"ERROR"}, f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}") return True for constraint in object.constraints: if constraint.type == 'CHILD_OF': if constraint.target is not None: #print(type(constraint.target)) if constraint.target.name != activeSkeleton: - raise RuntimeError('Constraint doesnt match active skeleton on: ' + object.name) + self.report({"ERROR"}, f"ALAMO - Constraint doesn't match active skeleton on: {object.name}") return True def checkFaceNumber(mesh_list): #checks if the number of faces exceeds max ushort, which is used to save the indices for object in mesh_list: if len(object.data.polygons) > 65535: - raise RuntimeError('Face number exceeds uShort max on object: ' + object.name + ' split mesh into multiple objects') + self.report({"ERROR"}, f'ALAMO - Face number exceeds uShort max on object: {object.name}; split mesh into multiple objects') return True def checkAutosmooth(mesh_list): #prints a warning if Autosmooth is used for object in mesh_list: if object.data.use_auto_smooth: - print('Warning: ' + object.name + ' uses autosmooth, ingame shading might not match blender, use edgesplit instead') + self.report({"ERROR"}, f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead') def checkTranslation(mesh_list): #prints warning when translation is not default for object in mesh_list: if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or object.scale != mathutils.Vector((1.0, 1.0, 1.0)): - print('Warning: ' + object.name + ' is not aligned with the world origin, apply translation or bind to bone') + self.report({"ERROR"}, f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone') def checkTranslationArmature(): #prints warning when translation is not default armature = utils.findArmature() if armature != None: if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): - print('Warning: active Armature is not aligned with the world origin') + self.report({"ERROR"}, 'ALAMO - active Armature is not aligned with the world origin') def unhide(): hiddenList = [] @@ -1455,14 +1469,21 @@ def exportAnimations(filePath): mesh_list = create_export_list(bpy.context.scene.collection) + has_errors = [] + #check if export objects satisfy requirements (has material, UVs, ...) - checkShadowMesh(mesh_list) - checkUV(mesh_list) - checkFaceNumber(mesh_list) - checkAutosmooth(mesh_list) - checkTranslation(mesh_list) - checkTranslationArmature() - checkInvalidArmatureModifier(mesh_list) + has_errors.append(checkShadowMesh(mesh_list)) + has_errors.append(checkUV(mesh_list)) + has_errors.append(checkFaceNumber(mesh_list)) + has_errors.append(checkAutosmooth(mesh_list)) + has_errors.append(checkTranslation(mesh_list)) + has_errors.append(checkTranslationArmature()) + has_errors.append(checkInvalidArmatureModifier(mesh_list)) + + for error in has_errors: + if error: + with disable_exception_traceback(): + raise Exception('ALAMO - EXPORT FAILED') hiddenList = unhide() collection_is_hidden_list = unhide_collections(bpy.context.scene.collection) diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index 1a8f040..bce978b 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -353,7 +353,7 @@ def validate(data): break if(fitting): return True - print("animation bones not matching active armature") + self.report({"WARNING"}, "animation bones not matching active armature") return False class AnimationImporter(): diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 23ce144..33812d3 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -102,8 +102,7 @@ def process_active_junk(): meshName = processMeshChunk() meshNameList.append(meshName) elif active_chunk == b"\x00\x13\x00\00": # light chunk is irrelevant - print( - 'WARNING: file contains light objects, these are not supported and might cause minor issues') + self.report({"WARNING"}, "ALAMO - File contains light objects, these are not supported and might cause minor issues") size = read_chunk_length() file.seek(size, 1) # skip to next chunk elif active_chunk == b"\x00\x06\x00\00": @@ -781,7 +780,7 @@ def set_alamo_shader(currentSubMesh): # create material and assign break if currentKey is None: - print("Warning: unknown shader: " + shaderName + + self.report({"WARNING"}, "ALAMO - Unknown shader: " + shaderName + " setting shader to alDefault.fx") currentKey = "alDefault.fx" @@ -1006,7 +1005,7 @@ def textureOverride(path, submod, texture_name): if os.path.isfile(newPath): return newPath else: - print(f'{texture_name} not found in {submod}, falling back to default') + self.report({"WARNING"}, f'ALAMO - {texture_name} not found in {submod}, falling back to default') return path def load_image(texture_name): @@ -1024,8 +1023,7 @@ def load_image(texture_name): if os.path.isfile(path): img = bpy.data.images.load(path) else: - print("Couldn't find texture: " + texture_name) - self.report({"WARNING"}, "Couldn't find texture: " + texture_name) + self.report({"WARNING"}, "ALAMO - Couldn't find texture: " + texture_name) return def validate_material_prop(name): @@ -1035,7 +1033,7 @@ def validate_material_prop(name): if(name in material_props): return True else: - print("Unknown material porperty: " + name) + self.report({"WARNING"}, "ALAMO - Unknown material porperty: " + name) return False def read_int(material): From d6a19521a04214c7e818edde720824930bd104b8 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Mon, 30 Aug 2021 15:05:30 -0700 Subject: [PATCH 027/100] Improved and re-enabled multi-proxy visibility keyframing --- io_alamo_tools/__init__.py | 44 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 7b62587..2e810d9 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 2, 4), + "version": (0, 0, 2, 5), "blender": (2, 93, 0), "category": "Import-Export" } @@ -92,11 +92,15 @@ class keyframeProxyShow(bpy.types.Operator): def execute(self, context): bones = bpy.context.selected_pose_bones + arm = bpy.context.active_object.data for bone in bones: - if bone.parent is not None: - print(f'{bone.parent.name = }') + arm.bones.active = bone.bone bone.proxyIsHiddenAnimation = False bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + + for area in bpy.context.screen.areas: + if area.type == 'DOPESHEET_EDITOR': + area.tag_redraw() return {'FINISHED'} class keyframeProxyHide(bpy.types.Operator): @@ -106,9 +110,15 @@ class keyframeProxyHide(bpy.types.Operator): def execute(self, context): bones = bpy.context.selected_pose_bones + arm = bpy.context.active_object.data for bone in bones: + arm.bones.active = bone.bone bone.proxyIsHiddenAnimation = True bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + + for area in bpy.context.screen.areas: + if area.type == 'DOPESHEET_EDITOR': + area.tag_redraw() return {'FINISHED'} class keyframeProxyRemove(bpy.types.Operator): @@ -120,6 +130,10 @@ def execute(self, context): bones = bpy.context.selected_pose_bones for bone in bones: bone.keyframe_delete(data_path="proxyIsHiddenAnimation") + + for area in bpy.context.screen.areas: + if area.type == 'DOPESHEET_EDITOR': + area.tag_redraw() return {'FINISHED'} def skeletonEnumCallback(scene, context): @@ -174,15 +188,15 @@ def draw(self, context): c.prop(action, "AnimationEndFrame") - # bones = bpy.context.selected_pose_bones - # if bpy.context.mode == 'POSE' and len(bones) > 0: - # col = layout.column(align=True) - # col.operator('alamo.show_keyframe_proxy', text = "Show", - # icon="HIDE_OFF") - # col.operator('alamo.hide_keyframe_proxy', text = "Hide", - # icon="HIDE_ON") - # col.operator('alamo.remove_keyframe_proxy', text = "Remove", - # icon="X") + bones = bpy.context.selected_pose_bones + if bpy.context.mode == 'POSE' and len(bones) > 0: + col = layout.column(align=True) + col.operator('alamo.show_keyframe_proxy', text = "Show", + icon="HIDE_OFF") + col.operator('alamo.hide_keyframe_proxy', text = "Hide", + icon="HIDE_ON") + col.operator('alamo.remove_keyframe_proxy', text = "Remove", + icon="X") bone = bpy.context.active_bone if type(bone) != type(None): @@ -195,9 +209,9 @@ def draw(self, context): c.prop(bone, "altDecreaseStayHidden") c.prop(bone, "ProxyName") - elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): - poseBone = object.pose.bones[bone.name] - c.prop(poseBone, "proxyIsHiddenAnimation") + # elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): + # poseBone = object.pose.bones[bone.name] + # c.prop(poseBone, "proxyIsHiddenAnimation") class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): bl_label = "Alamo material properties" From 9247423448e881e9619c7fdf927a52a9bf2df6b2 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Mon, 30 Aug 2021 15:48:27 -0700 Subject: [PATCH 028/100] File permission checking attempt - doesn't work --- io_alamo_tools/export_ala.py | 34 +++++++++++++++++++++++++--------- io_alamo_tools/export_alo.py | 34 ++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index e733b9e..b7bd436 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -22,6 +22,18 @@ import sys import os import bmesh +from contextlib import contextmanager + +@contextmanager +def disable_exception_traceback(): + """ + All traceback information is suppressed and only the exception type and value are printed + Used to make user friendly errors + """ + default_value = getattr(sys, "tracebacklimit", 1000) # `1000` is a Python's default value + sys.tracebacklimit = 0 + yield + sys.tracebacklimit = default_value # revert changes def chunk_size(size): #high bit is used to determine if a chunk holds chunks or data @@ -425,15 +437,19 @@ def create_visibility_chunk(armature, bone): class AnimationExporter(): def exportAnimation(self, path): - file = open(path, 'wb') # open file in read binary mode - - global translationOffsetDict - translationOffsetDict = {} - global translationScaleDict - translationScaleDict = {} - file.write(create_animation()) - file.close() - file = None + if os.access(path, os.W_OK): + file = open(path, 'wb') # open file in read binary mode + + global translationOffsetDict + translationOffsetDict = {} + global translationScaleDict + translationScaleDict = {} + file.write(create_animation()) + file.close() + file = None + else: + with disable_exception_traceback(): + raise Exception(f"ALAMO - EXPORT FAILED; no write permission for {file}.") class ALA_Exporter(bpy.types.Operator): diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 42e4fc0..d74e826 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1491,20 +1491,26 @@ def exportAnimations(filePath): path = self.properties.filepath global file - file = open(path, 'wb') # open file in read binary mode - - bone_name_per_alo_index = create_skeleton() - create_mesh(mesh_list, bone_name_per_alo_index) - create_connections(mesh_list) - - file.close() - file = None - #removeShadowDoubles() - hide(hiddenList) - hide_collections(bpy.context.scene.collection, collection_is_hidden_list, 0) - - if(self.exportAnimations): - exportAnimations(path) + if os.access(path, os.W_OK): + print(f"Accessing {path = }") + file = open(path, 'wb') # open file in read binary mode + + bone_name_per_alo_index = create_skeleton() + create_mesh(mesh_list, bone_name_per_alo_index) + create_connections(mesh_list) + + file.close() + file = None + #removeShadowDoubles() + hide(hiddenList) + hide_collections(bpy.context.scene.collection, collection_is_hidden_list, 0) + + if(self.exportAnimations): + exportAnimations(path) + else: + print(f"Can't access {path = }") + with disable_exception_traceback(): + raise Exception(f"ALAMO - EXPORT FAILED; no write permission for {file}.") return {'FINISHED'} # this lets blender know the operator finished successfully. From 5331d7c828f59b551cdd93902a31e4883cbeb987 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Mon, 30 Aug 2021 16:26:19 -0700 Subject: [PATCH 029/100] File permission checking, attempt two --- io_alamo_tools/export_ala.py | 2 +- io_alamo_tools/export_alo.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index b7bd436..156ac7c 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -449,7 +449,7 @@ def exportAnimation(self, path): file = None else: with disable_exception_traceback(): - raise Exception(f"ALAMO - EXPORT FAILED; no write permission for {file}.") + raise Exception(f"ALAMO - EXPORT FAILED; can't write {os.path.split(path)[1]}") class ALA_Exporter(bpy.types.Operator): diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index d74e826..f52e425 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1510,7 +1510,7 @@ def exportAnimations(filePath): else: print(f"Can't access {path = }") with disable_exception_traceback(): - raise Exception(f"ALAMO - EXPORT FAILED; no write permission for {file}.") + raise Exception(f"ALAMO - EXPORT FAILED; can't write {os.path.split(path)[1]}") return {'FINISHED'} # this lets blender know the operator finished successfully. From 8cecc0211237364ce19f4c8529939354f11937cb Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Mon, 30 Aug 2021 17:23:08 -0700 Subject: [PATCH 030/100] Error reporting tweaks --- io_alamo_tools/export_alo.py | 27 +++++++++++++++++++++------ io_alamo_tools/import_alo.py | 5 ++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index f52e425..5e50519 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -65,12 +65,26 @@ class ALO_Exporter(bpy.types.Operator): default=True, ) + # useObjectNames: EnumProperty( + # name = "Use Object Names", + # description = "Whether the exporter should use object or mesh names.", + # items=( + # ('OBJECT', "Object", ""), + # ('MESH', "Mesh", ""), + # ), + # default = False, + # ) + def draw(self, context): layout = self.layout layout.prop(self, "exportAnimations") layout.prop(self, "exportHiddenObjects") + # row = layout.row() + # row.use_property_split = False + # row.prop('useObjectNames', "mode", text="", expand=True, icon_only=False) + def execute(self, context): # execute() is called by blender when running the operator. #skeleton and bones @@ -1311,6 +1325,10 @@ def selectNonManifoldVertices(object): bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.select_non_manifold() + def exportFailed(): + with disable_exception_traceback(): + raise Exception('ALAMO - EXPORT FAILED') + def checkShadowMesh(mesh_list): #checks if shadow meshes are correct and checks if material is missing for object in mesh_list: if len(object.data.materials) > 0: @@ -1482,8 +1500,7 @@ def exportAnimations(filePath): for error in has_errors: if error: - with disable_exception_traceback(): - raise Exception('ALAMO - EXPORT FAILED') + exportFailed() hiddenList = unhide() collection_is_hidden_list = unhide_collections(bpy.context.scene.collection) @@ -1492,7 +1509,6 @@ def exportAnimations(filePath): global file if os.access(path, os.W_OK): - print(f"Accessing {path = }") file = open(path, 'wb') # open file in read binary mode bone_name_per_alo_index = create_skeleton() @@ -1508,9 +1524,8 @@ def exportAnimations(filePath): if(self.exportAnimations): exportAnimations(path) else: - print(f"Can't access {path = }") - with disable_exception_traceback(): - raise Exception(f"ALAMO - EXPORT FAILED; can't write {os.path.split(path)[1]}") + self.report({"ERROR"}, f'ALAMO - Could not write to {os.path.split(path)[1]}') + exportFailed() return {'FINISHED'} # this lets blender know the operator finished successfully. diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 33812d3..aaab1d2 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1032,9 +1032,8 @@ def validate_material_prop(name): if(name in material_props): return True - else: - self.report({"WARNING"}, "ALAMO - Unknown material porperty: " + name) - return False + self.report({"WARNING"}, "ALAMO - Unknown material porperty: " + name) + return False def read_int(material): file.seek(4, 1) # skip size From 21ccb5dd9eb78ec51073f13287bf8a4d5e609474 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 31 Aug 2021 13:01:42 -0700 Subject: [PATCH 031/100] Bulk proxyIsHiddenAnimation UI, but it actually works. --- io_alamo_tools/__init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 2e810d9..75aa7fe 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 2, 5), + "version": (0, 0, 2, 6), "blender": (2, 93, 0), "category": "Import-Export" } @@ -92,11 +92,9 @@ class keyframeProxyShow(bpy.types.Operator): def execute(self, context): bones = bpy.context.selected_pose_bones - arm = bpy.context.active_object.data for bone in bones: - arm.bones.active = bone.bone bone.proxyIsHiddenAnimation = False - bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) for area in bpy.context.screen.areas: if area.type == 'DOPESHEET_EDITOR': @@ -110,11 +108,9 @@ class keyframeProxyHide(bpy.types.Operator): def execute(self, context): bones = bpy.context.selected_pose_bones - arm = bpy.context.active_object.data for bone in bones: - arm.bones.active = bone.bone bone.proxyIsHiddenAnimation = True - bone.keyframe_insert(data_path="proxyIsHiddenAnimation") + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) for area in bpy.context.screen.areas: if area.type == 'DOPESHEET_EDITOR': From 5694a444ee4a1842428e3598e28cb243b91c5b5b Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 31 Aug 2021 17:36:20 -0700 Subject: [PATCH 032/100] Option to export using object names --- io_alamo_tools/export_alo.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 5e50519..088ef55 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -65,25 +65,28 @@ class ALO_Exporter(bpy.types.Operator): default=True, ) - # useObjectNames: EnumProperty( - # name = "Use Object Names", - # description = "Whether the exporter should use object or mesh names.", - # items=( - # ('OBJECT', "Object", ""), - # ('MESH', "Mesh", ""), - # ), - # default = False, - # ) + useNamesFrom: EnumProperty( + name = "Use Names From", + description = "Whether the exporter should use object or mesh names.", + items=( + ('MESH', "Mesh", ""), + ('OBJECT', "Object", ""), + ), + default = 'MESH', + ) def draw(self, context): layout = self.layout + layout.use_property_split = True - layout.prop(self, "exportAnimations") - layout.prop(self, "exportHiddenObjects") + row = layout.row() + row.prop(self, "exportAnimations") + row = layout.row() + row.prop(self, "exportHiddenObjects") - # row = layout.row() - # row.use_property_split = False - # row.prop('useObjectNames', "mode", text="", expand=True, icon_only=False) + row = layout.row(heading="Names From") + row.use_property_split = False + row.prop(self, "useNamesFrom", expand = True) def execute(self, context): # execute() is called by blender when running the operator. @@ -1434,6 +1437,9 @@ def create_export_list(collection): for object in collection.objects: if(object.type == 'MESH' and (object.hide_viewport == False or self.exportHiddenObjects)): + if self.useNamesFrom == 'OBJECT': + object.data.name = object.name + export_list.append(object) for child in collection.children: @@ -1487,9 +1493,9 @@ def exportAnimations(filePath): mesh_list = create_export_list(bpy.context.scene.collection) + #check if export objects satisfy requirements (has material, UVs, ...) has_errors = [] - #check if export objects satisfy requirements (has material, UVs, ...) has_errors.append(checkShadowMesh(mesh_list)) has_errors.append(checkUV(mesh_list)) has_errors.append(checkFaceNumber(mesh_list)) From 3eb923826072fb8ecceeaaa21ac201049c3c98d8 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 31 Aug 2021 18:07:17 -0700 Subject: [PATCH 033/100] Enabled ExportHelper; now cannot export without file extension --- io_alamo_tools/export_alo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 088ef55..19a00fc 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -37,7 +37,7 @@ def disable_exception_traceback(): sys.tracebacklimit = default_value # revert changes -class ALO_Exporter(bpy.types.Operator): +class ALO_Exporter(bpy.types.Operator, ExportHelper): """ALO Exporter""" # blender will use this as a tooltip for menu items and buttons. bl_idname = "export.alo" # unique identifier for buttons and menu items to reference. From f71ee0bb12c809334a03549d4b3046a899407427 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 31 Aug 2021 18:54:27 -0700 Subject: [PATCH 034/100] proxyIsHiddenAnimation refactor attempt 1 --- io_alamo_tools/__init__.py | 54 ++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 75aa7fe..2113b2d 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -85,20 +85,31 @@ def execute(self, context): # bone.keyframe_insert(data_path="proxyIsHiddenAnimation") # return {'FINISHED'} +def keyframeProxySet(operation): + bones = bpy.context.selected_pose_bones + + for bone in bones: + if operation == 'SHOW': + bone.proxyIsHiddenAnimation = False + if operation == 'HIDE': + bone.proxyIsHiddenAnimation = True + + if operation == 'REMOVE': + bone.keyframe_delete(data_path="proxyIsHiddenAnimation") + else: + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + + for area in bpy.context.screen.areas: + if area.type == 'DOPESHEET_EDITOR': + area.tag_redraw() + class keyframeProxyShow(bpy.types.Operator): bl_idname = "alamo.show_keyframe_proxy" bl_label = "Show" bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" def execute(self, context): - bones = bpy.context.selected_pose_bones - for bone in bones: - bone.proxyIsHiddenAnimation = False - bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) - - for area in bpy.context.screen.areas: - if area.type == 'DOPESHEET_EDITOR': - area.tag_redraw() + keyframeProxySet('SHOW') return {'FINISHED'} class keyframeProxyHide(bpy.types.Operator): @@ -107,29 +118,16 @@ class keyframeProxyHide(bpy.types.Operator): bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" def execute(self, context): - bones = bpy.context.selected_pose_bones - for bone in bones: - bone.proxyIsHiddenAnimation = True - bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) - - for area in bpy.context.screen.areas: - if area.type == 'DOPESHEET_EDITOR': - area.tag_redraw() + keyframeProxySet('HIDE') return {'FINISHED'} class keyframeProxyRemove(bpy.types.Operator): bl_idname = "alamo.remove_keyframe_proxy" bl_label = "Remove" - bl_description = "Remove keyframes from all selected bones" + bl_description = "Remove active keyframes from all selected bones" def execute(self, context): - bones = bpy.context.selected_pose_bones - for bone in bones: - bone.keyframe_delete(data_path="proxyIsHiddenAnimation") - - for area in bpy.context.screen.areas: - if area.type == 'DOPESHEET_EDITOR': - area.tag_redraw() + keyframeProxySet('REMOVE') return {'FINISHED'} def skeletonEnumCallback(scene, context): @@ -186,12 +184,12 @@ def draw(self, context): bones = bpy.context.selected_pose_bones if bpy.context.mode == 'POSE' and len(bones) > 0: - col = layout.column(align=True) - col.operator('alamo.show_keyframe_proxy', text = "Show", + row = layout.row(align=True) + row.operator('alamo.show_keyframe_proxy', text = "Show", icon="HIDE_OFF") - col.operator('alamo.hide_keyframe_proxy', text = "Hide", + row.operator('alamo.hide_keyframe_proxy', text = "Hide", icon="HIDE_ON") - col.operator('alamo.remove_keyframe_proxy', text = "Remove", + row.operator('alamo.remove_keyframe_proxy', text = "", icon="X") bone = bpy.context.active_bone From 8ebd15bb0ef76d77e5bf31a022d98787fcbdb9c2 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 1 Sep 2021 12:13:24 -0700 Subject: [PATCH 035/100] UI WIP --- io_alamo_tools/__init__.py | 234 ++++++++++++++++++++++--------------- 1 file changed, 137 insertions(+), 97 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 2113b2d..3ae709e 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -61,43 +61,53 @@ def execute(self, context): bpy.context.view_layer.objects.active = object return {'FINISHED'} -# class ProxyShow(bpy.types.Operator): -# bl_idname = "alamo.show_proxy" -# bl_label = "Show" -# bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" - -# def execute(self, context): -# bones = bpy.context.selected_pose_bones -# for bone in bones: -# bone.proxyIsHiddenAnimation = False -# bone.keyframe_insert(data_path="proxyIsHiddenAnimation") -# return {'FINISHED'} - -# class ProxyHide(bpy.types.Operator): -# bl_idname = "alamo.hide_proxy" -# bl_label = "Hide" -# bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" - -# def execute(self, context): -# bones = bpy.context.selected_pose_bones -# for bone in bones: -# bone.proxyIsHiddenAnimation = True -# bone.keyframe_insert(data_path="proxyIsHiddenAnimation") -# return {'FINISHED'} +class ProxyShow(bpy.types.Operator): + bl_idname = "alamo.show_proxy" + bl_label = "Show" + bl_description = "Set proxyIsHidden to False for all selected bones" + + @classmethod + def poll(cls, context): + return len(bpy.context.selected_bones) > 0 + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.proxyIsHidden = False + return {'FINISHED'} + +class ProxyHide(bpy.types.Operator): + bl_idname = "alamo.hide_proxy" + bl_label = "Hide" + bl_description = "Set proxyIsHidden to True for all selected bones" + + @classmethod + def poll(cls, context): + return len(bpy.context.selected_bones) > 0 + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.proxyIsHidden = True + return {'FINISHED'} def keyframeProxySet(operation): bones = bpy.context.selected_pose_bones + keyframeType = "" for bone in bones: if operation == 'SHOW': bone.proxyIsHiddenAnimation = False + keyframeType = 'JITTER' if operation == 'HIDE': bone.proxyIsHiddenAnimation = True + keyframeType = 'EXTREME' if operation == 'REMOVE': bone.keyframe_delete(data_path="proxyIsHiddenAnimation") else: bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + bone.keyframe_type = keyframeType for area in bpy.context.screen.areas: if area.type == 'DOPESHEET_EDITOR': @@ -108,6 +118,10 @@ class keyframeProxyShow(bpy.types.Operator): bl_label = "Show" bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" + @classmethod + def poll(cls, context): + return len(bpy.context.selected_pose_bones) > 0 + def execute(self, context): keyframeProxySet('SHOW') return {'FINISHED'} @@ -117,6 +131,10 @@ class keyframeProxyHide(bpy.types.Operator): bl_label = "Hide" bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" + @classmethod + def poll(cls, context): + return len(bpy.context.selected_pose_bones) > 0 + def execute(self, context): keyframeProxySet('HIDE') return {'FINISHED'} @@ -126,6 +144,10 @@ class keyframeProxyRemove(bpy.types.Operator): bl_label = "Remove" bl_description = "Remove active keyframes from all selected bones" + @classmethod + def poll(cls, context): + return len(bpy.context.selected_pose_bones) > 0 + def execute(self, context): keyframeProxySet('REMOVE') return {'FINISHED'} @@ -149,7 +171,20 @@ class skeletonEnumClass(PropertyGroup): class ALAMO_PT_ToolsPanel(bpy.types.Panel): - bl_label = "ALAMO properties" + bl_label = "Alamo Properties" + bl_idname = "ALAMO_PT_ToolsPanel" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + # self.layout.separator() + pass + +class ALAMO_PT_ObjectPanel(bpy.types.Panel): + + bl_label = "Object Tools" + bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -158,54 +193,76 @@ def draw(self, context): object = context.object layout = self.layout scene = context.scene - c = layout.column() + col = layout.column() + + col.prop(scene.ActiveSkeleton, 'skeletonEnum') + col.prop(object, "HasCollision") + col.prop(object, "Hidden") - c.prop(scene.ActiveSkeleton, 'skeletonEnum') + armature = utils.findArmature() + if armature is not None: + hasChildConstraint = any( + constraint.type == 'CHILD_OF' + for constraint in object.constraints + ) - if type(object) != type(None): - if(object.type == 'MESH'): - if bpy.context.mode == 'OBJECT': - c.prop(object, "HasCollision") - c.prop(object, "Hidden") + if not hasChildConstraint: + self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') - armature = utils.findArmature() - if armature != None: - hasChildConstraint = False - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - hasChildConstraint = True - if not hasChildConstraint: - self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') + for action in bpy.data.actions: + row = col.row() + row.label(text = action.name) + row.prop(action, "AnimationEndFrame") - action = utils.getCurrentAction() - if(action != None): - c.prop(action, "AnimationEndFrame") +class ALAMO_PT_EditBonePanel(bpy.types.Panel): + bl_label = "Edit Bone Tools" + bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" - bones = bpy.context.selected_pose_bones - if bpy.context.mode == 'POSE' and len(bones) > 0: - row = layout.row(align=True) - row.operator('alamo.show_keyframe_proxy', text = "Show", + def draw(self, context): + self.layout.active = False + if bpy.context.mode == "EDIT_ARMATURE": + self.layout.active = True + col = self.layout.column() + bone = bpy.context.active_bone + col.prop(bone.billboardMode, "billboardMode") + col.prop(bone, "Visible") + col.prop(bone, "EnableProxy") + if bone.EnableProxy: + row = self.layout.row(align=True) + row.label(text="Proxy Visibility:") + row.operator('alamo.show_proxy', text = "", icon="HIDE_OFF") - row.operator('alamo.hide_keyframe_proxy', text = "Hide", + row.operator('alamo.hide_proxy', text = "", icon="HIDE_ON") - row.operator('alamo.remove_keyframe_proxy', text = "", - icon="X") + col.prop(bone, "proxyIsHidden") + col.prop(bone, "altDecreaseStayHidden") + col.prop(bone, "ProxyName") - bone = bpy.context.active_bone - if type(bone) != type(None): - if(type(bpy.context.active_bone ) is bpy.types.EditBone): - c.prop(bone.billboardMode, "billboardMode") - c.prop(bone, "Visible") - c.prop(bone, "EnableProxy") - if bone.EnableProxy: - c.prop(bone, "proxyIsHidden") - c.prop(bone, "altDecreaseStayHidden") - c.prop(bone, "ProxyName") - - # elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): - # poseBone = object.pose.bones[bone.name] - # c.prop(poseBone, "proxyIsHiddenAnimation") +class ALAMO_PT_PoseBonePanel(bpy.types.Panel): + + bl_label = "Pose Tools" + bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + layout.active = False + if bpy.context.mode == "POSE": + layout.active = True + layout.column().label(text="Keyframe Proxy Visibility:") + row = layout.row(align=True) + row.operator('alamo.show_keyframe_proxy', text = "Show", + icon="HIDE_OFF") + row.operator('alamo.hide_keyframe_proxy', text = "Hide", + icon="HIDE_ON") + row.operator('alamo.remove_keyframe_proxy', text = "", + icon="X") class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): bl_label = "Alamo material properties" @@ -218,48 +275,26 @@ def draw(self, context): layout = self.layout c = layout.column() - if type(object) != type(None): - if(object.type == 'MESH'): + if type(object) != type(None) and object.type == 'MESH': material = bpy.context.active_object.active_material - if (material != None): + if material is not None: #a None image is needed to represent not using a texture if 'None' not in bpy.data.images: bpy.data.images.new(name='None', width=1, height=1) c.prop(material.shaderList, "shaderList") - shaderProps = settings.material_parameter_dict[material.shaderList.shaderList] if material.shaderList.shaderList != 'alDefault.fx': - for property in shaderProps: - if property == 'BaseTexture': - layout.prop_search(material, "BaseTexture", bpy.data, "images") - elif property == 'CloudTexture': - layout.prop_search(material, "CloudTexture", bpy.data, "images") - elif property == 'DetailTexture': - layout.prop_search(material, "DetailTexture", bpy.data, "images") - elif property == 'CloudNormalTexture': - layout.prop_search(material, "CloudNormalTexture", bpy.data, "images") - elif property == 'NormalTexture': - layout.prop_search(material, "NormalTexture", bpy.data, "images") - elif property == 'NormalDetailTexture': - layout.prop_search(material, "NormalDetailTexture", bpy.data, "images") - elif property == 'GlossTexture': - layout.prop_search(material, "GlossTexture", bpy.data, "images") - elif property == 'WaveTexture': - layout.prop_search(material, "WaveTexture", bpy.data, "images") - elif property == 'DistortionTexture': - layout.prop_search(material, "DistortionTexture", bpy.data, "images") + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + for shader_prop in shader_props: + if shader_prop.find('Texture') > -1: + layout.prop_search(material, shader_prop, bpy.data, "images") else: - c.prop(material, property) - -def createShaderModeOptions(): - mode_options = [] - - for index, shader_name in enumerate(settings.material_parameter_dict): - mode_options.append((shader_name, shader_name, '', '', index)) - - return mode_options + c.prop(material, shader_prop) class shaderListProperties(bpy.types.PropertyGroup): - mode_options = createShaderModeOptions() + mode_options = [ + (shader_name, shader_name, '', '', index) + for index, shader_name in enumerate(settings.material_parameter_dict) + ] shaderList : bpy.props.EnumProperty( items=mode_options, @@ -313,10 +348,15 @@ def menu_func_export(self, context): ALA_Exporter, ALAMO_PT_materialPropertyPanel, createConstraintBoneButton, + ProxyShow, + ProxyHide, keyframeProxyShow, keyframeProxyHide, keyframeProxyRemove, - ALAMO_PT_ToolsPanel + ALAMO_PT_ToolsPanel, + ALAMO_PT_ObjectPanel, + ALAMO_PT_EditBonePanel, + ALAMO_PT_PoseBonePanel ) def register(): From ab95911b43dc1753d2ac402667243b072e2f3d24 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 1 Sep 2021 12:26:16 -0700 Subject: [PATCH 036/100] Fix export dumb --- io_alamo_tools/export_ala.py | 2 +- io_alamo_tools/export_alo.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index 156ac7c..60ebed1 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -437,7 +437,7 @@ def create_visibility_chunk(armature, bone): class AnimationExporter(): def exportAnimation(self, path): - if os.access(path, os.W_OK): + if os.access(path, os.W_OK) or not os.access(path, os.F_OK): file = open(path, 'wb') # open file in read binary mode global translationOffsetDict diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 19a00fc..25fd3f9 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1514,7 +1514,7 @@ def exportAnimations(filePath): path = self.properties.filepath global file - if os.access(path, os.W_OK): + if os.access(path, os.W_OK) or not os.access(path, os.F_OK): file = open(path, 'wb') # open file in read binary mode bone_name_per_alo_index = create_skeleton() From bbeac7e9627885ea78650eda4e70b38e084a4111 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 1 Sep 2021 15:26:25 -0700 Subject: [PATCH 037/100] Reorganize material properties --- io_alamo_tools/__init__.py | 49 +++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 3ae709e..42919a6 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -68,7 +68,9 @@ class ProxyShow(bpy.types.Operator): @classmethod def poll(cls, context): - return len(bpy.context.selected_bones) > 0 + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False def execute(self, context): bones = bpy.context.selected_bones @@ -83,7 +85,9 @@ class ProxyHide(bpy.types.Operator): @classmethod def poll(cls, context): - return len(bpy.context.selected_bones) > 0 + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False def execute(self, context): bones = bpy.context.selected_bones @@ -120,7 +124,9 @@ class keyframeProxyShow(bpy.types.Operator): @classmethod def poll(cls, context): - return len(bpy.context.selected_pose_bones) > 0 + if bpy.context.selected_pose_bones is not None: + return len(bpy.context.selected_pose_bones) > 0 + return False def execute(self, context): keyframeProxySet('SHOW') @@ -133,7 +139,9 @@ class keyframeProxyHide(bpy.types.Operator): @classmethod def poll(cls, context): - return len(bpy.context.selected_pose_bones) > 0 + if bpy.context.selected_pose_bones is not None: + return len(bpy.context.selected_pose_bones) > 0 + return False def execute(self, context): keyframeProxySet('HIDE') @@ -146,7 +154,9 @@ class keyframeProxyRemove(bpy.types.Operator): @classmethod def poll(cls, context): - return len(bpy.context.selected_pose_bones) > 0 + if bpy.context.selected_pose_bones is not None: + return len(bpy.context.selected_pose_bones) > 0 + return False def execute(self, context): keyframeProxySet('REMOVE') @@ -264,8 +274,30 @@ def draw(self, context): row.operator('alamo.remove_keyframe_proxy', text = "", icon="X") +class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): + bl_label = "Additional Properties" + bl_parent_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + object = context.object + layout = self.layout + c = layout.column() + + if type(object) != type(None) and object.type == 'MESH': + material = bpy.context.active_object.active_material + if material is not None and material.shaderList.shaderList != 'alDefault.fx': + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + for shader_prop in shader_props: + if shader_prop.find('Texture') == -1: # because contains() doesn't exist, apparently + c.prop(material, shader_prop) + class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo material properties" + bl_label = "Alamo Material Properties" + bl_id = "ALAMO_PT_materialPropertyPanel" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "material" @@ -285,10 +317,8 @@ def draw(self, context): if material.shaderList.shaderList != 'alDefault.fx': shader_props = settings.material_parameter_dict[material.shaderList.shaderList] for shader_prop in shader_props: - if shader_prop.find('Texture') > -1: + if shader_prop.find('Texture') > -1: # because contains() doesn't exist, apparently layout.prop_search(material, shader_prop, bpy.data, "images") - else: - c.prop(material, shader_prop) class shaderListProperties(bpy.types.PropertyGroup): mode_options = [ @@ -347,6 +377,7 @@ def menu_func_export(self, context): ALO_Exporter, ALA_Exporter, ALAMO_PT_materialPropertyPanel, + ALAMO_PT_materialPropertySubPanel, createConstraintBoneButton, ProxyShow, ProxyHide, From 767fce2b23273834c6c5dba5ad3959583ee76e28 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Fri, 3 Sep 2021 15:58:45 -0700 Subject: [PATCH 038/100] More UI WIP --- io_alamo_tools/__init__.py | 595 +++++++++++++++++++++++++++++-------- 1 file changed, 466 insertions(+), 129 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 42919a6..d72a403 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,3 +1,21 @@ +from . import_alo import ALO_Importer +from . export_ala import ALA_Exporter +from . export_alo import ALO_Exporter +from . import_ala import ALA_Importer +from bpy.types import (Panel, + Operator, + PropertyGroup, + ) +from bpy.props import (StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, + ) +from bpy.props import * +import mathutils +import bpy bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", @@ -22,20 +40,6 @@ from . import settings from . import utils -import bpy -import mathutils -from bpy.props import * -from bpy.props import (StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - PointerProperty, - ) -from bpy.types import (Panel, - Operator, - PropertyGroup, - ) class createConstraintBoneButton(bpy.types.Operator): bl_idname = "create.constraint_bone" @@ -49,7 +53,7 @@ def execute(self, context): utils.setModeToEdit() bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0, 0, 1)) + bone.tail = bone.head + mathutils.Vector((0, 0, 1)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') @@ -61,6 +65,187 @@ def execute(self, context): bpy.context.view_layer.objects.active = object return {'FINISHED'} + +class SetHasCollisionTrue(bpy.types.Operator): + bl_idname = "alamo.collision_true" + bl_label = "Show" + bl_description = "Set HasCollision to True for all selected objects" + + @classmethod + def poll(cls, context): + if bpy.context.selected_objects is not None: + return len(bpy.context.selected_objects) > 0 + return False + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.HasCollision = True + return {'FINISHED'} + + +class SetHasCollisionFalse(bpy.types.Operator): + bl_idname = "alamo.collision_false" + bl_label = "Hide" + bl_description = "Set HasCollision to False for all selected objects" + + @classmethod + def poll(cls, context): + if bpy.context.selected_objects is not None: + return len(bpy.context.selected_objects) > 0 + return False + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.HasCollision = False + return {'FINISHED'} + + +class SetHiddenTrue(bpy.types.Operator): + bl_idname = "alamo.hidden_true" + bl_label = "Show" + bl_description = "Set Hidden to True for all selected objects" + + @classmethod + def poll(cls, context): + if bpy.context.selected_objects is not None: + return len(bpy.context.selected_objects) > 0 + return False + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.Hidden = True + return {'FINISHED'} + + +class SetHiddenFalse(bpy.types.Operator): + bl_idname = "alamo.hidden_false" + bl_label = "Hide" + bl_description = "Set Hidden to False for all selected objects" + + @classmethod + def poll(cls, context): + if bpy.context.selected_objects is not None: + return len(bpy.context.selected_objects) > 0 + return False + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.Hidden = False + return {'FINISHED'} + + +class SetBoneVisible(bpy.types.Operator): + bl_idname = "alamo.bone_visible" + bl_label = "Show" + bl_description = "Set Visible to True for all selected bones" + + @classmethod + def poll(cls, context): + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.Visible = True + return {'FINISHED'} + + +class SetBoneInvisible(bpy.types.Operator): + bl_idname = "alamo.bone_invisible" + bl_label = "Hide" + bl_description = "Set Visible to False for all selected bones" + + @classmethod + def poll(cls, context): + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.Visible = False + return {'FINISHED'} + + +class SetAltDecreaseStayHiddenTrue(bpy.types.Operator): + bl_idname = "alamo.alt_decrease_stay_hidden_true" + bl_label = "Show" + bl_description = "Set altDecreaseStayHidden to True for all selected bones" + + @classmethod + def poll(cls, context): + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.altDecreaseStayHidden = True + return {'FINISHED'} + + +class SetAltDecreaseStayHiddenFalse(bpy.types.Operator): + bl_idname = "alamo.alt_decrease_stay_hidden_false" + bl_label = "Hide" + bl_description = "Set altDecreaseStayHidden to False for all selected bones" + + @classmethod + def poll(cls, context): + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.altDecreaseStayHidden = False + return {'FINISHED'} + + +class EnableProxyFalse(bpy.types.Operator): + bl_idname = "alamo.disable_proxy" + bl_label = "Show" + bl_description = "Set EnableProxy to False for all selected bones" + + @classmethod + def poll(cls, context): + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.EnableProxy = False + return {'FINISHED'} + + +class EnableProxyTrue(bpy.types.Operator): + bl_idname = "alamo.enable_proxy" + bl_label = "Hide" + bl_description = "Set EnableProxy to True for all selected bones" + + @classmethod + def poll(cls, context): + if bpy.context.selected_bones is not None: + return len(bpy.context.selected_bones) > 0 + return False + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.EnableProxy = True + return {'FINISHED'} + + class ProxyShow(bpy.types.Operator): bl_idname = "alamo.show_proxy" bl_label = "Show" @@ -78,6 +263,7 @@ def execute(self, context): bone.proxyIsHidden = False return {'FINISHED'} + class ProxyHide(bpy.types.Operator): bl_idname = "alamo.hide_proxy" bl_label = "Hide" @@ -95,6 +281,7 @@ def execute(self, context): bone.proxyIsHidden = True return {'FINISHED'} + def keyframeProxySet(operation): bones = bpy.context.selected_pose_bones keyframeType = "" @@ -110,13 +297,15 @@ def keyframeProxySet(operation): if operation == 'REMOVE': bone.keyframe_delete(data_path="proxyIsHiddenAnimation") else: - bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + bone.keyframe_insert( + data_path="proxyIsHiddenAnimation", group=bone.name) bone.keyframe_type = keyframeType for area in bpy.context.screen.areas: if area.type == 'DOPESHEET_EDITOR': area.tag_redraw() - + + class keyframeProxyShow(bpy.types.Operator): bl_idname = "alamo.show_keyframe_proxy" bl_label = "Show" @@ -132,6 +321,7 @@ def execute(self, context): keyframeProxySet('SHOW') return {'FINISHED'} + class keyframeProxyHide(bpy.types.Operator): bl_idname = "alamo.hide_keyframe_proxy" bl_label = "Hide" @@ -147,6 +337,7 @@ def execute(self, context): keyframeProxySet('HIDE') return {'FINISHED'} + class keyframeProxyRemove(bpy.types.Operator): bl_idname = "alamo.remove_keyframe_proxy" bl_label = "Remove" @@ -162,6 +353,7 @@ def execute(self, context): keyframeProxySet('REMOVE') return {'FINISHED'} + def skeletonEnumCallback(scene, context): armatures = [('None', 'None', '', '', 0)] counter = 1 @@ -172,6 +364,7 @@ def skeletonEnumCallback(scene, context): return armatures + class skeletonEnumClass(PropertyGroup): skeletonEnum : EnumProperty( name='Active Skeleton', @@ -179,6 +372,7 @@ class skeletonEnumClass(PropertyGroup): items = skeletonEnumCallback ) + class ALAMO_PT_ToolsPanel(bpy.types.Panel): bl_label = "Alamo Properties" @@ -191,6 +385,7 @@ def draw(self, context): # self.layout.separator() pass + class ALAMO_PT_ObjectPanel(bpy.types.Panel): bl_label = "Object Tools" @@ -204,10 +399,21 @@ def draw(self, context): layout = self.layout scene = context.scene col = layout.column() + layout.active = False + if bpy.context.mode == "OBJECT": + layout.active = True + + row = col.row(align=True) + row.label(text="Set HasCollision:") + row.operator('alamo.collision_true', text="", icon="CHECKMARK") + row.operator('alamo.collision_false', text="", icon="X") + + row = col.row(align=True) + row.label(text="Set Hidden:") + row.operator('alamo.hidden_true', text="", icon="HIDE_OFF") + row.operator('alamo.hidden_false', text="", icon="HIDE_ON") col.prop(scene.ActiveSkeleton, 'skeletonEnum') - col.prop(object, "HasCollision") - col.prop(object, "Hidden") armature = utils.findArmature() if armature is not None: @@ -217,13 +423,15 @@ def draw(self, context): ) if not hasChildConstraint: - self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') + self.layout.operator( + "create.constraint_bone", text='Create Constraint Bone') for action in bpy.data.actions: row = col.row() - row.label(text = action.name) + row.label(text=action.name) row.prop(action, "AnimationEndFrame") + class ALAMO_PT_EditBonePanel(bpy.types.Panel): bl_label = "Edit Bone Tools" @@ -233,24 +441,93 @@ class ALAMO_PT_EditBonePanel(bpy.types.Panel): bl_category = "ALAMO" def draw(self, context): - self.layout.active = False + bones = bpy.context.selected_bones + layout = self.layout + col = layout.column() + layout.active = False if bpy.context.mode == "EDIT_ARMATURE": - self.layout.active = True + layout.active = True + + row = col.row(align=True) + row.label(text="Set Visible:") + row.operator('alamo.bone_visible', text="", icon="HIDE_OFF") + row.operator('alamo.bone_invisible', text="", icon="HIDE_ON") + + row = col.row() + row.enabled = False + if bones is not None: + if len(bones) == 1: + row.prop(bones[0].billboardMode, "billboardMode") + row.enabled = True + else: + row.label(text="billboardMode") + else: + row.label(text="billboardMode") + + row = col.row() + row.enabled = False + if bones is not None: + if len(bones) == 1: + row.prop(bones[0], "ProxyName") + row.enabled = True + else: + row.label(text="ProxyName") + else: + row.label(text="ProxyName") + + +class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): + + bl_label = "" + bl_parent_id = 'ALAMO_PT_EditBonePanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + bl_options = {"HEADER_LAYOUT_EXPAND"} + + def draw_header(self, context): + row = self.layout.row(align=True) + row.active = False + if bpy.context.mode == "EDIT_ARMATURE": + row.active = True + row.label(text="Set Proxy:") + row.operator('alamo.enable_proxy', text="", icon="CHECKMARK") + row.operator('alamo.disable_proxy', text="", icon="X") + + def draw(self, context): + bones = bpy.context.selected_bones col = self.layout.column() - bone = bpy.context.active_bone - col.prop(bone.billboardMode, "billboardMode") - col.prop(bone, "Visible") - col.prop(bone, "EnableProxy") - if bone.EnableProxy: - row = self.layout.row(align=True) - row.label(text="Proxy Visibility:") - row.operator('alamo.show_proxy', text = "", - icon="HIDE_OFF") - row.operator('alamo.hide_proxy', text = "", - icon="HIDE_ON") - col.prop(bone, "proxyIsHidden") - col.prop(bone, "altDecreaseStayHidden") - col.prop(bone, "ProxyName") + all_same = True + + col.active = False + if bpy.context.mode == "EDIT_ARMATURE": + col.active = True + + if bones is not None: + has_changed = None + for bone in bones: + if has_changed is None: + has_changed = bone.EnableProxy + elif bone.EnableProxy != has_changed: + all_same = False + break + + col.active = all_same + + if not all_same: + col.label(icon="ERROR", text="Inconsistent EnableProxy states.") + col.label(icon="BLANK1", text="Change selection or set EnableProxy.") + + row = col.row(align=True) + row.label(text="Set Proxy Visibility:") + row.operator('alamo.show_proxy', text="", icon="HIDE_OFF") + row.operator('alamo.hide_proxy', text="", icon="HIDE_ON") + + row = col.row(align=True) + row.label(text="Set altDecreaseStayHidden:") + row.operator('alamo.alt_decrease_stay_hidden_true', text="", icon="CHECKMARK") + row.operator('alamo.alt_decrease_stay_hidden_false', text="", icon="X") + class ALAMO_PT_PoseBonePanel(bpy.types.Panel): @@ -265,60 +542,67 @@ def draw(self, context): layout.active = False if bpy.context.mode == "POSE": layout.active = True - layout.column().label(text="Keyframe Proxy Visibility:") + # layout.column().label(text="Keyframe Proxy Visibility:") row = layout.row(align=True) - row.operator('alamo.show_keyframe_proxy', text = "Show", - icon="HIDE_OFF") - row.operator('alamo.hide_keyframe_proxy', text = "Hide", - icon="HIDE_ON") - row.operator('alamo.remove_keyframe_proxy', text = "", - icon="X") + row.label(text="Keyframe Proxy Visibility:") + row.operator('alamo.show_keyframe_proxy', text="", + icon="HIDE_OFF") + row.operator('alamo.hide_keyframe_proxy', text="", + icon="HIDE_ON") + row.operator('alamo.remove_keyframe_proxy', text="", + icon="X") -class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): - bl_label = "Additional Properties" - bl_parent_id = "ALAMO_PT_materialPropertyPanel" + +class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): + bl_label = "Alamo Shader Properties" + bl_id = "ALAMO_PT_materialPropertyPanel" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "material" - bl_options = {'DEFAULT_CLOSED'} def draw(self, context): object = context.object layout = self.layout - c = layout.column() + col = layout.column() if type(object) != type(None) and object.type == 'MESH': - material = bpy.context.active_object.active_material - if material is not None and material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] - for shader_prop in shader_props: - if shader_prop.find('Texture') == -1: # because contains() doesn't exist, apparently - c.prop(material, shader_prop) + material = bpy.context.active_object.active_material + if material is not None: + # a None image is needed to represent not using a texture + if 'None' not in bpy.data.images: + bpy.data.images.new(name='None', width=1, height=1) + col.prop(material.shaderList, "shaderList") + if material.shaderList.shaderList != 'alDefault.fx': + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find('Texture') > -1: + layout.prop_search( + material, shader_prop, bpy.data, "images") -class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo Material Properties" - bl_id = "ALAMO_PT_materialPropertyPanel" + +class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): + bl_label = "Additional Properties" + bl_parent_id = "ALAMO_PT_materialPropertyPanel" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "material" + bl_options = {'DEFAULT_CLOSED'} def draw(self, context): object = context.object layout = self.layout - c = layout.column() + col = layout.column() if type(object) != type(None) and object.type == 'MESH': - material = bpy.context.active_object.active_material - if material is not None: - #a None image is needed to represent not using a texture - if 'None' not in bpy.data.images: - bpy.data.images.new(name='None', width=1, height=1) - c.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] - for shader_prop in shader_props: - if shader_prop.find('Texture') > -1: # because contains() doesn't exist, apparently - layout.prop_search(material, shader_prop, bpy.data, "images") + material = bpy.context.active_object.active_material + if material is not None and material.shaderList.shaderList != 'alDefault.fx': + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find('Texture') == -1: + col.prop(material, shader_prop) + class shaderListProperties(bpy.types.PropertyGroup): mode_options = [ @@ -326,12 +610,13 @@ class shaderListProperties(bpy.types.PropertyGroup): for index, shader_name in enumerate(settings.material_parameter_dict) ] - shaderList : bpy.props.EnumProperty( + shaderList: bpy.props.EnumProperty( items=mode_options, description="Choose ingame Shader", default="alDefault.fx", ) + class billboardListProperties(bpy.types.PropertyGroup): mode_options = [ ("Disable", "Disable", 'Description WIP', '', 0), @@ -344,29 +629,33 @@ class billboardListProperties(bpy.types.PropertyGroup): ("Sun", "Sun", 'Description WIP', '', 7), ] - billboardMode : bpy.props.EnumProperty( - items = mode_options, - description = "billboardMode", + billboardMode: bpy.props.EnumProperty( + items=mode_options, + description="billboardMode", default="Disable", ) + def proxy_name_update(self, context): - if self.ProxyName != self.ProxyName.upper(): #prevents endless recursion + if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion self.ProxyName = self.ProxyName.upper() -#blender registration +# blender registration + + def menu_func_import(self, context): - self.layout.operator(import_alo.ALO_Importer.bl_idname, text=".ALO Importer") - self.layout.operator(import_ala.ALA_Importer.bl_idname, text=".ALA Importer") + self.layout.operator(import_alo.ALO_Importer.bl_idname, + text=".ALO Importer") + self.layout.operator(import_ala.ALA_Importer.bl_idname, + text=".ALA Importer") + def menu_func_export(self, context): - self.layout.operator(export_alo.ALO_Exporter.bl_idname, text=".ALO Exporter") - self.layout.operator(export_ala.ALA_Exporter.bl_idname, text=".ALA Exporter") + self.layout.operator(export_alo.ALO_Exporter.bl_idname, + text=".ALO Exporter") + self.layout.operator(export_ala.ALA_Exporter.bl_idname, + text=".ALA Exporter") -from . import_alo import ALO_Importer -from . import_ala import ALA_Importer -from . export_alo import ALO_Exporter -from . export_ala import ALA_Exporter classes = ( skeletonEnumClass, @@ -379,6 +668,16 @@ def menu_func_export(self, context): ALAMO_PT_materialPropertyPanel, ALAMO_PT_materialPropertySubPanel, createConstraintBoneButton, + SetHasCollisionTrue, + SetHasCollisionFalse, + SetHiddenTrue, + SetHiddenFalse, + SetBoneVisible, + SetBoneInvisible, + SetAltDecreaseStayHiddenTrue, + SetAltDecreaseStayHiddenFalse, + EnableProxyFalse, + EnableProxyTrue, ProxyShow, ProxyHide, keyframeProxyShow, @@ -387,9 +686,11 @@ def menu_func_export(self, context): ALAMO_PT_ToolsPanel, ALAMO_PT_ObjectPanel, ALAMO_PT_EditBonePanel, + ALAMO_PT_EditBoneSubPanel, ALAMO_PT_PoseBonePanel ) + def register(): from bpy.utils import register_class @@ -400,9 +701,9 @@ def register(): bpy.types.TOPBAR_MT_file_export.append(menu_func_export) bpy.types.Scene.ActiveSkeleton = PointerProperty(type=skeletonEnumClass) - bpy.types.Scene.modelFileName = StringProperty(name="") + bpy.types.Scene.modelFileName = StringProperty(name="") - bpy.types.Action.AnimationEndFrame = IntProperty(default = 24) + bpy.types.Action.AnimationEndFrame = IntProperty(default=24) bpy.types.EditBone.Visible = BoolProperty(default=True) bpy.types.EditBone.EnableProxy = BoolProperty() @@ -410,58 +711,93 @@ def register(): bpy.types.PoseBone.proxyIsHiddenAnimation = BoolProperty() bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) - bpy.types.EditBone.billboardMode = bpy.props.PointerProperty(type=billboardListProperties) + bpy.types.EditBone.billboardMode = bpy.props.PointerProperty( + type=billboardListProperties) bpy.types.Object.HasCollision = BoolProperty() bpy.types.Object.Hidden = BoolProperty() bpy.types.Material.BaseTexture = bpy.props.StringProperty(default='None') bpy.types.Material.DetailTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty( + default='None') bpy.types.Material.NormalTexture = bpy.props.StringProperty(default='None') bpy.types.Material.GlossTexture = bpy.props.StringProperty(default='None') bpy.types.Material.WaveTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.DistortionTexture = bpy.props.StringProperty( + default='None') bpy.types.Material.CloudTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default='None') - - bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) - bpy.types.Material.Emissive = bpy.props.FloatVectorProperty(min = 0, max = 1, size = 4, default=(0,0,0,0)) - bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,0)) - bpy.types.Material.Specular = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,0)) - bpy.types.Material.Shininess = bpy.props.FloatProperty(min=0, max=255, default = 32) - bpy.types.Material.Colorization = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,0)) - bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0,1,0,0)) - bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0,0,0,0)) - bpy.types.Material.Color = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,1)) - bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0,0,0,0)) - bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty(min=0, max=1, size=3, default=(0.5,0.5,0.5)) - #shield shader properties - bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty(min=0, max=255, default=0.5) - bpy.types.Material.BaseUVScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - bpy.types.Material.WaveUVScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - bpy.types.Material.DistortUVScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=-0.15) - bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=-0.15) - bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=-0.25) - #tree properties - bpy.types.Material.BendScale = bpy.props.FloatProperty(min=-255, max=255, default=0.4) - #grass properties - bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,1)) - #skydome.fx properties - bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=0.001) - bpy.types.Material.CloudScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - #nebula.fx properties - bpy.types.Material.SFreq = bpy.props.FloatProperty(min=-255, max=255, default=0.002) - bpy.types.Material.TFreq = bpy.props.FloatProperty(min=-255, max=255, default=0.005) - bpy.types.Material.DistortionScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - #planet.fx properties - bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) - bpy.types.Material.CityColor = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) - bpy.types.Material.AtmospherePower = bpy.props.FloatProperty(min=-255, max=255, default=1) - #tryplanar mapping properties - bpy.types.Material.MappingScale = bpy.props.FloatProperty(min=0, max=255, default=0.1) - bpy.types.Material.BlendSharpness = bpy.props.FloatProperty(min=0, max=255, default=0.1) + bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty( + default='None') + + bpy.types.Material.shaderList = bpy.props.PointerProperty( + type=shaderListProperties) + bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(0, 0, 0, 0)) + bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(1, 1, 1, 0)) + bpy.types.Material.Specular = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(1, 1, 1, 0)) + bpy.types.Material.Shininess = bpy.props.FloatProperty( + min=0, max=255, default=32) + bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(1, 1, 1, 0)) + bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(0, 1, 0, 0)) + bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(0, 0, 0, 0)) + bpy.types.Material.Color = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(1, 1, 1, 1)) + bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(0, 0, 0, 0)) + bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( + min=0, max=1, size=3, default=(0.5, 0.5, 0.5)) + # shield shader properties + bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( + min=0, max=255, default=0.5) + bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( + min=-255, max=255, default=1) + bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( + min=-255, max=255, default=1) + bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( + min=-255, max=255, default=1) + bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( + min=-255, max=255, default=-0.15) + bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( + min=-255, max=255, default=-0.15) + bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( + min=-255, max=255, default=-0.25) + # tree properties + bpy.types.Material.BendScale = bpy.props.FloatProperty( + min=-255, max=255, default=0.4) + # grass properties + bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(1, 1, 1, 1)) + # skydome.fx properties + bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( + min=-255, max=255, default=0.001) + bpy.types.Material.CloudScale = bpy.props.FloatProperty( + min=-255, max=255, default=1) + # nebula.fx properties + bpy.types.Material.SFreq = bpy.props.FloatProperty( + min=-255, max=255, default=0.002) + bpy.types.Material.TFreq = bpy.props.FloatProperty( + min=-255, max=255, default=0.005) + bpy.types.Material.DistortionScale = bpy.props.FloatProperty( + min=-255, max=255, default=1) + # planet.fx properties + bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) + bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( + min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) + bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( + min=-255, max=255, default=1) + # tryplanar mapping properties + bpy.types.Material.MappingScale = bpy.props.FloatProperty( + min=0, max=255, default=0.1) + bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( + min=0, max=255, default=0.1) + def unregister(): from bpy.utils import unregister_class @@ -530,9 +866,10 @@ def unregister(): bpy.types.Material.Atmosphere bpy.types.Material.CityColor bpy.types.Material.AtmospherePower - #tryplanar mapping properties + # tryplanar mapping properties bpy.types.Material.MappingScale bpy.types.Material.BlendSharpness + if __name__ == "__main__": register() From 7827ee027982aebcd912748c51504534ab58a6b2 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sat, 4 Sep 2021 12:55:45 -0700 Subject: [PATCH 039/100] Keyframe types --- io_alamo_tools/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index d72a403..752bb18 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -284,6 +284,8 @@ def execute(self, context): def keyframeProxySet(operation): bones = bpy.context.selected_pose_bones + action = bpy.context.object.animation_data.action + frame = bpy.context.scene.frame_current keyframeType = "" for bone in bones: @@ -297,9 +299,10 @@ def keyframeProxySet(operation): if operation == 'REMOVE': bone.keyframe_delete(data_path="proxyIsHiddenAnimation") else: - bone.keyframe_insert( - data_path="proxyIsHiddenAnimation", group=bone.name) - bone.keyframe_type = keyframeType + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + for keyframe in action.fcurves.find(bone.path_from_id() + ".proxyIsHiddenAnimation").keyframe_points: + if int(keyframe.co[0]) == frame: + keyframe.type = keyframeType for area in bpy.context.screen.areas: if area.type == 'DOPESHEET_EDITOR': @@ -577,8 +580,8 @@ def draw(self, context): for shader_prop in shader_props: # because contains() doesn't exist, apparently if shader_prop.find('Texture') > -1: - layout.prop_search( - material, shader_prop, bpy.data, "images") + layout.prop_search(material, shader_prop, bpy.data, "images") + # layout.template_ID(material, shader_prop, new="image.new", open="image.open") class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): From f238571c6affced091fac5adc818dbe4bfe8e5bb Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sat, 4 Sep 2021 15:33:45 -0700 Subject: [PATCH 040/100] More UI --- io_alamo_tools/__init__.py | 133 ++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 32 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 752bb18..7e0bd5d 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -401,24 +401,40 @@ def draw(self, context): object = context.object layout = self.layout scene = context.scene - col = layout.column() layout.active = False if bpy.context.mode == "OBJECT": layout.active = True - row = col.row(align=True) + row = layout.row(align=True) row.label(text="Set HasCollision:") row.operator('alamo.collision_true', text="", icon="CHECKMARK") row.operator('alamo.collision_false', text="", icon="X") - row = col.row(align=True) + row = layout.row(align=True) row.label(text="Set Hidden:") row.operator('alamo.hidden_true', text="", icon="HIDE_OFF") row.operator('alamo.hidden_false', text="", icon="HIDE_ON") - col.prop(scene.ActiveSkeleton, 'skeletonEnum') + +class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): + + bl_label = "Armature Settings" + bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + object = context.object + layout = self.layout + scene = context.scene + # layout.active = False + + layout.prop(scene.ActiveSkeleton, 'skeletonEnum') armature = utils.findArmature() + createConstraintBone = self.layout.operator("create.constraint_bone", text='Create Constraint Bone') + createConstraintBone.active = False if armature is not None: hasChildConstraint = any( constraint.type == 'CHILD_OF' @@ -426,13 +442,7 @@ def draw(self, context): ) if not hasChildConstraint: - self.layout.operator( - "create.constraint_bone", text='Create Constraint Bone') - - for action in bpy.data.actions: - row = col.row() - row.label(text=action.name) - row.prop(action, "AnimationEndFrame") + createConstraintBone.active = True class ALAMO_PT_EditBonePanel(bpy.types.Panel): @@ -499,42 +509,40 @@ def draw_header(self, context): def draw(self, context): bones = bpy.context.selected_bones - col = self.layout.column() + layout = self.layout all_same = True - col.active = False + layout.active = False if bpy.context.mode == "EDIT_ARMATURE": - col.active = True - - if bones is not None: - has_changed = None - for bone in bones: - if has_changed is None: - has_changed = bone.EnableProxy - elif bone.EnableProxy != has_changed: - all_same = False - break - - col.active = all_same + if bones is not None: + has_changed = None + for bone in bones: + if has_changed is None: + has_changed = bone.EnableProxy + elif bone.EnableProxy != has_changed: + all_same = False + break + + layout.active = all_same if not all_same: - col.label(icon="ERROR", text="Inconsistent EnableProxy states.") - col.label(icon="BLANK1", text="Change selection or set EnableProxy.") + layout.label(icon="ERROR", text="Inconsistent EnableProxy states.") + layout.label(icon="BLANK1", text="Change selection or set EnableProxy.") - row = col.row(align=True) + row = layout.row(align=True) row.label(text="Set Proxy Visibility:") row.operator('alamo.show_proxy', text="", icon="HIDE_OFF") row.operator('alamo.hide_proxy', text="", icon="HIDE_ON") - row = col.row(align=True) + row = layout.row(align=True) row.label(text="Set altDecreaseStayHidden:") row.operator('alamo.alt_decrease_stay_hidden_true', text="", icon="CHECKMARK") row.operator('alamo.alt_decrease_stay_hidden_false', text="", icon="X") -class ALAMO_PT_PoseBonePanel(bpy.types.Panel): +class ALAMO_PT_AnimationPanel(bpy.types.Panel): - bl_label = "Pose Tools" + bl_label = "Animation" bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -555,6 +563,12 @@ def draw(self, context): row.operator('alamo.remove_keyframe_proxy', text="", icon="X") + box = layout.box() + box.label(text="Action End Frames:") + + for action in bpy.data.actions: + box.prop(action, "AnimationEndFrame", text=action.name) + class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): bl_label = "Alamo Shader Properties" @@ -584,6 +598,59 @@ def draw(self, context): # layout.template_ID(material, shader_prop, new="image.new", open="image.open") +class ALAMO_PT_DebugPanel(bpy.types.Panel): + + bl_label = "Debug" + bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + object = context.object + layout = self.layout + scene = context.scene + c = layout.column() + + c.prop(scene.ActiveSkeleton, 'skeletonEnum') + + if type(object) != type(None): + if(object.type == 'MESH'): + if bpy.context.mode == 'OBJECT': + c.prop(object, "HasCollision") + c.prop(object, "Hidden") + + armature = utils.findArmature() + if armature != None: + hasChildConstraint = False + for constraint in object.constraints: + if constraint.type == 'CHILD_OF': + hasChildConstraint = True + if not hasChildConstraint: + self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') + + action = utils.getCurrentAction() + if(action != None): + c.prop(action, "AnimationEndFrame") + + + bone = bpy.context.active_bone + if type(bone) != type(None): + if(type(bpy.context.active_bone ) is bpy.types.EditBone): + c.prop(bone.billboardMode, "billboardMode") + c.prop(bone, "Visible") + c.prop(bone, "EnableProxy") + if bone.EnableProxy: + c.prop(bone, "proxyIsHidden") + c.prop(bone, "altDecreaseStayHidden") + c.prop(bone, "ProxyName") + + elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): + poseBone = object.pose.bones[bone.name] + c.prop(poseBone, "proxyIsHiddenAnimation") + + class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): bl_label = "Additional Properties" bl_parent_id = "ALAMO_PT_materialPropertyPanel" @@ -688,9 +755,11 @@ def menu_func_export(self, context): keyframeProxyRemove, ALAMO_PT_ToolsPanel, ALAMO_PT_ObjectPanel, + ALAMO_PT_ArmatureSettingsPanel, ALAMO_PT_EditBonePanel, ALAMO_PT_EditBoneSubPanel, - ALAMO_PT_PoseBonePanel + ALAMO_PT_AnimationPanel, + ALAMO_PT_DebugPanel ) From 568a9ee51d0e0474ac20a17b3e6e37bc5aad6c02 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sat, 4 Sep 2021 18:32:51 -0700 Subject: [PATCH 041/100] validation refactor and button --- io_alamo_tools/__init__.py | 50 +++++++++--- io_alamo_tools/export_alo.py | 135 +++---------------------------- io_alamo_tools/validation.py | 151 +++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 135 deletions(-) create mode 100644 io_alamo_tools/validation.py diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 7e0bd5d..df38bf9 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -2,6 +2,7 @@ from . export_ala import ALA_Exporter from . export_alo import ALO_Exporter from . import_ala import ALA_Importer +from . import validation from bpy.types import (Panel, Operator, PropertyGroup, @@ -41,6 +42,25 @@ from . import utils +class ValidateFileButton(bpy.types.Operator): + bl_idname = "alamo.validate_file" + bl_label = "Validate" + + def execute(self, context): + mesh_list = validation.create_export_list(bpy.context.scene.collection, True, "DATA") + + #check if export objects satisfy requirements (has material, UVs, ...) + errors = validation.validate(mesh_list) + + if errors is not None and len(errors) > 0: + print(errors) + for error in errors: + self.report({"ERROR"}, error) + else: + self.report({'INFO'}, 'ALAMO - No errors! Export away.') + return {'FINISHED'} + + class createConstraintBoneButton(bpy.types.Operator): bl_idname = "create.constraint_bone" bl_label = "Create constraint bone" @@ -385,8 +405,9 @@ class ALAMO_PT_ToolsPanel(bpy.types.Panel): bl_category = "ALAMO" def draw(self, context): - # self.layout.separator() - pass + row = self.layout.row() + row.operator("alamo.validate_file") + row.scale_y = 3.0 class ALAMO_PT_ObjectPanel(bpy.types.Panel): @@ -433,8 +454,9 @@ def draw(self, context): layout.prop(scene.ActiveSkeleton, 'skeletonEnum') armature = utils.findArmature() - createConstraintBone = self.layout.operator("create.constraint_bone", text='Create Constraint Bone') - createConstraintBone.active = False + row = layout.row() + row.operator("create.constraint_bone", text='Create Constraint Bone') + row.active = False if armature is not None: hasChildConstraint = any( constraint.type == 'CHILD_OF' @@ -442,7 +464,7 @@ def draw(self, context): ) if not hasChildConstraint: - createConstraintBone.active = True + row.active = True class ALAMO_PT_EditBonePanel(bpy.types.Panel): @@ -462,7 +484,7 @@ def draw(self, context): layout.active = True row = col.row(align=True) - row.label(text="Set Visible:") + row.label(text="Set Bone Visibility:") row.operator('alamo.bone_visible', text="", icon="HIDE_OFF") row.operator('alamo.bone_invisible', text="", icon="HIDE_ON") @@ -563,11 +585,19 @@ def draw(self, context): row.operator('alamo.remove_keyframe_proxy', text="", icon="X") - box = layout.box() - box.label(text="Action End Frames:") +class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): + + bl_label = "Action End Frames" + bl_parent_id = 'ALAMO_PT_AnimationPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout for action in bpy.data.actions: - box.prop(action, "AnimationEndFrame", text=action.name) + layout.prop(action, "AnimationEndFrame", text=action.name) class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): @@ -737,6 +767,7 @@ def menu_func_export(self, context): ALA_Exporter, ALAMO_PT_materialPropertyPanel, ALAMO_PT_materialPropertySubPanel, + ValidateFileButton, createConstraintBoneButton, SetHasCollisionTrue, SetHasCollisionFalse, @@ -759,6 +790,7 @@ def menu_func_export(self, context): ALAMO_PT_EditBonePanel, ALAMO_PT_EditBoneSubPanel, ALAMO_PT_AnimationPanel, + ALAMO_PT_AnimationActionSubPanel, ALAMO_PT_DebugPanel ) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 25fd3f9..2309a4c 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1,5 +1,5 @@ import bpy -from . import settings, utils, export_ala +from . import settings, utils, export_ala, validation from bpy.props import (StringProperty, BoolProperty, @@ -25,6 +25,7 @@ import copy from contextlib import contextmanager + @contextmanager def disable_exception_traceback(): """ @@ -36,7 +37,6 @@ def disable_exception_traceback(): yield sys.tracebacklimit = default_value # revert changes - class ALO_Exporter(bpy.types.Operator, ExportHelper): """ALO Exporter""" # blender will use this as a tooltip for menu items and buttons. @@ -75,6 +75,7 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): default = 'MESH', ) + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -1319,103 +1320,10 @@ def chunk_size(size): #add 2147483648 instead of binary operation return size+2147483648 - def selectNonManifoldVertices(object): - if(bpy.context.mode != 'OBJECT'): - bpy.ops.object.mode_set(mode='OBJECT') - object.hide_set(False) - bpy.context.view_layer.objects.active = object - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='DESELECT') - bpy.ops.mesh.select_non_manifold() - def exportFailed(): with disable_exception_traceback(): raise Exception('ALAMO - EXPORT FAILED') - def checkShadowMesh(mesh_list): #checks if shadow meshes are correct and checks if material is missing - for object in mesh_list: - if len(object.data.materials) > 0: - shader = object.data.materials[0].shaderList.shaderList - if shader in ['MeshShadowVolume.fx', 'RSkinShadowVolume.fx']: - bm = bmesh.new() # create an empty BMesh - bm.from_mesh(object.data) # fill it in from a Mesh - bm.verts.ensure_lookup_table() - - for vertex in bm.verts: - if not vertex.is_manifold: - bm.free() - selectNonManifoldVertices(object) - self.report({"ERROR"}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}') - - for edge in bm.edges: - if len(edge.link_faces) < 2 : - bm.free() - selectNonManifoldVertices(object) - self.report({"ERROR"}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}') - - bm.free() - else: - self.report({"ERROR"}, f'ALAMO - Missing material on object: {object.name}') - - - def checkUV(mesh_list): #throws error if object lacks UVs - for object in mesh_list: - for material in object.data.materials: - if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': - if len(object.data.materials) > 1: - self.report({"ERROR"}, f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials') - else: - return - if object.HasCollision: - if len(object.data.materials) > 1: - self.report({"ERROR"}, f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials') - if object.data.uv_layers: #or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows - continue - else: - self.report({"ERROR"}, f'ALAMO - Missing UV: {object.name}') - - def checkInvalidArmatureModifier(mesh_list): #throws error if armature modifier lacks rig, this would crash the exporter later and checks if skeleton in modifier doesn't match active skeleton - activeSkeleton = bpy.context.scene.ActiveSkeleton.skeletonEnum - for object in mesh_list: - for modifier in object.modifiers: - if modifier.type == "ARMATURE": - if modifier.object == None: - self.report({"ERROR"}, f'ALAMO - Armature modifier without selected skeleton on: {object.name}') - return True - elif modifier.object.type != 'NoneType': - if modifier.object.name != activeSkeleton: - self.report({"ERROR"}, f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}") - return True - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - if constraint.target is not None: - #print(type(constraint.target)) - if constraint.target.name != activeSkeleton: - self.report({"ERROR"}, f"ALAMO - Constraint doesn't match active skeleton on: {object.name}") - return True - - def checkFaceNumber(mesh_list): #checks if the number of faces exceeds max ushort, which is used to save the indices - for object in mesh_list: - if len(object.data.polygons) > 65535: - self.report({"ERROR"}, f'ALAMO - Face number exceeds uShort max on object: {object.name}; split mesh into multiple objects') - return True - - def checkAutosmooth(mesh_list): #prints a warning if Autosmooth is used - for object in mesh_list: - if object.data.use_auto_smooth: - self.report({"ERROR"}, f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead') - - def checkTranslation(mesh_list): #prints warning when translation is not default - for object in mesh_list: - if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or object.scale != mathutils.Vector((1.0, 1.0, 1.0)): - self.report({"ERROR"}, f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone') - - def checkTranslationArmature(): #prints warning when translation is not default - armature = utils.findArmature() - if armature != None: - if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): - self.report({"ERROR"}, 'ALAMO - active Armature is not aligned with the world origin') - def unhide(): hiddenList = [] for object in bpy.data.objects: @@ -1429,24 +1337,6 @@ def hide(hiddenList): object.hide_render = hiddenList[counter] counter += 1 - def create_export_list(collection): - export_list = [] - - if(collection.hide_viewport): - return export_list - - for object in collection.objects: - if(object.type == 'MESH' and (object.hide_viewport == False or self.exportHiddenObjects)): - if self.useNamesFrom == 'OBJECT': - object.data.name = object.name - - export_list.append(object) - - for child in collection.children: - export_list.extend(create_export_list(child)) - - return export_list - #hidden objects and collections can't be accessed, avoid problems def unhide_collections(collection_parent): collection_is_hidden_list = [] @@ -1491,22 +1381,15 @@ def exportAnimations(filePath): exporter.exportAnimation(filePath + "_" + action.name + ".ALA") - mesh_list = create_export_list(bpy.context.scene.collection) + mesh_list = validation.create_export_list(bpy.context.scene.collection, self.exportHiddenObjects, self.useNamesFrom) #check if export objects satisfy requirements (has material, UVs, ...) - has_errors = [] - - has_errors.append(checkShadowMesh(mesh_list)) - has_errors.append(checkUV(mesh_list)) - has_errors.append(checkFaceNumber(mesh_list)) - has_errors.append(checkAutosmooth(mesh_list)) - has_errors.append(checkTranslation(mesh_list)) - has_errors.append(checkTranslationArmature()) - has_errors.append(checkInvalidArmatureModifier(mesh_list)) + errors = validation.validate(mesh_list) - for error in has_errors: - if error: - exportFailed() + if errors is not None and len(errors) > 0: + for error in errors: + self.report({"ERROR"}, error) + exportFailed() hiddenList = unhide() collection_is_hidden_list = unhide_collections(bpy.context.scene.collection) diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py new file mode 100644 index 0000000..fa8a872 --- /dev/null +++ b/io_alamo_tools/validation.py @@ -0,0 +1,151 @@ +import mathutils +import bpy +import bmesh +from . import utils + +def create_export_list(collection, exportHiddenObjects, useNamesFrom): + export_list = [] + + if(collection.hide_viewport): + return export_list + + for object in collection.objects: + if(object.type == 'MESH' and (object.hide_viewport == False or exportHiddenObjects)): + if useNamesFrom == 'OBJECT': + object.data.name = object.name + + export_list.append(object) + + for child in collection.children: + export_list.extend(create_export_list(child, exportHiddenObjects, useNamesFrom)) + + return export_list + +def selectNonManifoldVertices(object): + if(bpy.context.mode != 'OBJECT'): + bpy.ops.object.mode_set(mode='OBJECT') + object.hide_set(False) + bpy.context.view_layer.objects.active = object + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + bpy.ops.mesh.select_non_manifold() + +# checks if shadow meshes are correct and checks if material is missing +def checkShadowMesh(mesh_list): + error = [] + for object in mesh_list: + if len(object.data.materials) > 0: + shader = object.data.materials[0].shaderList.shaderList + if shader in ['MeshShadowVolume.fx', 'RSkinShadowVolume.fx']: + bm = bmesh.new() # create an empty BMesh + bm.from_mesh(object.data) # fill it in from a Mesh + bm.verts.ensure_lookup_table() + + for vertex in bm.verts: + if not vertex.is_manifold: + bm.free() + selectNonManifoldVertices(object) + error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] + break + + for edge in bm.edges: + if len(edge.link_faces) < 2: + bm.free() + selectNonManifoldVertices(object) + error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] + break + + bm.free() + else: + error += [f'ALAMO - Missing material on object: {object.name}'] + + return error + +def checkUV(mesh_list): # throws error if object lacks UVs + error = [] + for object in mesh_list: + for material in object.data.materials: + if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': + if len(object.data.materials) > 1: + error += [f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials'] + if object.HasCollision: + if len(object.data.materials) > 1: + error += [f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials'] + if object.data.uv_layers: # or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows + continue + else: + error += [f'ALAMO - Missing UV: {object.name}'] + + return error + +# throws error if armature modifier lacks rig, this would crash the exporter later and checks if skeleton in modifier doesn't match active skeleton +def checkInvalidArmatureModifier(mesh_list): + activeSkeleton = bpy.context.scene.ActiveSkeleton.skeletonEnum + error = [] + for object in mesh_list: + for modifier in object.modifiers: + if modifier.type == "ARMATURE": + if modifier.object == None: + error += [f'ALAMO - Armature modifier without selected skeleton on: {object.name}'] + break + elif modifier.object.type != 'NoneType': + if modifier.object.name != activeSkeleton: + error += [f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}"] + break + for constraint in object.constraints: + if constraint.type == 'CHILD_OF': + if constraint.target is not None: + # print(type(constraint.target)) + if constraint.target.name != activeSkeleton: + error += [f"ALAMO - Constraint doesn't match active skeleton on: {object.name}"] + break + + return error + +# checks if the number of faces exceeds max ushort, which is used to save the indices +def checkFaceNumber(mesh_list): + for object in mesh_list: + if len(object.data.polygons) > 65535: + return [f'ALAMO - Face number exceeds uShort max on object: {object.name}; split mesh into multiple objects'] + return [] + +def checkAutosmooth(mesh_list): # prints a warning if Autosmooth is used + for object in mesh_list: + if object.data.use_auto_smooth: + return [f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead'] + return [] + +def checkTranslation(mesh_list): # prints warning when translation is not default + for object in mesh_list: + if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or object.scale != mathutils.Vector((1.0, 1.0, 1.0)): + return [f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone'] + return [] + +def checkTranslationArmature(mesh_list): # prints warning when translation is not default + armature = utils.findArmature() + if armature != None: + if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): + return ['ALAMO - active Armature is not aligned with the world origin'] + return [] + +def validate(mesh_list): + errors = [] + checklist = [ + checkShadowMesh, + checkUV, + checkFaceNumber, + checkAutosmooth, + checkTranslation, + checkTranslationArmature, + checkInvalidArmatureModifier, + ] + + for check in checklist: + test = check(mesh_list) + print(f'{test = }') + if test is not None: + errors += test + + print(f'{errors = }') + + return errors From 6c8f5fbdc278629c8e838c98b0b44daec838a532 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 5 Sep 2021 08:46:35 -0700 Subject: [PATCH 042/100] validation refactor, fix for animation import error crashing script --- io_alamo_tools/__init__.py | 3 +- io_alamo_tools/import_ala.py | 3 +- io_alamo_tools/validation.py | 171 +++++++++++++++++++---------------- 3 files changed, 96 insertions(+), 81 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index df38bf9..35a0ccf 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -53,11 +53,10 @@ def execute(self, context): errors = validation.validate(mesh_list) if errors is not None and len(errors) > 0: - print(errors) for error in errors: self.report({"ERROR"}, error) else: - self.report({'INFO'}, 'ALAMO - No errors! Export away.') + self.report({'INFO'}, 'ALAMO - Validation complete. No errors detected!') return {'FINISHED'} diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index bce978b..b055763 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -353,8 +353,7 @@ def validate(data): break if(fitting): return True - self.report({"WARNING"}, "animation bones not matching active armature") - return False + raise Exception("animation bones not matching active armature") class AnimationImporter(): def loadAnimation(self, filePath): diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index fa8a872..76c8374 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -31,101 +31,116 @@ def selectNonManifoldVertices(object): bpy.ops.mesh.select_non_manifold() # checks if shadow meshes are correct and checks if material is missing -def checkShadowMesh(mesh_list): +def checkShadowMesh(object): error = [] - for object in mesh_list: - if len(object.data.materials) > 0: - shader = object.data.materials[0].shaderList.shaderList - if shader in ['MeshShadowVolume.fx', 'RSkinShadowVolume.fx']: - bm = bmesh.new() # create an empty BMesh - bm.from_mesh(object.data) # fill it in from a Mesh - bm.verts.ensure_lookup_table() - - for vertex in bm.verts: - if not vertex.is_manifold: - bm.free() - selectNonManifoldVertices(object) - error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] - break - - for edge in bm.edges: - if len(edge.link_faces) < 2: - bm.free() - selectNonManifoldVertices(object) - error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] - break - - bm.free() - else: - error += [f'ALAMO - Missing material on object: {object.name}'] + if len(object.data.materials) > 0: + shader = object.data.materials[0].shaderList.shaderList + if shader in ['MeshShadowVolume.fx', 'RSkinShadowVolume.fx']: + bm = bmesh.new() # create an empty BMesh + bm.from_mesh(object.data) # fill it in from a Mesh + bm.verts.ensure_lookup_table() + + for vertex in bm.verts: + if not vertex.is_manifold: + bm.free() + selectNonManifoldVertices(object) + error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] + break + + for edge in bm.edges: + if len(edge.link_faces) < 2: + bm.free() + selectNonManifoldVertices(object) + error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] + break + + bm.free() + else: + error += [f'ALAMO - Missing material on object: {object.name}'] return error -def checkUV(mesh_list): # throws error if object lacks UVs +def checkUV(object): # throws error if object lacks UVs error = [] - for object in mesh_list: - for material in object.data.materials: - if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': - if len(object.data.materials) > 1: - error += [f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials'] - if object.HasCollision: - if len(object.data.materials) > 1: - error += [f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials'] - if object.data.uv_layers: # or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows - continue - else: - error += [f'ALAMO - Missing UV: {object.name}'] + for material in object.data.materials: + if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': + if len(object.data.materials) > 1: + error += [f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials'] + if object.HasCollision: + if len(object.data.materials) > 1: + error += [f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials'] + if not object.data.uv_layers: # or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows + error += [f'ALAMO - Missing UV: {object.name}'] return error # throws error if armature modifier lacks rig, this would crash the exporter later and checks if skeleton in modifier doesn't match active skeleton -def checkInvalidArmatureModifier(mesh_list): +def checkInvalidArmatureModifier(object): activeSkeleton = bpy.context.scene.ActiveSkeleton.skeletonEnum error = [] - for object in mesh_list: - for modifier in object.modifiers: - if modifier.type == "ARMATURE": - if modifier.object == None: - error += [f'ALAMO - Armature modifier without selected skeleton on: {object.name}'] + for modifier in object.modifiers: + if modifier.type == "ARMATURE": + if modifier.object is None: + error += [f'ALAMO - Armature modifier without selected skeleton on: {object.name}'] + break + elif modifier.object.type != 'NoneType': + if modifier.object.name != activeSkeleton: + error += [f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}"] break - elif modifier.object.type != 'NoneType': - if modifier.object.name != activeSkeleton: - error += [f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}"] - break - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - if constraint.target is not None: - # print(type(constraint.target)) - if constraint.target.name != activeSkeleton: - error += [f"ALAMO - Constraint doesn't match active skeleton on: {object.name}"] - break + for constraint in object.constraints: + if ( + constraint.type == 'CHILD_OF' + and constraint.target is not None + and constraint.target.name != activeSkeleton + ): + error += [f"ALAMO - Constraint doesn't match active skeleton on: {object.name}"] + break return error # checks if the number of faces exceeds max ushort, which is used to save the indices -def checkFaceNumber(mesh_list): - for object in mesh_list: - if len(object.data.polygons) > 65535: - return [f'ALAMO - Face number exceeds uShort max on object: {object.name}; split mesh into multiple objects'] +def checkFaceNumber(object): + if len(object.data.polygons) > 65535: + return [f'ALAMO - {object.name} exceeds maximum face limit; split mesh into multiple objects'] return [] -def checkAutosmooth(mesh_list): # prints a warning if Autosmooth is used - for object in mesh_list: - if object.data.use_auto_smooth: - return [f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead'] +def checkAutosmooth(object): # prints a warning if Autosmooth is used + if object.data.use_auto_smooth: + return [f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead'] return [] -def checkTranslation(mesh_list): # prints warning when translation is not default - for object in mesh_list: - if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or object.scale != mathutils.Vector((1.0, 1.0, 1.0)): - return [f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone'] +def checkTranslation(object): # prints warning when translation is not default + if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ'): + return [f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone'] return [] -def checkTranslationArmature(mesh_list): # prints warning when translation is not default +def checkScale(object): # prints warning when scale is not default + if object.scale != mathutils.Vector((1.0, 1.0, 1.0)): + return [f'ALAMO - {object.name} has non-identity scale. Apply scale.'] + return [] + +# checks if vertices have 0 or > 1 groups +def checkVertexGroups(object): + print(len(object.vertex_groups)) + if object.vertex_groups is None or len(object.vertex_groups) == 0: + return [] + for vertex in object.data.vertices: + total = 0 + for group in vertex.groups: + if group.weight not in [0, 1]: + return [f'ALAMO - Object {object.name} has improper vertex groups'] + total += group.weight + if total not in [0, 1]: + return [f'ALAMO - Object {object.name} has improper vertex groups'] + print(total) + + return [] + +def checkTranslationArmature(): # prints warning when translation is not default armature = utils.findArmature() if armature != None: if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): - return ['ALAMO - active Armature is not aligned with the world origin'] + return [f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation'] return [] def validate(mesh_list): @@ -136,16 +151,18 @@ def validate(mesh_list): checkFaceNumber, checkAutosmooth, checkTranslation, - checkTranslationArmature, checkInvalidArmatureModifier, + checkScale, + checkVertexGroups, + ] + checklist_no_object = [ + checkTranslationArmature, ] for check in checklist: - test = check(mesh_list) - print(f'{test = }') - if test is not None: - errors += test - - print(f'{errors = }') + for object in mesh_list: + errors += check(object) + for check in checklist_no_object: + errors += check() return errors From 638c53141a09f511a9659aa3c2bb60ba075a0480 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 8 Sep 2021 20:13:30 -0700 Subject: [PATCH 043/100] Button status feedback --- io_alamo_tools/__init__.py | 126 +++++++++++++++-------------------- io_alamo_tools/validation.py | 2 +- 2 files changed, 56 insertions(+), 72 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 35a0ccf..758abf5 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -42,6 +42,31 @@ from . import utils +def CheckPropAllSame(objects, prop): + # True: All same, have value of True + # False: All same, have value of False + # None: Not all same + first_value = None + for object in objects: + if first_value is None: + first_value = getattr(object, prop) + elif getattr(object, prop) != first_value: + return None + return first_value + + +def ShouldEnable(objects, prop, set_to): + if objects is None or len(objects) <= 0: + return False + all_same = CheckPropAllSame(objects, prop) + if all_same is not None: + if set_to: + return not all_same + else: + return all_same + return True + + class ValidateFileButton(bpy.types.Operator): bl_idname = "alamo.validate_file" bl_label = "Validate" @@ -92,9 +117,7 @@ class SetHasCollisionTrue(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_objects is not None: - return len(bpy.context.selected_objects) > 0 - return False + return ShouldEnable(bpy.context.selected_objects, "HasCollision", True) def execute(self, context): objs = bpy.context.selected_objects @@ -110,9 +133,7 @@ class SetHasCollisionFalse(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_objects is not None: - return len(bpy.context.selected_objects) > 0 - return False + return ShouldEnable(bpy.context.selected_objects, "HasCollision", False) def execute(self, context): objs = bpy.context.selected_objects @@ -128,9 +149,7 @@ class SetHiddenTrue(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_objects is not None: - return len(bpy.context.selected_objects) > 0 - return False + return ShouldEnable(bpy.context.selected_objects, "Hidden", True) def execute(self, context): objs = bpy.context.selected_objects @@ -146,9 +165,7 @@ class SetHiddenFalse(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_objects is not None: - return len(bpy.context.selected_objects) > 0 - return False + return ShouldEnable(bpy.context.selected_objects, "Hidden", False) def execute(self, context): objs = bpy.context.selected_objects @@ -164,9 +181,7 @@ class SetBoneVisible(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "Visible", True) def execute(self, context): bones = bpy.context.selected_bones @@ -182,9 +197,7 @@ class SetBoneInvisible(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "Visible", False) def execute(self, context): bones = bpy.context.selected_bones @@ -200,9 +213,7 @@ class SetAltDecreaseStayHiddenTrue(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", True) def execute(self, context): bones = bpy.context.selected_bones @@ -218,9 +229,7 @@ class SetAltDecreaseStayHiddenFalse(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", False) def execute(self, context): bones = bpy.context.selected_bones @@ -236,9 +245,7 @@ class EnableProxyFalse(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "EnableProxy", False) def execute(self, context): bones = bpy.context.selected_bones @@ -254,9 +261,7 @@ class EnableProxyTrue(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "EnableProxy", True) def execute(self, context): bones = bpy.context.selected_bones @@ -272,9 +277,7 @@ class ProxyShow(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", False) def execute(self, context): bones = bpy.context.selected_bones @@ -290,9 +293,7 @@ class ProxyHide(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_bones is not None: - return len(bpy.context.selected_bones) > 0 - return False + return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", True) def execute(self, context): bones = bpy.context.selected_bones @@ -395,6 +396,13 @@ class skeletonEnumClass(PropertyGroup): ) +def rowbuilder(layout, label, operators, icons): + row = layout.row(align=True) + row.label(text=label) + row.operator(operators[0], text="", icon=icons[0]) + row.operator(operators[1], text="", icon=icons[1]) + + class ALAMO_PT_ToolsPanel(bpy.types.Panel): bl_label = "Alamo Properties" @@ -425,15 +433,9 @@ def draw(self, context): if bpy.context.mode == "OBJECT": layout.active = True - row = layout.row(align=True) - row.label(text="Set HasCollision:") - row.operator('alamo.collision_true', text="", icon="CHECKMARK") - row.operator('alamo.collision_false', text="", icon="X") + rowbuilder(layout, "Set HasCollision:", ["alamo.collision_true", "alamo.collision_false"], ["CHECKMARK", "X"]) - row = layout.row(align=True) - row.label(text="Set Hidden:") - row.operator('alamo.hidden_true', text="", icon="HIDE_OFF") - row.operator('alamo.hidden_false', text="", icon="HIDE_ON") + rowbuilder(layout, "Set Hidden:", ["alamo.hidden_false", "alamo.hidden_true"], ["HIDE_OFF", "HIDE_ON"]) class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): @@ -482,10 +484,7 @@ def draw(self, context): if bpy.context.mode == "EDIT_ARMATURE": layout.active = True - row = col.row(align=True) - row.label(text="Set Bone Visibility:") - row.operator('alamo.bone_visible', text="", icon="HIDE_OFF") - row.operator('alamo.bone_invisible', text="", icon="HIDE_ON") + rowbuilder(layout, "Set Bone Visibility:", ["alamo.bone_visible", "alamo.bone_invisible"], ["HIDE_OFF", "HIDE_ON"]) row = col.row() row.enabled = False @@ -520,45 +519,30 @@ class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): bl_options = {"HEADER_LAYOUT_EXPAND"} def draw_header(self, context): - row = self.layout.row(align=True) - row.active = False + layout = self.layout + layout.active = False if bpy.context.mode == "EDIT_ARMATURE": - row.active = True - row.label(text="Set Proxy:") - row.operator('alamo.enable_proxy', text="", icon="CHECKMARK") - row.operator('alamo.disable_proxy', text="", icon="X") + layout.active = True + + rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) def draw(self, context): bones = bpy.context.selected_bones layout = self.layout + all_same = True layout.active = False if bpy.context.mode == "EDIT_ARMATURE": - if bones is not None: - has_changed = None - for bone in bones: - if has_changed is None: - has_changed = bone.EnableProxy - elif bone.EnableProxy != has_changed: - all_same = False - break - layout.active = all_same if not all_same: layout.label(icon="ERROR", text="Inconsistent EnableProxy states.") layout.label(icon="BLANK1", text="Change selection or set EnableProxy.") - row = layout.row(align=True) - row.label(text="Set Proxy Visibility:") - row.operator('alamo.show_proxy', text="", icon="HIDE_OFF") - row.operator('alamo.hide_proxy', text="", icon="HIDE_ON") + rowbuilder(layout, "Set Proxy Visibility:", ["alamo.show_proxy", "alamo.hide_proxy"], ["HIDE_OFF", "HIDE_ON"]) - row = layout.row(align=True) - row.label(text="Set altDecreaseStayHidden:") - row.operator('alamo.alt_decrease_stay_hidden_true', text="", icon="CHECKMARK") - row.operator('alamo.alt_decrease_stay_hidden_false', text="", icon="X") + rowbuilder(layout, "Set altDecreaseStayHidden:", ["alamo.alt_decrease_stay_hidden_true", "alamo.alt_decrease_stay_hidden_false"], ["CHECKMARK", "X"]) class ALAMO_PT_AnimationPanel(bpy.types.Panel): diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 76c8374..dee37fa 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -22,7 +22,7 @@ def create_export_list(collection, exportHiddenObjects, useNamesFrom): return export_list def selectNonManifoldVertices(object): - if(bpy.context.mode != 'OBJECT'): + if bpy.context.mode != 'OBJECT': bpy.ops.object.mode_set(mode='OBJECT') object.hide_set(False) bpy.context.view_layer.objects.active = object From 25bd5b75b881288aaa873ac2aa63d0300b3bbd99 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 9 Sep 2021 10:37:37 -0700 Subject: [PATCH 044/100] UI Polish --- io_alamo_tools/__init__.py | 78 ++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 758abf5..ebbd2fc 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -41,6 +41,12 @@ from . import settings from . import utils +def CheckObjectType(objects, type): + for object in objects: + if object.type != type: + return False + return True + def CheckPropAllSame(objects, prop): # True: All same, have value of True @@ -58,6 +64,10 @@ def CheckPropAllSame(objects, prop): def ShouldEnable(objects, prop, set_to): if objects is None or len(objects) <= 0: return False + if bpy.context.mode == "OBJECT": + objects_same = CheckObjectType(objects, "MESH") + if not objects_same: + return False all_same = CheckPropAllSame(objects, prop) if all_same is not None: if set_to: @@ -67,6 +77,34 @@ def ShouldEnable(objects, prop, set_to): return True +def ShouldEnableAnim(bones, prop, set_to): + if bones is None or len(bones) <= 0: + return False + + frame = bpy.context.scene.frame_current + action = bpy.context.object.animation_data.action + has_keyframes = False + for bone in bones: + keyframes = action.fcurves.find(bone.path_from_id() + "." + prop) + if keyframes is not None: + for keyframe in keyframes.keyframe_points: + if int(keyframe.co[0]) == frame: + has_keyframes = True + break + + if set_to is None: + return has_keyframes + if not has_keyframes: + return True + + all_same = CheckPropAllSame(bones, prop) + if all_same is not None: + if set_to: + return not all_same + return all_same + return True + + class ValidateFileButton(bpy.types.Operator): bl_idname = "alamo.validate_file" bl_label = "Validate" @@ -89,6 +127,19 @@ class createConstraintBoneButton(bpy.types.Operator): bl_idname = "create.constraint_bone" bl_label = "Create constraint bone" + @classmethod + def poll(cls, context): + if len(bpy.context.selected_objects) > 1: + return False + if utils.findArmature() is not None: + hasChildConstraint = any( + constraint.type == 'CHILD_OF' + for constraint in bpy.context.object.constraints + ) + + return not hasChildConstraint + return False + def execute(self, context): object = bpy.context.view_layer.objects.active armature = utils.findArmature() @@ -320,9 +371,9 @@ def keyframeProxySet(operation): bone.keyframe_delete(data_path="proxyIsHiddenAnimation") else: bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) - for keyframe in action.fcurves.find(bone.path_from_id() + ".proxyIsHiddenAnimation").keyframe_points: - if int(keyframe.co[0]) == frame: - keyframe.type = keyframeType + # for keyframe in action.fcurves.find(bone.path_from_id() + ".proxyIsHiddenAnimation").keyframe_points: + # if int(keyframe.co[0]) == frame: + # keyframe.type = keyframeType for area in bpy.context.screen.areas: if area.type == 'DOPESHEET_EDITOR': @@ -336,9 +387,7 @@ class keyframeProxyShow(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_pose_bones is not None: - return len(bpy.context.selected_pose_bones) > 0 - return False + return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", False) def execute(self, context): keyframeProxySet('SHOW') @@ -352,9 +401,7 @@ class keyframeProxyHide(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_pose_bones is not None: - return len(bpy.context.selected_pose_bones) > 0 - return False + return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", True) def execute(self, context): keyframeProxySet('HIDE') @@ -368,9 +415,7 @@ class keyframeProxyRemove(bpy.types.Operator): @classmethod def poll(cls, context): - if bpy.context.selected_pose_bones is not None: - return len(bpy.context.selected_pose_bones) > 0 - return False + return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", None) def execute(self, context): keyframeProxySet('REMOVE') @@ -457,15 +502,6 @@ def draw(self, context): armature = utils.findArmature() row = layout.row() row.operator("create.constraint_bone", text='Create Constraint Bone') - row.active = False - if armature is not None: - hasChildConstraint = any( - constraint.type == 'CHILD_OF' - for constraint in object.constraints - ) - - if not hasChildConstraint: - row.active = True class ALAMO_PT_EditBonePanel(bpy.types.Panel): From e1c9e58300e974a81e73c7ef855cee483a5831b7 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 9 Sep 2021 10:38:36 -0700 Subject: [PATCH 045/100] Version increment --- io_alamo_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index ebbd2fc..e232b9f 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 2, 6), + "version": (0, 0, 2, 7), "blender": (2, 93, 0), "category": "Import-Export" } From 15a55af68ec8738f462221b3b51c63a3390db943 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 9 Sep 2021 12:19:00 -0700 Subject: [PATCH 046/100] Better animation button feedback --- io_alamo_tools/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index e232b9f..455ebca 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -84,17 +84,20 @@ def ShouldEnableAnim(bones, prop, set_to): frame = bpy.context.scene.frame_current action = bpy.context.object.animation_data.action has_keyframes = False + has_previous_keyframe = False for bone in bones: keyframes = action.fcurves.find(bone.path_from_id() + "." + prop) if keyframes is not None: for keyframe in keyframes.keyframe_points: + if int(keyframe.co[0]) <= frame: + has_previous_keyframe = True if int(keyframe.co[0]) == frame: has_keyframes = True break if set_to is None: return has_keyframes - if not has_keyframes: + if not has_previous_keyframe: return True all_same = CheckPropAllSame(bones, prop) From a35deac2022231943b58eb49580364ab513ab3af Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 9 Sep 2021 14:41:13 -0700 Subject: [PATCH 047/100] Active skeleton export option --- io_alamo_tools/export_alo.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 2309a4c..0d811fb 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -25,6 +25,16 @@ import copy from contextlib import contextmanager +def skeletonEnumCallback(scene, context): + armatures = [('None', 'None', '', '', 0)] + counter = 1 + for arm in bpy.data.objects: # test if armature exists + if arm.type == 'ARMATURE': + armatures.append((arm.name, arm.name, '', '', counter)) + counter += 1 + + return armatures + @contextmanager def disable_exception_traceback(): @@ -75,6 +85,12 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): default = 'MESH', ) + skeletonEnum : EnumProperty( + name='Active Skeleton', + description = "skeleton that is exported", + items = skeletonEnumCallback, + ) + def draw(self, context): layout = self.layout @@ -89,6 +105,9 @@ def draw(self, context): row.use_property_split = False row.prop(self, "useNamesFrom", expand = True) + row = layout.row() + row.prop(bpy.context.scene.ActiveSkeleton, "skeletonEnum") + def execute(self, context): # execute() is called by blender when running the operator. #skeleton and bones From 124d2bbbd377078cacb909313e4e436b997e024c Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 9 Sep 2021 16:09:38 -0700 Subject: [PATCH 048/100] Validate bone number --- io_alamo_tools/validation.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index dee37fa..69a5e55 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -121,7 +121,6 @@ def checkScale(object): # prints warning when scale is not default # checks if vertices have 0 or > 1 groups def checkVertexGroups(object): - print(len(object.vertex_groups)) if object.vertex_groups is None or len(object.vertex_groups) == 0: return [] for vertex in object.data.vertices: @@ -132,13 +131,27 @@ def checkVertexGroups(object): total += group.weight if total not in [0, 1]: return [f'ALAMO - Object {object.name} has improper vertex groups'] - print(total) return [] +def checkNumBones(object): + if type(object) != type(None) and object.type == 'MESH': + material = bpy.context.active_object.active_material + if material is not None and material.shaderList.shaderList.find("RSkin") > -1: + used_groups = [] + for vertex in object.data.vertices: + for group in vertex.groups: + if group.weight == 1: + used_groups.append(group.group) + + if len(set(used_groups)) > 23: + return [f'ALAMO - Object {object.name} uses too many bones.'] + return [] + + def checkTranslationArmature(): # prints warning when translation is not default armature = utils.findArmature() - if armature != None: + if armature is not None: if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): return [f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation'] return [] @@ -154,6 +167,7 @@ def validate(mesh_list): checkInvalidArmatureModifier, checkScale, checkVertexGroups, + checkNumBones, ] checklist_no_object = [ checkTranslationArmature, From 7f7e98ac39915c3f59e866a12e5db19b62ab16ca Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Thu, 9 Sep 2021 16:36:47 -0700 Subject: [PATCH 049/100] Validate number of proxy keyframes --- io_alamo_tools/validation.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 69a5e55..b4c44d6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -145,10 +145,9 @@ def checkNumBones(object): used_groups.append(group.group) if len(set(used_groups)) > 23: - return [f'ALAMO - Object {object.name} uses too many bones.'] + return [f'ALAMO - Object {object.name} has more than 23 bones.'] return [] - def checkTranslationArmature(): # prints warning when translation is not default armature = utils.findArmature() if armature is not None: @@ -156,6 +155,17 @@ def checkTranslationArmature(): # prints warning when translation is not defaul return [f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation'] return [] +def checkProxyKeyframes(): + actions = bpy.data.actions + local_errors = [] + for action in actions: + for fcurve in action.fcurves: + if fcurve.data_path.find("proxyIsHiddenAnimation") > -1 and len(fcurve.keyframe_points) > 2: + local_errors += [f'ALAMO - Action {action.name} has fcurves with more than 2 proxy keyframes.'] + break + return local_errors + + def validate(mesh_list): errors = [] checklist = [ @@ -171,6 +181,7 @@ def validate(mesh_list): ] checklist_no_object = [ checkTranslationArmature, + checkProxyKeyframes, ] for check in checklist: From 17c729f899a06fb7457546c9d7a8f52318e3c968 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 12 Sep 2021 10:03:46 -0700 Subject: [PATCH 050/100] Create Constraint Bone fix --- io_alamo_tools/__init__.py | 130 ++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 51 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 455ebca..fcb5ee3 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -126,21 +126,50 @@ def execute(self, context): return {'FINISHED'} +# Legacy version, provided for the debug panel class createConstraintBoneButton(bpy.types.Operator): bl_idname = "create.constraint_bone" bl_label = "Create constraint bone" + def execute(self, context): + object = bpy.context.view_layer.objects.active + armature = utils.findArmature() + + bpy.context.view_layer.objects.active = armature + utils.setModeToEdit() + + bone = armature.data.edit_bones.new(object.name) + bone.tail = bone.head + mathutils.Vector((0, 0, 1)) + bone.matrix = object.matrix_world + object.location = mathutils.Vector((0.0, 0.0, 0.0)) + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') + constraint = object.constraints.new('CHILD_OF') + constraint.target = armature + constraint.subtarget = bone.name + + utils.setModeToObject() + bpy.context.view_layer.objects.active = object + return {'FINISHED'} + + +class CreateConstraintBone(bpy.types.Operator): + bl_idname = "alamo.create_constraint_bone" + bl_label = "Create Constraint Bone" + @classmethod def poll(cls, context): - if len(bpy.context.selected_objects) > 1: - return False - if utils.findArmature() is not None: - hasChildConstraint = any( - constraint.type == 'CHILD_OF' - for constraint in bpy.context.object.constraints - ) - - return not hasChildConstraint + object = bpy.context.object + if type(object) != type(None): + if(object.type == 'MESH'): + if bpy.context.mode == 'OBJECT': + armature = utils.findArmature() + if armature != None: + hasChildConstraint = False + for constraint in object.constraints: + if constraint.type == 'CHILD_OF': + hasChildConstraint = True + if not hasChildConstraint: + return True return False def execute(self, context): @@ -502,9 +531,7 @@ def draw(self, context): layout.prop(scene.ActiveSkeleton, 'skeletonEnum') - armature = utils.findArmature() - row = layout.row() - row.operator("create.constraint_bone", text='Create Constraint Bone') + layout.operator("alamo.create_constraint_bone") class ALAMO_PT_EditBonePanel(bpy.types.Panel): @@ -536,17 +563,6 @@ def draw(self, context): else: row.label(text="billboardMode") - row = col.row() - row.enabled = False - if bones is not None: - if len(bones) == 1: - row.prop(bones[0], "ProxyName") - row.enabled = True - else: - row.label(text="ProxyName") - else: - row.label(text="ProxyName") - class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): @@ -583,6 +599,17 @@ def draw(self, context): rowbuilder(layout, "Set altDecreaseStayHidden:", ["alamo.alt_decrease_stay_hidden_true", "alamo.alt_decrease_stay_hidden_false"], ["CHECKMARK", "X"]) + row = layout.row() + row.enabled = False + if bones is not None: + if len(bones) == 1: + row.prop(bones[0], "ProxyName") + row.enabled = True + else: + row.label(text="ProxyName") + else: + row.label(text="ProxyName") + class ALAMO_PT_AnimationPanel(bpy.types.Panel): @@ -622,34 +649,6 @@ def draw(self, context): layout.prop(action, "AnimationEndFrame", text=action.name) -class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo Shader Properties" - bl_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - - def draw(self, context): - object = context.object - layout = self.layout - col = layout.column() - - if type(object) != type(None) and object.type == 'MESH': - material = bpy.context.active_object.active_material - if material is not None: - # a None image is needed to represent not using a texture - if 'None' not in bpy.data.images: - bpy.data.images.new(name='None', width=1, height=1) - col.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find('Texture') > -1: - layout.prop_search(material, shader_prop, bpy.data, "images") - # layout.template_ID(material, shader_prop, new="image.new", open="image.open") - - class ALAMO_PT_DebugPanel(bpy.types.Panel): bl_label = "Debug" @@ -703,6 +702,34 @@ def draw(self, context): c.prop(poseBone, "proxyIsHiddenAnimation") +class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): + bl_label = "Alamo Shader Properties" + bl_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + + def draw(self, context): + object = context.object + layout = self.layout + col = layout.column() + + if type(object) != type(None) and object.type == 'MESH': + material = bpy.context.active_object.active_material + if material is not None: + # a None image is needed to represent not using a texture + if 'None' not in bpy.data.images: + bpy.data.images.new(name='None', width=1, height=1) + col.prop(material.shaderList, "shaderList") + if material.shaderList.shaderList != 'alDefault.fx': + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find('Texture') > -1: + layout.prop_search(material, shader_prop, bpy.data, "images") + # layout.template_ID(material, shader_prop, new="image.new", open="image.open") + + class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): bl_label = "Additional Properties" bl_parent_id = "ALAMO_PT_materialPropertyPanel" @@ -790,6 +817,7 @@ def menu_func_export(self, context): ALAMO_PT_materialPropertyPanel, ALAMO_PT_materialPropertySubPanel, ValidateFileButton, + CreateConstraintBone, createConstraintBoneButton, SetHasCollisionTrue, SetHasCollisionFalse, From cb6d598d6658e118a3f4c523a01a128f2df262aa Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 12 Sep 2021 12:29:58 -0700 Subject: [PATCH 051/100] Validate duplicate keyframes --- io_alamo_tools/__init__.py | 8 ++--- io_alamo_tools/validation.py | 59 +++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index fcb5ee3..94b9a95 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -116,11 +116,11 @@ def execute(self, context): mesh_list = validation.create_export_list(bpy.context.scene.collection, True, "DATA") #check if export objects satisfy requirements (has material, UVs, ...) - errors = validation.validate(mesh_list) + messages = validation.validate(mesh_list) - if errors is not None and len(errors) > 0: - for error in errors: - self.report({"ERROR"}, error) + if messages is not None and len(messages) > 0: + for message in messages: + self.report(*message) else: self.report({'INFO'}, 'ALAMO - Validation complete. No errors detected!') return {'FINISHED'} diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index b4c44d6..1589cd1 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -44,19 +44,19 @@ def checkShadowMesh(object): if not vertex.is_manifold: bm.free() selectNonManifoldVertices(object) - error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] + error += [({'ERROR'}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}')] break for edge in bm.edges: if len(edge.link_faces) < 2: bm.free() selectNonManifoldVertices(object) - error += [f'ALAMO - Non manifold geometry shadow mesh: {object.name}'] + error += [({'ERROR'}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}')] break bm.free() else: - error += [f'ALAMO - Missing material on object: {object.name}'] + error += [({'ERROR'}, f'ALAMO - Missing material on object: {object.name}')] return error @@ -65,12 +65,12 @@ def checkUV(object): # throws error if object lacks UVs for material in object.data.materials: if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': if len(object.data.materials) > 1: - error += [f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials'] + error += [({'ERROR'}, f'ALAMO - Multiple materials on shadow volume: {object.name}; remove additional materials')] if object.HasCollision: if len(object.data.materials) > 1: - error += [f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials'] + error += [({'ERROR'}, f'ALAMO - Multiple submeshes/materials on collision mesh: {object.name}; remove additional materials')] if not object.data.uv_layers: # or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows - error += [f'ALAMO - Missing UV: {object.name}'] + error += [({'ERROR'}, f'ALAMO - Missing UV: {object.name}')] return error @@ -81,11 +81,11 @@ def checkInvalidArmatureModifier(object): for modifier in object.modifiers: if modifier.type == "ARMATURE": if modifier.object is None: - error += [f'ALAMO - Armature modifier without selected skeleton on: {object.name}'] + error += [({'ERROR'}, f'ALAMO - Armature modifier without selected skeleton on: {object.name}')] break elif modifier.object.type != 'NoneType': if modifier.object.name != activeSkeleton: - error += [f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}"] + error += [({'ERROR'}, f"ALAMO - Armature modifier skeleton doesn't match active skeleton on: {object.name}")] break for constraint in object.constraints: if ( @@ -93,7 +93,7 @@ def checkInvalidArmatureModifier(object): and constraint.target is not None and constraint.target.name != activeSkeleton ): - error += [f"ALAMO - Constraint doesn't match active skeleton on: {object.name}"] + error += [({'ERROR'}, f"ALAMO - Constraint doesn't match active skeleton on: {object.name}")] break return error @@ -101,22 +101,22 @@ def checkInvalidArmatureModifier(object): # checks if the number of faces exceeds max ushort, which is used to save the indices def checkFaceNumber(object): if len(object.data.polygons) > 65535: - return [f'ALAMO - {object.name} exceeds maximum face limit; split mesh into multiple objects'] + return [({'ERROR'}, f'ALAMO - {object.name} exceeds maximum face limit; split mesh into multiple objects')] return [] def checkAutosmooth(object): # prints a warning if Autosmooth is used if object.data.use_auto_smooth: - return [f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead'] + return [({'ERROR'}, f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead')] return [] def checkTranslation(object): # prints warning when translation is not default if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ'): - return [f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone'] + return [({'ERROR'}, f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone')] return [] def checkScale(object): # prints warning when scale is not default if object.scale != mathutils.Vector((1.0, 1.0, 1.0)): - return [f'ALAMO - {object.name} has non-identity scale. Apply scale.'] + return [({'ERROR'}, f'ALAMO - {object.name} has non-identity scale. Apply scale.')] return [] # checks if vertices have 0 or > 1 groups @@ -127,10 +127,10 @@ def checkVertexGroups(object): total = 0 for group in vertex.groups: if group.weight not in [0, 1]: - return [f'ALAMO - Object {object.name} has improper vertex groups'] + return [({'ERROR'}, f'ALAMO - Object {object.name} has improper vertex groups')] total += group.weight if total not in [0, 1]: - return [f'ALAMO - Object {object.name} has improper vertex groups'] + return [({'ERROR'}, f'ALAMO - Object {object.name} has improper vertex groups')] return [] @@ -145,27 +145,38 @@ def checkNumBones(object): used_groups.append(group.group) if len(set(used_groups)) > 23: - return [f'ALAMO - Object {object.name} has more than 23 bones.'] + return [({'ERROR'}, f'ALAMO - Object {object.name} has more than 23 bones.')] return [] def checkTranslationArmature(): # prints warning when translation is not default armature = utils.findArmature() - if armature is not None: - if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): - return [f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation'] + if armature is not None and ( + armature.location != mathutils.Vector((0.0, 0.0, 0.0)) + or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') + or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)) + ): + return [({'ERROR'}, f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation')] return [] def checkProxyKeyframes(): - actions = bpy.data.actions local_errors = [] + actions = bpy.data.actions + current_frame = bpy.context.scene.frame_current for action in actions: for fcurve in action.fcurves: - if fcurve.data_path.find("proxyIsHiddenAnimation") > -1 and len(fcurve.keyframe_points) > 2: - local_errors += [f'ALAMO - Action {action.name} has fcurves with more than 2 proxy keyframes.'] - break + if fcurve.data_path.find("proxyIsHiddenAnimation") > -1: + previous_keyframe = None + armature = utils.findArmature() + if armature is not None: + for keyframe in fcurve.keyframe_points: + bpy.context.scene.frame_set(int(keyframe.co[0])) + this_keyframe = armature.path_resolve(fcurve.data_path) + if this_keyframe == previous_keyframe: + local_errors += [({'WARNING'}, f'ALAMO - {fcurve.group.name} has duplicate keyframe on frame {bpy.context.scene.frame_current}')] + previous_keyframe = this_keyframe + bpy.context.scene.frame_set(current_frame) return local_errors - def validate(mesh_list): errors = [] checklist = [ From 537116384b50a4491aab4102d3f03cc701ece2fb Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 12 Sep 2021 12:36:48 -0700 Subject: [PATCH 052/100] Readme update, version change to 0.0.3.0 --- README.md | 28 +++++++++++++++++++--------- io_alamo_tools/__init__.py | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7b23166..41111b3 100644 --- a/README.md +++ b/README.md @@ -50,26 +50,36 @@ However it also means that an exported shadow mesh should not be able to cause a ## Sidebar The sidebar(default hotkey: 'N') offers an ALAMO properties option. -This lists the file format specific properties of the active object. -The avaible properties change depending on the mode and object type. +This lists the file format specific properties of the active object. -Scene propierties(always avaible): - - ActiveSkeleton: Files can only contain a single skeleton. Choose the skeleton that is used when exporting - - AnimationEndFrame: per action, length of the animation +Validate: + - Check for any problems that would cause an export to fail. -Object properties(Mesh in object-mode): +Object tools (Mesh in object-mode): - HasCollision: treated as collider ingame - Hidden: invisible ingame + +Armature Settings (always avaible): + - ActiveSkeleton: Files can only contain a single skeleton. Choose the skeleton that is used when exporting -Bone properties (Bone in edit-mode): +Bone Tools (Bone in edit-mode): + - billboardMode: Sets the billboard mode. Can only be set on individually-selected bones. - Visible: Visibility of attached object - EnableProxy: bone is a proxy to spawn effects ingame, enables additional options: - proxyIsHidden: flag that determines if the proxy is initially hidden - altDecreaseStayHidden: prevents proxies to become visible when the alt level decreases - - ProxyName: name of the effect to be attached to the proxy + - ProxyName: name of the effect to be attached to the proxy. Can only be set on individually-selected bones. Bone properties (Bone in pose-mode): - - proxyIsHiddenAnimation: animated visibility of the proxy, when hovering over it with mouse: press 'I' to set keyframe + - proxyIsHiddenAnimation: animated visibility of the proxy + - Action End Frames: per action, length of the animation + + Debug: + - Unmodified original UI + + ### Gotchas + - Any clicks on the sidebar, but not on an active control, will be treated as a click in the 3d view. This can cause you to lose your selection. + - On validation, errors only pop up briefly, and warnings don't pop up at all. Recommend opening an Info panel when validating. ## Alamo material properties diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 94b9a95..eb6d622 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 2, 7), + "version": (0, 0, 3, 0), "blender": (2, 93, 0), "category": "Import-Export" } From f1ab52be98703afda2500f2ef0b2723bb8adbb96 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 12 Sep 2021 15:10:35 -0700 Subject: [PATCH 053/100] Fix shadow mesh validation --- io_alamo_tools/validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 1589cd1..05db1b6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -42,14 +42,14 @@ def checkShadowMesh(object): for vertex in bm.verts: if not vertex.is_manifold: - bm.free() + # bm.free() selectNonManifoldVertices(object) error += [({'ERROR'}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}')] break for edge in bm.edges: if len(edge.link_faces) < 2: - bm.free() + # bm.free() selectNonManifoldVertices(object) error += [({'ERROR'}, f'ALAMO - Non manifold geometry shadow mesh: {object.name}')] break From ca08d9f531f38f7843d4e61d5b243a620b2e6d79 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 12 Sep 2021 15:10:53 -0700 Subject: [PATCH 054/100] Increment version --- io_alamo_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index eb6d622..ba9539a 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 0), + "version": (0, 0, 3, 1), "blender": (2, 93, 0), "category": "Import-Export" } From 581435388bef09a7d828f7fceae126bcf8d97d39 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Mon, 4 Oct 2021 15:49:31 -0700 Subject: [PATCH 055/100] Validation fixes --- io_alamo_tools/__init__.py | 2 +- io_alamo_tools/export_alo.py | 8 ++++---- io_alamo_tools/validation.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index ba9539a..3fbf961 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 1), + "version": (0, 0, 3, 2), "blender": (2, 93, 0), "category": "Import-Export" } diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 0d811fb..131f209 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1403,11 +1403,11 @@ def exportAnimations(filePath): mesh_list = validation.create_export_list(bpy.context.scene.collection, self.exportHiddenObjects, self.useNamesFrom) #check if export objects satisfy requirements (has material, UVs, ...) - errors = validation.validate(mesh_list) + messages = validation.validate(mesh_list) - if errors is not None and len(errors) > 0: - for error in errors: - self.report({"ERROR"}, error) + if messages is not None and len(messages) > 0: + for message in messages: + self.report(*message) exportFailed() hiddenList = unhide() diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 05db1b6..a495ce6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -136,7 +136,7 @@ def checkVertexGroups(object): def checkNumBones(object): if type(object) != type(None) and object.type == 'MESH': - material = bpy.context.active_object.active_material + material = object.active_material if material is not None and material.shaderList.shaderList.find("RSkin") > -1: used_groups = [] for vertex in object.data.vertices: From 6eadd9647e363cc9d591a024e655ec8b12bfc4af Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Fri, 15 Oct 2021 08:44:52 -0700 Subject: [PATCH 056/100] Minor UI fixes; disabled checkProxyKeyframes validation --- io_alamo_tools/__init__.py | 62 +++++++++++++++++++----------------- io_alamo_tools/validation.py | 18 +++++++---- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 3fbf961..c2681cf 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 2), + "version": (0, 0, 3, 3), "blender": (2, 93, 0), "category": "Import-Export" } @@ -117,7 +117,7 @@ def execute(self, context): #check if export objects satisfy requirements (has material, UVs, ...) messages = validation.validate(mesh_list) - + if messages is not None and len(messages) > 0: for message in messages: self.report(*message) @@ -159,17 +159,19 @@ class CreateConstraintBone(bpy.types.Operator): @classmethod def poll(cls, context): object = bpy.context.object - if type(object) != type(None): - if(object.type == 'MESH'): - if bpy.context.mode == 'OBJECT': - armature = utils.findArmature() - if armature != None: - hasChildConstraint = False - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - hasChildConstraint = True - if not hasChildConstraint: - return True + if ( + type(object) != type(None) + and object.type == 'MESH' + and bpy.context.mode == 'OBJECT' + ): + armature = utils.findArmature() + if armature is not None: + hasChildConstraint = False + for constraint in object.constraints: + if constraint.type == 'CHILD_OF': + hasChildConstraint = True + if not hasChildConstraint: + return True return False def execute(self, context): @@ -195,7 +197,7 @@ def execute(self, context): class SetHasCollisionTrue(bpy.types.Operator): bl_idname = "alamo.collision_true" - bl_label = "Show" + bl_label = "" bl_description = "Set HasCollision to True for all selected objects" @classmethod @@ -211,7 +213,7 @@ def execute(self, context): class SetHasCollisionFalse(bpy.types.Operator): bl_idname = "alamo.collision_false" - bl_label = "Hide" + bl_label = "" bl_description = "Set HasCollision to False for all selected objects" @classmethod @@ -227,7 +229,7 @@ def execute(self, context): class SetHiddenTrue(bpy.types.Operator): bl_idname = "alamo.hidden_true" - bl_label = "Show" + bl_label = "" bl_description = "Set Hidden to True for all selected objects" @classmethod @@ -243,7 +245,7 @@ def execute(self, context): class SetHiddenFalse(bpy.types.Operator): bl_idname = "alamo.hidden_false" - bl_label = "Hide" + bl_label = "" bl_description = "Set Hidden to False for all selected objects" @classmethod @@ -259,7 +261,7 @@ def execute(self, context): class SetBoneVisible(bpy.types.Operator): bl_idname = "alamo.bone_visible" - bl_label = "Show" + bl_label = "" bl_description = "Set Visible to True for all selected bones" @classmethod @@ -275,7 +277,7 @@ def execute(self, context): class SetBoneInvisible(bpy.types.Operator): bl_idname = "alamo.bone_invisible" - bl_label = "Hide" + bl_label = "" bl_description = "Set Visible to False for all selected bones" @classmethod @@ -291,7 +293,7 @@ def execute(self, context): class SetAltDecreaseStayHiddenTrue(bpy.types.Operator): bl_idname = "alamo.alt_decrease_stay_hidden_true" - bl_label = "Show" + bl_label = "" bl_description = "Set altDecreaseStayHidden to True for all selected bones" @classmethod @@ -307,7 +309,7 @@ def execute(self, context): class SetAltDecreaseStayHiddenFalse(bpy.types.Operator): bl_idname = "alamo.alt_decrease_stay_hidden_false" - bl_label = "Hide" + bl_label = "" bl_description = "Set altDecreaseStayHidden to False for all selected bones" @classmethod @@ -323,7 +325,7 @@ def execute(self, context): class EnableProxyFalse(bpy.types.Operator): bl_idname = "alamo.disable_proxy" - bl_label = "Show" + bl_label = "" bl_description = "Set EnableProxy to False for all selected bones" @classmethod @@ -339,7 +341,7 @@ def execute(self, context): class EnableProxyTrue(bpy.types.Operator): bl_idname = "alamo.enable_proxy" - bl_label = "Hide" + bl_label = "" bl_description = "Set EnableProxy to True for all selected bones" @classmethod @@ -355,7 +357,7 @@ def execute(self, context): class ProxyShow(bpy.types.Operator): bl_idname = "alamo.show_proxy" - bl_label = "Show" + bl_label = "" bl_description = "Set proxyIsHidden to False for all selected bones" @classmethod @@ -371,7 +373,7 @@ def execute(self, context): class ProxyHide(bpy.types.Operator): bl_idname = "alamo.hide_proxy" - bl_label = "Hide" + bl_label = "" bl_description = "Set proxyIsHidden to True for all selected bones" @classmethod @@ -414,7 +416,7 @@ def keyframeProxySet(operation): class keyframeProxyShow(bpy.types.Operator): bl_idname = "alamo.show_keyframe_proxy" - bl_label = "Show" + bl_label = "" bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" @classmethod @@ -428,7 +430,7 @@ def execute(self, context): class keyframeProxyHide(bpy.types.Operator): bl_idname = "alamo.hide_keyframe_proxy" - bl_label = "Hide" + bl_label = "" bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" @classmethod @@ -442,7 +444,7 @@ def execute(self, context): class keyframeProxyRemove(bpy.types.Operator): bl_idname = "alamo.remove_keyframe_proxy" - bl_label = "Remove" + bl_label = "" bl_description = "Remove active keyframes from all selected bones" @classmethod @@ -467,7 +469,7 @@ def skeletonEnumCallback(scene, context): class skeletonEnumClass(PropertyGroup): skeletonEnum : EnumProperty( - name='Active Skeleton', + name = 'Active Skeleton', description = "skeleton that is exported", items = skeletonEnumCallback ) @@ -590,7 +592,7 @@ def draw(self, context): layout.active = False if bpy.context.mode == "EDIT_ARMATURE": layout.active = all_same - + if not all_same: layout.label(icon="ERROR", text="Inconsistent EnableProxy states.") layout.label(icon="BLANK1", text="Change selection or set EnableProxy.") diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index a495ce6..365acb6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -162,15 +162,19 @@ def checkProxyKeyframes(): local_errors = [] actions = bpy.data.actions current_frame = bpy.context.scene.frame_current - for action in actions: - for fcurve in action.fcurves: - if fcurve.data_path.find("proxyIsHiddenAnimation") > -1: - previous_keyframe = None - armature = utils.findArmature() - if armature is not None: + armature = findArmature() + if armature is not None: + for action in actions: + print(action.name) + for fcurve in action.fcurves: + print(fcurve.group.name) + if fcurve.data_path.find("proxyIsHiddenAnimation") > -1: + previous_keyframe = None for keyframe in fcurve.keyframe_points: bpy.context.scene.frame_set(int(keyframe.co[0])) + # TODO Keyframes don't store what action they're from. Maybe check each keyframe against the current action? this_keyframe = armature.path_resolve(fcurve.data_path) + print(previous_keyframe, this_keyframe) if this_keyframe == previous_keyframe: local_errors += [({'WARNING'}, f'ALAMO - {fcurve.group.name} has duplicate keyframe on frame {bpy.context.scene.frame_current}')] previous_keyframe = this_keyframe @@ -192,7 +196,7 @@ def validate(mesh_list): ] checklist_no_object = [ checkTranslationArmature, - checkProxyKeyframes, + # checkProxyKeyframes, # Disabled until it can be fixed ] for check in checklist: From 4c1ec0f80a7432c2b91f7cf836fc8b14e4b6b049 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Tue, 9 Nov 2021 12:58:47 -0800 Subject: [PATCH 057/100] Copy ProxyName to selected bones --- io_alamo_tools/__init__.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index c2681cf..8caa626 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 3), + "version": (0, 0, 3, 4), "blender": (2, 93, 0), "category": "Import-Export" } @@ -387,6 +387,23 @@ def execute(self, context): return {'FINISHED'} +class CopyProxyNameToSelected(bpy.types.Operator): + bl_idname = "alamo.copy_proxy_name" + bl_label = "" + bl_description = "Copy Proxy Name to Selected Bones" + + @classmethod + def poll(cls, context): + return bpy.context.selected_bones is not None and len(bpy.context.selected_bones) > 1 + + def execute(self, context): + bones = bpy.context.selected_bones + name = bones[0].ProxyName + for bone in bones: + bone.ProxyName = name + return {'FINISHED'} + + def keyframeProxySet(operation): bones = bpy.context.selected_pose_bones action = bpy.context.object.animation_data.action @@ -601,14 +618,13 @@ def draw(self, context): rowbuilder(layout, "Set altDecreaseStayHidden:", ["alamo.alt_decrease_stay_hidden_true", "alamo.alt_decrease_stay_hidden_false"], ["CHECKMARK", "X"]) - row = layout.row() + row = layout.row(align=True) row.enabled = False - if bones is not None: - if len(bones) == 1: - row.prop(bones[0], "ProxyName") - row.enabled = True - else: - row.label(text="ProxyName") + if bones is not None and len(bones) > 0: + row.prop(bones[0], "ProxyName") + row.operator('alamo.copy_proxy_name', text="", + icon="DUPLICATE") + row.enabled = True else: row.label(text="ProxyName") @@ -833,6 +849,7 @@ def menu_func_export(self, context): EnableProxyTrue, ProxyShow, ProxyHide, + CopyProxyNameToSelected, keyframeProxyShow, keyframeProxyHide, keyframeProxyRemove, From 20e1d8fdc2482de250f0c50a1b3412791e691a57 Mon Sep 17 00:00:00 2001 From: Andrew Fullard Date: Fri, 26 Nov 2021 16:23:03 -0500 Subject: [PATCH 058/100] Adjustments for easier scripting of import/export --- io_alamo_tools/export_ala.py | 2 +- io_alamo_tools/export_alo.py | 2 +- io_alamo_tools/import_ala.py | 2 +- io_alamo_tools/import_alo.py | 2 +- io_alamo_tools/utils.py | 3 +++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index 60ebed1..d837a1a 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -454,7 +454,7 @@ def exportAnimation(self, path): class ALA_Exporter(bpy.types.Operator): """ALA Exporter""" # blender will use this as a tooltip for menu items and buttons. - bl_idname = "export.ala" # unique identifier for buttons and menu items to reference. + bl_idname = "export_anim.ala" # unique identifier for buttons and menu items to reference. bl_label = "Export ALA File" # display name in the interface. bl_options = {'REGISTER', 'UNDO'} # enable undo for the operator. bl_info = { diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 131f209..0d163ad 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -50,7 +50,7 @@ def disable_exception_traceback(): class ALO_Exporter(bpy.types.Operator, ExportHelper): """ALO Exporter""" # blender will use this as a tooltip for menu items and buttons. - bl_idname = "export.alo" # unique identifier for buttons and menu items to reference. + bl_idname = "export_mesh.alo" # unique identifier for buttons and menu items to reference. bl_label = "Export ALO File" # display name in the interface. bl_options = {'REGISTER', 'UNDO'} # enable undo for the operator. bl_info = { diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index b055763..3f14c3e 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -385,7 +385,7 @@ def loadAnimation(self, filePath): class ALA_Importer(bpy.types.Operator): """ALA Importer""" # blender will use this as a tooltip for menu items and buttons. - bl_idname = "import.ala" # unique identifier for buttons and menu items to reference. + bl_idname = "import_anim.ala" # unique identifier for buttons and menu items to reference. bl_label = "Import ALA File" # display name in the interface. bl_options = {'REGISTER', 'UNDO'} # enable undo for the operator. filename_ext = ".ala" diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index aaab1d2..4605035 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -39,7 +39,7 @@ def boneEnumCallback(scene, context): class ALO_Importer(bpy.types.Operator): """ALO Importer""" # blender will use this as a tooltip for menu items and buttons. - bl_idname = "import.alo" # unique identifier for buttons and menu items to reference. + bl_idname = "import_mesh.alo" # unique identifier for buttons and menu items to reference. bl_label = "Import ALO File" # display name in the interface. bl_options = {'REGISTER', 'UNDO'} # enable undo for the operator. filename_ext = ".alo" diff --git a/io_alamo_tools/utils.py b/io_alamo_tools/utils.py index 0789691..49ec69a 100644 --- a/io_alamo_tools/utils.py +++ b/io_alamo_tools/utils.py @@ -7,6 +7,9 @@ def findArmature(): armatureName = bpy.context.scene.ActiveSkeleton.skeletonEnum if armatureName == 'None': return None + if armatureName == '': + print("Warning! Armature name is empty. Is this intended?") + return None armature = bpy.data.objects[armatureName] return armature From fb3ba6aa1c41d705654b5747d0a20770e389f1c7 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Fri, 8 Jul 2022 17:34:13 -0700 Subject: [PATCH 059/100] UI refresh WIP --- io_alamo_tools/__init__.py | 210 ++++++++++------------------------- io_alamo_tools/changelog.md | 10 ++ io_alamo_tools/export_ala.py | 26 ++--- io_alamo_tools/export_alo.py | 32 +++--- io_alamo_tools/import_ala.py | 6 +- io_alamo_tools/ui_wip.py | 100 +++++++++++++++++ io_alamo_tools/validation.py | 10 +- 7 files changed, 204 insertions(+), 190 deletions(-) create mode 100644 io_alamo_tools/changelog.md create mode 100644 io_alamo_tools/ui_wip.py diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 8caa626..799a707 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,3 +1,4 @@ + from . import_alo import ALO_Importer from . export_ala import ALA_Exporter from . export_alo import ALO_Exporter @@ -17,13 +18,7 @@ from bpy.props import * import mathutils import bpy -bl_info = { - "name": "ALAMO Tools", - "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 4), - "blender": (2, 93, 0), - "category": "Import-Export" -} +import os if "bpy" in locals(): import importlib @@ -41,6 +36,14 @@ from . import settings from . import utils +bl_info = { + "name": "ALAMO Tools", + "author": "Gaukler, evilbobthebob, inertial", + "version": (0, 0, 3, 4), + "blender": (2, 93, 0), + "category": "Import-Export" +} + def CheckObjectType(objects, type): for object in objects: if object.type != type: @@ -126,7 +129,7 @@ def execute(self, context): return {'FINISHED'} -# Legacy version, provided for the debug panel +# Legacy version, included for the debug panel class createConstraintBoneButton(bpy.types.Operator): bl_idname = "create.constraint_bone" bl_label = "Create constraint bone" @@ -139,7 +142,7 @@ def execute(self, context): utils.setModeToEdit() bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0, 0, 1)) + bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') @@ -182,7 +185,7 @@ def execute(self, context): utils.setModeToEdit() bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0, 0, 1)) + bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') @@ -195,70 +198,6 @@ def execute(self, context): return {'FINISHED'} -class SetHasCollisionTrue(bpy.types.Operator): - bl_idname = "alamo.collision_true" - bl_label = "" - bl_description = "Set HasCollision to True for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "HasCollision", True) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.HasCollision = True - return {'FINISHED'} - - -class SetHasCollisionFalse(bpy.types.Operator): - bl_idname = "alamo.collision_false" - bl_label = "" - bl_description = "Set HasCollision to False for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "HasCollision", False) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.HasCollision = False - return {'FINISHED'} - - -class SetHiddenTrue(bpy.types.Operator): - bl_idname = "alamo.hidden_true" - bl_label = "" - bl_description = "Set Hidden to True for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "Hidden", True) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.Hidden = True - return {'FINISHED'} - - -class SetHiddenFalse(bpy.types.Operator): - bl_idname = "alamo.hidden_false" - bl_label = "" - bl_description = "Set Hidden to False for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "Hidden", False) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.Hidden = False - return {'FINISHED'} - - class SetBoneVisible(bpy.types.Operator): bl_idname = "alamo.bone_visible" bl_label = "" @@ -499,10 +438,10 @@ def rowbuilder(layout, label, operators, icons): row.operator(operators[1], text="", icon=icons[1]) -class ALAMO_PT_ToolsPanel(bpy.types.Panel): +class ALAMO_PT_ValidationPanel(bpy.types.Panel): - bl_label = "Alamo Properties" - bl_idname = "ALAMO_PT_ToolsPanel" + bl_label = "Validation" + bl_idname = "ALAMO_PT_ValidationPanel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -513,31 +452,9 @@ def draw(self, context): row.scale_y = 3.0 -class ALAMO_PT_ObjectPanel(bpy.types.Panel): - - bl_label = "Object Tools" - bl_parent_id = 'ALAMO_PT_ToolsPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - object = context.object - layout = self.layout - scene = context.scene - layout.active = False - if bpy.context.mode == "OBJECT": - layout.active = True - - rowbuilder(layout, "Set HasCollision:", ["alamo.collision_true", "alamo.collision_false"], ["CHECKMARK", "X"]) - - rowbuilder(layout, "Set Hidden:", ["alamo.hidden_false", "alamo.hidden_true"], ["HIDE_OFF", "HIDE_ON"]) - - class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): - bl_label = "Armature Settings" - bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_label = "Armature" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -555,8 +472,7 @@ def draw(self, context): class ALAMO_PT_EditBonePanel(bpy.types.Panel): - bl_label = "Edit Bone Tools" - bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_label = "Bone" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -570,17 +486,18 @@ def draw(self, context): layout.active = True rowbuilder(layout, "Set Bone Visibility:", ["alamo.bone_visible", "alamo.bone_invisible"], ["HIDE_OFF", "HIDE_ON"]) + rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) row = col.row() row.enabled = False if bones is not None: if len(bones) == 1: - row.prop(bones[0].billboardMode, "billboardMode") + row.prop(bones[0].billboardMode, "billboardMode", text="Billboard") row.enabled = True else: - row.label(text="billboardMode") + row.label(text="Billboard") else: - row.label(text="billboardMode") + row.label(text="Billboard") class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): @@ -590,15 +507,7 @@ class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" - bl_options = {"HEADER_LAYOUT_EXPAND"} - - def draw_header(self, context): - layout = self.layout - layout.active = False - if bpy.context.mode == "EDIT_ARMATURE": - layout.active = True - - rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) + bl_options = {"HIDE_HEADER"} def draw(self, context): bones = bpy.context.selected_bones @@ -632,7 +541,6 @@ def draw(self, context): class ALAMO_PT_AnimationPanel(bpy.types.Panel): bl_label = "Animation" - bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -660,9 +568,13 @@ class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" + bl_options = {"HIDE_HEADER"} def draw(self, context): layout = self.layout + if bpy.data.actions is not None and bpy.data.actions > 1: + row = layout.row() + row.label(text="Action End Frames") for action in bpy.data.actions: layout.prop(action, "AnimationEndFrame", text=action.name) @@ -670,7 +582,6 @@ def draw(self, context): class ALAMO_PT_DebugPanel(bpy.types.Panel): bl_label = "Debug" - bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -837,10 +748,6 @@ def menu_func_export(self, context): ValidateFileButton, CreateConstraintBone, createConstraintBoneButton, - SetHasCollisionTrue, - SetHasCollisionFalse, - SetHiddenTrue, - SetHiddenFalse, SetBoneVisible, SetBoneInvisible, SetAltDecreaseStayHiddenTrue, @@ -853,8 +760,7 @@ def menu_func_export(self, context): keyframeProxyShow, keyframeProxyHide, keyframeProxyRemove, - ALAMO_PT_ToolsPanel, - ALAMO_PT_ObjectPanel, + ALAMO_PT_ValidationPanel, ALAMO_PT_ArmatureSettingsPanel, ALAMO_PT_EditBonePanel, ALAMO_PT_EditBoneSubPanel, @@ -863,9 +769,7 @@ def menu_func_export(self, context): ALAMO_PT_DebugPanel ) - def register(): - from bpy.utils import register_class for cls in classes: register_class(cls) @@ -906,70 +810,70 @@ def register(): bpy.types.Material.shaderList = bpy.props.PointerProperty( type=shaderListProperties) bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 0, 0, 0)) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) bpy.types.Material.Specular = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) bpy.types.Material.Shininess = bpy.props.FloatProperty( - min=0, max=255, default=32) + min=0.0, max=255.0, default=32.0) bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 1, 0, 0)) + min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0)) bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 0, 0, 0)) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) bpy.types.Material.Color = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 1)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 0, 0, 0)) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( - min=0, max=1, size=3, default=(0.5, 0.5, 0.5)) + min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5)) # shield shader properties bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( - min=0, max=255, default=0.5) + min=0.0, max=255.0, default=0.5) bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) + min=-255.0, max=255.0, default=1.0) bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) + min=-255.0, max=255.0, default=1.0) bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) + min=-255.0, max=255.0, default=1.0) bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=-0.15) + min=-255.0, max=255.0, default=-0.15) bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=-0.15) + min=-255.0, max=255.0, default=-0.15) bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=-0.25) + min=-255.0, max=255.0, default=-0.25) # tree properties bpy.types.Material.BendScale = bpy.props.FloatProperty( - min=-255, max=255, default=0.4) + min=-255.0, max=255.0, default=0.4) # grass properties bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 1)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) # skydome.fx properties bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=0.001) + min=-255.0, max=255.0, default=0.001) bpy.types.Material.CloudScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) + min=-255.0, max=255.0, default=1.0) # nebula.fx properties bpy.types.Material.SFreq = bpy.props.FloatProperty( - min=-255, max=255, default=0.002) + min=-255.0, max=255.0, default=0.002) bpy.types.Material.TFreq = bpy.props.FloatProperty( - min=-255, max=255, default=0.005) + min=-255.0, max=255.0, default=0.005) bpy.types.Material.DistortionScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) + min=-255.0, max=255.0, default=1.0) # planet.fx properties bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( - min=-255, max=255, default=1) + min=-255.0, max=255.0, default=1.0) # tryplanar mapping properties bpy.types.Material.MappingScale = bpy.props.FloatProperty( - min=0, max=255, default=0.1) + min=0.0, max=255.0, default=0.1) bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( - min=0, max=255, default=0.1) + min=0.0, max=255.0, default=0.1) def unregister(): diff --git a/io_alamo_tools/changelog.md b/io_alamo_tools/changelog.md new file mode 100644 index 0000000..2274d16 --- /dev/null +++ b/io_alamo_tools/changelog.md @@ -0,0 +1,10 @@ +### UI Refresh +- Moved many UI elements to a three-state "checkbox", which will hopefully improve clarity +- Simplified some labels +- Removed an unnecessary level of layout nesting + +### Other + +- Proxy keyframe validation fixed and re-enabled +- Validator now correctly checks for maximum *triangles*, rather than faces +- Adjustments for easier scripting of import/export \ No newline at end of file diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index d837a1a..134c3cf 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -47,9 +47,9 @@ def calculateTranslationScale(bone, translationOffset, armature, translationList scene.frame_set(0) pose = armature.pose.bones[bone.name] # get the pose bone # search for maximum translation - maxX = 0 - maxY = 0 - maxZ = 0 + maxX = 0.0 + maxY = 0.0 + maxZ = 0.0 action = utils.getCurrentAction() animLength = action.AnimationEndFrame @@ -69,12 +69,12 @@ def calculateTranslationScale(bone, translationOffset, armature, translationList scene.frame_set(scene.frame_current + 1) scene.frame_set(0) - if (maxX == 0): - maxX = 1 - if (maxY == 0): - maxY = 1 - if (maxZ == 0): - maxZ = 1 + if (maxX == 0.0): + maxX = 1.0 + if (maxY == 0.0): + maxY = 1.0 + if (maxZ == 0.0): + maxZ = 1.0 scaleX = maxX / 65535.0 scaleY = maxY / 65535.0 @@ -87,7 +87,7 @@ def calculateTranslationScale(bone, translationOffset, armature, translationList return mathutils.Vector(translationScale) else: - return mathutils.Vector((0, 0, 0)) + return mathutils.Vector((0.0, 0.0, 0.0)) def calculateTranslationOffset(bone, armature, translationList): scene = bpy.context.scene # current scene @@ -100,9 +100,9 @@ def calculateTranslationOffset(bone, armature, translationList): if (bone.name in translationList): scene.frame_set(0) - minX = 65535 - minY = 65535 - minZ = 65535 + minX = 65535.0 + minY = 65535.0 + minZ = 65535.0 action = utils.getCurrentAction() animLength = action.AnimationEndFrame diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 0d163ad..c995d9d 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -341,11 +341,11 @@ def check_if_material_is_used(material, mesh): class vertexData(): def __init__(self): - self.co = mathutils.Vector((0, 0, 0)) - self.uv = mathutils.Vector((0, 0)) - self.normal = mathutils.Vector((0, 0, 0)) - self.tangent = mathutils.Vector((0, 0, 0)) - self.bitangent = mathutils.Vector((0, 0, 0)) + self.co = mathutils.Vector((0.0, 0.0, 0.0)) + self.uv = mathutils.Vector((0.0, 0.0)) + self.normal = mathutils.Vector((0.0, 0.0, 0.0)) + self.tangent = mathutils.Vector((0.0, 0.0, 0.0)) + self.bitangent = mathutils.Vector((0.0, 0.0, 0.0)) self.bone_index = 0 self.face_index = 0 @@ -565,7 +565,7 @@ def shadow_vertex_face_data(bm, mesh, object): vertex.co = vert.co vertex.normal = face.normal - vertex.uv = mathutils.Vector((0, 0)) + vertex.uv = mathutils.Vector((0.0, 0.0)) meshVertex = mesh.vertices[vert.index] vertex.bone_index = getMaxWeightGroupIndex(meshVertex) @@ -591,15 +591,15 @@ def shadow_vertex_face_data(bm, mesh, object): f2v1 = per_face_vertex_id[face2.index][edge.verts[0].index] f2v2 = per_face_vertex_id[face2.index][edge.verts[1].index] - mid1 = mathutils.Vector((0, 0, 0)) + mid1 = mathutils.Vector((0.0, 0.0, 0.0)) for vert in face1.verts: mid1 += vert.co - mid1 /= 3 + mid1 /= 3.0 - mid2 = mathutils.Vector((0, 0, 0)) + mid2 = mathutils.Vector((0.0, 0.0, 0.0)) for vert in face2.verts: mid2 += vert.co - mid2 /= 3 + mid2 /= 3.0 face1v1 = edge.verts[0].co * 0.75 + mid1 * 0.25 @@ -911,8 +911,8 @@ def calc_bb_face(face): def calc_bb_two_bb(bb1, bb2): #takes two bbs and calculates combined bb - bb_min = mathutils.Vector((0, 0, 0)) - bb_max = mathutils.Vector((0, 0, 0)) + bb_min = mathutils.Vector((0.0, 0.0, 0.0)) + bb_max = mathutils.Vector((0.0, 0.0, 0.0)) for i in range (0, 3): bb_min[i] = min(bb1[0][i], bb2[0][i]) @@ -943,8 +943,8 @@ def calc_volume_bb(bb_min, bb_max): def calc_parent_space(parent, child): - child.parent_space_min = mathutils.Vector((0, 0, 0)) - child.parent_space_max = mathutils.Vector((0, 0, 0)) + child.parent_space_min = mathutils.Vector((0.0, 0.0, 0.0)) + child.parent_space_max = mathutils.Vector((0.0, 0.0, 0.0)) for i in range(0, 3): distance = parent.bb_max[i] - parent.bb_min[i] if distance != 0: @@ -1149,8 +1149,8 @@ def print_elements(self): bb = calc_bb_list(list_start) root = treeNode(bb[0], bb[1]) - root.parent_space_min = mathutils.Vector((0, 0, 0)) - root.parent_space_max = mathutils.Vector((255, 255, 255)) + root.parent_space_min = mathutils.Vector((0.0, 0.0, 0.0)) + root.parent_space_max = mathutils.Vector((255.0, 255.0, 255.0)) #print('median cut starts') median_cut(root, list_start) #print('parent space calculation starts') diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index 3f14c3e..2e7fa2f 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -94,7 +94,7 @@ def read_translation_data(data): counter_limit = data.num_frames * data.translation_block_size * 3 counter = 0 while (counter < counter_limit): - vector = mathutils.Vector((0, 0, 0)) + vector = mathutils.Vector((0.0, 0.0, 0.0)) vector[0] = utils.read_u_short(file.read(2)) vector[1] = utils.read_u_short(file.read(2)) vector[2] = utils.read_u_short(file.read(2)) @@ -194,7 +194,7 @@ def read_bone_animation_info(data): continue elif active_child_chunk == b"\06": file.seek(1, 1) # skip mini chunk size - vector = mathutils.Vector((0, 0, 0)) + vector = mathutils.Vector((0.0, 0.0, 0.0)) vector[0] = utils.read_float(file.read(4)) vector[1] = utils.read_float(file.read(4)) vector[2] = utils.read_float(file.read(4)) @@ -202,7 +202,7 @@ def read_bone_animation_info(data): continue elif active_child_chunk == b"\07": file.seek(1, 1) # skip mini chunk size - vector = mathutils.Vector((0, 0, 0)) + vector = mathutils.Vector((0.0, 0.0, 0.0)) vector[0] = utils.read_float(file.read(4)) vector[1] = utils.read_float(file.read(4)) vector[2] = utils.read_float(file.read(4)) diff --git a/io_alamo_tools/ui_wip.py b/io_alamo_tools/ui_wip.py new file mode 100644 index 0000000..01b9971 --- /dev/null +++ b/io_alamo_tools/ui_wip.py @@ -0,0 +1,100 @@ +import bpy + +def CheckObjectType(objects, type): + for object in objects: + if object.type != type: + return False + return True + +def CheckPropAllSame(objects, prop): + # True: All same, have value of True + # False: All same, have value of False + # None: Not all same + first_value = None + for object in objects: + if first_value is None: + first_value = getattr(object, prop) + elif getattr(object, prop) != first_value: + return None + return first_value + +def ShouldEnable(objects): + if objects is None or len(objects) <= 0: + return False + if bpy.context.mode == "OBJECT": + objects_same = CheckObjectType(objects, "MESH") + if not objects_same: + return False + return True + +def threebox(layout, all_same, operator, label): + icon="ERROR" + if all_same is None: + icon="LAYER_ACTIVE" + if all_same == True: + icon="CHECKMARK" + if all_same == False: + icon="BLANK1" + + row=layout.row() + row.operator(operator, text="", icon=icon) + row.label(text=label) + +def setProp(all_same, objects, prop): + set_to = False + if all_same in (None, False) : + set_to = True + + for object in objects: + setattr(object, prop, set_to) + +def propop_builder(prop, object_type): + + class PropOp(bpy.types.Operator): + bl_idname = "alamo.set_" + prop.lower() + bl_label = "Set " + prop + " for all selected objects" + bl_description = "" + + @classmethod + def poll(cls, context): + return ShouldEnable(eval("bpy.context.selected_" + object_type)) + + def execute(self, context): + setProp(CheckPropAllSame(eval("bpy.context.selected_" + object_type), prop), eval("bpy.context.selected_" + object_type), prop) + return {'FINISHED'} + + return PropOp + +SetCollision = propop_builder("HasCollision", "objects") +SetHidden = propop_builder("Hidden", "objects") + +class ALAMO_PT_ObjectPanel(bpy.types.Panel): + bl_label = "Object" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + layout.active = False + if bpy.context.mode == "OBJECT": + layout.active = True + + threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "HasCollision"), "alamo.set_hascollision", "Collision") + threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "Hidden"), "alamo.set_hidden", "Hidden") + + +def register(): + bpy.utils.register_class(SetCollision) + bpy.utils.register_class(SetHidden) + bpy.utils.register_class(ALAMO_PT_ObjectPanel) + + +def unregister(): + bpy.utils.unregister_class(SetCollision) + bpy.utils.unregister_class(SetHidden) + bpy.utils.unregister_class(ALAMO_PT_ObjectPanel) + + +if __name__ == "__main__": + register() diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 365acb6..95858c6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -98,9 +98,9 @@ def checkInvalidArmatureModifier(object): return error -# checks if the number of faces exceeds max ushort, which is used to save the indices -def checkFaceNumber(object): - if len(object.data.polygons) > 65535: +# checks if the number of triangles exceeds max ushort, which is used to save the indices +def checkFaceNumber(object): + if sum(len(polygon.vertices) - 2 for polygon in object.data.polygons) > 65535: return [({'ERROR'}, f'ALAMO - {object.name} exceeds maximum face limit; split mesh into multiple objects')] return [] @@ -162,7 +162,7 @@ def checkProxyKeyframes(): local_errors = [] actions = bpy.data.actions current_frame = bpy.context.scene.frame_current - armature = findArmature() + armature = utils.findArmature() if armature is not None: for action in actions: print(action.name) @@ -196,7 +196,7 @@ def validate(mesh_list): ] checklist_no_object = [ checkTranslationArmature, - # checkProxyKeyframes, # Disabled until it can be fixed + checkProxyKeyframes, ] for check in checklist: From 005082902df722fd234c9a42c5bd54aa35c3cbef Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:15:20 -0700 Subject: [PATCH 060/100] Finishing UI refresh --- io_alamo_tools/__init__.py | 903 ++++++++++++++++++----------------- io_alamo_tools/changelog.md | 2 +- io_alamo_tools/ui_wip.py | 100 ---- io_alamo_tools/validation.py | 2 +- 4 files changed, 458 insertions(+), 549 deletions(-) delete mode 100644 io_alamo_tools/ui_wip.py diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 799a707..190425e 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,20 +1,21 @@ - -from . import_alo import ALO_Importer -from . export_ala import ALA_Exporter -from . export_alo import ALO_Exporter -from . import_ala import ALA_Importer +from .import_alo import ALO_Importer +from .export_ala import ALA_Exporter +from .export_alo import ALO_Exporter +from .import_ala import ALA_Importer from . import validation -from bpy.types import (Panel, - Operator, - PropertyGroup, - ) -from bpy.props import (StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - PointerProperty, - ) +from bpy.types import ( + Panel, + Operator, + PropertyGroup, +) +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, +) from bpy.props import * import mathutils import bpy @@ -22,6 +23,7 @@ if "bpy" in locals(): import importlib + importlib.reload(import_alo) importlib.reload(import_ala) importlib.reload(export_alo) @@ -41,9 +43,14 @@ "author": "Gaukler, evilbobthebob, inertial", "version": (0, 0, 3, 4), "blender": (2, 93, 0), - "category": "Import-Export" + "category": "Import-Export", } + +classes = () + + +# UI Utilities #################################################################################### def CheckObjectType(objects, type): for object in objects: if object.type != type: @@ -51,10 +58,22 @@ def CheckObjectType(objects, type): return True +def ShouldEnable(objects): + if objects is None or len(objects) <= 0: + return False + if bpy.context.mode == "OBJECT": + objects_same = CheckObjectType(objects, "MESH") + if not objects_same: + return False + return True + + def CheckPropAllSame(objects, prop): # True: All same, have value of True # False: All same, have value of False # None: Not all same + if objects is None or len(objects) <= 0: + return None first_value = None for object in objects: if first_value is None: @@ -64,51 +83,171 @@ def CheckPropAllSame(objects, prop): return first_value -def ShouldEnable(objects, prop, set_to): - if objects is None or len(objects) <= 0: - return False - if bpy.context.mode == "OBJECT": - objects_same = CheckObjectType(objects, "MESH") - if not objects_same: +def check_anim_prop_all_same(bones, prop): + """Like check_prop_all_same(), but for animation""" + + all_same = [] + + if bones is not None and len(bones) > 0: + all_same = list(set(getattr(bone, prop) for bone in bones)) + + if len(all_same) == 1: + return all_same[0] + + return None + + +def threebox(layout, all_same, operator, label): + icon = "ERROR" + if all_same is None: + icon = "LAYER_ACTIVE" + if all_same is True: + icon = "CHECKMARK" + if all_same is False: + icon = "BLANK1" + + row = layout.row() + row.operator(operator, text="", icon=icon) + row.label(text=label) + + +def setProp(all_same, objects, prop): + set_to = False + if all_same in (None, False): + set_to = True + + for object in objects: + setattr(object, prop, set_to) + + +def proxy_name_update(self, context): + if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion + self.ProxyName = self.ProxyName.upper() + + +def skeletonEnumCallback(scene, context): + armatures = [("None", "None", "", "", 0)] + counter = 1 + for arm in bpy.data.objects: # test if armature exists + if arm.type == "ARMATURE": + armatures.append((arm.name, arm.name, "", "", counter)) + counter += 1 + + return armatures + + +# Operators ####################################################################################### +def propop_builder(prop, object_type): + """Property Operator Builder""" + + class PropOp(bpy.types.Operator): + bl_idname = "alamo.set_" + prop.lower() + bl_label = "Set " + prop + " for all selected objects" + bl_description = "" + + @classmethod + def poll(cls, context): + return ShouldEnable(eval("bpy.context.selected_" + object_type)) + + def execute(self, context): + setProp( + CheckPropAllSame(eval("bpy.context.selected_" + object_type), prop), + eval("bpy.context.selected_" + object_type), + prop, + ) + return {"FINISHED"} + + return PropOp + + +SetCollision = propop_builder("HasCollision", "objects") +SetHidden = propop_builder("Hidden", "objects") +SetBoneHidden = propop_builder("Visible", "bones") +SetAltDecreaseStayHidden = propop_builder("altDecreaseStayHidden", "bones") +SetProxy = propop_builder("EnableProxy", "bones") +SetProxyHidden = propop_builder("proxyIsHidden", "bones") + +classes = ( + *classes, + SetCollision, + SetHidden, + SetBoneHidden, + SetAltDecreaseStayHidden, + SetProxy, + SetProxyHidden, +) + + +class keyframeProxySet(bpy.types.Operator): + bl_idname = "alamo.set_keyframe_proxy" + bl_label = "" + bl_description = ( + "Create a keyframe and set proxyIsHiddenAnimation for all selected bones" + ) + + @classmethod + def poll(cls, context): + bones = bpy.context.selected_pose_bones + return bones is not None and len(bones) > 0 + + def execute(self, context): + bones = bpy.context.selected_pose_bones + + all_same = check_anim_prop_all_same(bones, "proxyIsHiddenAnimation") + operation = "SHOW" if all_same is True else "HIDE" + + for bone in list(bones): + if operation == "SHOW": + bone.proxyIsHiddenAnimation = False + if operation == "HIDE": + bone.proxyIsHiddenAnimation = True + + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + + for area in bpy.context.screen.areas: + if area.type == "DOPESHEET_EDITOR": + area.tag_redraw() + + return {"FINISHED"} + + +class keyframeProxyDelete(bpy.types.Operator): + bl_idname = "alamo.delete_keyframe_proxy" + bl_label = "" + bl_description = ( + "Create a keyframe and set proxyIsHiddenAnimation for all selected bones" + ) + + @classmethod + def poll(cls, context): + bones = bpy.context.selected_pose_bones + action = bpy.context.object.animation_data.action + frame = bpy.context.scene.frame_current + + if bones is None or len(bones) <= 0: return False - all_same = CheckPropAllSame(objects, prop) - if all_same is not None: - if set_to: - return not all_same - else: - return all_same - return True + for bone in list(bones): + keyframes = action.fcurves.find( + bone.path_from_id() + ".proxyIsHiddenAnimation" + ) + if keyframes is not None: + for keyframe in keyframes.keyframe_points: + if int(keyframe.co[0]) == frame: + return True -def ShouldEnableAnim(bones, prop, set_to): - if bones is None or len(bones) <= 0: return False - frame = bpy.context.scene.frame_current - action = bpy.context.object.animation_data.action - has_keyframes = False - has_previous_keyframe = False - for bone in bones: - keyframes = action.fcurves.find(bone.path_from_id() + "." + prop) - if keyframes is not None: - for keyframe in keyframes.keyframe_points: - if int(keyframe.co[0]) <= frame: - has_previous_keyframe = True - if int(keyframe.co[0]) == frame: - has_keyframes = True - break - - if set_to is None: - return has_keyframes - if not has_previous_keyframe: - return True - - all_same = CheckPropAllSame(bones, prop) - if all_same is not None: - if set_to: - return not all_same - return all_same - return True + def execute(self, context): + bones = list(bpy.context.selected_pose_bones) + for bone in bones: + bone.keyframe_delete(data_path="proxyIsHiddenAnimation") + + for area in bpy.context.screen.areas: + if area.type in ("DOPESHEET_EDITOR", "VIEW_3D"): + area.tag_redraw() + + return {"FINISHED"} class ValidateFileButton(bpy.types.Operator): @@ -116,17 +255,19 @@ class ValidateFileButton(bpy.types.Operator): bl_label = "Validate" def execute(self, context): - mesh_list = validation.create_export_list(bpy.context.scene.collection, True, "DATA") + mesh_list = validation.create_export_list( + bpy.context.scene.collection, True, "DATA" + ) - #check if export objects satisfy requirements (has material, UVs, ...) + # check if export objects satisfy requirements (has material, UVs, ...) messages = validation.validate(mesh_list) if messages is not None and len(messages) > 0: for message in messages: self.report(*message) else: - self.report({'INFO'}, 'ALAMO - Validation complete. No errors detected!') - return {'FINISHED'} + self.report({"INFO"}, "ALAMO - Validation complete. No errors detected!") + return {"FINISHED"} # Legacy version, included for the debug panel @@ -145,14 +286,14 @@ def execute(self, context): bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') - constraint = object.constraints.new('CHILD_OF') + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") + constraint = object.constraints.new("CHILD_OF") constraint.target = armature constraint.subtarget = bone.name utils.setModeToObject() bpy.context.view_layer.objects.active = object - return {'FINISHED'} + return {"FINISHED"} class CreateConstraintBone(bpy.types.Operator): @@ -164,14 +305,14 @@ def poll(cls, context): object = bpy.context.object if ( type(object) != type(None) - and object.type == 'MESH' - and bpy.context.mode == 'OBJECT' + and object.type == "MESH" + and bpy.context.mode == "OBJECT" ): armature = utils.findArmature() if armature is not None: hasChildConstraint = False for constraint in object.constraints: - if constraint.type == 'CHILD_OF': + if constraint.type == "CHILD_OF": hasChildConstraint = True if not hasChildConstraint: return True @@ -188,142 +329,14 @@ def execute(self, context): bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') - constraint = object.constraints.new('CHILD_OF') + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") + constraint = object.constraints.new("CHILD_OF") constraint.target = armature constraint.subtarget = bone.name utils.setModeToObject() bpy.context.view_layer.objects.active = object - return {'FINISHED'} - - -class SetBoneVisible(bpy.types.Operator): - bl_idname = "alamo.bone_visible" - bl_label = "" - bl_description = "Set Visible to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "Visible", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.Visible = True - return {'FINISHED'} - - -class SetBoneInvisible(bpy.types.Operator): - bl_idname = "alamo.bone_invisible" - bl_label = "" - bl_description = "Set Visible to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "Visible", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.Visible = False - return {'FINISHED'} - - -class SetAltDecreaseStayHiddenTrue(bpy.types.Operator): - bl_idname = "alamo.alt_decrease_stay_hidden_true" - bl_label = "" - bl_description = "Set altDecreaseStayHidden to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.altDecreaseStayHidden = True - return {'FINISHED'} - - -class SetAltDecreaseStayHiddenFalse(bpy.types.Operator): - bl_idname = "alamo.alt_decrease_stay_hidden_false" - bl_label = "" - bl_description = "Set altDecreaseStayHidden to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.altDecreaseStayHidden = False - return {'FINISHED'} - - -class EnableProxyFalse(bpy.types.Operator): - bl_idname = "alamo.disable_proxy" - bl_label = "" - bl_description = "Set EnableProxy to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "EnableProxy", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.EnableProxy = False - return {'FINISHED'} - - -class EnableProxyTrue(bpy.types.Operator): - bl_idname = "alamo.enable_proxy" - bl_label = "" - bl_description = "Set EnableProxy to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "EnableProxy", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.EnableProxy = True - return {'FINISHED'} - - -class ProxyShow(bpy.types.Operator): - bl_idname = "alamo.show_proxy" - bl_label = "" - bl_description = "Set proxyIsHidden to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.proxyIsHidden = False - return {'FINISHED'} - - -class ProxyHide(bpy.types.Operator): - bl_idname = "alamo.hide_proxy" - bl_label = "" - bl_description = "Set proxyIsHidden to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.proxyIsHidden = True - return {'FINISHED'} + return {"FINISHED"} class CopyProxyNameToSelected(bpy.types.Operator): @@ -333,117 +346,34 @@ class CopyProxyNameToSelected(bpy.types.Operator): @classmethod def poll(cls, context): - return bpy.context.selected_bones is not None and len(bpy.context.selected_bones) > 1 + return ( + bpy.context.selected_bones is not None + and len(bpy.context.selected_bones) > 1 + ) def execute(self, context): - bones = bpy.context.selected_bones + bones = list(bpy.context.selected_bones) name = bones[0].ProxyName for bone in bones: bone.ProxyName = name - return {'FINISHED'} - - -def keyframeProxySet(operation): - bones = bpy.context.selected_pose_bones - action = bpy.context.object.animation_data.action - frame = bpy.context.scene.frame_current - keyframeType = "" - - for bone in bones: - if operation == 'SHOW': - bone.proxyIsHiddenAnimation = False - keyframeType = 'JITTER' - if operation == 'HIDE': - bone.proxyIsHiddenAnimation = True - keyframeType = 'EXTREME' - - if operation == 'REMOVE': - bone.keyframe_delete(data_path="proxyIsHiddenAnimation") - else: - bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) - # for keyframe in action.fcurves.find(bone.path_from_id() + ".proxyIsHiddenAnimation").keyframe_points: - # if int(keyframe.co[0]) == frame: - # keyframe.type = keyframeType - - for area in bpy.context.screen.areas: - if area.type == 'DOPESHEET_EDITOR': - area.tag_redraw() - - -class keyframeProxyShow(bpy.types.Operator): - bl_idname = "alamo.show_keyframe_proxy" - bl_label = "" - bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", False) - - def execute(self, context): - keyframeProxySet('SHOW') - return {'FINISHED'} - - -class keyframeProxyHide(bpy.types.Operator): - bl_idname = "alamo.hide_keyframe_proxy" - bl_label = "" - bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", True) - - def execute(self, context): - keyframeProxySet('HIDE') - return {'FINISHED'} - - -class keyframeProxyRemove(bpy.types.Operator): - bl_idname = "alamo.remove_keyframe_proxy" - bl_label = "" - bl_description = "Remove active keyframes from all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", None) - - def execute(self, context): - keyframeProxySet('REMOVE') - return {'FINISHED'} - - -def skeletonEnumCallback(scene, context): - armatures = [('None', 'None', '', '', 0)] - counter = 1 - for arm in bpy.data.objects: # test if armature exists - if arm.type == 'ARMATURE': - armatures.append((arm.name, arm.name, '', '', counter)) - counter += 1 - - return armatures + return {"FINISHED"} class skeletonEnumClass(PropertyGroup): - skeletonEnum : EnumProperty( - name = 'Active Skeleton', - description = "skeleton that is exported", - items = skeletonEnumCallback + skeletonEnum: EnumProperty( + name="Active Skeleton", + description="skeleton that is exported", + items=skeletonEnumCallback, ) -def rowbuilder(layout, label, operators, icons): - row = layout.row(align=True) - row.label(text=label) - row.operator(operators[0], text="", icon=icons[0]) - row.operator(operators[1], text="", icon=icons[1]) - - +# Panels ########################################################################################## class ALAMO_PT_ValidationPanel(bpy.types.Panel): bl_label = "Validation" bl_idname = "ALAMO_PT_ValidationPanel" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" def draw(self, context): @@ -452,20 +382,48 @@ def draw(self, context): row.scale_y = 3.0 +class ALAMO_PT_ObjectPanel(bpy.types.Panel): + bl_label = "Object" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + layout.active = False + if bpy.context.mode == "OBJECT": + layout.active = True + + threebox( + layout, + CheckPropAllSame(bpy.context.selected_objects, "HasCollision"), + "alamo.set_hascollision", + "Collision", + ) + threebox( + layout, + CheckPropAllSame(bpy.context.selected_objects, "Hidden"), + "alamo.set_hidden", + "Hidden", + ) + + class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): bl_label = "Armature" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" def draw(self, context): - object = context.object layout = self.layout scene = context.scene - # layout.active = False - layout.prop(scene.ActiveSkeleton, 'skeletonEnum') + col = layout.column(align=True) + col.label(text="Active Skeleton") + col.prop(scene.ActiveSkeleton, "skeletonEnum", text="") + + layout.separator() layout.operator("alamo.create_constraint_bone") @@ -473,39 +431,53 @@ def draw(self, context): class ALAMO_PT_EditBonePanel(bpy.types.Panel): bl_label = "Bone" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" def draw(self, context): bones = bpy.context.selected_bones layout = self.layout - col = layout.column() + layout.active = False if bpy.context.mode == "EDIT_ARMATURE": layout.active = True - rowbuilder(layout, "Set Bone Visibility:", ["alamo.bone_visible", "alamo.bone_invisible"], ["HIDE_OFF", "HIDE_ON"]) - rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) - + col = layout.column(align=True) + col.label(text="Billboard Mode") row = col.row() row.enabled = False if bones is not None: if len(bones) == 1: - row.prop(bones[0].billboardMode, "billboardMode", text="Billboard") + row.prop(list(bones)[0].billboardMode, "billboardMode", text="") row.enabled = True else: row.label(text="Billboard") else: row.label(text="Billboard") + layout.separator() + + threebox( + layout, + CheckPropAllSame(bpy.context.selected_bones, "Visible"), + "alamo.set_visible", + "Visible", + ) + threebox( + layout, + CheckPropAllSame(bpy.context.selected_bones, "EnableProxy"), + "alamo.set_enableproxy", + "Enable Proxy", + ) + class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): bl_label = "" - bl_parent_id = 'ALAMO_PT_EditBonePanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_parent_id = "ALAMO_PT_EditBonePanel" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" bl_options = {"HIDE_HEADER"} @@ -513,26 +485,35 @@ def draw(self, context): bones = bpy.context.selected_bones layout = self.layout - all_same = True + all_same = CheckPropAllSame(bones, "EnableProxy") layout.active = False - if bpy.context.mode == "EDIT_ARMATURE": + if bpy.context.mode == "EDIT_ARMATURE" and all_same is not None: layout.active = all_same - if not all_same: - layout.label(icon="ERROR", text="Inconsistent EnableProxy states.") - layout.label(icon="BLANK1", text="Change selection or set EnableProxy.") - - rowbuilder(layout, "Set Proxy Visibility:", ["alamo.show_proxy", "alamo.hide_proxy"], ["HIDE_OFF", "HIDE_ON"]) - - rowbuilder(layout, "Set altDecreaseStayHidden:", ["alamo.alt_decrease_stay_hidden_true", "alamo.alt_decrease_stay_hidden_false"], ["CHECKMARK", "X"]) - - row = layout.row(align=True) + if bpy.context.mode == "EDIT_ARMATURE" and all_same is None and len(bones) > 0: + layout.label(icon="ERROR", text="Inconsistent EnableProxy states") + + threebox( + layout, + all_same, + "alamo.set_proxyishidden", + "Proxy Visible", + ) + threebox( + layout, + all_same, + "alamo.set_altdecreasestayhidden", + "altDecreaseStayHidden", + ) + + col = layout.column(align=True) + col.label(text="Proxy Name") + row = col.row(align=True) row.enabled = False - if bones is not None and len(bones) > 0: - row.prop(bones[0], "ProxyName") - row.operator('alamo.copy_proxy_name', text="", - icon="DUPLICATE") + if bpy.context.mode == "EDIT_ARMATURE" and bones is not None and len(bones) > 0 and all_same: + row.prop(list(bones)[0], "ProxyName", text="") + row.operator("alamo.copy_proxy_name", text="", icon="DUPLICATE") row.enabled = True else: row.label(text="ProxyName") @@ -541,8 +522,8 @@ def draw(self, context): class ALAMO_PT_AnimationPanel(bpy.types.Panel): bl_label = "Animation" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" def draw(self, context): @@ -550,31 +531,33 @@ def draw(self, context): layout.active = False if bpy.context.mode == "POSE": layout.active = True - # layout.column().label(text="Keyframe Proxy Visibility:") - row = layout.row(align=True) - row.label(text="Keyframe Proxy Visibility:") - row.operator('alamo.show_keyframe_proxy', text="", - icon="HIDE_OFF") - row.operator('alamo.hide_keyframe_proxy', text="", - icon="HIDE_ON") - row.operator('alamo.remove_keyframe_proxy', text="", - icon="X") + + threebox( + layout, + check_anim_prop_all_same( + bpy.context.selected_pose_bones, "proxyIsHiddenAnimation" + ), + "alamo.set_keyframe_proxy", + "Keyframe Proxy Visible", + ) + + row = layout.row() + row.operator("alamo.delete_keyframe_proxy", text="", icon="X") + row.label(text="Delete Proxy Keyframes") class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): bl_label = "Action End Frames" - bl_parent_id = 'ALAMO_PT_AnimationPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_parent_id = "ALAMO_PT_AnimationPanel" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" bl_options = {"HIDE_HEADER"} def draw(self, context): - layout = self.layout - if bpy.data.actions is not None and bpy.data.actions > 1: - row = layout.row() - row.label(text="Action End Frames") + layout = self.layout.column(align=True) + layout.label(text="Action End Frames") for action in bpy.data.actions: layout.prop(action, "AnimationEndFrame", text=action.name) @@ -582,10 +565,10 @@ def draw(self, context): class ALAMO_PT_DebugPanel(bpy.types.Panel): bl_label = "Debug" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_category = "ALAMO" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} def draw(self, context): object = context.object @@ -593,11 +576,11 @@ def draw(self, context): scene = context.scene c = layout.column() - c.prop(scene.ActiveSkeleton, 'skeletonEnum') + c.prop(scene.ActiveSkeleton, "skeletonEnum") if type(object) != type(None): - if(object.type == 'MESH'): - if bpy.context.mode == 'OBJECT': + if object.type == "MESH": + if bpy.context.mode == "OBJECT": c.prop(object, "HasCollision") c.prop(object, "Hidden") @@ -605,19 +588,20 @@ def draw(self, context): if armature != None: hasChildConstraint = False for constraint in object.constraints: - if constraint.type == 'CHILD_OF': + if constraint.type == "CHILD_OF": hasChildConstraint = True if not hasChildConstraint: - self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') + self.layout.operator( + "create.constraint_bone", text="Create Constraint Bone" + ) action = utils.getCurrentAction() - if(action != None): + if action != None: c.prop(action, "AnimationEndFrame") - bone = bpy.context.active_bone if type(bone) != type(None): - if(type(bpy.context.active_bone ) is bpy.types.EditBone): + if type(bpy.context.active_bone) is bpy.types.EditBone: c.prop(bone.billboardMode, "billboardMode") c.prop(bone, "Visible") c.prop(bone, "EnableProxy") @@ -626,7 +610,10 @@ def draw(self, context): c.prop(bone, "altDecreaseStayHidden") c.prop(bone, "ProxyName") - elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): + elif ( + type(bpy.context.active_bone) is bpy.types.Bone + and bpy.context.mode == "POSE" + ): poseBone = object.pose.bones[bone.name] c.prop(poseBone, "proxyIsHiddenAnimation") @@ -643,19 +630,24 @@ def draw(self, context): layout = self.layout col = layout.column() - if type(object) != type(None) and object.type == 'MESH': + # if type(object) != type(None) and object.type == "MESH": + if type(object) != type(None) and object.type == "MESH": material = bpy.context.active_object.active_material if material is not None: # a None image is needed to represent not using a texture - if 'None' not in bpy.data.images: - bpy.data.images.new(name='None', width=1, height=1) + if "None" not in bpy.data.images: + bpy.data.images.new(name="None", width=1, height=1) col.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + if material.shaderList.shaderList != "alDefault.fx": + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] for shader_prop in shader_props: # because contains() doesn't exist, apparently - if shader_prop.find('Texture') > -1: - layout.prop_search(material, shader_prop, bpy.data, "images") + if shader_prop.find("Texture") > -1: + layout.prop_search( + material, shader_prop, bpy.data, "images" + ) # layout.template_ID(material, shader_prop, new="image.new", open="image.open") @@ -665,26 +657,31 @@ class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "material" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} def draw(self, context): object = context.object layout = self.layout col = layout.column() - if type(object) != type(None) and object.type == 'MESH': + if type(object) != type(None) and object.type == "MESH": material = bpy.context.active_object.active_material - if material is not None and material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] + if ( + material is not None + and material.shaderList.shaderList != "alDefault.fx" + ): + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] for shader_prop in shader_props: # because contains() doesn't exist, apparently - if shader_prop.find('Texture') == -1: + if shader_prop.find("Texture") == -1: col.prop(material, shader_prop) class shaderListProperties(bpy.types.PropertyGroup): mode_options = [ - (shader_name, shader_name, '', '', index) + (shader_name, shader_name, "", "", index) for index, shader_name in enumerate(settings.material_parameter_dict) ] @@ -697,14 +694,14 @@ class shaderListProperties(bpy.types.PropertyGroup): class billboardListProperties(bpy.types.PropertyGroup): mode_options = [ - ("Disable", "Disable", 'Description WIP', '', 0), - ("Parallel", "Parallel", 'Description WIP', '', 1), - ("Face", "Face", 'Description WIP', '', 2), - ("ZAxis View", "ZAxis View", 'Description WIP', '', 3), - ("ZAxis Light", "ZAxis Light", 'Description WIP', '', 4), - ("ZAxis Wind", "ZAxis Wind", 'Description WIP', '', 5), - ("Sunlight Glow", "Sunlight Glow", 'Description WIP', '', 6), - ("Sun", "Sun", 'Description WIP', '', 7), + ("Disable", "Disable", "Description WIP", "", 0), + ("Parallel", "Parallel", "Description WIP", "", 1), + ("Face", "Face", "Description WIP", "", 2), + ("ZAxis View", "ZAxis View", "Description WIP", "", 3), + ("ZAxis Light", "ZAxis Light", "Description WIP", "", 4), + ("ZAxis Wind", "ZAxis Wind", "Description WIP", "", 5), + ("Sunlight Glow", "Sunlight Glow", "Description WIP", "", 6), + ("Sun", "Sun", "Description WIP", "", 7), ] billboardMode: bpy.props.EnumProperty( @@ -714,28 +711,19 @@ class billboardListProperties(bpy.types.PropertyGroup): ) -def proxy_name_update(self, context): - if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion - self.ProxyName = self.ProxyName.upper() - -# blender registration - - +# Registration #################################################################################### def menu_func_import(self, context): - self.layout.operator(import_alo.ALO_Importer.bl_idname, - text=".ALO Importer") - self.layout.operator(import_ala.ALA_Importer.bl_idname, - text=".ALA Importer") + self.layout.operator(import_alo.ALO_Importer.bl_idname, text=".ALO Importer") + self.layout.operator(import_ala.ALA_Importer.bl_idname, text=".ALA Importer") def menu_func_export(self, context): - self.layout.operator(export_alo.ALO_Exporter.bl_idname, - text=".ALO Exporter") - self.layout.operator(export_ala.ALA_Exporter.bl_idname, - text=".ALA Exporter") + self.layout.operator(export_alo.ALO_Exporter.bl_idname, text=".ALO Exporter") + self.layout.operator(export_ala.ALA_Exporter.bl_idname, text=".ALA Exporter") classes = ( + *classes, skeletonEnumClass, billboardListProperties, shaderListProperties, @@ -748,29 +736,23 @@ def menu_func_export(self, context): ValidateFileButton, CreateConstraintBone, createConstraintBoneButton, - SetBoneVisible, - SetBoneInvisible, - SetAltDecreaseStayHiddenTrue, - SetAltDecreaseStayHiddenFalse, - EnableProxyFalse, - EnableProxyTrue, - ProxyShow, - ProxyHide, CopyProxyNameToSelected, - keyframeProxyShow, - keyframeProxyHide, - keyframeProxyRemove, + keyframeProxySet, + keyframeProxyDelete, ALAMO_PT_ValidationPanel, + ALAMO_PT_ObjectPanel, ALAMO_PT_ArmatureSettingsPanel, ALAMO_PT_EditBonePanel, ALAMO_PT_EditBoneSubPanel, ALAMO_PT_AnimationPanel, ALAMO_PT_AnimationActionSubPanel, - ALAMO_PT_DebugPanel + ALAMO_PT_DebugPanel, ) + def register(): from bpy.utils import register_class + for cls in classes: register_class(cls) @@ -789,95 +771,122 @@ def register(): bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) bpy.types.EditBone.billboardMode = bpy.props.PointerProperty( - type=billboardListProperties) + type=billboardListProperties + ) bpy.types.Object.HasCollision = BoolProperty() bpy.types.Object.Hidden = BoolProperty() - bpy.types.Material.BaseTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DetailTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty( - default='None') - bpy.types.Material.NormalTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.GlossTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.WaveTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DistortionTexture = bpy.props.StringProperty( - default='None') - bpy.types.Material.CloudTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty( - default='None') - - bpy.types.Material.shaderList = bpy.props.PointerProperty( - type=shaderListProperties) + bpy.types.Material.BaseTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.DetailTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.NormalTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.GlossTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.WaveTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.CloudTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default="None") + + bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) bpy.types.Material.Specular = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) bpy.types.Material.Shininess = bpy.props.FloatProperty( - min=0.0, max=255.0, default=32.0) + min=0.0, max=255.0, default=32.0 + ) bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0)) + min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0) + ) bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) bpy.types.Material.Color = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) + ) bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5)) + min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5) + ) # shield shader properties bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.5) + min=0.0, max=255.0, default=0.5 + ) bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255.0, max=255.0, default=1.0 + ) bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255.0, max=255.0, default=1.0 + ) bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255.0, max=255.0, default=1.0 + ) bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15) + min=-255.0, max=255.0, default=-0.15 + ) bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15) + min=-255.0, max=255.0, default=-0.15 + ) bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.25) + min=-255.0, max=255.0, default=-0.25 + ) # tree properties bpy.types.Material.BendScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.4) + min=-255.0, max=255.0, default=0.4 + ) # grass properties bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) + ) # skydome.fx properties bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.001) + min=-255.0, max=255.0, default=0.001 + ) bpy.types.Material.CloudScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255.0, max=255.0, default=1.0 + ) # nebula.fx properties bpy.types.Material.SFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.002) + min=-255.0, max=255.0, default=0.002 + ) bpy.types.Material.TFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.005) + min=-255.0, max=255.0, default=0.005 + ) bpy.types.Material.DistortionScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255.0, max=255.0, default=1.0 + ) # planet.fx properties bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) + ) bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) + ) bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255.0, max=255.0, default=1.0 + ) # tryplanar mapping properties bpy.types.Material.MappingScale = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1) + min=0.0, max=255.0, default=0.1 + ) bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1) + min=0.0, max=255.0, default=0.1 + ) def unregister(): from bpy.utils import unregister_class + for cls in reversed(classes): unregister_class(cls) diff --git a/io_alamo_tools/changelog.md b/io_alamo_tools/changelog.md index 2274d16..7dee5ad 100644 --- a/io_alamo_tools/changelog.md +++ b/io_alamo_tools/changelog.md @@ -5,6 +5,6 @@ ### Other -- Proxy keyframe validation fixed and re-enabled + - Validator now correctly checks for maximum *triangles*, rather than faces - Adjustments for easier scripting of import/export \ No newline at end of file diff --git a/io_alamo_tools/ui_wip.py b/io_alamo_tools/ui_wip.py deleted file mode 100644 index 01b9971..0000000 --- a/io_alamo_tools/ui_wip.py +++ /dev/null @@ -1,100 +0,0 @@ -import bpy - -def CheckObjectType(objects, type): - for object in objects: - if object.type != type: - return False - return True - -def CheckPropAllSame(objects, prop): - # True: All same, have value of True - # False: All same, have value of False - # None: Not all same - first_value = None - for object in objects: - if first_value is None: - first_value = getattr(object, prop) - elif getattr(object, prop) != first_value: - return None - return first_value - -def ShouldEnable(objects): - if objects is None or len(objects) <= 0: - return False - if bpy.context.mode == "OBJECT": - objects_same = CheckObjectType(objects, "MESH") - if not objects_same: - return False - return True - -def threebox(layout, all_same, operator, label): - icon="ERROR" - if all_same is None: - icon="LAYER_ACTIVE" - if all_same == True: - icon="CHECKMARK" - if all_same == False: - icon="BLANK1" - - row=layout.row() - row.operator(operator, text="", icon=icon) - row.label(text=label) - -def setProp(all_same, objects, prop): - set_to = False - if all_same in (None, False) : - set_to = True - - for object in objects: - setattr(object, prop, set_to) - -def propop_builder(prop, object_type): - - class PropOp(bpy.types.Operator): - bl_idname = "alamo.set_" + prop.lower() - bl_label = "Set " + prop + " for all selected objects" - bl_description = "" - - @classmethod - def poll(cls, context): - return ShouldEnable(eval("bpy.context.selected_" + object_type)) - - def execute(self, context): - setProp(CheckPropAllSame(eval("bpy.context.selected_" + object_type), prop), eval("bpy.context.selected_" + object_type), prop) - return {'FINISHED'} - - return PropOp - -SetCollision = propop_builder("HasCollision", "objects") -SetHidden = propop_builder("Hidden", "objects") - -class ALAMO_PT_ObjectPanel(bpy.types.Panel): - bl_label = "Object" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - layout = self.layout - layout.active = False - if bpy.context.mode == "OBJECT": - layout.active = True - - threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "HasCollision"), "alamo.set_hascollision", "Collision") - threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "Hidden"), "alamo.set_hidden", "Hidden") - - -def register(): - bpy.utils.register_class(SetCollision) - bpy.utils.register_class(SetHidden) - bpy.utils.register_class(ALAMO_PT_ObjectPanel) - - -def unregister(): - bpy.utils.unregister_class(SetCollision) - bpy.utils.unregister_class(SetHidden) - bpy.utils.unregister_class(ALAMO_PT_ObjectPanel) - - -if __name__ == "__main__": - register() diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 95858c6..f3c1ba8 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -196,7 +196,7 @@ def validate(mesh_list): ] checklist_no_object = [ checkTranslationArmature, - checkProxyKeyframes, + # checkProxyKeyframes, ] for check in checklist: From d90c21d22ad8a986bc2e24726e9bac1521d8cccf Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:16:36 -0700 Subject: [PATCH 061/100] Version bump --- io_alamo_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 190425e..b753ae9 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -41,7 +41,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 4), + "version": (0, 0, 4, 0), "blender": (2, 93, 0), "category": "Import-Export", } From 1b0d5c2c91a76e0bd4c175db6362907da49d49bb Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 27 Jul 2022 12:07:01 -0700 Subject: [PATCH 062/100] Revert "Version bump" This reverts commit d90c21d22ad8a986bc2e24726e9bac1521d8cccf. --- io_alamo_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index b753ae9..190425e 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -41,7 +41,7 @@ bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 4, 0), + "version": (0, 0, 3, 4), "blender": (2, 93, 0), "category": "Import-Export", } From 618070fbbf31100c4095053279870c7651c9ab45 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 27 Jul 2022 12:07:05 -0700 Subject: [PATCH 063/100] Revert "Finishing UI refresh" This reverts commit 005082902df722fd234c9a42c5bd54aa35c3cbef. --- io_alamo_tools/__init__.py | 903 +++++++++++++++++------------------ io_alamo_tools/changelog.md | 2 +- io_alamo_tools/ui_wip.py | 100 ++++ io_alamo_tools/validation.py | 2 +- 4 files changed, 549 insertions(+), 458 deletions(-) create mode 100644 io_alamo_tools/ui_wip.py diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 190425e..799a707 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,21 +1,20 @@ -from .import_alo import ALO_Importer -from .export_ala import ALA_Exporter -from .export_alo import ALO_Exporter -from .import_ala import ALA_Importer + +from . import_alo import ALO_Importer +from . export_ala import ALA_Exporter +from . export_alo import ALO_Exporter +from . import_ala import ALA_Importer from . import validation -from bpy.types import ( - Panel, - Operator, - PropertyGroup, -) -from bpy.props import ( - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - PointerProperty, -) +from bpy.types import (Panel, + Operator, + PropertyGroup, + ) +from bpy.props import (StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, + ) from bpy.props import * import mathutils import bpy @@ -23,7 +22,6 @@ if "bpy" in locals(): import importlib - importlib.reload(import_alo) importlib.reload(import_ala) importlib.reload(export_alo) @@ -43,14 +41,9 @@ "author": "Gaukler, evilbobthebob, inertial", "version": (0, 0, 3, 4), "blender": (2, 93, 0), - "category": "Import-Export", + "category": "Import-Export" } - -classes = () - - -# UI Utilities #################################################################################### def CheckObjectType(objects, type): for object in objects: if object.type != type: @@ -58,22 +51,10 @@ def CheckObjectType(objects, type): return True -def ShouldEnable(objects): - if objects is None or len(objects) <= 0: - return False - if bpy.context.mode == "OBJECT": - objects_same = CheckObjectType(objects, "MESH") - if not objects_same: - return False - return True - - def CheckPropAllSame(objects, prop): # True: All same, have value of True # False: All same, have value of False # None: Not all same - if objects is None or len(objects) <= 0: - return None first_value = None for object in objects: if first_value is None: @@ -83,171 +64,51 @@ def CheckPropAllSame(objects, prop): return first_value -def check_anim_prop_all_same(bones, prop): - """Like check_prop_all_same(), but for animation""" - - all_same = [] - - if bones is not None and len(bones) > 0: - all_same = list(set(getattr(bone, prop) for bone in bones)) - - if len(all_same) == 1: - return all_same[0] - - return None - - -def threebox(layout, all_same, operator, label): - icon = "ERROR" - if all_same is None: - icon = "LAYER_ACTIVE" - if all_same is True: - icon = "CHECKMARK" - if all_same is False: - icon = "BLANK1" - - row = layout.row() - row.operator(operator, text="", icon=icon) - row.label(text=label) - - -def setProp(all_same, objects, prop): - set_to = False - if all_same in (None, False): - set_to = True - - for object in objects: - setattr(object, prop, set_to) - - -def proxy_name_update(self, context): - if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion - self.ProxyName = self.ProxyName.upper() - - -def skeletonEnumCallback(scene, context): - armatures = [("None", "None", "", "", 0)] - counter = 1 - for arm in bpy.data.objects: # test if armature exists - if arm.type == "ARMATURE": - armatures.append((arm.name, arm.name, "", "", counter)) - counter += 1 - - return armatures - - -# Operators ####################################################################################### -def propop_builder(prop, object_type): - """Property Operator Builder""" - - class PropOp(bpy.types.Operator): - bl_idname = "alamo.set_" + prop.lower() - bl_label = "Set " + prop + " for all selected objects" - bl_description = "" - - @classmethod - def poll(cls, context): - return ShouldEnable(eval("bpy.context.selected_" + object_type)) - - def execute(self, context): - setProp( - CheckPropAllSame(eval("bpy.context.selected_" + object_type), prop), - eval("bpy.context.selected_" + object_type), - prop, - ) - return {"FINISHED"} - - return PropOp - - -SetCollision = propop_builder("HasCollision", "objects") -SetHidden = propop_builder("Hidden", "objects") -SetBoneHidden = propop_builder("Visible", "bones") -SetAltDecreaseStayHidden = propop_builder("altDecreaseStayHidden", "bones") -SetProxy = propop_builder("EnableProxy", "bones") -SetProxyHidden = propop_builder("proxyIsHidden", "bones") - -classes = ( - *classes, - SetCollision, - SetHidden, - SetBoneHidden, - SetAltDecreaseStayHidden, - SetProxy, - SetProxyHidden, -) - - -class keyframeProxySet(bpy.types.Operator): - bl_idname = "alamo.set_keyframe_proxy" - bl_label = "" - bl_description = ( - "Create a keyframe and set proxyIsHiddenAnimation for all selected bones" - ) - - @classmethod - def poll(cls, context): - bones = bpy.context.selected_pose_bones - return bones is not None and len(bones) > 0 - - def execute(self, context): - bones = bpy.context.selected_pose_bones - - all_same = check_anim_prop_all_same(bones, "proxyIsHiddenAnimation") - operation = "SHOW" if all_same is True else "HIDE" - - for bone in list(bones): - if operation == "SHOW": - bone.proxyIsHiddenAnimation = False - if operation == "HIDE": - bone.proxyIsHiddenAnimation = True - - bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) - - for area in bpy.context.screen.areas: - if area.type == "DOPESHEET_EDITOR": - area.tag_redraw() - - return {"FINISHED"} - - -class keyframeProxyDelete(bpy.types.Operator): - bl_idname = "alamo.delete_keyframe_proxy" - bl_label = "" - bl_description = ( - "Create a keyframe and set proxyIsHiddenAnimation for all selected bones" - ) - - @classmethod - def poll(cls, context): - bones = bpy.context.selected_pose_bones - action = bpy.context.object.animation_data.action - frame = bpy.context.scene.frame_current - - if bones is None or len(bones) <= 0: +def ShouldEnable(objects, prop, set_to): + if objects is None or len(objects) <= 0: + return False + if bpy.context.mode == "OBJECT": + objects_same = CheckObjectType(objects, "MESH") + if not objects_same: return False + all_same = CheckPropAllSame(objects, prop) + if all_same is not None: + if set_to: + return not all_same + else: + return all_same + return True - for bone in list(bones): - keyframes = action.fcurves.find( - bone.path_from_id() + ".proxyIsHiddenAnimation" - ) - if keyframes is not None: - for keyframe in keyframes.keyframe_points: - if int(keyframe.co[0]) == frame: - return True +def ShouldEnableAnim(bones, prop, set_to): + if bones is None or len(bones) <= 0: return False - def execute(self, context): - bones = list(bpy.context.selected_pose_bones) - for bone in bones: - bone.keyframe_delete(data_path="proxyIsHiddenAnimation") - - for area in bpy.context.screen.areas: - if area.type in ("DOPESHEET_EDITOR", "VIEW_3D"): - area.tag_redraw() - - return {"FINISHED"} + frame = bpy.context.scene.frame_current + action = bpy.context.object.animation_data.action + has_keyframes = False + has_previous_keyframe = False + for bone in bones: + keyframes = action.fcurves.find(bone.path_from_id() + "." + prop) + if keyframes is not None: + for keyframe in keyframes.keyframe_points: + if int(keyframe.co[0]) <= frame: + has_previous_keyframe = True + if int(keyframe.co[0]) == frame: + has_keyframes = True + break + + if set_to is None: + return has_keyframes + if not has_previous_keyframe: + return True + + all_same = CheckPropAllSame(bones, prop) + if all_same is not None: + if set_to: + return not all_same + return all_same + return True class ValidateFileButton(bpy.types.Operator): @@ -255,19 +116,17 @@ class ValidateFileButton(bpy.types.Operator): bl_label = "Validate" def execute(self, context): - mesh_list = validation.create_export_list( - bpy.context.scene.collection, True, "DATA" - ) + mesh_list = validation.create_export_list(bpy.context.scene.collection, True, "DATA") - # check if export objects satisfy requirements (has material, UVs, ...) + #check if export objects satisfy requirements (has material, UVs, ...) messages = validation.validate(mesh_list) if messages is not None and len(messages) > 0: for message in messages: self.report(*message) else: - self.report({"INFO"}, "ALAMO - Validation complete. No errors detected!") - return {"FINISHED"} + self.report({'INFO'}, 'ALAMO - Validation complete. No errors detected!') + return {'FINISHED'} # Legacy version, included for the debug panel @@ -286,14 +145,14 @@ def execute(self, context): bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") - constraint = object.constraints.new("CHILD_OF") + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') + constraint = object.constraints.new('CHILD_OF') constraint.target = armature constraint.subtarget = bone.name utils.setModeToObject() bpy.context.view_layer.objects.active = object - return {"FINISHED"} + return {'FINISHED'} class CreateConstraintBone(bpy.types.Operator): @@ -305,14 +164,14 @@ def poll(cls, context): object = bpy.context.object if ( type(object) != type(None) - and object.type == "MESH" - and bpy.context.mode == "OBJECT" + and object.type == 'MESH' + and bpy.context.mode == 'OBJECT' ): armature = utils.findArmature() if armature is not None: hasChildConstraint = False for constraint in object.constraints: - if constraint.type == "CHILD_OF": + if constraint.type == 'CHILD_OF': hasChildConstraint = True if not hasChildConstraint: return True @@ -329,14 +188,142 @@ def execute(self, context): bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") - constraint = object.constraints.new("CHILD_OF") + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') + constraint = object.constraints.new('CHILD_OF') constraint.target = armature constraint.subtarget = bone.name utils.setModeToObject() bpy.context.view_layer.objects.active = object - return {"FINISHED"} + return {'FINISHED'} + + +class SetBoneVisible(bpy.types.Operator): + bl_idname = "alamo.bone_visible" + bl_label = "" + bl_description = "Set Visible to True for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "Visible", True) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.Visible = True + return {'FINISHED'} + + +class SetBoneInvisible(bpy.types.Operator): + bl_idname = "alamo.bone_invisible" + bl_label = "" + bl_description = "Set Visible to False for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "Visible", False) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.Visible = False + return {'FINISHED'} + + +class SetAltDecreaseStayHiddenTrue(bpy.types.Operator): + bl_idname = "alamo.alt_decrease_stay_hidden_true" + bl_label = "" + bl_description = "Set altDecreaseStayHidden to True for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", True) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.altDecreaseStayHidden = True + return {'FINISHED'} + + +class SetAltDecreaseStayHiddenFalse(bpy.types.Operator): + bl_idname = "alamo.alt_decrease_stay_hidden_false" + bl_label = "" + bl_description = "Set altDecreaseStayHidden to False for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", False) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.altDecreaseStayHidden = False + return {'FINISHED'} + + +class EnableProxyFalse(bpy.types.Operator): + bl_idname = "alamo.disable_proxy" + bl_label = "" + bl_description = "Set EnableProxy to False for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "EnableProxy", False) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.EnableProxy = False + return {'FINISHED'} + + +class EnableProxyTrue(bpy.types.Operator): + bl_idname = "alamo.enable_proxy" + bl_label = "" + bl_description = "Set EnableProxy to True for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "EnableProxy", True) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.EnableProxy = True + return {'FINISHED'} + + +class ProxyShow(bpy.types.Operator): + bl_idname = "alamo.show_proxy" + bl_label = "" + bl_description = "Set proxyIsHidden to False for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", False) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.proxyIsHidden = False + return {'FINISHED'} + + +class ProxyHide(bpy.types.Operator): + bl_idname = "alamo.hide_proxy" + bl_label = "" + bl_description = "Set proxyIsHidden to True for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", True) + + def execute(self, context): + bones = bpy.context.selected_bones + for bone in bones: + bone.proxyIsHidden = True + return {'FINISHED'} class CopyProxyNameToSelected(bpy.types.Operator): @@ -346,34 +333,117 @@ class CopyProxyNameToSelected(bpy.types.Operator): @classmethod def poll(cls, context): - return ( - bpy.context.selected_bones is not None - and len(bpy.context.selected_bones) > 1 - ) + return bpy.context.selected_bones is not None and len(bpy.context.selected_bones) > 1 def execute(self, context): - bones = list(bpy.context.selected_bones) + bones = bpy.context.selected_bones name = bones[0].ProxyName for bone in bones: bone.ProxyName = name - return {"FINISHED"} + return {'FINISHED'} + + +def keyframeProxySet(operation): + bones = bpy.context.selected_pose_bones + action = bpy.context.object.animation_data.action + frame = bpy.context.scene.frame_current + keyframeType = "" + + for bone in bones: + if operation == 'SHOW': + bone.proxyIsHiddenAnimation = False + keyframeType = 'JITTER' + if operation == 'HIDE': + bone.proxyIsHiddenAnimation = True + keyframeType = 'EXTREME' + + if operation == 'REMOVE': + bone.keyframe_delete(data_path="proxyIsHiddenAnimation") + else: + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + # for keyframe in action.fcurves.find(bone.path_from_id() + ".proxyIsHiddenAnimation").keyframe_points: + # if int(keyframe.co[0]) == frame: + # keyframe.type = keyframeType + + for area in bpy.context.screen.areas: + if area.type == 'DOPESHEET_EDITOR': + area.tag_redraw() + + +class keyframeProxyShow(bpy.types.Operator): + bl_idname = "alamo.show_keyframe_proxy" + bl_label = "" + bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", False) + + def execute(self, context): + keyframeProxySet('SHOW') + return {'FINISHED'} + + +class keyframeProxyHide(bpy.types.Operator): + bl_idname = "alamo.hide_keyframe_proxy" + bl_label = "" + bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", True) + + def execute(self, context): + keyframeProxySet('HIDE') + return {'FINISHED'} + + +class keyframeProxyRemove(bpy.types.Operator): + bl_idname = "alamo.remove_keyframe_proxy" + bl_label = "" + bl_description = "Remove active keyframes from all selected bones" + + @classmethod + def poll(cls, context): + return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", None) + + def execute(self, context): + keyframeProxySet('REMOVE') + return {'FINISHED'} + + +def skeletonEnumCallback(scene, context): + armatures = [('None', 'None', '', '', 0)] + counter = 1 + for arm in bpy.data.objects: # test if armature exists + if arm.type == 'ARMATURE': + armatures.append((arm.name, arm.name, '', '', counter)) + counter += 1 + + return armatures class skeletonEnumClass(PropertyGroup): - skeletonEnum: EnumProperty( - name="Active Skeleton", - description="skeleton that is exported", - items=skeletonEnumCallback, + skeletonEnum : EnumProperty( + name = 'Active Skeleton', + description = "skeleton that is exported", + items = skeletonEnumCallback ) -# Panels ########################################################################################## +def rowbuilder(layout, label, operators, icons): + row = layout.row(align=True) + row.label(text=label) + row.operator(operators[0], text="", icon=icons[0]) + row.operator(operators[1], text="", icon=icons[1]) + + class ALAMO_PT_ValidationPanel(bpy.types.Panel): bl_label = "Validation" bl_idname = "ALAMO_PT_ValidationPanel" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" def draw(self, context): @@ -382,48 +452,20 @@ def draw(self, context): row.scale_y = 3.0 -class ALAMO_PT_ObjectPanel(bpy.types.Panel): - bl_label = "Object" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "ALAMO" - - def draw(self, context): - layout = self.layout - layout.active = False - if bpy.context.mode == "OBJECT": - layout.active = True - - threebox( - layout, - CheckPropAllSame(bpy.context.selected_objects, "HasCollision"), - "alamo.set_hascollision", - "Collision", - ) - threebox( - layout, - CheckPropAllSame(bpy.context.selected_objects, "Hidden"), - "alamo.set_hidden", - "Hidden", - ) - - class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): bl_label = "Armature" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" def draw(self, context): + object = context.object layout = self.layout scene = context.scene + # layout.active = False - col = layout.column(align=True) - col.label(text="Active Skeleton") - col.prop(scene.ActiveSkeleton, "skeletonEnum", text="") - - layout.separator() + layout.prop(scene.ActiveSkeleton, 'skeletonEnum') layout.operator("alamo.create_constraint_bone") @@ -431,53 +473,39 @@ def draw(self, context): class ALAMO_PT_EditBonePanel(bpy.types.Panel): bl_label = "Bone" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" def draw(self, context): bones = bpy.context.selected_bones layout = self.layout - + col = layout.column() layout.active = False if bpy.context.mode == "EDIT_ARMATURE": layout.active = True - col = layout.column(align=True) - col.label(text="Billboard Mode") + rowbuilder(layout, "Set Bone Visibility:", ["alamo.bone_visible", "alamo.bone_invisible"], ["HIDE_OFF", "HIDE_ON"]) + rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) + row = col.row() row.enabled = False if bones is not None: if len(bones) == 1: - row.prop(list(bones)[0].billboardMode, "billboardMode", text="") + row.prop(bones[0].billboardMode, "billboardMode", text="Billboard") row.enabled = True else: row.label(text="Billboard") else: row.label(text="Billboard") - layout.separator() - - threebox( - layout, - CheckPropAllSame(bpy.context.selected_bones, "Visible"), - "alamo.set_visible", - "Visible", - ) - threebox( - layout, - CheckPropAllSame(bpy.context.selected_bones, "EnableProxy"), - "alamo.set_enableproxy", - "Enable Proxy", - ) - class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): bl_label = "" - bl_parent_id = "ALAMO_PT_EditBonePanel" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_parent_id = 'ALAMO_PT_EditBonePanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" bl_options = {"HIDE_HEADER"} @@ -485,35 +513,26 @@ def draw(self, context): bones = bpy.context.selected_bones layout = self.layout - all_same = CheckPropAllSame(bones, "EnableProxy") + all_same = True layout.active = False - if bpy.context.mode == "EDIT_ARMATURE" and all_same is not None: + if bpy.context.mode == "EDIT_ARMATURE": layout.active = all_same - if bpy.context.mode == "EDIT_ARMATURE" and all_same is None and len(bones) > 0: - layout.label(icon="ERROR", text="Inconsistent EnableProxy states") - - threebox( - layout, - all_same, - "alamo.set_proxyishidden", - "Proxy Visible", - ) - threebox( - layout, - all_same, - "alamo.set_altdecreasestayhidden", - "altDecreaseStayHidden", - ) - - col = layout.column(align=True) - col.label(text="Proxy Name") - row = col.row(align=True) + if not all_same: + layout.label(icon="ERROR", text="Inconsistent EnableProxy states.") + layout.label(icon="BLANK1", text="Change selection or set EnableProxy.") + + rowbuilder(layout, "Set Proxy Visibility:", ["alamo.show_proxy", "alamo.hide_proxy"], ["HIDE_OFF", "HIDE_ON"]) + + rowbuilder(layout, "Set altDecreaseStayHidden:", ["alamo.alt_decrease_stay_hidden_true", "alamo.alt_decrease_stay_hidden_false"], ["CHECKMARK", "X"]) + + row = layout.row(align=True) row.enabled = False - if bpy.context.mode == "EDIT_ARMATURE" and bones is not None and len(bones) > 0 and all_same: - row.prop(list(bones)[0], "ProxyName", text="") - row.operator("alamo.copy_proxy_name", text="", icon="DUPLICATE") + if bones is not None and len(bones) > 0: + row.prop(bones[0], "ProxyName") + row.operator('alamo.copy_proxy_name', text="", + icon="DUPLICATE") row.enabled = True else: row.label(text="ProxyName") @@ -522,8 +541,8 @@ def draw(self, context): class ALAMO_PT_AnimationPanel(bpy.types.Panel): bl_label = "Animation" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" def draw(self, context): @@ -531,33 +550,31 @@ def draw(self, context): layout.active = False if bpy.context.mode == "POSE": layout.active = True - - threebox( - layout, - check_anim_prop_all_same( - bpy.context.selected_pose_bones, "proxyIsHiddenAnimation" - ), - "alamo.set_keyframe_proxy", - "Keyframe Proxy Visible", - ) - - row = layout.row() - row.operator("alamo.delete_keyframe_proxy", text="", icon="X") - row.label(text="Delete Proxy Keyframes") + # layout.column().label(text="Keyframe Proxy Visibility:") + row = layout.row(align=True) + row.label(text="Keyframe Proxy Visibility:") + row.operator('alamo.show_keyframe_proxy', text="", + icon="HIDE_OFF") + row.operator('alamo.hide_keyframe_proxy', text="", + icon="HIDE_ON") + row.operator('alamo.remove_keyframe_proxy', text="", + icon="X") class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): bl_label = "Action End Frames" - bl_parent_id = "ALAMO_PT_AnimationPanel" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_parent_id = 'ALAMO_PT_AnimationPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" bl_options = {"HIDE_HEADER"} def draw(self, context): - layout = self.layout.column(align=True) - layout.label(text="Action End Frames") + layout = self.layout + if bpy.data.actions is not None and bpy.data.actions > 1: + row = layout.row() + row.label(text="Action End Frames") for action in bpy.data.actions: layout.prop(action, "AnimationEndFrame", text=action.name) @@ -565,10 +582,10 @@ def draw(self, context): class ALAMO_PT_DebugPanel(bpy.types.Panel): bl_label = "Debug" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' bl_category = "ALAMO" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} def draw(self, context): object = context.object @@ -576,11 +593,11 @@ def draw(self, context): scene = context.scene c = layout.column() - c.prop(scene.ActiveSkeleton, "skeletonEnum") + c.prop(scene.ActiveSkeleton, 'skeletonEnum') if type(object) != type(None): - if object.type == "MESH": - if bpy.context.mode == "OBJECT": + if(object.type == 'MESH'): + if bpy.context.mode == 'OBJECT': c.prop(object, "HasCollision") c.prop(object, "Hidden") @@ -588,20 +605,19 @@ def draw(self, context): if armature != None: hasChildConstraint = False for constraint in object.constraints: - if constraint.type == "CHILD_OF": + if constraint.type == 'CHILD_OF': hasChildConstraint = True if not hasChildConstraint: - self.layout.operator( - "create.constraint_bone", text="Create Constraint Bone" - ) + self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') action = utils.getCurrentAction() - if action != None: + if(action != None): c.prop(action, "AnimationEndFrame") + bone = bpy.context.active_bone if type(bone) != type(None): - if type(bpy.context.active_bone) is bpy.types.EditBone: + if(type(bpy.context.active_bone ) is bpy.types.EditBone): c.prop(bone.billboardMode, "billboardMode") c.prop(bone, "Visible") c.prop(bone, "EnableProxy") @@ -610,10 +626,7 @@ def draw(self, context): c.prop(bone, "altDecreaseStayHidden") c.prop(bone, "ProxyName") - elif ( - type(bpy.context.active_bone) is bpy.types.Bone - and bpy.context.mode == "POSE" - ): + elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): poseBone = object.pose.bones[bone.name] c.prop(poseBone, "proxyIsHiddenAnimation") @@ -630,24 +643,19 @@ def draw(self, context): layout = self.layout col = layout.column() - # if type(object) != type(None) and object.type == "MESH": - if type(object) != type(None) and object.type == "MESH": + if type(object) != type(None) and object.type == 'MESH': material = bpy.context.active_object.active_material if material is not None: # a None image is needed to represent not using a texture - if "None" not in bpy.data.images: - bpy.data.images.new(name="None", width=1, height=1) + if 'None' not in bpy.data.images: + bpy.data.images.new(name='None', width=1, height=1) col.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != "alDefault.fx": - shader_props = settings.material_parameter_dict[ - material.shaderList.shaderList - ] + if material.shaderList.shaderList != 'alDefault.fx': + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] for shader_prop in shader_props: # because contains() doesn't exist, apparently - if shader_prop.find("Texture") > -1: - layout.prop_search( - material, shader_prop, bpy.data, "images" - ) + if shader_prop.find('Texture') > -1: + layout.prop_search(material, shader_prop, bpy.data, "images") # layout.template_ID(material, shader_prop, new="image.new", open="image.open") @@ -657,31 +665,26 @@ class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "material" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} def draw(self, context): object = context.object layout = self.layout col = layout.column() - if type(object) != type(None) and object.type == "MESH": + if type(object) != type(None) and object.type == 'MESH': material = bpy.context.active_object.active_material - if ( - material is not None - and material.shaderList.shaderList != "alDefault.fx" - ): - shader_props = settings.material_parameter_dict[ - material.shaderList.shaderList - ] + if material is not None and material.shaderList.shaderList != 'alDefault.fx': + shader_props = settings.material_parameter_dict[material.shaderList.shaderList] for shader_prop in shader_props: # because contains() doesn't exist, apparently - if shader_prop.find("Texture") == -1: + if shader_prop.find('Texture') == -1: col.prop(material, shader_prop) class shaderListProperties(bpy.types.PropertyGroup): mode_options = [ - (shader_name, shader_name, "", "", index) + (shader_name, shader_name, '', '', index) for index, shader_name in enumerate(settings.material_parameter_dict) ] @@ -694,14 +697,14 @@ class shaderListProperties(bpy.types.PropertyGroup): class billboardListProperties(bpy.types.PropertyGroup): mode_options = [ - ("Disable", "Disable", "Description WIP", "", 0), - ("Parallel", "Parallel", "Description WIP", "", 1), - ("Face", "Face", "Description WIP", "", 2), - ("ZAxis View", "ZAxis View", "Description WIP", "", 3), - ("ZAxis Light", "ZAxis Light", "Description WIP", "", 4), - ("ZAxis Wind", "ZAxis Wind", "Description WIP", "", 5), - ("Sunlight Glow", "Sunlight Glow", "Description WIP", "", 6), - ("Sun", "Sun", "Description WIP", "", 7), + ("Disable", "Disable", 'Description WIP', '', 0), + ("Parallel", "Parallel", 'Description WIP', '', 1), + ("Face", "Face", 'Description WIP', '', 2), + ("ZAxis View", "ZAxis View", 'Description WIP', '', 3), + ("ZAxis Light", "ZAxis Light", 'Description WIP', '', 4), + ("ZAxis Wind", "ZAxis Wind", 'Description WIP', '', 5), + ("Sunlight Glow", "Sunlight Glow", 'Description WIP', '', 6), + ("Sun", "Sun", 'Description WIP', '', 7), ] billboardMode: bpy.props.EnumProperty( @@ -711,19 +714,28 @@ class billboardListProperties(bpy.types.PropertyGroup): ) -# Registration #################################################################################### +def proxy_name_update(self, context): + if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion + self.ProxyName = self.ProxyName.upper() + +# blender registration + + def menu_func_import(self, context): - self.layout.operator(import_alo.ALO_Importer.bl_idname, text=".ALO Importer") - self.layout.operator(import_ala.ALA_Importer.bl_idname, text=".ALA Importer") + self.layout.operator(import_alo.ALO_Importer.bl_idname, + text=".ALO Importer") + self.layout.operator(import_ala.ALA_Importer.bl_idname, + text=".ALA Importer") def menu_func_export(self, context): - self.layout.operator(export_alo.ALO_Exporter.bl_idname, text=".ALO Exporter") - self.layout.operator(export_ala.ALA_Exporter.bl_idname, text=".ALA Exporter") + self.layout.operator(export_alo.ALO_Exporter.bl_idname, + text=".ALO Exporter") + self.layout.operator(export_ala.ALA_Exporter.bl_idname, + text=".ALA Exporter") classes = ( - *classes, skeletonEnumClass, billboardListProperties, shaderListProperties, @@ -736,23 +748,29 @@ def menu_func_export(self, context): ValidateFileButton, CreateConstraintBone, createConstraintBoneButton, + SetBoneVisible, + SetBoneInvisible, + SetAltDecreaseStayHiddenTrue, + SetAltDecreaseStayHiddenFalse, + EnableProxyFalse, + EnableProxyTrue, + ProxyShow, + ProxyHide, CopyProxyNameToSelected, - keyframeProxySet, - keyframeProxyDelete, + keyframeProxyShow, + keyframeProxyHide, + keyframeProxyRemove, ALAMO_PT_ValidationPanel, - ALAMO_PT_ObjectPanel, ALAMO_PT_ArmatureSettingsPanel, ALAMO_PT_EditBonePanel, ALAMO_PT_EditBoneSubPanel, ALAMO_PT_AnimationPanel, ALAMO_PT_AnimationActionSubPanel, - ALAMO_PT_DebugPanel, + ALAMO_PT_DebugPanel ) - def register(): from bpy.utils import register_class - for cls in classes: register_class(cls) @@ -771,122 +789,95 @@ def register(): bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) bpy.types.EditBone.billboardMode = bpy.props.PointerProperty( - type=billboardListProperties - ) + type=billboardListProperties) bpy.types.Object.HasCollision = BoolProperty() bpy.types.Object.Hidden = BoolProperty() - bpy.types.Material.BaseTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.DetailTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.NormalTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.GlossTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.WaveTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.CloudTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default="None") - - bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) + bpy.types.Material.BaseTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.DetailTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty( + default='None') + bpy.types.Material.NormalTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.GlossTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.WaveTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.DistortionTexture = bpy.props.StringProperty( + default='None') + bpy.types.Material.CloudTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty( + default='None') + + bpy.types.Material.shaderList = bpy.props.PointerProperty( + type=shaderListProperties) bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) bpy.types.Material.Specular = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) bpy.types.Material.Shininess = bpy.props.FloatProperty( - min=0.0, max=255.0, default=32.0 - ) + min=0.0, max=255.0, default=32.0) bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0)) bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) bpy.types.Material.Color = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) - ) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) - ) + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5) - ) + min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5)) # shield shader properties bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.5 - ) + min=0.0, max=255.0, default=0.5) bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) + min=-255.0, max=255.0, default=1.0) bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) + min=-255.0, max=255.0, default=1.0) bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) + min=-255.0, max=255.0, default=1.0) bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15 - ) + min=-255.0, max=255.0, default=-0.15) bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15 - ) + min=-255.0, max=255.0, default=-0.15) bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.25 - ) + min=-255.0, max=255.0, default=-0.25) # tree properties bpy.types.Material.BendScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.4 - ) + min=-255.0, max=255.0, default=0.4) # grass properties bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) - ) + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) # skydome.fx properties bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.001 - ) + min=-255.0, max=255.0, default=0.001) bpy.types.Material.CloudScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) + min=-255.0, max=255.0, default=1.0) # nebula.fx properties bpy.types.Material.SFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.002 - ) + min=-255.0, max=255.0, default=0.002) bpy.types.Material.TFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.005 - ) + min=-255.0, max=255.0, default=0.005) bpy.types.Material.DistortionScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) + min=-255.0, max=255.0, default=1.0) # planet.fx properties bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) - ) + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) - ) + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) + min=-255.0, max=255.0, default=1.0) # tryplanar mapping properties bpy.types.Material.MappingScale = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1 - ) + min=0.0, max=255.0, default=0.1) bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1 - ) + min=0.0, max=255.0, default=0.1) def unregister(): from bpy.utils import unregister_class - for cls in reversed(classes): unregister_class(cls) diff --git a/io_alamo_tools/changelog.md b/io_alamo_tools/changelog.md index 7dee5ad..2274d16 100644 --- a/io_alamo_tools/changelog.md +++ b/io_alamo_tools/changelog.md @@ -5,6 +5,6 @@ ### Other - +- Proxy keyframe validation fixed and re-enabled - Validator now correctly checks for maximum *triangles*, rather than faces - Adjustments for easier scripting of import/export \ No newline at end of file diff --git a/io_alamo_tools/ui_wip.py b/io_alamo_tools/ui_wip.py new file mode 100644 index 0000000..01b9971 --- /dev/null +++ b/io_alamo_tools/ui_wip.py @@ -0,0 +1,100 @@ +import bpy + +def CheckObjectType(objects, type): + for object in objects: + if object.type != type: + return False + return True + +def CheckPropAllSame(objects, prop): + # True: All same, have value of True + # False: All same, have value of False + # None: Not all same + first_value = None + for object in objects: + if first_value is None: + first_value = getattr(object, prop) + elif getattr(object, prop) != first_value: + return None + return first_value + +def ShouldEnable(objects): + if objects is None or len(objects) <= 0: + return False + if bpy.context.mode == "OBJECT": + objects_same = CheckObjectType(objects, "MESH") + if not objects_same: + return False + return True + +def threebox(layout, all_same, operator, label): + icon="ERROR" + if all_same is None: + icon="LAYER_ACTIVE" + if all_same == True: + icon="CHECKMARK" + if all_same == False: + icon="BLANK1" + + row=layout.row() + row.operator(operator, text="", icon=icon) + row.label(text=label) + +def setProp(all_same, objects, prop): + set_to = False + if all_same in (None, False) : + set_to = True + + for object in objects: + setattr(object, prop, set_to) + +def propop_builder(prop, object_type): + + class PropOp(bpy.types.Operator): + bl_idname = "alamo.set_" + prop.lower() + bl_label = "Set " + prop + " for all selected objects" + bl_description = "" + + @classmethod + def poll(cls, context): + return ShouldEnable(eval("bpy.context.selected_" + object_type)) + + def execute(self, context): + setProp(CheckPropAllSame(eval("bpy.context.selected_" + object_type), prop), eval("bpy.context.selected_" + object_type), prop) + return {'FINISHED'} + + return PropOp + +SetCollision = propop_builder("HasCollision", "objects") +SetHidden = propop_builder("Hidden", "objects") + +class ALAMO_PT_ObjectPanel(bpy.types.Panel): + bl_label = "Object" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + layout.active = False + if bpy.context.mode == "OBJECT": + layout.active = True + + threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "HasCollision"), "alamo.set_hascollision", "Collision") + threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "Hidden"), "alamo.set_hidden", "Hidden") + + +def register(): + bpy.utils.register_class(SetCollision) + bpy.utils.register_class(SetHidden) + bpy.utils.register_class(ALAMO_PT_ObjectPanel) + + +def unregister(): + bpy.utils.unregister_class(SetCollision) + bpy.utils.unregister_class(SetHidden) + bpy.utils.unregister_class(ALAMO_PT_ObjectPanel) + + +if __name__ == "__main__": + register() diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index f3c1ba8..95858c6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -196,7 +196,7 @@ def validate(mesh_list): ] checklist_no_object = [ checkTranslationArmature, - # checkProxyKeyframes, + checkProxyKeyframes, ] for check in checklist: From 01f1a7ed6528aa9dbfd4df66363185d103a8d02d Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Wed, 27 Jul 2022 12:07:07 -0700 Subject: [PATCH 064/100] Revert "UI refresh WIP" This reverts commit fb3ba6aa1c41d705654b5747d0a20770e389f1c7. --- io_alamo_tools/__init__.py | 210 +++++++++++++++++++++++++---------- io_alamo_tools/changelog.md | 10 -- io_alamo_tools/export_ala.py | 26 ++--- io_alamo_tools/export_alo.py | 32 +++--- io_alamo_tools/import_ala.py | 6 +- io_alamo_tools/ui_wip.py | 100 ----------------- io_alamo_tools/validation.py | 10 +- 7 files changed, 190 insertions(+), 204 deletions(-) delete mode 100644 io_alamo_tools/changelog.md delete mode 100644 io_alamo_tools/ui_wip.py diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 799a707..8caa626 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,4 +1,3 @@ - from . import_alo import ALO_Importer from . export_ala import ALA_Exporter from . export_alo import ALO_Exporter @@ -18,7 +17,13 @@ from bpy.props import * import mathutils import bpy -import os +bl_info = { + "name": "ALAMO Tools", + "author": "Gaukler, evilbobthebob, inertial", + "version": (0, 0, 3, 4), + "blender": (2, 93, 0), + "category": "Import-Export" +} if "bpy" in locals(): import importlib @@ -36,14 +41,6 @@ from . import settings from . import utils -bl_info = { - "name": "ALAMO Tools", - "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 4), - "blender": (2, 93, 0), - "category": "Import-Export" -} - def CheckObjectType(objects, type): for object in objects: if object.type != type: @@ -129,7 +126,7 @@ def execute(self, context): return {'FINISHED'} -# Legacy version, included for the debug panel +# Legacy version, provided for the debug panel class createConstraintBoneButton(bpy.types.Operator): bl_idname = "create.constraint_bone" bl_label = "Create constraint bone" @@ -142,7 +139,7 @@ def execute(self, context): utils.setModeToEdit() bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) + bone.tail = bone.head + mathutils.Vector((0, 0, 1)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') @@ -185,7 +182,7 @@ def execute(self, context): utils.setModeToEdit() bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) + bone.tail = bone.head + mathutils.Vector((0, 0, 1)) bone.matrix = object.matrix_world object.location = mathutils.Vector((0.0, 0.0, 0.0)) object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') @@ -198,6 +195,70 @@ def execute(self, context): return {'FINISHED'} +class SetHasCollisionTrue(bpy.types.Operator): + bl_idname = "alamo.collision_true" + bl_label = "" + bl_description = "Set HasCollision to True for all selected objects" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_objects, "HasCollision", True) + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.HasCollision = True + return {'FINISHED'} + + +class SetHasCollisionFalse(bpy.types.Operator): + bl_idname = "alamo.collision_false" + bl_label = "" + bl_description = "Set HasCollision to False for all selected objects" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_objects, "HasCollision", False) + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.HasCollision = False + return {'FINISHED'} + + +class SetHiddenTrue(bpy.types.Operator): + bl_idname = "alamo.hidden_true" + bl_label = "" + bl_description = "Set Hidden to True for all selected objects" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_objects, "Hidden", True) + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.Hidden = True + return {'FINISHED'} + + +class SetHiddenFalse(bpy.types.Operator): + bl_idname = "alamo.hidden_false" + bl_label = "" + bl_description = "Set Hidden to False for all selected objects" + + @classmethod + def poll(cls, context): + return ShouldEnable(bpy.context.selected_objects, "Hidden", False) + + def execute(self, context): + objs = bpy.context.selected_objects + for obj in objs: + obj.Hidden = False + return {'FINISHED'} + + class SetBoneVisible(bpy.types.Operator): bl_idname = "alamo.bone_visible" bl_label = "" @@ -438,10 +499,10 @@ def rowbuilder(layout, label, operators, icons): row.operator(operators[1], text="", icon=icons[1]) -class ALAMO_PT_ValidationPanel(bpy.types.Panel): +class ALAMO_PT_ToolsPanel(bpy.types.Panel): - bl_label = "Validation" - bl_idname = "ALAMO_PT_ValidationPanel" + bl_label = "Alamo Properties" + bl_idname = "ALAMO_PT_ToolsPanel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -452,9 +513,31 @@ def draw(self, context): row.scale_y = 3.0 +class ALAMO_PT_ObjectPanel(bpy.types.Panel): + + bl_label = "Object Tools" + bl_parent_id = 'ALAMO_PT_ToolsPanel' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "ALAMO" + + def draw(self, context): + object = context.object + layout = self.layout + scene = context.scene + layout.active = False + if bpy.context.mode == "OBJECT": + layout.active = True + + rowbuilder(layout, "Set HasCollision:", ["alamo.collision_true", "alamo.collision_false"], ["CHECKMARK", "X"]) + + rowbuilder(layout, "Set Hidden:", ["alamo.hidden_false", "alamo.hidden_true"], ["HIDE_OFF", "HIDE_ON"]) + + class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): - bl_label = "Armature" + bl_label = "Armature Settings" + bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -472,7 +555,8 @@ def draw(self, context): class ALAMO_PT_EditBonePanel(bpy.types.Panel): - bl_label = "Bone" + bl_label = "Edit Bone Tools" + bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -486,18 +570,17 @@ def draw(self, context): layout.active = True rowbuilder(layout, "Set Bone Visibility:", ["alamo.bone_visible", "alamo.bone_invisible"], ["HIDE_OFF", "HIDE_ON"]) - rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) row = col.row() row.enabled = False if bones is not None: if len(bones) == 1: - row.prop(bones[0].billboardMode, "billboardMode", text="Billboard") + row.prop(bones[0].billboardMode, "billboardMode") row.enabled = True else: - row.label(text="Billboard") + row.label(text="billboardMode") else: - row.label(text="Billboard") + row.label(text="billboardMode") class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): @@ -507,7 +590,15 @@ class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" - bl_options = {"HIDE_HEADER"} + bl_options = {"HEADER_LAYOUT_EXPAND"} + + def draw_header(self, context): + layout = self.layout + layout.active = False + if bpy.context.mode == "EDIT_ARMATURE": + layout.active = True + + rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) def draw(self, context): bones = bpy.context.selected_bones @@ -541,6 +632,7 @@ def draw(self, context): class ALAMO_PT_AnimationPanel(bpy.types.Panel): bl_label = "Animation" + bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -568,13 +660,9 @@ class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" - bl_options = {"HIDE_HEADER"} def draw(self, context): layout = self.layout - if bpy.data.actions is not None and bpy.data.actions > 1: - row = layout.row() - row.label(text="Action End Frames") for action in bpy.data.actions: layout.prop(action, "AnimationEndFrame", text=action.name) @@ -582,6 +670,7 @@ def draw(self, context): class ALAMO_PT_DebugPanel(bpy.types.Panel): bl_label = "Debug" + bl_parent_id = 'ALAMO_PT_ToolsPanel' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "ALAMO" @@ -748,6 +837,10 @@ def menu_func_export(self, context): ValidateFileButton, CreateConstraintBone, createConstraintBoneButton, + SetHasCollisionTrue, + SetHasCollisionFalse, + SetHiddenTrue, + SetHiddenFalse, SetBoneVisible, SetBoneInvisible, SetAltDecreaseStayHiddenTrue, @@ -760,7 +853,8 @@ def menu_func_export(self, context): keyframeProxyShow, keyframeProxyHide, keyframeProxyRemove, - ALAMO_PT_ValidationPanel, + ALAMO_PT_ToolsPanel, + ALAMO_PT_ObjectPanel, ALAMO_PT_ArmatureSettingsPanel, ALAMO_PT_EditBonePanel, ALAMO_PT_EditBoneSubPanel, @@ -769,7 +863,9 @@ def menu_func_export(self, context): ALAMO_PT_DebugPanel ) + def register(): + from bpy.utils import register_class for cls in classes: register_class(cls) @@ -810,70 +906,70 @@ def register(): bpy.types.Material.shaderList = bpy.props.PointerProperty( type=shaderListProperties) bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) + min=0, max=1, size=4, default=(0, 0, 0, 0)) bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) + min=0, max=1, size=4, default=(1, 1, 1, 0)) bpy.types.Material.Specular = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) + min=0, max=1, size=4, default=(1, 1, 1, 0)) bpy.types.Material.Shininess = bpy.props.FloatProperty( - min=0.0, max=255.0, default=32.0) + min=0, max=255, default=32) bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0)) + min=0, max=1, size=4, default=(1, 1, 1, 0)) bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0)) + min=0, max=1, size=4, default=(0, 1, 0, 0)) bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) + min=0, max=1, size=4, default=(0, 0, 0, 0)) bpy.types.Material.Color = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) + min=0, max=1, size=4, default=(1, 1, 1, 1)) bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0)) + min=0, max=1, size=4, default=(0, 0, 0, 0)) bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5)) + min=0, max=1, size=3, default=(0.5, 0.5, 0.5)) # shield shader properties bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.5) + min=0, max=255, default=0.5) bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255, max=255, default=1) bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255, max=255, default=1) bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255, max=255, default=1) bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15) + min=-255, max=255, default=-0.15) bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15) + min=-255, max=255, default=-0.15) bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.25) + min=-255, max=255, default=-0.25) # tree properties bpy.types.Material.BendScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.4) + min=-255, max=255, default=0.4) # grass properties bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0)) + min=0, max=1, size=4, default=(1, 1, 1, 1)) # skydome.fx properties bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.001) + min=-255, max=255, default=0.001) bpy.types.Material.CloudScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255, max=255, default=1) # nebula.fx properties bpy.types.Material.SFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.002) + min=-255, max=255, default=0.002) bpy.types.Material.TFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.005) + min=-255, max=255, default=0.005) bpy.types.Material.DistortionScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255, max=255, default=1) # planet.fx properties bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) + min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5)) + min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0) + min=-255, max=255, default=1) # tryplanar mapping properties bpy.types.Material.MappingScale = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1) + min=0, max=255, default=0.1) bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1) + min=0, max=255, default=0.1) def unregister(): diff --git a/io_alamo_tools/changelog.md b/io_alamo_tools/changelog.md deleted file mode 100644 index 2274d16..0000000 --- a/io_alamo_tools/changelog.md +++ /dev/null @@ -1,10 +0,0 @@ -### UI Refresh -- Moved many UI elements to a three-state "checkbox", which will hopefully improve clarity -- Simplified some labels -- Removed an unnecessary level of layout nesting - -### Other - -- Proxy keyframe validation fixed and re-enabled -- Validator now correctly checks for maximum *triangles*, rather than faces -- Adjustments for easier scripting of import/export \ No newline at end of file diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index 134c3cf..d837a1a 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -47,9 +47,9 @@ def calculateTranslationScale(bone, translationOffset, armature, translationList scene.frame_set(0) pose = armature.pose.bones[bone.name] # get the pose bone # search for maximum translation - maxX = 0.0 - maxY = 0.0 - maxZ = 0.0 + maxX = 0 + maxY = 0 + maxZ = 0 action = utils.getCurrentAction() animLength = action.AnimationEndFrame @@ -69,12 +69,12 @@ def calculateTranslationScale(bone, translationOffset, armature, translationList scene.frame_set(scene.frame_current + 1) scene.frame_set(0) - if (maxX == 0.0): - maxX = 1.0 - if (maxY == 0.0): - maxY = 1.0 - if (maxZ == 0.0): - maxZ = 1.0 + if (maxX == 0): + maxX = 1 + if (maxY == 0): + maxY = 1 + if (maxZ == 0): + maxZ = 1 scaleX = maxX / 65535.0 scaleY = maxY / 65535.0 @@ -87,7 +87,7 @@ def calculateTranslationScale(bone, translationOffset, armature, translationList return mathutils.Vector(translationScale) else: - return mathutils.Vector((0.0, 0.0, 0.0)) + return mathutils.Vector((0, 0, 0)) def calculateTranslationOffset(bone, armature, translationList): scene = bpy.context.scene # current scene @@ -100,9 +100,9 @@ def calculateTranslationOffset(bone, armature, translationList): if (bone.name in translationList): scene.frame_set(0) - minX = 65535.0 - minY = 65535.0 - minZ = 65535.0 + minX = 65535 + minY = 65535 + minZ = 65535 action = utils.getCurrentAction() animLength = action.AnimationEndFrame diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index c995d9d..0d163ad 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -341,11 +341,11 @@ def check_if_material_is_used(material, mesh): class vertexData(): def __init__(self): - self.co = mathutils.Vector((0.0, 0.0, 0.0)) - self.uv = mathutils.Vector((0.0, 0.0)) - self.normal = mathutils.Vector((0.0, 0.0, 0.0)) - self.tangent = mathutils.Vector((0.0, 0.0, 0.0)) - self.bitangent = mathutils.Vector((0.0, 0.0, 0.0)) + self.co = mathutils.Vector((0, 0, 0)) + self.uv = mathutils.Vector((0, 0)) + self.normal = mathutils.Vector((0, 0, 0)) + self.tangent = mathutils.Vector((0, 0, 0)) + self.bitangent = mathutils.Vector((0, 0, 0)) self.bone_index = 0 self.face_index = 0 @@ -565,7 +565,7 @@ def shadow_vertex_face_data(bm, mesh, object): vertex.co = vert.co vertex.normal = face.normal - vertex.uv = mathutils.Vector((0.0, 0.0)) + vertex.uv = mathutils.Vector((0, 0)) meshVertex = mesh.vertices[vert.index] vertex.bone_index = getMaxWeightGroupIndex(meshVertex) @@ -591,15 +591,15 @@ def shadow_vertex_face_data(bm, mesh, object): f2v1 = per_face_vertex_id[face2.index][edge.verts[0].index] f2v2 = per_face_vertex_id[face2.index][edge.verts[1].index] - mid1 = mathutils.Vector((0.0, 0.0, 0.0)) + mid1 = mathutils.Vector((0, 0, 0)) for vert in face1.verts: mid1 += vert.co - mid1 /= 3.0 + mid1 /= 3 - mid2 = mathutils.Vector((0.0, 0.0, 0.0)) + mid2 = mathutils.Vector((0, 0, 0)) for vert in face2.verts: mid2 += vert.co - mid2 /= 3.0 + mid2 /= 3 face1v1 = edge.verts[0].co * 0.75 + mid1 * 0.25 @@ -911,8 +911,8 @@ def calc_bb_face(face): def calc_bb_two_bb(bb1, bb2): #takes two bbs and calculates combined bb - bb_min = mathutils.Vector((0.0, 0.0, 0.0)) - bb_max = mathutils.Vector((0.0, 0.0, 0.0)) + bb_min = mathutils.Vector((0, 0, 0)) + bb_max = mathutils.Vector((0, 0, 0)) for i in range (0, 3): bb_min[i] = min(bb1[0][i], bb2[0][i]) @@ -943,8 +943,8 @@ def calc_volume_bb(bb_min, bb_max): def calc_parent_space(parent, child): - child.parent_space_min = mathutils.Vector((0.0, 0.0, 0.0)) - child.parent_space_max = mathutils.Vector((0.0, 0.0, 0.0)) + child.parent_space_min = mathutils.Vector((0, 0, 0)) + child.parent_space_max = mathutils.Vector((0, 0, 0)) for i in range(0, 3): distance = parent.bb_max[i] - parent.bb_min[i] if distance != 0: @@ -1149,8 +1149,8 @@ def print_elements(self): bb = calc_bb_list(list_start) root = treeNode(bb[0], bb[1]) - root.parent_space_min = mathutils.Vector((0.0, 0.0, 0.0)) - root.parent_space_max = mathutils.Vector((255.0, 255.0, 255.0)) + root.parent_space_min = mathutils.Vector((0, 0, 0)) + root.parent_space_max = mathutils.Vector((255, 255, 255)) #print('median cut starts') median_cut(root, list_start) #print('parent space calculation starts') diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index 2e7fa2f..3f14c3e 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -94,7 +94,7 @@ def read_translation_data(data): counter_limit = data.num_frames * data.translation_block_size * 3 counter = 0 while (counter < counter_limit): - vector = mathutils.Vector((0.0, 0.0, 0.0)) + vector = mathutils.Vector((0, 0, 0)) vector[0] = utils.read_u_short(file.read(2)) vector[1] = utils.read_u_short(file.read(2)) vector[2] = utils.read_u_short(file.read(2)) @@ -194,7 +194,7 @@ def read_bone_animation_info(data): continue elif active_child_chunk == b"\06": file.seek(1, 1) # skip mini chunk size - vector = mathutils.Vector((0.0, 0.0, 0.0)) + vector = mathutils.Vector((0, 0, 0)) vector[0] = utils.read_float(file.read(4)) vector[1] = utils.read_float(file.read(4)) vector[2] = utils.read_float(file.read(4)) @@ -202,7 +202,7 @@ def read_bone_animation_info(data): continue elif active_child_chunk == b"\07": file.seek(1, 1) # skip mini chunk size - vector = mathutils.Vector((0.0, 0.0, 0.0)) + vector = mathutils.Vector((0, 0, 0)) vector[0] = utils.read_float(file.read(4)) vector[1] = utils.read_float(file.read(4)) vector[2] = utils.read_float(file.read(4)) diff --git a/io_alamo_tools/ui_wip.py b/io_alamo_tools/ui_wip.py deleted file mode 100644 index 01b9971..0000000 --- a/io_alamo_tools/ui_wip.py +++ /dev/null @@ -1,100 +0,0 @@ -import bpy - -def CheckObjectType(objects, type): - for object in objects: - if object.type != type: - return False - return True - -def CheckPropAllSame(objects, prop): - # True: All same, have value of True - # False: All same, have value of False - # None: Not all same - first_value = None - for object in objects: - if first_value is None: - first_value = getattr(object, prop) - elif getattr(object, prop) != first_value: - return None - return first_value - -def ShouldEnable(objects): - if objects is None or len(objects) <= 0: - return False - if bpy.context.mode == "OBJECT": - objects_same = CheckObjectType(objects, "MESH") - if not objects_same: - return False - return True - -def threebox(layout, all_same, operator, label): - icon="ERROR" - if all_same is None: - icon="LAYER_ACTIVE" - if all_same == True: - icon="CHECKMARK" - if all_same == False: - icon="BLANK1" - - row=layout.row() - row.operator(operator, text="", icon=icon) - row.label(text=label) - -def setProp(all_same, objects, prop): - set_to = False - if all_same in (None, False) : - set_to = True - - for object in objects: - setattr(object, prop, set_to) - -def propop_builder(prop, object_type): - - class PropOp(bpy.types.Operator): - bl_idname = "alamo.set_" + prop.lower() - bl_label = "Set " + prop + " for all selected objects" - bl_description = "" - - @classmethod - def poll(cls, context): - return ShouldEnable(eval("bpy.context.selected_" + object_type)) - - def execute(self, context): - setProp(CheckPropAllSame(eval("bpy.context.selected_" + object_type), prop), eval("bpy.context.selected_" + object_type), prop) - return {'FINISHED'} - - return PropOp - -SetCollision = propop_builder("HasCollision", "objects") -SetHidden = propop_builder("Hidden", "objects") - -class ALAMO_PT_ObjectPanel(bpy.types.Panel): - bl_label = "Object" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - layout = self.layout - layout.active = False - if bpy.context.mode == "OBJECT": - layout.active = True - - threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "HasCollision"), "alamo.set_hascollision", "Collision") - threebox(layout, CheckPropAllSame(bpy.context.selected_objects, "Hidden"), "alamo.set_hidden", "Hidden") - - -def register(): - bpy.utils.register_class(SetCollision) - bpy.utils.register_class(SetHidden) - bpy.utils.register_class(ALAMO_PT_ObjectPanel) - - -def unregister(): - bpy.utils.unregister_class(SetCollision) - bpy.utils.unregister_class(SetHidden) - bpy.utils.unregister_class(ALAMO_PT_ObjectPanel) - - -if __name__ == "__main__": - register() diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 95858c6..365acb6 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -98,9 +98,9 @@ def checkInvalidArmatureModifier(object): return error -# checks if the number of triangles exceeds max ushort, which is used to save the indices -def checkFaceNumber(object): - if sum(len(polygon.vertices) - 2 for polygon in object.data.polygons) > 65535: +# checks if the number of faces exceeds max ushort, which is used to save the indices +def checkFaceNumber(object): + if len(object.data.polygons) > 65535: return [({'ERROR'}, f'ALAMO - {object.name} exceeds maximum face limit; split mesh into multiple objects')] return [] @@ -162,7 +162,7 @@ def checkProxyKeyframes(): local_errors = [] actions = bpy.data.actions current_frame = bpy.context.scene.frame_current - armature = utils.findArmature() + armature = findArmature() if armature is not None: for action in actions: print(action.name) @@ -196,7 +196,7 @@ def validate(mesh_list): ] checklist_no_object = [ checkTranslationArmature, - checkProxyKeyframes, + # checkProxyKeyframes, # Disabled until it can be fixed ] for check in checklist: From ae9ccf97149edc0cb524b2fb631d07a00a44ba76 Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sat, 13 Aug 2022 16:40:56 -0700 Subject: [PATCH 065/100] maybe usable --- io_alamo_tools/UI.py | 725 ++++++++++++++++++++++++++++++++++ io_alamo_tools/UI_material.py | 265 +++++++++++++ io_alamo_tools/__init__.py | 84 ++++ io_alamo_tools/changelog.md | 1 + 4 files changed, 1075 insertions(+) create mode 100644 io_alamo_tools/UI.py create mode 100644 io_alamo_tools/UI_material.py create mode 100644 io_alamo_tools/changelog.md diff --git a/io_alamo_tools/UI.py b/io_alamo_tools/UI.py new file mode 100644 index 0000000..2b9a760 --- /dev/null +++ b/io_alamo_tools/UI.py @@ -0,0 +1,725 @@ +from . import validation +from . import settings +from . import utils +from bpy.types import ( + Panel, + Operator, + PropertyGroup, +) +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, +) +# from bpy.props import * +import mathutils +import bpy + + +classes = () + + +# UI Utilities #################################################################################### +def CheckObjectType(objects, target_type): + for obj in objects: + if obj.type != target_type: + return False + return True + + +def ShouldEnable(objects): + if objects is None or len(objects) <= 0: + return False + if bpy.context.mode == "OBJECT": + objects_same = CheckObjectType(objects, "MESH") + if not objects_same: + return False + return True + + +def CheckPropAllSame(objects, prop): + # True: All same, have value of True + # False: All same, have value of False + # None: Not all same + if objects is None or len(objects) <= 0: + return None + first_value = None + for object in objects: + if first_value is None: + first_value = getattr(object, prop) + elif getattr(object, prop) != first_value: + return None + return first_value + + +def check_anim_prop_all_same(bones, prop): + """Like check_prop_all_same(), but for animation""" + + all_same = [] + + if bones is not None and len(bones) > 0: + all_same = list(set(getattr(bone, prop) for bone in bones)) + + if len(all_same) == 1: + return all_same[0] + + return None + + +def threebox(layout, all_same, operator, label): + icon = "ERROR" + if all_same is None: + icon = "LAYER_ACTIVE" + if all_same is True: + icon = "BLANK1" + if all_same is False: + icon = "CHECKMARK" + + row = layout.row() + row.operator(operator, text="", icon=icon) + row.label(text=label) + +def setProp(all_same, objects, prop): + set_to = False + if all_same in (None, False): + set_to = True + + for object in objects: + setattr(object, prop, set_to) + + +def proxy_name_update(self, context): + if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion + self.ProxyName = self.ProxyName.upper() + + +def skeletonEnumCallback(scene, context): + armatures = [("None", "None", "", "", 0)] + counter = 1 + for arm in bpy.data.objects: # test if armature exists + if arm.type == "ARMATURE": + armatures.append((arm.name, arm.name, "", "", counter)) + counter += 1 + + return armatures + + +# Operators ####################################################################################### +class keyframeProxySet(bpy.types.Operator): + bl_idname = "alamo.set_keyframe_proxy" + bl_label = "" + bl_description = "Create a keyframe and set proxyIsHiddenAnimation for all selected bones" + + @classmethod + def poll(cls, context): + bones = bpy.context.selected_pose_bones + return bones is not None and len(bones) > 0 + + def execute(self, context): + bones = bpy.context.selected_pose_bones + + all_same = check_anim_prop_all_same(bones, "proxyIsHiddenAnimation") + operation = "SHOW" if all_same is True else "HIDE" + + for bone in list(bones): + if operation == "SHOW": + bone.proxyIsHiddenAnimation = False + if operation == "HIDE": + bone.proxyIsHiddenAnimation = True + + bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) + + for area in bpy.context.screen.areas: + if area.type == "DOPESHEET_EDITOR": + area.tag_redraw() + + return {"FINISHED"} + + +class keyframeProxyDelete(bpy.types.Operator): + bl_idname = "alamo.delete_keyframe_proxy" + bl_label = "" + bl_description = "Delete proxyIsHiddenAnimation keyframes for all selected bones" + + @classmethod + def poll(cls, context): + bones = bpy.context.selected_pose_bones + action = utils.getCurrentAction() + frame = bpy.context.scene.frame_current + + if bones is None or len(bones) <= 0 or action is None: + return False + + for bone in list(bones): + keyframes = action.fcurves.find( + bone.path_from_id() + ".proxyIsHiddenAnimation" + ) + if keyframes is not None: + for keyframe in keyframes.keyframe_points: + if int(keyframe.co[0]) == frame: + return True + + return False + + def execute(self, context): + bones = list(bpy.context.selected_pose_bones) + for bone in bones: + bone.keyframe_delete(data_path="proxyIsHiddenAnimation") + + for area in bpy.context.screen.areas: + if area.type in ("DOPESHEET_EDITOR", "VIEW_3D"): + area.tag_redraw() + + return {"FINISHED"} + + +class ValidateFileButton(bpy.types.Operator): + bl_idname = "alamo.validate_file" + bl_label = "Validate" + + def execute(self, context): + mesh_list = validation.create_export_list( + bpy.context.scene.collection, True, "DATA" + ) + + # check if export objects satisfy requirements (has material, UVs, ...) + messages = validation.validate(mesh_list) + + if messages is not None and len(messages) > 0: + for message in messages: + self.report(*message) + else: + self.report({"INFO"}, "ALAMO - Validation complete. No errors detected!") + return {"FINISHED"} + + +# Legacy version, included for the debug panel +class createConstraintBoneButton(bpy.types.Operator): + bl_idname = "create.constraint_bone" + bl_label = "Create constraint bone" + + def execute(self, context): + object = bpy.context.view_layer.objects.active + armature = utils.findArmature() + + bpy.context.view_layer.objects.active = armature + utils.setModeToEdit() + + bone = armature.data.edit_bones.new(object.name) + bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) + bone.matrix = object.matrix_world + object.location = mathutils.Vector((0.0, 0.0, 0.0)) + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") + constraint = object.constraints.new("CHILD_OF") + constraint.target = armature + constraint.subtarget = bone.name + + utils.setModeToObject() + bpy.context.view_layer.objects.active = object + return {"FINISHED"} + + +class CreateConstraintBone(bpy.types.Operator): + bl_idname = "alamo.create_constraint_bone" + bl_label = "Create Constraint Bone" + bl_description = "Adds a bone and parents the active object to it" + + @classmethod + def poll(cls, context): + object = bpy.context.object + if ( + type(object) != type(None) + and object.type == "MESH" + and bpy.context.mode == "OBJECT" + ): + armature = utils.findArmature() + if armature is not None: + has_child_constraint = False + for constraint in object.constraints: + if constraint.type == "CHILD_OF": + has_child_constraint = True + if not has_child_constraint: + return True + return False + + def execute(self, context): + object = bpy.context.view_layer.objects.active + armature = utils.findArmature() + + bpy.context.view_layer.objects.active = armature + utils.setModeToEdit() + + bone = armature.data.edit_bones.new(object.name) + bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) + bone.matrix = object.matrix_world + object.location = mathutils.Vector((0.0, 0.0, 0.0)) + object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") + constraint = object.constraints.new("CHILD_OF") + constraint.target = armature + constraint.subtarget = bone.name + + utils.setModeToObject() + bpy.context.view_layer.objects.active = object + return {"FINISHED"} + + +class CopyProxyNameToSelected(bpy.types.Operator): + bl_idname = "alamo.copy_proxy_name" + bl_label = "" + bl_description = "Copy Proxy Name to Selected Bones" + + @classmethod + def poll(cls, context): + return ( + bpy.context.selected_bones is not None + and len(bpy.context.selected_bones) > 1 + ) + + def execute(self, context): + bones = list(bpy.context.selected_bones) + name = bones[0].ProxyName + for bone in bones: + bone.ProxyName = name + return {"FINISHED"} + + +class skeletonEnumClass(PropertyGroup): + skeletonEnum: EnumProperty( + name="Active Skeleton", + description="skeleton that is exported", + items=skeletonEnumCallback, + ) + + +# Panels ########################################################################################## +class ALAMO_PT_SettingsPanel(bpy.types.Panel): + + bl_label = "Settings" + bl_idname = "ALAMO_PT_SettingsPanel" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + + row = layout.row() + row.operator("alamo.validate_file") + row.scale_y = 3.0 + + layout.separator() + + col = layout.column(align=True) + col.label(text="Active Skeleton") + row = col.row() + row.scale_y = 1.25 + row.prop(context.scene.ActiveSkeleton, "skeletonEnum", text="") + + +class ALAMO_PT_InfoPanel(bpy.types.Panel): + + bl_label = "Info" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + col = self.layout.column(align=True) + col.label(text="Hold ALT when clicking a checkbox") + col.label(text="to apply it to all selected objects/bones.") + + col = self.layout.column(align=True) + col.label(text="Buttons in Animation are NOT checkboxes.") + + +class ALAMO_PT_ObjectPanel(bpy.types.Panel): + + bl_label = "Object" + bl_context = "objectmode" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + + obj = context.object + if obj is not None and obj.type == 'MESH': + col = layout.column() + col.prop(obj, "HasCollision", text="Collision") + col.prop(obj, "Hidden") + col.scale_y = 1.25 + + layout.separator() + + col = layout.column() + col.scale_y = 1.25 + col.operator("alamo.create_constraint_bone") + + +class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): + + bl_label = "Armature" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + scene = context.scene + + +class ALAMO_PT_EditBonePanel(bpy.types.Panel): + + bl_label = "Bone" + bl_context = "armature_edit" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + bones = bpy.context.selected_bones + layout = self.layout + + layout.active = False + if bpy.context.mode == "EDIT_ARMATURE": + layout.active = True + + col = layout.column(align=True) + col.label(text="Billboard Mode") + row = col.row() + row.scale_y = 1.25 + row.enabled = False + if bones is not None: + if len(bones) == 1: + row.prop(list(bones)[0].billboardMode, "billboardMode", text="") + row.enabled = True + else: + row.label(text="Select a single bone") + else: + row.label(text="-") + + layout.separator() + + col = layout.column() + col.scale_y = 1.25 + col.active = False + if bpy.context.active_bone is not None: + col.active = True + col.prop(bpy.context.active_bone, "Visible") + col.prop(bpy.context.active_bone, "EnableProxy", text="Enable Proxy") + + +class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): + + bl_label = "" + bl_parent_id = "ALAMO_PT_EditBonePanel" + bl_context = "armature_edit" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + bl_options = {"HIDE_HEADER"} + + def draw(self, context): + bone = bpy.context.active_bone + bones = bpy.context.selected_bones + layout = self.layout + + all_same = CheckPropAllSame(bones, "EnableProxy") + + layout.active = False + if all_same is not None: + layout.active = all_same + + if all_same is None and len(bones) > 0: + layout.label(icon="ERROR", text="Inconsistent EnableProxy states") + + col = layout.column() + col.scale_y = 1.25 + col.active = False + if bone is not None and bone.EnableProxy: + col.active = True + col.prop(bone, "proxyIsHidden", text="Proxy Visible", invert_checkbox=True) + col.prop(bone, "altDecreaseStayHidden") + + col = layout.column(align=True) + col.label(text="Proxy Name") + row = col.row(align=True) + row.scale_y = 1.25 + row.enabled = False + if bones is not None and len(bones) > 0 and all_same: + row.prop(list(bones)[0], "ProxyName", text="") + row.operator("alamo.copy_proxy_name", text="", icon="DUPLICATE") + row.enabled = True + else: + row.label(text="ProxyName") + + +class ALAMO_PT_AnimationPanel(bpy.types.Panel): + + bl_label = "Animation" + bl_context = "posemode" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + + def draw(self, context): + layout = self.layout + + threebox( + layout, + check_anim_prop_all_same( + bpy.context.selected_pose_bones, "proxyIsHiddenAnimation" + ), + "alamo.set_keyframe_proxy", + "Keyframe Proxy Visible", + ) + + row = layout.row() + row.operator("alamo.delete_keyframe_proxy", text="", icon="X") + row.label(text="Delete Keyframes") + + # col = layout.column() + # col.scale_y = 1.25 + # col.prop(bpy.context.active_pose_bone, "proxyIsHiddenAnimation", text="Proxy Visible", invert_checkbox=True) + + +class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): + + bl_label = "Action End Frames" + bl_parent_id = "ALAMO_PT_AnimationPanel" + bl_context = "posemode" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + bl_options = {"HIDE_HEADER"} + + def draw(self, context): + layout = self.layout.column(align=True) + layout.label(text="Action End Frames") + for action in bpy.data.actions: + layout.prop(action, "AnimationEndFrame", text=action.name) + + +class ALAMO_PT_DebugPanel(bpy.types.Panel): + + bl_label = "Debug" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "ALAMO" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + object = context.object + layout = self.layout + scene = context.scene + c = layout.column() + + c.prop(scene.ActiveSkeleton, "skeletonEnum") + + if type(object) != type(None): + if object.type == "MESH": + if bpy.context.mode == "OBJECT": + c.prop(object, "HasCollision") + c.prop(object, "Hidden") + + armature = utils.findArmature() + if armature != None: + has_child_constraint = False + for constraint in object.constraints: + if constraint.type == "CHILD_OF": + has_child_constraint = True + if not has_child_constraint: + self.layout.operator( + "create.constraint_bone", text="Create Constraint Bone" + ) + + action = utils.getCurrentAction() + if action != None: + c.prop(action, "AnimationEndFrame") + + bone = bpy.context.active_bone + if type(bone) != type(None): + if type(bpy.context.active_bone) is bpy.types.EditBone: + c.prop(bone.billboardMode, "billboardMode") + c.prop(bone, "Visible") + c.prop(bone, "EnableProxy") + if bone.EnableProxy: + c.prop(bone, "proxyIsHidden") + c.prop(bone, "altDecreaseStayHidden") + c.prop(bone, "ProxyName") + + elif ( + type(bpy.context.active_bone) is bpy.types.Bone + and bpy.context.mode == "POSE" + ): + poseBone = object.pose.bones[bone.name] + c.prop(poseBone, "proxyIsHiddenAnimation") + + +class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): + bl_label = "Alamo Shader Properties" + bl_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + + def draw(self, context): + object = context.object + layout = self.layout + col = layout.column() + + # if type(object) != type(None) and object.type == "MESH": + if type(object) != type(None) and object.type == "MESH": + material = bpy.context.active_object.active_material + if material is not None: + # a None image is needed to represent not using a texture + if "None" not in bpy.data.images: + bpy.data.images.new(name="None", width=1, height=1) + col.prop(material.shaderList, "shaderList") + if material.shaderList.shaderList != "alDefault.fx": + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find("Texture") > -1: + layout.prop_search( + material, shader_prop, bpy.data, "images" + ) + # layout.template_ID(material, shader_prop, new="image.new", open="image.open") + + +class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): + bl_label = "Additional Properties" + bl_parent_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + object = context.object + layout = self.layout + col = layout.column() + + if type(object) != type(None) and object.type == "MESH": + material = bpy.context.active_object.active_material + if ( + material is not None + and material.shaderList.shaderList != "alDefault.fx" + ): + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find("Texture") == -1: + col.prop(material, shader_prop) + + +class shaderListProperties(bpy.types.PropertyGroup): + mode_options = [ + (shader_name, shader_name, "", "", index) + for index, shader_name in enumerate(settings.material_parameter_dict) + ] + + shaderList: bpy.props.EnumProperty( + items=mode_options, + description="Choose ingame Shader", + default="alDefault.fx", + ) + + +class billboardListProperties(bpy.types.PropertyGroup): + mode_options = [ + ("Disable", "Disable", "Description WIP", "", 0), + ("Parallel", "Parallel", "Description WIP", "", 1), + ("Face", "Face", "Description WIP", "", 2), + ("ZAxis View", "ZAxis View", "Description WIP", "", 3), + ("ZAxis Light", "ZAxis Light", "Description WIP", "", 4), + ("ZAxis Wind", "ZAxis Wind", "Description WIP", "", 5), + ("Sunlight Glow", "Sunlight Glow", "Description WIP", "", 6), + ("Sun", "Sun", "Description WIP", "", 7), + ] + + billboardMode: bpy.props.EnumProperty( + items=mode_options, + description="billboardMode", + default="Disable", + ) + + +# Registration #################################################################################### +classes = ( + *classes, + skeletonEnumClass, + billboardListProperties, + shaderListProperties, + ALAMO_PT_materialPropertyPanel, + ALAMO_PT_materialPropertySubPanel, + ValidateFileButton, + CreateConstraintBone, + createConstraintBoneButton, + CopyProxyNameToSelected, + keyframeProxySet, + keyframeProxyDelete, + ALAMO_PT_SettingsPanel, + ALAMO_PT_InfoPanel, + ALAMO_PT_ObjectPanel, + ALAMO_PT_EditBonePanel, + ALAMO_PT_EditBoneSubPanel, + ALAMO_PT_AnimationPanel, + ALAMO_PT_AnimationActionSubPanel, + # ALAMO_PT_DebugPanel, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Scene.ActiveSkeleton = PointerProperty(type=skeletonEnumClass) + bpy.types.Scene.modelFileName = StringProperty(name="") + + bpy.types.Action.AnimationEndFrame = IntProperty(default=24) + + bpy.types.EditBone.Visible = BoolProperty(default=True) + bpy.types.EditBone.EnableProxy = BoolProperty() + bpy.types.EditBone.proxyIsHidden = BoolProperty() + bpy.types.PoseBone.proxyIsHiddenAnimation = BoolProperty() + bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() + bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) + bpy.types.EditBone.billboardMode = bpy.props.PointerProperty( + type=billboardListProperties + ) + + bpy.types.Object.HasCollision = BoolProperty() + bpy.types.Object.Hidden = BoolProperty() + + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) + + bpy.types.Scene.ActiveSkeleton + + bpy.types.Action.AnimationEndFrame + + bpy.types.EditBone.Visible + bpy.types.EditBone.EnableProxy + bpy.types.EditBone.proxyIsHidden + bpy.types.PoseBone.proxyIsHiddenAnimation + bpy.types.EditBone.altDecreaseStayHidden + bpy.types.EditBone.ProxyName + bpy.types.EditBone.billboardMode + + bpy.types.Object.HasCollision + bpy.types.Object.Hidden + + +if __name__ == "__main__": + register() diff --git a/io_alamo_tools/UI_material.py b/io_alamo_tools/UI_material.py new file mode 100644 index 0000000..b0b0c07 --- /dev/null +++ b/io_alamo_tools/UI_material.py @@ -0,0 +1,265 @@ +from . import settings +from . import utils +from bpy.types import ( + Panel, + PropertyGroup, +) +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, +) +import bpy + + +class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): + bl_label = "Alamo Shader Properties" + bl_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + + def draw(self, context): + object = context.object + layout = self.layout + col = layout.column() + + # if type(object) != type(None) and object.type == "MESH": + if type(object) != type(None) and object.type == "MESH": + material = bpy.context.active_object.active_material + if material is not None: + # a None image is needed to represent not using a texture + if "None" not in bpy.data.images: + bpy.data.images.new(name="None", width=1, height=1) + col.prop(material.shaderList, "shaderList") + if material.shaderList.shaderList != "alDefault.fx": + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find("Texture") > -1: + layout.prop_search( + material, shader_prop, bpy.data, "images" + ) + # layout.template_ID(material, shader_prop, new="image.new", open="image.open") + + +class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): + bl_label = "Additional Properties" + bl_parent_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + object = context.object + layout = self.layout + col = layout.column() + + if type(object) != type(None) and object.type == "MESH": + material = bpy.context.active_object.active_material + if ( + material is not None + and material.shaderList.shaderList != "alDefault.fx" + ): + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find("Texture") == -1: + col.prop(material, shader_prop) + + +class shaderListProperties(bpy.types.PropertyGroup): + mode_options = [ + (shader_name, shader_name, "", "", index) + for index, shader_name in enumerate(settings.material_parameter_dict) + ] + + shaderList: bpy.props.EnumProperty( + items=mode_options, + description="Choose ingame Shader", + default="alDefault.fx", + ) + + +# Registration #################################################################################### +classes = ( + shaderListProperties, + ALAMO_PT_materialPropertyPanel, + ALAMO_PT_materialPropertySubPanel, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Material.BaseTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.DetailTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.NormalTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.GlossTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.WaveTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.CloudTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default="None") + + bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) + bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) + bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) + bpy.types.Material.Specular = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) + bpy.types.Material.Shininess = bpy.props.FloatProperty( + min=0.0, max=255.0, default=32.0 + ) + bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) + bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0) + ) + bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) + bpy.types.Material.Color = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) + ) + bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) + bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5) + ) + # shield shader properties + bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( + min=0.0, max=255.0, default=0.5 + ) + bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=-0.15 + ) + bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=-0.15 + ) + bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=-0.25 + ) + # tree properties + bpy.types.Material.BendScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.4 + ) + # grass properties + bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) + ) + # skydome.fx properties + bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.001 + ) + bpy.types.Material.CloudScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + # nebula.fx properties + bpy.types.Material.SFreq = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.002 + ) + bpy.types.Material.TFreq = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.005 + ) + bpy.types.Material.DistortionScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + # planet.fx properties + bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) + ) + bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) + ) + bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + # tryplanar mapping properties + bpy.types.Material.MappingScale = bpy.props.FloatProperty( + min=0.0, max=255.0, default=0.1 + ) + bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( + min=0.0, max=255.0, default=0.1 + ) + + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) + + bpy.types.Material.BaseTexture + bpy.types.Material.DetailTexture + bpy.types.Material.NormalTexture + bpy.types.Material.NormalDetailTexture + bpy.types.Material.GlossTexture + bpy.types.Material.WaveTexture + bpy.types.Material.DistortionTexture + bpy.types.Material.CloudTexture + bpy.types.Material.CloudNormalTexture + + bpy.types.Material.shaderList + bpy.types.Material.Emissive + bpy.types.Material.Diffuse + bpy.types.Material.Specular + bpy.types.Material.Shininess + bpy.types.Material.Colorization + bpy.types.Material.DebugColor + bpy.types.Material.UVOffset + bpy.types.Material.Color + bpy.types.Material.UVScrollRate + bpy.types.Material.DiffuseColor + # shield shader properties + bpy.types.Material.EdgeBrightness + bpy.types.Material.BaseUVScale + bpy.types.Material.WaveUVScale + bpy.types.Material.DistortUVScale + bpy.types.Material.BaseUVScrollRate + bpy.types.Material.WaveUVScrollRate + bpy.types.Material.DistortUVScrollRate + # tree properties + bpy.types.Material.BendScale + # grass properties + bpy.types.Material.Diffuse1 + # skydome.fx properties + bpy.types.Material.CloudScrollRate + bpy.types.Material.CloudScale + # nebula.fx properties + bpy.types.Material.SFreq + bpy.types.Material.TFreq + bpy.types.Material.DistortionScale + # planet.fx properties + bpy.types.Material.Atmosphere + bpy.types.Material.CityColor + bpy.types.Material.AtmospherePower + # tryplanar mapping properties + bpy.types.Material.MappingScale + bpy.types.Material.BlendSharpness + + +if __name__ == "__main__": + register() diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 8caa626..f4ce29d 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -25,6 +25,7 @@ "category": "Import-Export" } +<<<<<<< Updated upstream if "bpy" in locals(): import importlib importlib.reload(import_alo) @@ -47,6 +48,38 @@ def CheckObjectType(objects, type): return False return True +======= +import bpy +import importlib + +ADDON_FOLDER = 'io_alamo_tools' + +modules = ( + '.validation', + '.UI', + '.UI_material', + '.import_alo', + '.import_ala', + '.export_alo', + '.export_ala', + '.settings', + '.utils', +) + +def import_modules(): + for mod in modules: + #print('importing with importlib.import_module =' + str(mod) + "=") + importlib.import_module(mod, ADDON_FOLDER) + +def reimport_modules(): + ''' + Reimports the modules. Extremely useful while developing the addon + ''' + for mod in modules: + # Reimporting modules during addon development + want_reload_module = importlib.import_module(mod, ADDON_FOLDER) + importlib.reload(want_reload_module) +>>>>>>> Stashed changes def CheckPropAllSame(objects, prop): # True: All same, have value of True @@ -472,6 +505,7 @@ def execute(self, context): keyframeProxySet('REMOVE') return {'FINISHED'} +<<<<<<< Updated upstream def skeletonEnumCallback(scene, context): armatures = [('None', 'None', '', '', 0)] @@ -862,9 +896,37 @@ def menu_func_export(self, context): ALAMO_PT_AnimationActionSubPanel, ALAMO_PT_DebugPanel ) +======= +from . import validation +from . import UI +from . import UI_material +from . import import_alo +from . import import_ala +from . import export_alo +from . import export_ala +from . import settings +from . import utils +>>>>>>> Stashed changes + +classes = ( + import_alo.ALO_Importer, + import_ala.ALA_Importer, + export_alo.ALO_Exporter, + export_ala.ALA_Exporter, +) + +def menu_func_import(self, context): + self.layout.operator(import_alo.ALO_Importer.bl_idname, text=".ALO Importer") + self.layout.operator(import_ala.ALA_Importer.bl_idname, text=".ALA Importer") + + +def menu_func_export(self, context): + self.layout.operator(export_alo.ALO_Exporter.bl_idname, text=".ALO Exporter") + self.layout.operator(export_ala.ALA_Exporter.bl_idname, text=".ALA Exporter") def register(): +<<<<<<< Updated upstream from bpy.utils import register_class for cls in classes: @@ -1042,6 +1104,28 @@ def unregister(): # tryplanar mapping properties bpy.types.Material.MappingScale bpy.types.Material.BlendSharpness +======= + import_modules() + UI.register() + UI_material.register() + + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export) + + +def unregister(): + UI.unregister() + UI_material.unregister() + + for cls in reversed(classes): + bpy.utils.unregister_class(cls) + + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) +>>>>>>> Stashed changes if __name__ == "__main__": diff --git a/io_alamo_tools/changelog.md b/io_alamo_tools/changelog.md new file mode 100644 index 0000000..9697a75 --- /dev/null +++ b/io_alamo_tools/changelog.md @@ -0,0 +1 @@ +# ` ¯\_(ツ)_/¯ ` \ No newline at end of file From 3efeaff45ea4341cd9f226d21c5396444e88c05a Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 14 Aug 2022 10:45:32 -0700 Subject: [PATCH 066/100] fix git weirdness --- io_alamo_tools/__init__.py | 1024 +----------------------------------- 1 file changed, 1 insertion(+), 1023 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index f4ce29d..b639472 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -25,30 +25,6 @@ "category": "Import-Export" } -<<<<<<< Updated upstream -if "bpy" in locals(): - import importlib - importlib.reload(import_alo) - importlib.reload(import_ala) - importlib.reload(export_alo) - importlib.reload(export_ala) - importlib.reload(settings) - importlib.reload(utils) -else: - from . import import_alo - from . import import_ala - from . import export_alo - from . import export_ala - from . import settings - from . import utils - -def CheckObjectType(objects, type): - for object in objects: - if object.type != type: - return False - return True - -======= import bpy import importlib @@ -78,825 +54,8 @@ def reimport_modules(): for mod in modules: # Reimporting modules during addon development want_reload_module = importlib.import_module(mod, ADDON_FOLDER) - importlib.reload(want_reload_module) ->>>>>>> Stashed changes - -def CheckPropAllSame(objects, prop): - # True: All same, have value of True - # False: All same, have value of False - # None: Not all same - first_value = None - for object in objects: - if first_value is None: - first_value = getattr(object, prop) - elif getattr(object, prop) != first_value: - return None - return first_value - - -def ShouldEnable(objects, prop, set_to): - if objects is None or len(objects) <= 0: - return False - if bpy.context.mode == "OBJECT": - objects_same = CheckObjectType(objects, "MESH") - if not objects_same: - return False - all_same = CheckPropAllSame(objects, prop) - if all_same is not None: - if set_to: - return not all_same - else: - return all_same - return True - - -def ShouldEnableAnim(bones, prop, set_to): - if bones is None or len(bones) <= 0: - return False - - frame = bpy.context.scene.frame_current - action = bpy.context.object.animation_data.action - has_keyframes = False - has_previous_keyframe = False - for bone in bones: - keyframes = action.fcurves.find(bone.path_from_id() + "." + prop) - if keyframes is not None: - for keyframe in keyframes.keyframe_points: - if int(keyframe.co[0]) <= frame: - has_previous_keyframe = True - if int(keyframe.co[0]) == frame: - has_keyframes = True - break - - if set_to is None: - return has_keyframes - if not has_previous_keyframe: - return True - - all_same = CheckPropAllSame(bones, prop) - if all_same is not None: - if set_to: - return not all_same - return all_same - return True - - -class ValidateFileButton(bpy.types.Operator): - bl_idname = "alamo.validate_file" - bl_label = "Validate" - - def execute(self, context): - mesh_list = validation.create_export_list(bpy.context.scene.collection, True, "DATA") - - #check if export objects satisfy requirements (has material, UVs, ...) - messages = validation.validate(mesh_list) - - if messages is not None and len(messages) > 0: - for message in messages: - self.report(*message) - else: - self.report({'INFO'}, 'ALAMO - Validation complete. No errors detected!') - return {'FINISHED'} - - -# Legacy version, provided for the debug panel -class createConstraintBoneButton(bpy.types.Operator): - bl_idname = "create.constraint_bone" - bl_label = "Create constraint bone" - - def execute(self, context): - object = bpy.context.view_layer.objects.active - armature = utils.findArmature() - - bpy.context.view_layer.objects.active = armature - utils.setModeToEdit() - - bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0, 0, 1)) - bone.matrix = object.matrix_world - object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') - constraint = object.constraints.new('CHILD_OF') - constraint.target = armature - constraint.subtarget = bone.name - - utils.setModeToObject() - bpy.context.view_layer.objects.active = object - return {'FINISHED'} - - -class CreateConstraintBone(bpy.types.Operator): - bl_idname = "alamo.create_constraint_bone" - bl_label = "Create Constraint Bone" - - @classmethod - def poll(cls, context): - object = bpy.context.object - if ( - type(object) != type(None) - and object.type == 'MESH' - and bpy.context.mode == 'OBJECT' - ): - armature = utils.findArmature() - if armature is not None: - hasChildConstraint = False - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - hasChildConstraint = True - if not hasChildConstraint: - return True - return False - - def execute(self, context): - object = bpy.context.view_layer.objects.active - armature = utils.findArmature() - - bpy.context.view_layer.objects.active = armature - utils.setModeToEdit() - - bone = armature.data.edit_bones.new(object.name) - bone.tail = bone.head + mathutils.Vector((0, 0, 1)) - bone.matrix = object.matrix_world - object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') - constraint = object.constraints.new('CHILD_OF') - constraint.target = armature - constraint.subtarget = bone.name - - utils.setModeToObject() - bpy.context.view_layer.objects.active = object - return {'FINISHED'} - - -class SetHasCollisionTrue(bpy.types.Operator): - bl_idname = "alamo.collision_true" - bl_label = "" - bl_description = "Set HasCollision to True for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "HasCollision", True) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.HasCollision = True - return {'FINISHED'} - - -class SetHasCollisionFalse(bpy.types.Operator): - bl_idname = "alamo.collision_false" - bl_label = "" - bl_description = "Set HasCollision to False for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "HasCollision", False) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.HasCollision = False - return {'FINISHED'} - - -class SetHiddenTrue(bpy.types.Operator): - bl_idname = "alamo.hidden_true" - bl_label = "" - bl_description = "Set Hidden to True for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "Hidden", True) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.Hidden = True - return {'FINISHED'} - - -class SetHiddenFalse(bpy.types.Operator): - bl_idname = "alamo.hidden_false" - bl_label = "" - bl_description = "Set Hidden to False for all selected objects" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_objects, "Hidden", False) - - def execute(self, context): - objs = bpy.context.selected_objects - for obj in objs: - obj.Hidden = False - return {'FINISHED'} - - -class SetBoneVisible(bpy.types.Operator): - bl_idname = "alamo.bone_visible" - bl_label = "" - bl_description = "Set Visible to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "Visible", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.Visible = True - return {'FINISHED'} - - -class SetBoneInvisible(bpy.types.Operator): - bl_idname = "alamo.bone_invisible" - bl_label = "" - bl_description = "Set Visible to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "Visible", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.Visible = False - return {'FINISHED'} - - -class SetAltDecreaseStayHiddenTrue(bpy.types.Operator): - bl_idname = "alamo.alt_decrease_stay_hidden_true" - bl_label = "" - bl_description = "Set altDecreaseStayHidden to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.altDecreaseStayHidden = True - return {'FINISHED'} - - -class SetAltDecreaseStayHiddenFalse(bpy.types.Operator): - bl_idname = "alamo.alt_decrease_stay_hidden_false" - bl_label = "" - bl_description = "Set altDecreaseStayHidden to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "altDecreaseStayHidden", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.altDecreaseStayHidden = False - return {'FINISHED'} - - -class EnableProxyFalse(bpy.types.Operator): - bl_idname = "alamo.disable_proxy" - bl_label = "" - bl_description = "Set EnableProxy to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "EnableProxy", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.EnableProxy = False - return {'FINISHED'} - - -class EnableProxyTrue(bpy.types.Operator): - bl_idname = "alamo.enable_proxy" - bl_label = "" - bl_description = "Set EnableProxy to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "EnableProxy", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.EnableProxy = True - return {'FINISHED'} - - -class ProxyShow(bpy.types.Operator): - bl_idname = "alamo.show_proxy" - bl_label = "" - bl_description = "Set proxyIsHidden to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", False) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.proxyIsHidden = False - return {'FINISHED'} - - -class ProxyHide(bpy.types.Operator): - bl_idname = "alamo.hide_proxy" - bl_label = "" - bl_description = "Set proxyIsHidden to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnable(bpy.context.selected_bones, "proxyIsHidden", True) - - def execute(self, context): - bones = bpy.context.selected_bones - for bone in bones: - bone.proxyIsHidden = True - return {'FINISHED'} - - -class CopyProxyNameToSelected(bpy.types.Operator): - bl_idname = "alamo.copy_proxy_name" - bl_label = "" - bl_description = "Copy Proxy Name to Selected Bones" - - @classmethod - def poll(cls, context): - return bpy.context.selected_bones is not None and len(bpy.context.selected_bones) > 1 - - def execute(self, context): - bones = bpy.context.selected_bones - name = bones[0].ProxyName - for bone in bones: - bone.ProxyName = name - return {'FINISHED'} - - -def keyframeProxySet(operation): - bones = bpy.context.selected_pose_bones - action = bpy.context.object.animation_data.action - frame = bpy.context.scene.frame_current - keyframeType = "" - - for bone in bones: - if operation == 'SHOW': - bone.proxyIsHiddenAnimation = False - keyframeType = 'JITTER' - if operation == 'HIDE': - bone.proxyIsHiddenAnimation = True - keyframeType = 'EXTREME' - - if operation == 'REMOVE': - bone.keyframe_delete(data_path="proxyIsHiddenAnimation") - else: - bone.keyframe_insert(data_path="proxyIsHiddenAnimation", group=bone.name) - # for keyframe in action.fcurves.find(bone.path_from_id() + ".proxyIsHiddenAnimation").keyframe_points: - # if int(keyframe.co[0]) == frame: - # keyframe.type = keyframeType - - for area in bpy.context.screen.areas: - if area.type == 'DOPESHEET_EDITOR': - area.tag_redraw() - - -class keyframeProxyShow(bpy.types.Operator): - bl_idname = "alamo.show_keyframe_proxy" - bl_label = "" - bl_description = "Create a keyframe and set proxyIsHiddenAnimation to False for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", False) - - def execute(self, context): - keyframeProxySet('SHOW') - return {'FINISHED'} - - -class keyframeProxyHide(bpy.types.Operator): - bl_idname = "alamo.hide_keyframe_proxy" - bl_label = "" - bl_description = "Create a keyframe and set proxyIsHiddenAnimation to True for all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", True) - - def execute(self, context): - keyframeProxySet('HIDE') - return {'FINISHED'} - - -class keyframeProxyRemove(bpy.types.Operator): - bl_idname = "alamo.remove_keyframe_proxy" - bl_label = "" - bl_description = "Remove active keyframes from all selected bones" - - @classmethod - def poll(cls, context): - return ShouldEnableAnim(bpy.context.selected_pose_bones, "proxyIsHiddenAnimation", None) - - def execute(self, context): - keyframeProxySet('REMOVE') - return {'FINISHED'} - -<<<<<<< Updated upstream - -def skeletonEnumCallback(scene, context): - armatures = [('None', 'None', '', '', 0)] - counter = 1 - for arm in bpy.data.objects: # test if armature exists - if arm.type == 'ARMATURE': - armatures.append((arm.name, arm.name, '', '', counter)) - counter += 1 - - return armatures - - -class skeletonEnumClass(PropertyGroup): - skeletonEnum : EnumProperty( - name = 'Active Skeleton', - description = "skeleton that is exported", - items = skeletonEnumCallback - ) - - -def rowbuilder(layout, label, operators, icons): - row = layout.row(align=True) - row.label(text=label) - row.operator(operators[0], text="", icon=icons[0]) - row.operator(operators[1], text="", icon=icons[1]) + importlib.reload(want_reload_module) - -class ALAMO_PT_ToolsPanel(bpy.types.Panel): - - bl_label = "Alamo Properties" - bl_idname = "ALAMO_PT_ToolsPanel" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - row = self.layout.row() - row.operator("alamo.validate_file") - row.scale_y = 3.0 - - -class ALAMO_PT_ObjectPanel(bpy.types.Panel): - - bl_label = "Object Tools" - bl_parent_id = 'ALAMO_PT_ToolsPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - object = context.object - layout = self.layout - scene = context.scene - layout.active = False - if bpy.context.mode == "OBJECT": - layout.active = True - - rowbuilder(layout, "Set HasCollision:", ["alamo.collision_true", "alamo.collision_false"], ["CHECKMARK", "X"]) - - rowbuilder(layout, "Set Hidden:", ["alamo.hidden_false", "alamo.hidden_true"], ["HIDE_OFF", "HIDE_ON"]) - - -class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): - - bl_label = "Armature Settings" - bl_parent_id = 'ALAMO_PT_ToolsPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - object = context.object - layout = self.layout - scene = context.scene - # layout.active = False - - layout.prop(scene.ActiveSkeleton, 'skeletonEnum') - - layout.operator("alamo.create_constraint_bone") - - -class ALAMO_PT_EditBonePanel(bpy.types.Panel): - - bl_label = "Edit Bone Tools" - bl_parent_id = 'ALAMO_PT_ToolsPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - bones = bpy.context.selected_bones - layout = self.layout - col = layout.column() - layout.active = False - if bpy.context.mode == "EDIT_ARMATURE": - layout.active = True - - rowbuilder(layout, "Set Bone Visibility:", ["alamo.bone_visible", "alamo.bone_invisible"], ["HIDE_OFF", "HIDE_ON"]) - - row = col.row() - row.enabled = False - if bones is not None: - if len(bones) == 1: - row.prop(bones[0].billboardMode, "billboardMode") - row.enabled = True - else: - row.label(text="billboardMode") - else: - row.label(text="billboardMode") - - -class ALAMO_PT_EditBoneSubPanel(bpy.types.Panel): - - bl_label = "" - bl_parent_id = 'ALAMO_PT_EditBonePanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - bl_options = {"HEADER_LAYOUT_EXPAND"} - - def draw_header(self, context): - layout = self.layout - layout.active = False - if bpy.context.mode == "EDIT_ARMATURE": - layout.active = True - - rowbuilder(layout, "Set Proxy:", ["alamo.enable_proxy", "alamo.disable_proxy"], ["CHECKMARK", "X"]) - - def draw(self, context): - bones = bpy.context.selected_bones - layout = self.layout - - all_same = True - - layout.active = False - if bpy.context.mode == "EDIT_ARMATURE": - layout.active = all_same - - if not all_same: - layout.label(icon="ERROR", text="Inconsistent EnableProxy states.") - layout.label(icon="BLANK1", text="Change selection or set EnableProxy.") - - rowbuilder(layout, "Set Proxy Visibility:", ["alamo.show_proxy", "alamo.hide_proxy"], ["HIDE_OFF", "HIDE_ON"]) - - rowbuilder(layout, "Set altDecreaseStayHidden:", ["alamo.alt_decrease_stay_hidden_true", "alamo.alt_decrease_stay_hidden_false"], ["CHECKMARK", "X"]) - - row = layout.row(align=True) - row.enabled = False - if bones is not None and len(bones) > 0: - row.prop(bones[0], "ProxyName") - row.operator('alamo.copy_proxy_name', text="", - icon="DUPLICATE") - row.enabled = True - else: - row.label(text="ProxyName") - - -class ALAMO_PT_AnimationPanel(bpy.types.Panel): - - bl_label = "Animation" - bl_parent_id = 'ALAMO_PT_ToolsPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - layout = self.layout - layout.active = False - if bpy.context.mode == "POSE": - layout.active = True - # layout.column().label(text="Keyframe Proxy Visibility:") - row = layout.row(align=True) - row.label(text="Keyframe Proxy Visibility:") - row.operator('alamo.show_keyframe_proxy', text="", - icon="HIDE_OFF") - row.operator('alamo.hide_keyframe_proxy', text="", - icon="HIDE_ON") - row.operator('alamo.remove_keyframe_proxy', text="", - icon="X") - - -class ALAMO_PT_AnimationActionSubPanel(bpy.types.Panel): - - bl_label = "Action End Frames" - bl_parent_id = 'ALAMO_PT_AnimationPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - layout = self.layout - for action in bpy.data.actions: - layout.prop(action, "AnimationEndFrame", text=action.name) - - -class ALAMO_PT_DebugPanel(bpy.types.Panel): - - bl_label = "Debug" - bl_parent_id = 'ALAMO_PT_ToolsPanel' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - object = context.object - layout = self.layout - scene = context.scene - c = layout.column() - - c.prop(scene.ActiveSkeleton, 'skeletonEnum') - - if type(object) != type(None): - if(object.type == 'MESH'): - if bpy.context.mode == 'OBJECT': - c.prop(object, "HasCollision") - c.prop(object, "Hidden") - - armature = utils.findArmature() - if armature != None: - hasChildConstraint = False - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - hasChildConstraint = True - if not hasChildConstraint: - self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') - - action = utils.getCurrentAction() - if(action != None): - c.prop(action, "AnimationEndFrame") - - - bone = bpy.context.active_bone - if type(bone) != type(None): - if(type(bpy.context.active_bone ) is bpy.types.EditBone): - c.prop(bone.billboardMode, "billboardMode") - c.prop(bone, "Visible") - c.prop(bone, "EnableProxy") - if bone.EnableProxy: - c.prop(bone, "proxyIsHidden") - c.prop(bone, "altDecreaseStayHidden") - c.prop(bone, "ProxyName") - - elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): - poseBone = object.pose.bones[bone.name] - c.prop(poseBone, "proxyIsHiddenAnimation") - - -class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo Shader Properties" - bl_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - - def draw(self, context): - object = context.object - layout = self.layout - col = layout.column() - - if type(object) != type(None) and object.type == 'MESH': - material = bpy.context.active_object.active_material - if material is not None: - # a None image is needed to represent not using a texture - if 'None' not in bpy.data.images: - bpy.data.images.new(name='None', width=1, height=1) - col.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find('Texture') > -1: - layout.prop_search(material, shader_prop, bpy.data, "images") - # layout.template_ID(material, shader_prop, new="image.new", open="image.open") - - -class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): - bl_label = "Additional Properties" - bl_parent_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - object = context.object - layout = self.layout - col = layout.column() - - if type(object) != type(None) and object.type == 'MESH': - material = bpy.context.active_object.active_material - if material is not None and material.shaderList.shaderList != 'alDefault.fx': - shader_props = settings.material_parameter_dict[material.shaderList.shaderList] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find('Texture') == -1: - col.prop(material, shader_prop) - - -class shaderListProperties(bpy.types.PropertyGroup): - mode_options = [ - (shader_name, shader_name, '', '', index) - for index, shader_name in enumerate(settings.material_parameter_dict) - ] - - shaderList: bpy.props.EnumProperty( - items=mode_options, - description="Choose ingame Shader", - default="alDefault.fx", - ) - - -class billboardListProperties(bpy.types.PropertyGroup): - mode_options = [ - ("Disable", "Disable", 'Description WIP', '', 0), - ("Parallel", "Parallel", 'Description WIP', '', 1), - ("Face", "Face", 'Description WIP', '', 2), - ("ZAxis View", "ZAxis View", 'Description WIP', '', 3), - ("ZAxis Light", "ZAxis Light", 'Description WIP', '', 4), - ("ZAxis Wind", "ZAxis Wind", 'Description WIP', '', 5), - ("Sunlight Glow", "Sunlight Glow", 'Description WIP', '', 6), - ("Sun", "Sun", 'Description WIP', '', 7), - ] - - billboardMode: bpy.props.EnumProperty( - items=mode_options, - description="billboardMode", - default="Disable", - ) - - -def proxy_name_update(self, context): - if self.ProxyName != self.ProxyName.upper(): # prevents endless recursion - self.ProxyName = self.ProxyName.upper() - -# blender registration - - -def menu_func_import(self, context): - self.layout.operator(import_alo.ALO_Importer.bl_idname, - text=".ALO Importer") - self.layout.operator(import_ala.ALA_Importer.bl_idname, - text=".ALA Importer") - - -def menu_func_export(self, context): - self.layout.operator(export_alo.ALO_Exporter.bl_idname, - text=".ALO Exporter") - self.layout.operator(export_ala.ALA_Exporter.bl_idname, - text=".ALA Exporter") - - -classes = ( - skeletonEnumClass, - billboardListProperties, - shaderListProperties, - ALO_Importer, - ALA_Importer, - ALO_Exporter, - ALA_Exporter, - ALAMO_PT_materialPropertyPanel, - ALAMO_PT_materialPropertySubPanel, - ValidateFileButton, - CreateConstraintBone, - createConstraintBoneButton, - SetHasCollisionTrue, - SetHasCollisionFalse, - SetHiddenTrue, - SetHiddenFalse, - SetBoneVisible, - SetBoneInvisible, - SetAltDecreaseStayHiddenTrue, - SetAltDecreaseStayHiddenFalse, - EnableProxyFalse, - EnableProxyTrue, - ProxyShow, - ProxyHide, - CopyProxyNameToSelected, - keyframeProxyShow, - keyframeProxyHide, - keyframeProxyRemove, - ALAMO_PT_ToolsPanel, - ALAMO_PT_ObjectPanel, - ALAMO_PT_ArmatureSettingsPanel, - ALAMO_PT_EditBonePanel, - ALAMO_PT_EditBoneSubPanel, - ALAMO_PT_AnimationPanel, - ALAMO_PT_AnimationActionSubPanel, - ALAMO_PT_DebugPanel -) -======= from . import validation from . import UI from . import UI_material @@ -906,7 +65,6 @@ def menu_func_export(self, context): from . import export_ala from . import settings from . import utils ->>>>>>> Stashed changes classes = ( import_alo.ALO_Importer, @@ -926,185 +84,6 @@ def menu_func_export(self, context): def register(): -<<<<<<< Updated upstream - - from bpy.utils import register_class - for cls in classes: - register_class(cls) - - bpy.types.TOPBAR_MT_file_import.append(menu_func_import) - bpy.types.TOPBAR_MT_file_export.append(menu_func_export) - - bpy.types.Scene.ActiveSkeleton = PointerProperty(type=skeletonEnumClass) - bpy.types.Scene.modelFileName = StringProperty(name="") - - bpy.types.Action.AnimationEndFrame = IntProperty(default=24) - - bpy.types.EditBone.Visible = BoolProperty(default=True) - bpy.types.EditBone.EnableProxy = BoolProperty() - bpy.types.EditBone.proxyIsHidden = BoolProperty() - bpy.types.PoseBone.proxyIsHiddenAnimation = BoolProperty() - bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() - bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) - bpy.types.EditBone.billboardMode = bpy.props.PointerProperty( - type=billboardListProperties) - - bpy.types.Object.HasCollision = BoolProperty() - bpy.types.Object.Hidden = BoolProperty() - - bpy.types.Material.BaseTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DetailTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty( - default='None') - bpy.types.Material.NormalTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.GlossTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.WaveTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DistortionTexture = bpy.props.StringProperty( - default='None') - bpy.types.Material.CloudTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty( - default='None') - - bpy.types.Material.shaderList = bpy.props.PointerProperty( - type=shaderListProperties) - bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 0, 0, 0)) - bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 0)) - bpy.types.Material.Specular = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 0)) - bpy.types.Material.Shininess = bpy.props.FloatProperty( - min=0, max=255, default=32) - bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 0)) - bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 1, 0, 0)) - bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 0, 0, 0)) - bpy.types.Material.Color = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 1)) - bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0, 0, 0, 0)) - bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( - min=0, max=1, size=3, default=(0.5, 0.5, 0.5)) - # shield shader properties - bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( - min=0, max=255, default=0.5) - bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) - bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) - bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) - bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=-0.15) - bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=-0.15) - bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=-0.25) - # tree properties - bpy.types.Material.BendScale = bpy.props.FloatProperty( - min=-255, max=255, default=0.4) - # grass properties - bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(1, 1, 1, 1)) - # skydome.fx properties - bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( - min=-255, max=255, default=0.001) - bpy.types.Material.CloudScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) - # nebula.fx properties - bpy.types.Material.SFreq = bpy.props.FloatProperty( - min=-255, max=255, default=0.002) - bpy.types.Material.TFreq = bpy.props.FloatProperty( - min=-255, max=255, default=0.005) - bpy.types.Material.DistortionScale = bpy.props.FloatProperty( - min=-255, max=255, default=1) - # planet.fx properties - bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) - bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( - min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) - bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( - min=-255, max=255, default=1) - # tryplanar mapping properties - bpy.types.Material.MappingScale = bpy.props.FloatProperty( - min=0, max=255, default=0.1) - bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( - min=0, max=255, default=0.1) - - -def unregister(): - from bpy.utils import unregister_class - for cls in reversed(classes): - unregister_class(cls) - - bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) - bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) - - bpy.types.Scene.ActiveSkeleton - - bpy.types.Action.AnimationEndFrame - - bpy.types.EditBone.Visible - bpy.types.EditBone.EnableProxy - bpy.types.EditBone.proxyIsHidden - bpy.types.PoseBone.proxyIsHiddenAnimation - bpy.types.EditBone.altDecreaseStayHidden - bpy.types.EditBone.ProxyName - bpy.types.EditBone.billboardMode - - bpy.types.Object.HasCollision - bpy.types.Object.Hidden - - bpy.types.Material.BaseTexture - bpy.types.Material.DetailTexture - bpy.types.Material.NormalTexture - bpy.types.Material.NormalDetailTexture - bpy.types.Material.GlossTexture - bpy.types.Material.WaveTexture - bpy.types.Material.DistortionTexture - bpy.types.Material.CloudTexture - bpy.types.Material.CloudNormalTexture - - bpy.types.Material.shaderList - bpy.types.Material.Emissive - bpy.types.Material.Diffuse - bpy.types.Material.Specular - bpy.types.Material.Shininess - bpy.types.Material.Colorization - bpy.types.Material.DebugColor - bpy.types.Material.UVOffset - bpy.types.Material.Color - bpy.types.Material.UVScrollRate - bpy.types.Material.DiffuseColor - # shield shader properties - bpy.types.Material.EdgeBrightness - bpy.types.Material.BaseUVScale - bpy.types.Material.WaveUVScale - bpy.types.Material.DistortUVScale - bpy.types.Material.BaseUVScrollRate - bpy.types.Material.WaveUVScrollRate - bpy.types.Material.DistortUVScrollRate - # tree properties - bpy.types.Material.BendScale - # grass properties - bpy.types.Material.Diffuse1 - # skydome.fx properties - bpy.types.Material.CloudScrollRate - bpy.types.Material.CloudScale - # nebula.fx properties - bpy.types.Material.SFreq - bpy.types.Material.TFreq - bpy.types.Material.DistortionScale - # planet.fx properties - bpy.types.Material.Atmosphere - bpy.types.Material.CityColor - bpy.types.Material.AtmospherePower - # tryplanar mapping properties - bpy.types.Material.MappingScale - bpy.types.Material.BlendSharpness -======= import_modules() UI.register() UI_material.register() @@ -1125,7 +104,6 @@ def unregister(): bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) ->>>>>>> Stashed changes if __name__ == "__main__": From c73658018242159ef2a94553483a8559dcb5bfcd Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 14 Aug 2022 11:06:59 -0700 Subject: [PATCH 067/100] cleanup --- io_alamo_tools/UI.py | 159 ++++++---------------------------- io_alamo_tools/UI_material.py | 19 +--- 2 files changed, 31 insertions(+), 147 deletions(-) diff --git a/io_alamo_tools/UI.py b/io_alamo_tools/UI.py index 2b9a760..dd193c2 100644 --- a/io_alamo_tools/UI.py +++ b/io_alamo_tools/UI.py @@ -1,27 +1,16 @@ -from . import validation -from . import settings -from . import utils -from bpy.types import ( - Panel, - Operator, - PropertyGroup, -) from bpy.props import ( StringProperty, BoolProperty, IntProperty, - FloatProperty, EnumProperty, PointerProperty, ) -# from bpy.props import * +from . import validation +from . import utils import mathutils import bpy -classes = () - - # UI Utilities #################################################################################### def CheckObjectType(objects, target_type): for obj in objects: @@ -47,10 +36,10 @@ def CheckPropAllSame(objects, prop): if objects is None or len(objects) <= 0: return None first_value = None - for object in objects: + for obj in objects: if first_value is None: - first_value = getattr(object, prop) - elif getattr(object, prop) != first_value: + first_value = getattr(obj, prop) + elif getattr(obj, prop) != first_value: return None return first_value @@ -87,8 +76,8 @@ def setProp(all_same, objects, prop): if all_same in (None, False): set_to = True - for object in objects: - setattr(object, prop, set_to) + for obj in objects: + setattr(obj, prop, set_to) def proxy_name_update(self, context): @@ -202,23 +191,23 @@ class createConstraintBoneButton(bpy.types.Operator): bl_label = "Create constraint bone" def execute(self, context): - object = bpy.context.view_layer.objects.active + obj = bpy.context.view_layer.objects.active armature = utils.findArmature() bpy.context.view_layer.objects.active = armature utils.setModeToEdit() - bone = armature.data.edit_bones.new(object.name) + bone = armature.data.edit_bones.new(obj.name) bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) - bone.matrix = object.matrix_world - object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") - constraint = object.constraints.new("CHILD_OF") + bone.matrix = obj.matrix_world + obj.location = mathutils.Vector((0.0, 0.0, 0.0)) + obj.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") + constraint = obj.constraints.new("CHILD_OF") constraint.target = armature constraint.subtarget = bone.name utils.setModeToObject() - bpy.context.view_layer.objects.active = object + bpy.context.view_layer.objects.active = obj return {"FINISHED"} @@ -229,16 +218,16 @@ class CreateConstraintBone(bpy.types.Operator): @classmethod def poll(cls, context): - object = bpy.context.object + obj = bpy.context.object if ( - type(object) != type(None) - and object.type == "MESH" + obj is not None + and obj.type == "MESH" and bpy.context.mode == "OBJECT" ): armature = utils.findArmature() if armature is not None: has_child_constraint = False - for constraint in object.constraints: + for constraint in obj.constraints: if constraint.type == "CHILD_OF": has_child_constraint = True if not has_child_constraint: @@ -246,23 +235,23 @@ def poll(cls, context): return False def execute(self, context): - object = bpy.context.view_layer.objects.active + obj = bpy.context.view_layer.objects.active armature = utils.findArmature() bpy.context.view_layer.objects.active = armature utils.setModeToEdit() - bone = armature.data.edit_bones.new(object.name) + bone = armature.data.edit_bones.new(obj.name) bone.tail = bone.head + mathutils.Vector((0.0, 0.0, 1.0)) - bone.matrix = object.matrix_world - object.location = mathutils.Vector((0.0, 0.0, 0.0)) - object.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") - constraint = object.constraints.new("CHILD_OF") + bone.matrix = obj.matrix_world + obj.location = mathutils.Vector((0.0, 0.0, 0.0)) + obj.rotation_euler = mathutils.Euler((0.0, 0.0, 0.0), "XYZ") + constraint = obj.constraints.new("CHILD_OF") constraint.target = armature constraint.subtarget = bone.name utils.setModeToObject() - bpy.context.view_layer.objects.active = object + bpy.context.view_layer.objects.active = obj return {"FINISHED"} @@ -286,7 +275,7 @@ def execute(self, context): return {"FINISHED"} -class skeletonEnumClass(PropertyGroup): +class skeletonEnumClass(bpy.types.PropertyGroup): skeletonEnum: EnumProperty( name="Active Skeleton", description="skeleton that is exported", @@ -360,18 +349,6 @@ def draw(self, context): col.operator("alamo.create_constraint_bone") -class ALAMO_PT_ArmatureSettingsPanel(bpy.types.Panel): - - bl_label = "Armature" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "ALAMO" - - def draw(self, context): - layout = self.layout - scene = context.scene - - class ALAMO_PT_EditBonePanel(bpy.types.Panel): bl_label = "Bone" @@ -560,80 +537,6 @@ def draw(self, context): c.prop(poseBone, "proxyIsHiddenAnimation") -class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo Shader Properties" - bl_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - - def draw(self, context): - object = context.object - layout = self.layout - col = layout.column() - - # if type(object) != type(None) and object.type == "MESH": - if type(object) != type(None) and object.type == "MESH": - material = bpy.context.active_object.active_material - if material is not None: - # a None image is needed to represent not using a texture - if "None" not in bpy.data.images: - bpy.data.images.new(name="None", width=1, height=1) - col.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != "alDefault.fx": - shader_props = settings.material_parameter_dict[ - material.shaderList.shaderList - ] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find("Texture") > -1: - layout.prop_search( - material, shader_prop, bpy.data, "images" - ) - # layout.template_ID(material, shader_prop, new="image.new", open="image.open") - - -class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): - bl_label = "Additional Properties" - bl_parent_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - bl_options = {"DEFAULT_CLOSED"} - - def draw(self, context): - object = context.object - layout = self.layout - col = layout.column() - - if type(object) != type(None) and object.type == "MESH": - material = bpy.context.active_object.active_material - if ( - material is not None - and material.shaderList.shaderList != "alDefault.fx" - ): - shader_props = settings.material_parameter_dict[ - material.shaderList.shaderList - ] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find("Texture") == -1: - col.prop(material, shader_prop) - - -class shaderListProperties(bpy.types.PropertyGroup): - mode_options = [ - (shader_name, shader_name, "", "", index) - for index, shader_name in enumerate(settings.material_parameter_dict) - ] - - shaderList: bpy.props.EnumProperty( - items=mode_options, - description="Choose ingame Shader", - default="alDefault.fx", - ) - - class billboardListProperties(bpy.types.PropertyGroup): mode_options = [ ("Disable", "Disable", "Description WIP", "", 0), @@ -655,12 +558,8 @@ class billboardListProperties(bpy.types.PropertyGroup): # Registration #################################################################################### classes = ( - *classes, skeletonEnumClass, billboardListProperties, - shaderListProperties, - ALAMO_PT_materialPropertyPanel, - ALAMO_PT_materialPropertySubPanel, ValidateFileButton, CreateConstraintBone, createConstraintBoneButton, @@ -674,7 +573,7 @@ class billboardListProperties(bpy.types.PropertyGroup): ALAMO_PT_EditBoneSubPanel, ALAMO_PT_AnimationPanel, ALAMO_PT_AnimationActionSubPanel, - # ALAMO_PT_DebugPanel, + ALAMO_PT_DebugPanel, ) @@ -693,9 +592,7 @@ def register(): bpy.types.PoseBone.proxyIsHiddenAnimation = BoolProperty() bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) - bpy.types.EditBone.billboardMode = bpy.props.PointerProperty( - type=billboardListProperties - ) + bpy.types.EditBone.billboardMode = bpy.props.PointerProperty(type=billboardListProperties) bpy.types.Object.HasCollision = BoolProperty() bpy.types.Object.Hidden = BoolProperty() diff --git a/io_alamo_tools/UI_material.py b/io_alamo_tools/UI_material.py index b0b0c07..7142566 100644 --- a/io_alamo_tools/UI_material.py +++ b/io_alamo_tools/UI_material.py @@ -1,18 +1,5 @@ -from . import settings -from . import utils -from bpy.types import ( - Panel, - PropertyGroup, -) -from bpy.props import ( - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - PointerProperty, -) import bpy +from . import settings class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): @@ -57,11 +44,11 @@ class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): bl_options = {"DEFAULT_CLOSED"} def draw(self, context): - object = context.object + obj = context.object layout = self.layout col = layout.column() - if type(object) != type(None) and object.type == "MESH": + if obj is not None and obj.type == "MESH": material = bpy.context.active_object.active_material if ( material is not None From 2d72df1a19ade896be22345fbb871c669d4a4a5e Mon Sep 17 00:00:00 2001 From: inertial <8052204+inertials-revenge@users.noreply.github.com> Date: Sun, 14 Aug 2022 11:13:41 -0700 Subject: [PATCH 068/100] cleanup 2 --- io_alamo_tools/__init__.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index b639472..52c0fa6 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -1,8 +1,3 @@ -from . import_alo import ALO_Importer -from . export_ala import ALA_Exporter -from . export_alo import ALO_Exporter -from . import_ala import ALA_Importer -from . import validation from bpy.types import (Panel, Operator, PropertyGroup, @@ -17,6 +12,8 @@ from bpy.props import * import mathutils import bpy +import importlib + bl_info = { "name": "ALAMO Tools", "author": "Gaukler, evilbobthebob, inertial", @@ -25,9 +22,6 @@ "category": "Import-Export" } -import bpy -import importlib - ADDON_FOLDER = 'io_alamo_tools' modules = ( From ff1d363ca0438b922970c8b1d2165ec454a355ad Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 12 Jun 2023 16:53:20 -0400 Subject: [PATCH 069/100] Add MeshAdditiveOffset support --- io_alamo_tools/settings.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index ba334a5..596f8f7 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -40,7 +40,8 @@ "Tree.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BendScale", "BaseTexture", "NormalTexture"], "LightProxy.fx": ["Diffuse"], "MeshBumpSpecColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "NormalTexture", "GlossTexture"] + "BaseTexture", "NormalTexture", "GlossTexture"], + "MeshAdditiveOffset.fx": ["BaseTexture", "UVOffset"] } vertex_format_dict = { @@ -73,7 +74,8 @@ "TerrainMeshGloss.fx" : "alD3dVertNU2", "Tree.fx" : "alD3dVertNU2", "LightProxy.fx" : "alD3dVertNU2", - "MeshBumpSpecColorize.fx" : "alD3dVertNU2U3U3" + "MeshBumpSpecColorize.fx" : "alD3dVertNU2U3U3", + "MeshAdditiveOffset.fx" : "alD3dVertNU2" } billboard_array = {"Disable":0, "Parallel":1, "Face":2, "ZAxis View": 3, "ZAxis Light":4, "ZAxis Wind":5, "Sunlight Glow":6, "Sun":7} From 198b30320dc4274ea71b01581dcd6049ae647220 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 13 Jun 2023 15:22:44 -0400 Subject: [PATCH 070/100] Support for MeshAdditiveVColor --- io_alamo_tools/settings.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index 596f8f7..2f0c3ae 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -41,7 +41,8 @@ "LightProxy.fx": ["Diffuse"], "MeshBumpSpecColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", "BaseTexture", "NormalTexture", "GlossTexture"], - "MeshAdditiveOffset.fx": ["BaseTexture", "UVOffset"] + "MeshAdditiveOffset.fx": ["BaseTexture", "UVOffset"], + "MeshAdditiveVColor.fx": ["BaseTexture", "UVScrollRate", "Color"] } vertex_format_dict = { @@ -75,7 +76,8 @@ "Tree.fx" : "alD3dVertNU2", "LightProxy.fx" : "alD3dVertNU2", "MeshBumpSpecColorize.fx" : "alD3dVertNU2U3U3", - "MeshAdditiveOffset.fx" : "alD3dVertNU2" + "MeshAdditiveOffset.fx" : "alD3dVertNU2", + "MeshAdditiveVColor.fx" : "alD3dVertNU2C" } billboard_array = {"Disable":0, "Parallel":1, "Face":2, "ZAxis View": 3, "ZAxis Light":4, "ZAxis Wind":5, "Sunlight Glow":6, "Sun":7} From 0910cfb7833d2ee37d99e7e89bc20062d8f610a0 Mon Sep 17 00:00:00 2001 From: Yyhrs Date: Sat, 1 Jul 2023 12:13:55 +0200 Subject: [PATCH 071/100] Update __init__.py, export_alo.py, and 3 more files... --- io_alamo_tools/__init__.py | 4 ++ io_alamo_tools/export_alo.py | 23 +++++++--- io_alamo_tools/import_ala.py | 2 +- io_alamo_tools/import_alo.py | 24 ++++------ io_alamo_tools/settings.py | 87 +++++++++++++++++++----------------- 5 files changed, 76 insertions(+), 64 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 0fd34f1..66a1f9e 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -168,6 +168,8 @@ def draw(self, context): layout.prop_search(material, "WaveTexture", bpy.data, "images") elif property == 'DistortionTexture': layout.prop_search(material, "DistortionTexture", bpy.data, "images") + elif property == 'SpecularTexture': + layout.prop_search(material, "SpecularTexture", bpy.data, "images") else: c.prop(material, property) @@ -267,6 +269,7 @@ def register(): bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default='None') bpy.types.Material.NormalTexture = bpy.props.StringProperty(default='None') bpy.types.Material.GlossTexture = bpy.props.StringProperty(default='None') + bpy.types.Material.SpecularTexture = bpy.props.StringProperty(default='None') bpy.types.Material.WaveTexture = bpy.props.StringProperty(default='None') bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default='None') bpy.types.Material.CloudTexture = bpy.props.StringProperty(default='None') @@ -340,6 +343,7 @@ def unregister(): bpy.types.Material.GlossTexture bpy.types.Material.WaveTexture bpy.types.Material.DistortionTexture + bpy.types.Material.SpecularTexture bpy.types.Material.CloudTexture bpy.types.Material.CloudNormalTexture diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index b9f301e..e27bbbf 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -45,10 +45,14 @@ class ALO_Exporter(bpy.types.Operator): description="Export all animation actions as .ALA files, into the same directory", default=True, ) - exportHiddenObjects : BoolProperty( name="Export Hidden Objects", description="Export all objects, regardless of if they are hidden", + default=False, + ) + checkshadowObjects : BoolProperty( + name="Check Shadow Objects", + description="Export all shadows, regardless of if they have non-manifold", default=True, ) @@ -57,6 +61,7 @@ def draw(self, context): layout.prop(self, "exportAnimations") layout.prop(self, "exportHiddenObjects") + layout.prop(self, "checkshadowObjects") def execute(self, context): # execute() is called by blender when running the operator. @@ -510,17 +515,17 @@ def shadow_vertex_face_data(bm, mesh, object): # list of all faces using the current material per_face_vertex_id = {} + vertices = [] face_indices = [] + vertex_index_map = {} - alo_index = 0 + alo_index = 0 for face in bm.faces: indexArray = {} - for vert in face.verts: vertex = vertexData() - vertex.co = vert.co vertex.normal = face.normal key = (vert.index, face.normal.x, face.normal.y, face.normal.z) @@ -538,10 +543,8 @@ def shadow_vertex_face_data(bm, mesh, object): face_indices.append(alo_index) indexArray[vert.index] = alo_index alo_index += 1 - per_face_vertex_id[face.index] = indexArray - for edge in bm.edges: face1 = edge.link_faces[0] face2 = edge.link_faces[1] @@ -598,6 +601,7 @@ def shadow_vertex_face_data(bm, mesh, object): def create_sub_mesh_data_chunk(mesh, material, object, bone_name_per_alo_index): + print(mesh.name) sub_mesh_data_header = b"\x00\x00\x01\00" sub_mesh_data_header += utils.pack_int(0) file.write(sub_mesh_data_header) @@ -816,6 +820,8 @@ def create_mat_info_chunks(mat_name, material): chunk += mat_tex_chunk("WaveTexture", material.WaveTexture) elif (parameter == "DistortionTexture"): chunk += mat_tex_chunk("DistortionTexture", material.DistortionTexture) + elif (parameter == "SpecularTexture"): + chunk += mat_tex_chunk("SpecularTexture", material.SpecularTexture) elif (parameter == "UVScrollRate"): chunk += mat_float4_chunk("UVScrollRate", material.UVScrollRate) elif (parameter == "DistortionScale"): @@ -1459,7 +1465,9 @@ def exportAnimations(filePath): mesh_list = create_export_list(bpy.context.scene.collection) #check if export objects satisfy requirements (has material, UVs, ...) - checkShadowMesh(mesh_list) + + if(self.checkshadowObjects): + checkShadowMesh(mesh_list) checkUV(mesh_list) checkFaceNumber(mesh_list) checkAutosmooth(mesh_list) @@ -1473,6 +1481,7 @@ def exportAnimations(filePath): path = self.properties.filepath global file + file = open(path, 'wb') # open file in read binary mode bone_name_per_alo_index = create_skeleton() diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index 1a8f040..c6804f1 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -281,7 +281,7 @@ def create_animation(data): scene = bpy.context.scene scene.frame_start = 0 scene.frame_end = data.num_frames-1 - scene.render.fps = data.fps + scene.render.fps = int(data.fps) scene.frame_set(0) utils.setModeToObject() diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index aed447b..f677fbe 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -428,38 +428,29 @@ def set_up_textures(material): bsdf = nodes.new("ShaderNodeBsdfPrincipled") base_image_node = nodes.new("ShaderNodeTexImage") + invert_color_node = nodes.new("ShaderNodeInvert") normal_image_node = nodes.new("ShaderNodeTexImage") normal_map_node = nodes.new("ShaderNodeNormalMap") normal_map_node.space = 'TANGENT' - normal_map_node.uv_map = 'MainUV' - - uvmap = nodes.new("ShaderNodeUVMap") - uvmap.uv_map = "MainUV" if material.BaseTexture != 'None': - links.new(output.inputs['Surface'], bsdf.outputs['BSDF']) - links.new(bsdf.inputs['Base Color'], base_image_node.outputs['Color']) - links.new(bsdf.inputs['Alpha'], base_image_node.outputs['Alpha']) - links.new(base_image_node.inputs['Vector'], uvmap.outputs['UV']) - + links.new(bsdf.inputs['Base Color'], base_image_node.outputs['Color']) + links.new(invert_color_node.inputs['Color'], base_image_node.outputs['Alpha']) + links.new(bsdf.inputs['Alpha'], invert_color_node.outputs['Color']) if material.BaseTexture in bpy.data.images: diffuse_texture = bpy.data.images[material.BaseTexture] base_image_node.image = diffuse_texture - if material.NormalTexture != 'None': links.new(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) links.new(normal_map_node.outputs['Normal'], bsdf.inputs['Normal']) - links.new(normal_image_node.inputs['Vector'], uvmap.outputs['UV']) - if material.NormalTexture in bpy.data.images: normal_texture = bpy.data.images[material.NormalTexture] normal_image_node.image = normal_texture normal_image_node.image.colorspace_settings.name = 'Raw' - # distribute nodes along the x axis - for index, node in enumerate((uvmap, base_image_node, bsdf, output)): + for index, node in enumerate((base_image_node, bsdf, output)): node.location.x = 200.0 * index normal_map_node.location = bsdf.location @@ -556,7 +547,8 @@ def process_texture_chunk(material): file.seek(1, 1) # skip string end byte load_image(texture_name) - exec('material.' + texture_function_name + '= texture_name') + if texture_function_name != "SpecularTexture": + exec('material.' + texture_function_name + '= texture_name') def createUVLayer(layerName, uv_coordinates): vert_uvs = uv_coordinates @@ -801,7 +793,7 @@ def load_image(texture_name): def validate_material_prop(name): material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular","Shininess","Colorization" \ ,"DebugColor","UVOffset","Color","UVScrollRate","DiffuseColor","EdgeBrightness","BaseUVScale","WaveUVScale","DistortUVScale","BaseUVScrollRate","WaveUVScrollRate","DistortUVScrollRate","BendScale" \ - , "Diffuse1","CloudScrollRate","CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower"] + , "Diffuse1","CloudScrollRate","CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture"] if(name in material_props): return True diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index 74dc494..1cce19e 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -1,44 +1,48 @@ import bpy material_parameter_dict = { - "alDefault.fx": [""], - "BatchMeshAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "BatchMeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "Grass.fx": ["Emissive", "Diffuse", "Diffuse1", "BendScale", "BaseTexture"], - "MeshAdditive.fx": ["BaseTexture", "UVScrollRate", "Color"], - "MeshAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "MeshAlphaScroll.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "MeshBumpColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "NormalTexture"], - "MeshBumpColorizeVertex.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "NormalTexture"], - "MeshBumpColorizeDetail.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "DetailTexture", "NormalTexture", "MappingScale", "BlendSharpness", "NormalDetailTexture"], - "MeshBumpLight.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "NormalTexture"], - "MeshCollision.fx": ["Color"], - "MeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "MeshGlossColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture", "GlossTexture"], - "MeshShadowVolume.fx": ["DebugColor"], - "MeshShield.fx": ["Color", "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", - "BaseUVScrollRate", - "WaveUVScrollRate", "DistortUVScrollRate", "BaseTexture", "WaveTexture", "DistortionTexture"], - "Nebula.fx": ["BaseTexture", "UVScrollRate", "DistortionScale", "SFreq", "TFreq"], - "Planet.fx": ["Emissive", "Diffuse", "Specular", "Atmosphere", "CityColor", "AtmospherePower", - "CloudScrollRate", "BaseTexture", "NormalTexture", "CloudTexture", "CloudNormalTexture"], - "RSkinAdditive.fx": ["BaseTexture", "UVScrollRate", "Color"], - "RSkinAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "RSkinBumpColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "NormalTexture"], - "RSkinGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "RSkinGlossColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "BaseTexture", - "GlossTexture"], - "RSkinShadowVolume.fx": ["DebugColor"], - "Skydome.fx": ["Emissive", "CloudScrollRate", "CloudScale", "BaseTexture", "CloudTexture"], - "TerrainMeshBump.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture", "NormalTexture"], - "TerrainMeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "Tree.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BendScale", "BaseTexture", "NormalTexture"], - "LightProxy.fx": ["Diffuse"] + "alDefault.fx": [""], + "BatchMeshAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "BatchMeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "Grass.fx": ["Emissive", "Diffuse", "Diffuse1", "BendScale", "BaseTexture"], + "MeshAdditive.fx": ["BaseTexture", "UVScrollRate", "Color"], + "MeshAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "MeshAlphaScroll.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "MeshBumpColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture"], + "MeshBumpColorizeVertex.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture"], + "MeshBumpColorizeDetail.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "DetailTexture", "NormalTexture", "MappingScale", "BlendSharpness", "NormalDetailTexture"], + "MeshBumpLight.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture"], + "MeshCollision.fx": ["Color"], + "MeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "MeshGlossColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture", "GlossTexture"], + "MeshShadowVolume.fx": ["DebugColor"], + "MeshShield.fx": ["Color", "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", + "BaseUVScrollRate", + "WaveUVScrollRate", "DistortUVScrollRate", "BaseTexture", "WaveTexture", "DistortionTexture"], + "Nebula.fx": ["BaseTexture", "UVScrollRate", "DistortionScale", "SFreq", "TFreq"], + "Planet.fx": ["Emissive", "Diffuse", "Specular", "Atmosphere", "CityColor", "AtmospherePower", + "CloudScrollRate", "BaseTexture", "NormalTexture", "CloudTexture", "CloudNormalTexture"], + "RSkinAdditive.fx": ["BaseTexture", "UVScrollRate", "Color"], + "RSkinAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "RSkinBumpColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture"], + "RSkinGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "RSkinGlossColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "BaseTexture", + "GlossTexture"], + "RSkinShadowVolume.fx": ["DebugColor"], + "Skydome.fx": ["Emissive", "CloudScrollRate", "CloudScale", "BaseTexture", "CloudTexture"], + "TerrainMeshBump.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture", "NormalTexture"], + "TerrainMeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "Tree.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BendScale", "BaseTexture", "NormalTexture"], + "LightProxy.fx": ["Diffuse"], + "MeshAlphaGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "MeshBumpSpecGlowColorize.fx": ["Emissive", "Diffuse", "Colorization", "UVOffset", "BaseTexture", "NormalTexture", "SpecularTexture"], + "MeshBumpReflectColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture"] } vertex_format_dict = { @@ -70,12 +74,15 @@ "TerrainMeshBumb.fx" : "alD3dVertNU2U3U3", "TerrainMeshGloss.fx" : "alD3dVertNU2", "Tree.fx" : "alD3dVertNU2", - "LightProxy.fx" : "alD3dVertNU2" + "LightProxy.fx" : "alD3dVertNU2", + "MeshAlphaGloss.fx" : "alD3dVertNU2", + "MeshBumpSpecGlowColorize.fx" : "alD3dVertNU2C", + "MeshBumpReflectColorize.fx" : "alD3dVertNU2U3U3" } billboard_array = {"Disable":0, "Parallel":1, "Face":2, "ZAxis View": 3, "ZAxis Light":4, "ZAxis Wind":5, "Sunlight Glow":6, "Sun":7} -bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx"] +bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpReflectColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx"] rotation_curve_name = ['].rotation_quaternion', '].rotation_euler'] From 505854389f1cff103f158635959a7f2d4bbd3532 Mon Sep 17 00:00:00 2001 From: Yyhrs Date: Sat, 1 Jul 2023 21:29:42 +0200 Subject: [PATCH 072/100] Update import_alo.py --- io_alamo_tools/import_alo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index f677fbe..f65b15c 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -923,7 +923,9 @@ def loadAnimations(filePath): createdArmature.parent = armature createdArmature.parent_bone = self.parentName createdArmature.parent_type = 'BONE' - + for object in bpy.data.objects: + for constraint in object.constraints: + constraint.inverse_matrix = mathutils.Matrix.Identity(4) return {'FINISHED'} # this lets blender know the operator finished successfully. def invoke(self, context, event): From eadbdbd729a11908015ff5122dd09e094c8a488e Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 5 Sep 2023 14:29:53 -0400 Subject: [PATCH 073/100] Force FPS to integer value --- io_alamo_tools/import_ala.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index 3f14c3e..8aa158e 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -281,7 +281,7 @@ def create_animation(data): scene = bpy.context.scene scene.frame_start = 0 scene.frame_end = data.num_frames-1 - scene.render.fps = data.fps + scene.render.fps = int(data.fps) scene.frame_set(0) utils.setModeToObject() From 2cc6c688efea0e4dcced8448936d852d0e04a202 Mon Sep 17 00:00:00 2001 From: Yyhrs Date: Tue, 21 Nov 2023 21:32:18 +0100 Subject: [PATCH 074/100] Update import_alo.py --- io_alamo_tools/import_alo.py | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index f65b15c..4ca9f5b 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -289,7 +289,7 @@ def construct_mesh(currentMesh): face.material_index = subMeshCounter # create UVs - createUVLayer("MainUV", UVs) + createUVLayer("UVMap", UVs) assign_vertex_groups(animationMapping, currentMesh) return mesh @@ -714,24 +714,6 @@ def read_string_mini_chunk(): file.seek(1, 1) # skip end byte of name return string - def hideObject(object): - - #set correct area type via context overwrite - context_override = bpy.context.copy() - area = None - for window in bpy.context.window_manager.windows: - screen = window.screen - for a in screen.areas: - if a.type == 'VIEW_3D': - area = a - break - - context_override['area'] = area - - bpy.ops.object.select_all(context_override, action='DESELECT') - object.select_set(True) - bpy.ops.object.hide_view_set(context_override) - def hideLODs(): #hides all but the most detailed LOD in Blender for object in bpy.data.objects: @@ -745,7 +727,7 @@ def hideLODs(): #hide smaller LODS counter = 0 while(counter < lodCounter-1): - hideObject(bpy.data.objects[object.name[:-1] + str(counter)]) + bpy.data.objects[object.name[:-1] + str(counter)].hide_viewport = True counter += 1 #hide object if its a shadow or a collision @@ -754,13 +736,13 @@ def hideLODs(): if len(object.material_slots) != 0: shader = object.material_slots[0].material.shaderList.shaderList if(shader == 'MeshCollision.fx' or shader == 'RSkinShadowVolume.fx' or shader == 'MeshShadowVolume.fx'): - hideObject(object) + object.hide_viewport = True #hide objects that are set to not visible for object in bpy.data.objects: if (object.type == 'MESH'): if object.Hidden == True: - hideObject(object) + object.hide_viewport = True def deleteRoot(): armature = utils.findArmature() From 973d9af42b69d3051622c9b59718976e36dc3690 Mon Sep 17 00:00:00 2001 From: Yyhrs Date: Wed, 22 Nov 2023 22:01:08 +0100 Subject: [PATCH 075/100] Update import_alo.py --- io_alamo_tools/import_alo.py | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 4ca9f5b..52e0288 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -415,25 +415,16 @@ def read_animation_mapping(currentSubMesh): return animation_mapping def set_up_textures(material): - material.use_nodes = True nt = material.node_tree nodes = nt.nodes links = nt.links - - #clean up while(nodes): nodes.remove(nodes[0]) - output = nodes.new("ShaderNodeOutputMaterial") bsdf = nodes.new("ShaderNodeBsdfPrincipled") - base_image_node = nodes.new("ShaderNodeTexImage") invert_color_node = nodes.new("ShaderNodeInvert") normal_image_node = nodes.new("ShaderNodeTexImage") - - normal_map_node = nodes.new("ShaderNodeNormalMap") - normal_map_node.space = 'TANGENT' - if material.BaseTexture != 'None': links.new(output.inputs['Surface'], bsdf.outputs['BSDF']) links.new(bsdf.inputs['Base Color'], base_image_node.outputs['Color']) @@ -443,24 +434,20 @@ def set_up_textures(material): diffuse_texture = bpy.data.images[material.BaseTexture] base_image_node.image = diffuse_texture if material.NormalTexture != 'None': + normal_map_node = nodes.new("ShaderNodeNormalMap") + normal_map_node.space = 'TANGENT' links.new(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) links.new(normal_map_node.outputs['Normal'], bsdf.inputs['Normal']) if material.NormalTexture in bpy.data.images: normal_texture = bpy.data.images[material.NormalTexture] normal_image_node.image = normal_texture - normal_image_node.image.colorspace_settings.name = 'Raw' - # distribute nodes along the x axis - for index, node in enumerate((base_image_node, bsdf, output)): - node.location.x = 200.0 * index - - normal_map_node.location = bsdf.location - normal_map_node.location.y += 300.0 - - output.location.x += 200.0 - bsdf.location.x += 200.0 - - normal_image_node.location = base_image_node.location - normal_image_node.location.y += 300.0 + normal_image_node.location.y = -400 + normal_map_node.location.x = normal_image_node.location.x + normal_image_node.width + 100 + normal_map_node.location.y = -400 + invert_color_node.location.x = base_image_node.location.x + base_image_node.width + 100 + invert_color_node.location.y = -200 + bsdf.location.x = invert_color_node.location.x + invert_color_node.width + 100 + output.location.x = bsdf.location.x + bsdf.width + 100 def create_object(currentMesh): global mesh From 587e4341fe49702f959b2c3fba203d9b814eb077 Mon Sep 17 00:00:00 2001 From: Yyhrs Date: Wed, 22 Nov 2023 23:08:12 +0100 Subject: [PATCH 076/100] Update import_alo.py --- io_alamo_tools/import_alo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 52e0288..81bfbee 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -714,7 +714,7 @@ def hideLODs(): #hide smaller LODS counter = 0 while(counter < lodCounter-1): - bpy.data.objects[object.name[:-1] + str(counter)].hide_viewport = True + bpy.data.objects[object.name[:-1] + str(counter)].hide_set(True) counter += 1 #hide object if its a shadow or a collision @@ -723,13 +723,13 @@ def hideLODs(): if len(object.material_slots) != 0: shader = object.material_slots[0].material.shaderList.shaderList if(shader == 'MeshCollision.fx' or shader == 'RSkinShadowVolume.fx' or shader == 'MeshShadowVolume.fx'): - object.hide_viewport = True + object.hide_set(True) #hide objects that are set to not visible for object in bpy.data.objects: if (object.type == 'MESH'): if object.Hidden == True: - object.hide_viewport = True + object.hide_set(True) def deleteRoot(): armature = utils.findArmature() From 21a770a680e96ec185c4fc998246016f2ed62fd6 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Tue, 26 Mar 2024 19:06:37 +0100 Subject: [PATCH 077/100] Update export_alo.py --- io_alamo_tools/export_alo.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index e27bbbf..5e7ba45 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1374,11 +1374,6 @@ def checkFaceNumber(mesh_list): #checks if the number of faces exceeds max usho raise RuntimeError('Face number exceeds uShort max on object: ' + object.name + ' split mesh into multiple objects') return True - def checkAutosmooth(mesh_list): #prints a warning if Autosmooth is used - for object in mesh_list: - if object.data.use_auto_smooth: - print('Warning: ' + object.name + ' uses autosmooth, ingame shading might not match blender, use edgesplit instead') - def checkTranslation(mesh_list): #prints warning when translation is not default for object in mesh_list: if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or object.scale != mathutils.Vector((1.0, 1.0, 1.0)): @@ -1470,7 +1465,6 @@ def exportAnimations(filePath): checkShadowMesh(mesh_list) checkUV(mesh_list) checkFaceNumber(mesh_list) - checkAutosmooth(mesh_list) checkTranslation(mesh_list) checkTranslationArmature() checkInvalidArmatureModifier(mesh_list) From 6e4d9bf831c023ea3bdd8956973bf32fea69a7b9 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sun, 26 May 2024 21:43:21 +0200 Subject: [PATCH 078/100] Update armature.py --- io_alamo_tools/armature.py | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 io_alamo_tools/armature.py diff --git a/io_alamo_tools/armature.py b/io_alamo_tools/armature.py new file mode 100644 index 0000000..32824af --- /dev/null +++ b/io_alamo_tools/armature.py @@ -0,0 +1,68 @@ +centers = [] + +for object in bpy.context.selected_objects: + center = object.matrix_world @ (1 / 8 * sum((Vector(vector) for vector in object.bound_box), Vector())) + object.location -= center + centers += [center] + +print(centers) + +import random + +index = 0 + +for vector in centers: + print(str(index)) + bone = bpy.data.armatures['Armature'].edit_bones.new(str(index)) + bone.head = vector + bone.tail = vector + Vector((random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1))).normalized() + index += 1 + + + +import random + +index = 0 +objects = [] + +for object in bpy.context.selected_objects: + objects += [object] + +random.shuffle(objects) + +for object in objects: + constraint = object.constraints.new('CHILD_OF') + constraint.target = bpy.data.objects['Armature'] + constraint.subtarget = str(index) + constraint.inverse_matrix = Matrix.Identity(4) + index += 1 + + + + + + + + + + + + + + +for object in bpy.context.selected_objects: + for constraint in object.constraints: + constraint.inverse_matrix = Matrix.Identity(4) + + +import random + +index = 0 +objects = [] +for object in bpy.context.selected_objects: + objects += [object] +random.shuffle(objects) +for object in objects: + for constraint in object.constraints[:]: + constraint.subtarget = str(index) + index += 1 \ No newline at end of file From f03da597a2febf61cafdf54558b3273dbc1bd782 Mon Sep 17 00:00:00 2001 From: Andrew Fullard Date: Fri, 31 May 2024 12:04:50 -0400 Subject: [PATCH 079/100] Remove armature script --- io_alamo_tools/armature.py | 68 -------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 io_alamo_tools/armature.py diff --git a/io_alamo_tools/armature.py b/io_alamo_tools/armature.py deleted file mode 100644 index 32824af..0000000 --- a/io_alamo_tools/armature.py +++ /dev/null @@ -1,68 +0,0 @@ -centers = [] - -for object in bpy.context.selected_objects: - center = object.matrix_world @ (1 / 8 * sum((Vector(vector) for vector in object.bound_box), Vector())) - object.location -= center - centers += [center] - -print(centers) - -import random - -index = 0 - -for vector in centers: - print(str(index)) - bone = bpy.data.armatures['Armature'].edit_bones.new(str(index)) - bone.head = vector - bone.tail = vector + Vector((random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1))).normalized() - index += 1 - - - -import random - -index = 0 -objects = [] - -for object in bpy.context.selected_objects: - objects += [object] - -random.shuffle(objects) - -for object in objects: - constraint = object.constraints.new('CHILD_OF') - constraint.target = bpy.data.objects['Armature'] - constraint.subtarget = str(index) - constraint.inverse_matrix = Matrix.Identity(4) - index += 1 - - - - - - - - - - - - - - -for object in bpy.context.selected_objects: - for constraint in object.constraints: - constraint.inverse_matrix = Matrix.Identity(4) - - -import random - -index = 0 -objects = [] -for object in bpy.context.selected_objects: - objects += [object] -random.shuffle(objects) -for object in objects: - for constraint in object.constraints[:]: - constraint.subtarget = str(index) - index += 1 \ No newline at end of file From 9e7ea0130221e8e118829fcf0ded2ab8788c14c9 Mon Sep 17 00:00:00 2001 From: Andrew Fullard Date: Fri, 31 May 2024 12:22:20 -0400 Subject: [PATCH 080/100] Bump version and add 1138 as author --- io_alamo_tools/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 94343ed..c69c169 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -16,8 +16,8 @@ bl_info = { "name": "ALAMO Tools", - "author": "Gaukler, evilbobthebob, inertial", - "version": (0, 0, 3, 4), + "author": "Gaukler, evilbobthebob, inertial, 1138", + "version": (0, 0, 3, 5), "blender": (2, 93, 0), "category": "Import-Export" } From 848f0b71132af1d504873831e8fbdaf8dc8040bf Mon Sep 17 00:00:00 2001 From: Andrew Fullard Date: Fri, 31 May 2024 13:23:54 -0400 Subject: [PATCH 081/100] Fix errors caused by merge --- io_alamo_tools/UI_material.py | 506 +++++++++++++++++----------------- io_alamo_tools/__init__.py | 267 ------------------ io_alamo_tools/export_ala.py | 8 +- io_alamo_tools/export_alo.py | 107 +------ io_alamo_tools/import_alo.py | 139 +++++++--- io_alamo_tools/settings.py | 11 +- 6 files changed, 379 insertions(+), 659 deletions(-) diff --git a/io_alamo_tools/UI_material.py b/io_alamo_tools/UI_material.py index 7142566..7f5874e 100644 --- a/io_alamo_tools/UI_material.py +++ b/io_alamo_tools/UI_material.py @@ -1,252 +1,254 @@ -import bpy -from . import settings - - -class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo Shader Properties" - bl_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - - def draw(self, context): - object = context.object - layout = self.layout - col = layout.column() - - # if type(object) != type(None) and object.type == "MESH": - if type(object) != type(None) and object.type == "MESH": - material = bpy.context.active_object.active_material - if material is not None: - # a None image is needed to represent not using a texture - if "None" not in bpy.data.images: - bpy.data.images.new(name="None", width=1, height=1) - col.prop(material.shaderList, "shaderList") - if material.shaderList.shaderList != "alDefault.fx": - shader_props = settings.material_parameter_dict[ - material.shaderList.shaderList - ] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find("Texture") > -1: - layout.prop_search( - material, shader_prop, bpy.data, "images" - ) - # layout.template_ID(material, shader_prop, new="image.new", open="image.open") - - -class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): - bl_label = "Additional Properties" - bl_parent_id = "ALAMO_PT_materialPropertyPanel" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - bl_options = {"DEFAULT_CLOSED"} - - def draw(self, context): - obj = context.object - layout = self.layout - col = layout.column() - - if obj is not None and obj.type == "MESH": - material = bpy.context.active_object.active_material - if ( - material is not None - and material.shaderList.shaderList != "alDefault.fx" - ): - shader_props = settings.material_parameter_dict[ - material.shaderList.shaderList - ] - for shader_prop in shader_props: - # because contains() doesn't exist, apparently - if shader_prop.find("Texture") == -1: - col.prop(material, shader_prop) - - -class shaderListProperties(bpy.types.PropertyGroup): - mode_options = [ - (shader_name, shader_name, "", "", index) - for index, shader_name in enumerate(settings.material_parameter_dict) - ] - - shaderList: bpy.props.EnumProperty( - items=mode_options, - description="Choose ingame Shader", - default="alDefault.fx", - ) - - -# Registration #################################################################################### -classes = ( - shaderListProperties, - ALAMO_PT_materialPropertyPanel, - ALAMO_PT_materialPropertySubPanel, -) - - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Material.BaseTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.DetailTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.NormalTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.GlossTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.WaveTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.CloudTexture = bpy.props.StringProperty(default="None") - bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default="None") - - bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) - bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) - ) - bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) - ) - bpy.types.Material.Specular = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) - ) - bpy.types.Material.Shininess = bpy.props.FloatProperty( - min=0.0, max=255.0, default=32.0 - ) - bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) - ) - bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0) - ) - bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) - ) - bpy.types.Material.Color = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) - ) - bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) - ) - bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5) - ) - # shield shader properties - bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.5 - ) - bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) - bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) - bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) - bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15 - ) - bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.15 - ) - bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=-0.25 - ) - # tree properties - bpy.types.Material.BendScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.4 - ) - # grass properties - bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) - ) - # skydome.fx properties - bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.001 - ) - bpy.types.Material.CloudScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) - # nebula.fx properties - bpy.types.Material.SFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.002 - ) - bpy.types.Material.TFreq = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=0.005 - ) - bpy.types.Material.DistortionScale = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) - # planet.fx properties - bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) - ) - bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( - min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) - ) - bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( - min=-255.0, max=255.0, default=1.0 - ) - # tryplanar mapping properties - bpy.types.Material.MappingScale = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1 - ) - bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( - min=0.0, max=255.0, default=0.1 - ) - - -def unregister(): - for cls in reversed(classes): - bpy.utils.unregister_class(cls) - - bpy.types.Material.BaseTexture - bpy.types.Material.DetailTexture - bpy.types.Material.NormalTexture - bpy.types.Material.NormalDetailTexture - bpy.types.Material.GlossTexture - bpy.types.Material.WaveTexture - bpy.types.Material.DistortionTexture - bpy.types.Material.CloudTexture - bpy.types.Material.CloudNormalTexture - - bpy.types.Material.shaderList - bpy.types.Material.Emissive - bpy.types.Material.Diffuse - bpy.types.Material.Specular - bpy.types.Material.Shininess - bpy.types.Material.Colorization - bpy.types.Material.DebugColor - bpy.types.Material.UVOffset - bpy.types.Material.Color - bpy.types.Material.UVScrollRate - bpy.types.Material.DiffuseColor - # shield shader properties - bpy.types.Material.EdgeBrightness - bpy.types.Material.BaseUVScale - bpy.types.Material.WaveUVScale - bpy.types.Material.DistortUVScale - bpy.types.Material.BaseUVScrollRate - bpy.types.Material.WaveUVScrollRate - bpy.types.Material.DistortUVScrollRate - # tree properties - bpy.types.Material.BendScale - # grass properties - bpy.types.Material.Diffuse1 - # skydome.fx properties - bpy.types.Material.CloudScrollRate - bpy.types.Material.CloudScale - # nebula.fx properties - bpy.types.Material.SFreq - bpy.types.Material.TFreq - bpy.types.Material.DistortionScale - # planet.fx properties - bpy.types.Material.Atmosphere - bpy.types.Material.CityColor - bpy.types.Material.AtmospherePower - # tryplanar mapping properties - bpy.types.Material.MappingScale - bpy.types.Material.BlendSharpness - - -if __name__ == "__main__": - register() +import bpy +from . import settings + + +class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): + bl_label = "Alamo Shader Properties" + bl_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + + def draw(self, context): + object = context.object + layout = self.layout + col = layout.column() + + # if type(object) != type(None) and object.type == "MESH": + if type(object) != type(None) and object.type == "MESH": + material = bpy.context.active_object.active_material + if material is not None: + # a None image is needed to represent not using a texture + if "None" not in bpy.data.images: + bpy.data.images.new(name="None", width=1, height=1) + col.prop(material.shaderList, "shaderList") + if material.shaderList.shaderList != "alDefault.fx": + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find("Texture") > -1: + layout.prop_search( + material, shader_prop, bpy.data, "images" + ) + # layout.template_ID(material, shader_prop, new="image.new", open="image.open") + + +class ALAMO_PT_materialPropertySubPanel(bpy.types.Panel): + bl_label = "Additional Properties" + bl_parent_id = "ALAMO_PT_materialPropertyPanel" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + obj = context.object + layout = self.layout + col = layout.column() + + if obj is not None and obj.type == "MESH": + material = bpy.context.active_object.active_material + if ( + material is not None + and material.shaderList.shaderList != "alDefault.fx" + ): + shader_props = settings.material_parameter_dict[ + material.shaderList.shaderList + ] + for shader_prop in shader_props: + # because contains() doesn't exist, apparently + if shader_prop.find("Texture") == -1: + col.prop(material, shader_prop) + + +class shaderListProperties(bpy.types.PropertyGroup): + mode_options = [ + (shader_name, shader_name, "", "", index) + for index, shader_name in enumerate(settings.material_parameter_dict) + ] + + shaderList: bpy.props.EnumProperty( + items=mode_options, + description="Choose ingame Shader", + default="alDefault.fx", + ) + + +# Registration #################################################################################### +classes = ( + shaderListProperties, + ALAMO_PT_materialPropertyPanel, + ALAMO_PT_materialPropertySubPanel, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Material.BaseTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.DetailTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.NormalTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.GlossTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.SpecularTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.WaveTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.CloudTexture = bpy.props.StringProperty(default="None") + bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default="None") + + bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) + bpy.types.Material.Emissive = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) + bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) + bpy.types.Material.Specular = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) + bpy.types.Material.Shininess = bpy.props.FloatProperty( + min=0.0, max=255.0, default=32.0 + ) + bpy.types.Material.Colorization = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 0.0) + ) + bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 1.0, 0.0, 0.0) + ) + bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) + bpy.types.Material.Color = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) + ) + bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.0, 0.0, 0.0, 0.0) + ) + bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=3, default=(0.5, 0.5, 0.5) + ) + # shield shader properties + bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty( + min=0.0, max=255.0, default=0.5 + ) + bpy.types.Material.BaseUVScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + bpy.types.Material.WaveUVScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + bpy.types.Material.DistortUVScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=-0.15 + ) + bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=-0.15 + ) + bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=-0.25 + ) + # tree properties + bpy.types.Material.BendScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.4 + ) + # grass properties + bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(1.0, 1.0, 1.0, 1.0) + ) + # skydome.fx properties + bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.001 + ) + bpy.types.Material.CloudScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + # nebula.fx properties + bpy.types.Material.SFreq = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.002 + ) + bpy.types.Material.TFreq = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.005 + ) + bpy.types.Material.DistortionScale = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + # planet.fx properties + bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) + ) + bpy.types.Material.CityColor = bpy.props.FloatVectorProperty( + min=0.0, max=1.0, size=4, default=(0.5, 0.5, 0.5, 0.5) + ) + bpy.types.Material.AtmospherePower = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=1.0 + ) + # tryplanar mapping properties + bpy.types.Material.MappingScale = bpy.props.FloatProperty( + min=0.0, max=255.0, default=0.1 + ) + bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( + min=0.0, max=255.0, default=0.1 + ) + + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) + + bpy.types.Material.BaseTexture + bpy.types.Material.DetailTexture + bpy.types.Material.NormalTexture + bpy.types.Material.NormalDetailTexture + bpy.types.Material.GlossTexture + bpy.types.Material.WaveTexture + bpy.types.Material.DistortionTexture + bpy.types.Material.SpecularTexture + bpy.types.Material.CloudTexture + bpy.types.Material.CloudNormalTexture + + bpy.types.Material.shaderList + bpy.types.Material.Emissive + bpy.types.Material.Diffuse + bpy.types.Material.Specular + bpy.types.Material.Shininess + bpy.types.Material.Colorization + bpy.types.Material.DebugColor + bpy.types.Material.UVOffset + bpy.types.Material.Color + bpy.types.Material.UVScrollRate + bpy.types.Material.DiffuseColor + # shield shader properties + bpy.types.Material.EdgeBrightness + bpy.types.Material.BaseUVScale + bpy.types.Material.WaveUVScale + bpy.types.Material.DistortUVScale + bpy.types.Material.BaseUVScrollRate + bpy.types.Material.WaveUVScrollRate + bpy.types.Material.DistortUVScrollRate + # tree properties + bpy.types.Material.BendScale + # grass properties + bpy.types.Material.Diffuse1 + # skydome.fx properties + bpy.types.Material.CloudScrollRate + bpy.types.Material.CloudScale + # nebula.fx properties + bpy.types.Material.SFreq + bpy.types.Material.TFreq + bpy.types.Material.DistortionScale + # planet.fx properties + bpy.types.Material.Atmosphere + bpy.types.Material.CityColor + bpy.types.Material.AtmospherePower + # tryplanar mapping properties + bpy.types.Material.MappingScale + bpy.types.Material.BlendSharpness + + +if __name__ == "__main__": + register() diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index c69c169..6af1e2c 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -67,147 +67,6 @@ def reimport_modules(): export_ala.ALA_Exporter, ) -class skeletonEnumClass(PropertyGroup): - skeletonEnum : EnumProperty( - name='Active Skeleton', - description = "skeleton that is exported", - items = skeletonEnumCallback - ) - -class ALAMO_PT_ToolsPanel(bpy.types.Panel): - - bl_label = "ALAMO properties" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "ALAMO" - - def draw(self, context): - object = context.object - layout = self.layout - scene = context.scene - c = layout.column() - - c.prop(scene.ActiveSkeleton, 'skeletonEnum') - - if type(object) != type(None): - if(object.type == 'MESH'): - if bpy.context.mode == 'OBJECT': - c.prop(object, "HasCollision") - c.prop(object, "Hidden") - - armature = utils.findArmature() - if armature != None: - hasChildConstraint = False - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - hasChildConstraint = True - if not hasChildConstraint: - self.layout.operator("create.constraint_bone", text = 'Create Constraint Bone') - - action = utils.getCurrentAction() - if(action != None): - c.prop(action, "AnimationEndFrame") - - - bone = bpy.context.active_bone - if type(bone) != type(None): - if(type(bpy.context.active_bone ) is bpy.types.EditBone): - c.prop(bone.billboardMode, "billboardMode") - c.prop(bone, "Visible") - c.prop(bone, "EnableProxy") - if bone.EnableProxy: - c.prop(bone, "proxyIsHidden") - c.prop(bone, "altDecreaseStayHidden") - c.prop(bone, "ProxyName") - - elif (type(bpy.context.active_bone) is bpy.types.Bone and bpy.context.mode == 'POSE'): - poseBone = object.pose.bones[bone.name] - c.prop(poseBone, "proxyIsHiddenAnimation") - -class ALAMO_PT_materialPropertyPanel(bpy.types.Panel): - bl_label = "Alamo material properties" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - - def draw(self, context): - object = context.object - layout = self.layout - c = layout.column() - - if type(object) != type(None): - if(object.type == 'MESH'): - material = bpy.context.active_object.active_material - if (material != None): - #a None image is needed to represent not using a texture - if 'None' not in bpy.data.images: - bpy.data.images.new(name='None', width=1, height=1) - c.prop(material.shaderList, "shaderList") - shaderProps = settings.material_parameter_dict[material.shaderList.shaderList] - if material.shaderList.shaderList != 'alDefault.fx': - for property in shaderProps: - if property == 'BaseTexture': - layout.prop_search(material, "BaseTexture", bpy.data, "images") - elif property == 'CloudTexture': - layout.prop_search(material, "CloudTexture", bpy.data, "images") - elif property == 'DetailTexture': - layout.prop_search(material, "DetailTexture", bpy.data, "images") - elif property == 'CloudNormalTexture': - layout.prop_search(material, "CloudNormalTexture", bpy.data, "images") - elif property == 'NormalTexture': - layout.prop_search(material, "NormalTexture", bpy.data, "images") - elif property == 'NormalDetailTexture': - layout.prop_search(material, "NormalDetailTexture", bpy.data, "images") - elif property == 'GlossTexture': - layout.prop_search(material, "GlossTexture", bpy.data, "images") - elif property == 'WaveTexture': - layout.prop_search(material, "WaveTexture", bpy.data, "images") - elif property == 'DistortionTexture': - layout.prop_search(material, "DistortionTexture", bpy.data, "images") - elif property == 'SpecularTexture': - layout.prop_search(material, "SpecularTexture", bpy.data, "images") - else: - c.prop(material, property) - -def createShaderModeOptions(): - mode_options = [] - - for index, shader_name in enumerate(settings.material_parameter_dict): - mode_options.append((shader_name, shader_name, '', '', index)) - - return mode_options - -class shaderListProperties(bpy.types.PropertyGroup): - mode_options = createShaderModeOptions() - - shaderList : bpy.props.EnumProperty( - items=mode_options, - description="Choose ingame Shader", - default="alDefault.fx", - ) - -class billboardListProperties(bpy.types.PropertyGroup): - mode_options = [ - ("Disable", "Disable", 'Description WIP', '', 0), - ("Parallel", "Parallel", 'Description WIP', '', 1), - ("Face", "Face", 'Description WIP', '', 2), - ("ZAxis View", "ZAxis View", 'Description WIP', '', 3), - ("ZAxis Light", "ZAxis Light", 'Description WIP', '', 4), - ("ZAxis Wind", "ZAxis Wind", 'Description WIP', '', 5), - ("Sunlight Glow", "Sunlight Glow", 'Description WIP', '', 6), - ("Sun", "Sun", 'Description WIP', '', 7), - ] - - billboardMode : bpy.props.EnumProperty( - items = mode_options, - description = "billboardMode", - default="Disable", - ) - -def proxy_name_update(self, context): - if self.ProxyName != self.ProxyName.upper(): #prevents endless recursion - self.ProxyName = self.ProxyName.upper() - #blender registration def menu_func_import(self, context): self.layout.operator(import_alo.ALO_Importer.bl_idname, text=".ALO Importer") @@ -230,69 +89,6 @@ def register(): bpy.types.TOPBAR_MT_file_import.append(menu_func_import) bpy.types.TOPBAR_MT_file_export.append(menu_func_export) - - bpy.types.Action.AnimationEndFrame = IntProperty(default = 24) - - bpy.types.EditBone.Visible = BoolProperty(default=True) - bpy.types.EditBone.EnableProxy = BoolProperty() - bpy.types.EditBone.proxyIsHidden = BoolProperty() - bpy.types.PoseBone.proxyIsHiddenAnimation = BoolProperty() - bpy.types.EditBone.altDecreaseStayHidden = BoolProperty() - bpy.types.EditBone.ProxyName = StringProperty(update=proxy_name_update) - bpy.types.EditBone.billboardMode = bpy.props.PointerProperty(type=billboardListProperties) - - bpy.types.Object.HasCollision = BoolProperty() - bpy.types.Object.Hidden = BoolProperty() - - bpy.types.Material.BaseTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DetailTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.NormalDetailTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.NormalTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.GlossTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.SpecularTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.WaveTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.DistortionTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.CloudTexture = bpy.props.StringProperty(default='None') - bpy.types.Material.CloudNormalTexture = bpy.props.StringProperty(default='None') - - bpy.types.Material.shaderList = bpy.props.PointerProperty(type=shaderListProperties) - bpy.types.Material.Emissive = bpy.props.FloatVectorProperty(min = 0, max = 1, size = 4, default=(0,0,0,0)) - bpy.types.Material.Diffuse = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,0)) - bpy.types.Material.Specular = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,0)) - bpy.types.Material.Shininess = bpy.props.FloatProperty(min=0, max=255, default = 32) - bpy.types.Material.Colorization = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,0)) - bpy.types.Material.DebugColor = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0,1,0,0)) - bpy.types.Material.UVOffset = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0,0,0,0)) - bpy.types.Material.Color = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,1)) - bpy.types.Material.UVScrollRate = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0,0,0,0)) - bpy.types.Material.DiffuseColor = bpy.props.FloatVectorProperty(min=0, max=1, size=3, default=(0.5,0.5,0.5)) - #shield shader properties - bpy.types.Material.EdgeBrightness = bpy.props.FloatProperty(min=0, max=255, default=0.5) - bpy.types.Material.BaseUVScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - bpy.types.Material.WaveUVScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - bpy.types.Material.DistortUVScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - bpy.types.Material.BaseUVScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=-0.15) - bpy.types.Material.WaveUVScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=-0.15) - bpy.types.Material.DistortUVScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=-0.25) - #tree properties - bpy.types.Material.BendScale = bpy.props.FloatProperty(min=-255, max=255, default=0.4) - #grass properties - bpy.types.Material.Diffuse1 = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(1,1,1,1)) - #skydome.fx properties - bpy.types.Material.CloudScrollRate = bpy.props.FloatProperty(min=-255, max=255, default=0.001) - bpy.types.Material.CloudScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - #nebula.fx properties - bpy.types.Material.SFreq = bpy.props.FloatProperty(min=-255, max=255, default=0.002) - bpy.types.Material.TFreq = bpy.props.FloatProperty(min=-255, max=255, default=0.005) - bpy.types.Material.DistortionScale = bpy.props.FloatProperty(min=-255, max=255, default=1) - #planet.fx properties - bpy.types.Material.Atmosphere = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) - bpy.types.Material.CityColor = bpy.props.FloatVectorProperty(min=0, max=1, size=4, default=(0.5, 0.5, 0.5, 0.5)) - bpy.types.Material.AtmospherePower = bpy.props.FloatProperty(min=-255, max=255, default=1) - #tryplanar mapping properties - bpy.types.Material.MappingScale = bpy.props.FloatProperty(min=0, max=255, default=0.1) - bpy.types.Material.BlendSharpness = bpy.props.FloatProperty(min=0, max=255, default=0.1) - def unregister(): UI.unregister() UI_material.unregister() @@ -303,68 +99,5 @@ def unregister(): bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) - - bpy.types.Action.AnimationEndFrame - - bpy.types.EditBone.Visible - bpy.types.EditBone.EnableProxy - bpy.types.EditBone.proxyIsHidden - bpy.types.PoseBone.proxyIsHiddenAnimation - bpy.types.EditBone.altDecreaseStayHidden - bpy.types.EditBone.ProxyName - bpy.types.EditBone.billboardMode - - bpy.types.Object.HasCollision - bpy.types.Object.Hidden - - bpy.types.Material.BaseTexture - bpy.types.Material.DetailTexture - bpy.types.Material.NormalTexture - bpy.types.Material.NormalDetailTexture - bpy.types.Material.GlossTexture - bpy.types.Material.WaveTexture - bpy.types.Material.DistortionTexture - bpy.types.Material.SpecularTexture - bpy.types.Material.CloudTexture - bpy.types.Material.CloudNormalTexture - - bpy.types.Material.shaderList - bpy.types.Material.Emissive - bpy.types.Material.Diffuse - bpy.types.Material.Specular - bpy.types.Material.Shininess - bpy.types.Material.Colorization - bpy.types.Material.DebugColor - bpy.types.Material.UVOffset - bpy.types.Material.Color - bpy.types.Material.UVScrollRate - bpy.types.Material.DiffuseColor - # shield shader properties - bpy.types.Material.EdgeBrightness - bpy.types.Material.BaseUVScale - bpy.types.Material.WaveUVScale - bpy.types.Material.DistortUVScale - bpy.types.Material.BaseUVScrollRate - bpy.types.Material.WaveUVScrollRate - bpy.types.Material.DistortUVScrollRate - # tree properties - bpy.types.Material.BendScale - # grass properties - bpy.types.Material.Diffuse1 - # skydome.fx properties - bpy.types.Material.CloudScrollRate - bpy.types.Material.CloudScale - # nebula.fx properties - bpy.types.Material.SFreq - bpy.types.Material.TFreq - bpy.types.Material.DistortionScale - # planet.fx properties - bpy.types.Material.Atmosphere - bpy.types.Material.CityColor - bpy.types.Material.AtmospherePower - #tryplanar mapping properties - bpy.types.Material.MappingScale - bpy.types.Material.BlendSharpness - if __name__ == "__main__": register() diff --git a/io_alamo_tools/export_ala.py b/io_alamo_tools/export_ala.py index fa44941..370f0ec 100644 --- a/io_alamo_tools/export_ala.py +++ b/io_alamo_tools/export_ala.py @@ -395,9 +395,9 @@ def create_visibility_chunk(armature, bone): dataExists = False for curve in armature.animation_data.action.fcurves: # by spliting and comparing the data path we know which bone has rotation/location keyframes - parts = curve.data_path.split('"'); + parts = curve.data_path.split('"') if (parts[2] == '].proxyIsHiddenAnimation'): - if parts[1] == bone.name or (bone.parent != None and bone.parent.name == parts[1]): + if parts[1] == bone.name or (bone.parent is not None and bone.parent.name == parts[1]): dataExists = True break @@ -410,7 +410,7 @@ def create_visibility_chunk(armature, bone): pose = armature.pose.bones[bone.name] parentPose = {} - if bone.parent != None: + if bone.parent is not None: parentPose = armature.pose.bones[bone.parent.name] else: parentPose = None @@ -420,7 +420,7 @@ def create_visibility_chunk(armature, bone): scene.frame_set(0) while scene.frame_current <= animLength: - if pose.proxyIsHiddenAnimation == True or (parentPose != None and parentPose.proxyIsHiddenAnimation): + if pose.proxyIsHiddenAnimation or (parentPose is not None and parentPose.proxyIsHiddenAnimation): binary += '0' else: binary += '1' diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 5422afd..98ee715 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -71,11 +71,6 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): exportHiddenObjects : BoolProperty( name="Export Hidden Objects", description="Export all objects, regardless of if they are hidden", - default=False, - ) - checkshadowObjects : BoolProperty( - name="Check Shadow Objects", - description="Export all shadows, regardless of if they have non-manifold", default=True, ) @@ -100,9 +95,10 @@ def draw(self, context): layout = self.layout layout.use_property_split = True - layout.prop(self, "exportAnimations") - layout.prop(self, "exportHiddenObjects") - layout.prop(self, "checkshadowObjects") + row = layout.row() + row.prop(self, "exportAnimations") + row = layout.row() + row.prop(self, "exportHiddenObjects") row = layout.row(heading="Names From") row.use_property_split = False @@ -649,7 +645,6 @@ def shadow_vertex_face_data(bm, mesh, object): def create_sub_mesh_data_chunk(mesh, material, object, bone_name_per_alo_index): - print(mesh.name) sub_mesh_data_header = b"\x00\x00\x01\00" sub_mesh_data_header += utils.pack_int(0) file.write(sub_mesh_data_header) @@ -1350,84 +1345,6 @@ def exportFailed(): with disable_exception_traceback(): raise Exception('ALAMO - EXPORT FAILED') - def checkShadowMesh(mesh_list): #checks if shadow meshes are correct and checks if material is missing - for object in mesh_list: - if len(object.data.materials) == 0: - raise RuntimeError('Missing material on object: ' + object.name) - shader = object.data.materials[0].shaderList.shaderList - if shader == 'MeshShadowVolume.fx' or shader == 'RSkinShadowVolume.fx': - bm = bmesh.new() # create an empty BMesh - bm.from_mesh(object.data) # fill it in from a Mesh - bm.verts.ensure_lookup_table() - - for vertex in bm.verts: - if not vertex.is_manifold: - bm.free() - selectNonManifoldVertices(object) - raise RuntimeError('Non manifold geometry shadow mesh: ' + object.name) - - for edge in bm.edges: - if len(edge.link_faces) < 2 : - bm.free() - selectNonManifoldVertices(object) - raise RuntimeError('Non manifold geometry shadow mesh: ' + object.name) - - bm.free() - - - def checkUV(mesh_list): #throws error if object lacks UVs - for object in mesh_list: - for material in object.data.materials: - if material.shaderList.shaderList == 'MeshShadowVolume.fx' or material.shaderList.shaderList == 'RSkinShadowVolume.fx': - if len(object.data.materials) > 1: - raise RuntimeError('Multiple materials on shadow volume: ' + object.name + ' , remove additional materials') - else: - return - if object.HasCollision: - if len(object.data.materials) > 1: - raise RuntimeError('Multiple submeshes/materials on collision mesh: ' + object.name + ' , remove additional materials') - if object.data.uv_layers: #or material.shaderList.shaderList in settings.no_UV_Shaders: #currently UVs are needed for everything but shadows - continue - else: - raise RuntimeError('Missing UV: ' + object.name) - - def checkInvalidArmatureModifier(mesh_list): #throws error if armature modifier lacks rig, this would crash the exporter later and checks if skeleton in modifier doesn't match active skeleton - activeSkeleton = bpy.context.scene.ActiveSkeleton.skeletonEnum - for object in mesh_list: - for modifier in object.modifiers: - if modifier.type == "ARMATURE": - if modifier.object == None: - raise RuntimeError('Armature modifier without selected skeleton on: ' + object.name) - return True - elif modifier.object.type != 'NoneType': - if modifier.object.name != activeSkeleton: - raise RuntimeError('Armature modifier skeleton doesnt match active skeleton on: ' + object.name) - return True - for constraint in object.constraints: - if constraint.type == 'CHILD_OF': - if constraint.target is not None: - #print(type(constraint.target)) - if constraint.target.name != activeSkeleton: - raise RuntimeError('Constraint doesnt match active skeleton on: ' + object.name) - return True - - def checkFaceNumber(mesh_list): #checks if the number of faces exceeds max ushort, which is used to save the indices - for object in mesh_list: - if len(object.data.polygons) > 65535: - raise RuntimeError('Face number exceeds uShort max on object: ' + object.name + ' split mesh into multiple objects') - return True - - def checkTranslation(mesh_list): #prints warning when translation is not default - for object in mesh_list: - if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or object.scale != mathutils.Vector((1.0, 1.0, 1.0)): - print('Warning: ' + object.name + ' is not aligned with the world origin, apply translation or bind to bone') - - def checkTranslationArmature(): #prints warning when translation is not default - armature = utils.findArmature() - if armature != None: - if armature.location != mathutils.Vector((0.0, 0.0, 0.0)) or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)): - print('Warning: active Armature is not aligned with the world origin') - def unhide(): hiddenList = [] for object in bpy.data.objects: @@ -1488,15 +1405,14 @@ def exportAnimations(filePath): mesh_list = validation.create_export_list(bpy.context.scene.collection, self.exportHiddenObjects, self.useNamesFrom) #check if export objects satisfy requirements (has material, UVs, ...) + messages = validation.validate(mesh_list) - if(self.checkshadowObjects): - checkShadowMesh(mesh_list) - checkUV(mesh_list) - checkFaceNumber(mesh_list) - checkTranslation(mesh_list) - checkTranslationArmature() - checkInvalidArmatureModifier(mesh_list) + if messages is not None and len(messages) > 0: + for message in messages: + self.report(*message) + exportFailed() + hiddenList = unhide() collection_is_hidden_list = unhide_collections(bpy.context.scene.collection) @@ -1504,7 +1420,8 @@ def exportAnimations(filePath): global file - file = open(path, 'wb') # open file in read binary mode + if os.access(path, os.W_OK) or not os.access(path, os.F_OK): + file = open(path, 'wb') # open file in read binary mode bone_name_per_alo_index = create_skeleton() create_mesh(mesh_list, bone_name_per_alo_index) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 8ff195a..5f44316 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -586,35 +586,80 @@ def set_up_textures(material): nt = material.node_tree nodes = nt.nodes links = nt.links - while(nodes): nodes.remove(nodes[0]) - output = nodes.new("ShaderNodeOutputMaterial") - bsdf = nodes.new("ShaderNodeBsdfPrincipled") - base_image_node = nodes.new("ShaderNodeTexImage") - invert_color_node = nodes.new("ShaderNodeInvert") - normal_image_node = nodes.new("ShaderNodeTexImage") - if material.BaseTexture != 'None': - links.new(output.inputs['Surface'], bsdf.outputs['BSDF']) - links.new(bsdf.inputs['Base Color'], base_image_node.outputs['Color']) - links.new(invert_color_node.inputs['Color'], base_image_node.outputs['Alpha']) - links.new(bsdf.inputs['Alpha'], invert_color_node.outputs['Color']) - if material.BaseTexture in bpy.data.images: - diffuse_texture = bpy.data.images[material.BaseTexture] - base_image_node.image = diffuse_texture - if material.NormalTexture != 'None': - normal_map_node = nodes.new("ShaderNodeNormalMap") - normal_map_node.space = 'TANGENT' - links.new(normal_image_node.outputs['Color'], normal_map_node.inputs['Color']) - links.new(normal_map_node.outputs['Normal'], bsdf.inputs['Normal']) - if material.NormalTexture in bpy.data.images: - normal_texture = bpy.data.images[material.NormalTexture] - normal_image_node.image = normal_texture - normal_image_node.location.y = -400 - normal_map_node.location.x = normal_image_node.location.x + normal_image_node.width + 100 - normal_map_node.location.y = -400 - invert_color_node.location.x = base_image_node.location.x + base_image_node.width + 100 - invert_color_node.location.y = -200 - bsdf.location.x = invert_color_node.location.x + invert_color_node.width + 100 - output.location.x = bsdf.location.x + bsdf.width + 100 + + # clean up + while(nodes): + nodes.remove(nodes[0]) + + output = nodes.new("ShaderNodeOutputMaterial") + custom_node_name = material.name + "Group" + my_group = 'null' + + if ("Additive" in material.shaderList.shaderList): + material.blend_method = "BLEND" + my_group = material_group_additive( + self, context, custom_node_name, material, True) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 200.0 + links.new(mat_group.outputs[0], output.inputs['Surface']) + elif ("Alpha" in material.shaderList.shaderList): + material.blend_method = "BLEND" + my_group = material_group_additive( + self, context, custom_node_name, material, False) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 200.0 + links.new(mat_group.outputs[0], output.inputs['Surface']) + else: + bsdf = nodes.new("ShaderNodeBsdfPrincipled") + bsdf.inputs[4].default_value = 0.1 # Set metallic to 0.1 + bsdf.inputs[7].default_value = 0.2 # Set roughness to 0.2 + bsdf.location.x -= 300.0 + links.new(bsdf.outputs['BSDF'], output.inputs['Surface']) + my_group = material_group_basic( + self, context, custom_node_name, material) + mat_group = nt.nodes.new("ShaderNodeGroup") + mat_group.node_tree = bpy.data.node_groups[my_group.name] + mat_group.location.x -= 500.0 + links.new(mat_group.outputs[0], bsdf.inputs['Base Color']) + links.new(mat_group.outputs[1], bsdf.inputs[5]) + links.new(mat_group.outputs[2], bsdf.inputs['Normal']) + + def create_material(currentSubMesh): + if currentSubMesh.material.name != "DUMMYMATERIAL": + return + + oldMat = currentSubMesh.material + + texName = currentSubMesh.material.BaseTexture + texName = texName[0:len(texName) - 4] + " Material" + if texName in bpy.data.materials and oldMat.shaderList.shaderList != bpy.data.materials.get(texName).shaderList.shaderList: + texName += "1" + mat = assign_material(texName) + + mat.shaderList.shaderList = oldMat.shaderList.shaderList + + # TODO: Extract set_alamo_shader's shader finder to new function, use that here. + material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "DebugColor", "UVOffset", "Color", "UVScrollRate", "DiffuseColor", + "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale", "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture"] + + for texture in material_props: + if texture in oldMat: + mat[texture] = oldMat[texture] + + obj = bpy.context.object + + obj.data.materials.clear() + obj.data.materials.append(mat) + currentSubMesh.material = mat + + def assign_material(name): + if name in bpy.data.materials: + return bpy.data.materials.get(name) + else: + return bpy.data.materials.new(name) + def create_object(currentMesh): global mesh @@ -708,10 +753,6 @@ def process_texture_chunk(material): load_image(texture_name) exec('material.' + texture_function_name + '= texture_name') - load_image(texture_name) - if texture_function_name != "SpecularTexture": - exec('material.' + texture_function_name + '= texture_name') - def createUVLayer(layerName, uv_coordinates): vert_uvs = uv_coordinates mesh.uv_layers.new(name=layerName) @@ -887,6 +928,25 @@ def read_string_mini_chunk(): counter += 1 file.seek(1, 1) # skip end byte of name return string + + def hideObject(object): + + # set correct area type via context overwrite + context_override = bpy.context.copy() + area = None + for window in bpy.context.window_manager.windows: + screen = window.screen + for a in screen.areas: + if a.type == 'VIEW_3D': + area = a + break + + context_override['area'] = area + + bpy.ops.object.select_all(context_override, action='DESELECT') + object.select_set(True) + bpy.ops.object.hide_view_set(context_override) + object.hide_render = True def hideLODs(): # hides all but the most detailed LOD in Blender @@ -901,7 +961,8 @@ def hideLODs(): # hide smaller LODS counter = 0 while(counter < lodCounter-1): - bpy.data.objects[object.name[:-1] + str(counter)].hide_set(True) + hideObject( + bpy.data.objects[object.name[:-1] + str(counter)]) counter += 1 # hide object if its a shadow or a collision @@ -910,13 +971,13 @@ def hideLODs(): if len(object.material_slots) != 0: shader = object.material_slots[0].material.shaderList.shaderList if(shader == 'MeshCollision.fx' or shader == 'RSkinShadowVolume.fx' or shader == 'MeshShadowVolume.fx'): - object.hide_set(True) + hideObject(object) # hide objects that are set to not visible for object in bpy.data.objects: if (object.type == 'MESH'): if object.Hidden == True: - object.hide_set(True) + hideObject(object) def deleteRoot(): armature = utils.findArmature() @@ -966,9 +1027,9 @@ def load_image(texture_name): return def validate_material_prop(name): - material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular","Shininess","Colorization" \ - ,"DebugColor","UVOffset","Color","UVScrollRate","DiffuseColor","EdgeBrightness","BaseUVScale","WaveUVScale","DistortUVScale","BaseUVScrollRate","WaveUVScrollRate","DistortUVScrollRate","BendScale" \ - , "Diffuse1","CloudScrollRate","CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture"] + material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization" \ + , "DebugColor", "UVOffset", "Color", "UVScrollRate", "DiffuseColor", "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale" \ + , "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture"] if(name in material_props): return True diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index 987add0..e25ab63 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -7,7 +7,7 @@ "Grass.fx": ["Emissive", "Diffuse", "Diffuse1", "BendScale", "BaseTexture"], "MeshAdditive.fx": ["BaseTexture", "UVScrollRate", "Color"], "MeshAlpha.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], - "MeshAlphaScroll.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], + "MeshAlphaScroll.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "UVScrollRate" "BaseTexture"], "MeshBumpColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", "BaseTexture", "NormalTexture"], "MeshBumpColorizeVertex.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", @@ -39,6 +39,10 @@ "TerrainMeshGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], "Tree.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BendScale", "BaseTexture", "NormalTexture"], "LightProxy.fx": ["Diffuse"], + "MeshBumpSpecColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", + "BaseTexture", "NormalTexture", "GlossTexture"], + "MeshAdditiveOffset.fx": ["BaseTexture", "UVOffset"], + "MeshAdditiveVColor.fx": ["BaseTexture", "UVScrollRate", "Color"], "MeshAlphaGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], "MeshBumpSpecGlowColorize.fx": ["Emissive", "Diffuse", "Colorization", "UVOffset", "BaseTexture", "NormalTexture", "SpecularTexture"], "MeshBumpReflectColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", @@ -75,6 +79,9 @@ "TerrainMeshGloss.fx" : "alD3dVertNU2", "Tree.fx" : "alD3dVertNU2", "LightProxy.fx" : "alD3dVertNU2", + "MeshBumpSpecColorize.fx" : "alD3dVertNU2U3U3", + "MeshAdditiveOffset.fx" : "alD3dVertNU2", + "MeshAdditiveVColor.fx" : "alD3dVertNU2C", "MeshAlphaGloss.fx" : "alD3dVertNU2", "MeshBumpSpecGlowColorize.fx" : "alD3dVertNU2C", "MeshBumpReflectColorize.fx" : "alD3dVertNU2U3U3" @@ -82,7 +89,7 @@ billboard_array = {"Disable":0, "Parallel":1, "Face":2, "ZAxis View": 3, "ZAxis Light":4, "ZAxis Wind":5, "Sunlight Glow":6, "Sun":7} -bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpReflectColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx"] +bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpReflectColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx", "MeshBumpSpecColorize.fx"] rotation_curve_name = ['].rotation_quaternion', '].rotation_euler'] From 30adba247aa479b821eda0f2da68caa41c9f028a Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 28 Sep 2024 17:53:49 +0200 Subject: [PATCH 082/100] Update .gitignore, armature.py, and import_alo.py --- .gitignore | 5 +-- io_alamo_tools/armature.py | 68 ------------------------------------ io_alamo_tools/import_alo.py | 4 --- 3 files changed, 1 insertion(+), 76 deletions(-) delete mode 100644 io_alamo_tools/armature.py diff --git a/.gitignore b/.gitignore index d07ab76..3e3b88c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ -* -!.gitignore -!*/ -!io_alamo_tools/* \ No newline at end of file +*.lnk \ No newline at end of file diff --git a/io_alamo_tools/armature.py b/io_alamo_tools/armature.py deleted file mode 100644 index 32824af..0000000 --- a/io_alamo_tools/armature.py +++ /dev/null @@ -1,68 +0,0 @@ -centers = [] - -for object in bpy.context.selected_objects: - center = object.matrix_world @ (1 / 8 * sum((Vector(vector) for vector in object.bound_box), Vector())) - object.location -= center - centers += [center] - -print(centers) - -import random - -index = 0 - -for vector in centers: - print(str(index)) - bone = bpy.data.armatures['Armature'].edit_bones.new(str(index)) - bone.head = vector - bone.tail = vector + Vector((random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1))).normalized() - index += 1 - - - -import random - -index = 0 -objects = [] - -for object in bpy.context.selected_objects: - objects += [object] - -random.shuffle(objects) - -for object in objects: - constraint = object.constraints.new('CHILD_OF') - constraint.target = bpy.data.objects['Armature'] - constraint.subtarget = str(index) - constraint.inverse_matrix = Matrix.Identity(4) - index += 1 - - - - - - - - - - - - - - -for object in bpy.context.selected_objects: - for constraint in object.constraints: - constraint.inverse_matrix = Matrix.Identity(4) - - -import random - -index = 0 -objects = [] -for object in bpy.context.selected_objects: - objects += [object] -random.shuffle(objects) -for object in objects: - for constraint in object.constraints[:]: - constraint.subtarget = str(index) - index += 1 \ No newline at end of file diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 81bfbee..87193d1 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -815,9 +815,6 @@ def read_float4(material): if validate_material_prop(name): exec('material.' + name + '= value') - def setRenderToEevee(): - bpy.context.scene.render.engine = 'BLENDER_EEVEE' - def loadAnimations(filePath): #remove ending filePath = filePath[0:-4] @@ -872,7 +869,6 @@ def loadAnimations(filePath): global file filepath = self.properties.filepath file = open(filepath, 'rb') #open file in read binary mode - setRenderToEevee() process_active_junk() removeShadowDoubles() hideLODs() From def40c7d4da86aca6d34f7d1b9f4a8d1028c71c8 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 28 Sep 2024 18:16:40 +0200 Subject: [PATCH 083/100] Remove autosmooth check --- io_alamo_tools/validation.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 365acb6..6e2445a 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -104,11 +104,6 @@ def checkFaceNumber(object): return [({'ERROR'}, f'ALAMO - {object.name} exceeds maximum face limit; split mesh into multiple objects')] return [] -def checkAutosmooth(object): # prints a warning if Autosmooth is used - if object.data.use_auto_smooth: - return [({'ERROR'}, f'ALAMO - {object.name} uses autosmooth, ingame shading might not match blender; use edgesplit instead')] - return [] - def checkTranslation(object): # prints warning when translation is not default if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ'): return [({'ERROR'}, f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone')] @@ -187,7 +182,6 @@ def validate(mesh_list): checkShadowMesh, checkUV, checkFaceNumber, - checkAutosmooth, checkTranslation, checkInvalidArmatureModifier, checkScale, From 959f83d312aef89b41bc778bb73a28408ea14ad2 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 28 Sep 2024 20:42:51 +0200 Subject: [PATCH 084/100] Update to [new node group interface](https://developer.blender.org/docs/release_notes/4.0/python_api/#__tabbed_1_2); update to [new context override](https://docs.blender.org/api/current/bpy.ops.html#overriding-context) --- io_alamo_tools/import_alo.py | 41 +++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index d0a59ba..d498a9c 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -448,7 +448,7 @@ def material_group_additive(context, operator, group_name, material, is_emissive group_out = node('NodeGroupOutput') group_out.location.x += 200.0 - node_group.outputs.new('NodeSocketShader', 'Surface') + node_group.interface.new_socket(socket_type='NodeSocketShader', name='Surface', in_out='OUTPUT') mix_shader = node("ShaderNodeMixShader") @@ -462,8 +462,9 @@ def material_group_additive(context, operator, group_name, material, is_emissive if is_emissive: group_in = node('NodeGroupInput') group_in.location.x -= 700 - emissive = node_group.inputs.new( - 'NodeSocketFloat', 'Emissive Strength') + emissive = node_group.interface.new_socket(socket_type='NodeSocketFloat', + name='Emissive Strength', + in_out='INPUT') emissive.default_value = 1.0 color = node("ShaderNodeEmission") link(group_in.outputs[0], color.inputs[1]) @@ -504,15 +505,24 @@ def material_group_basic(context, operator, group_name, material): group_in = node('NodeGroupInput') group_in.location.x -= 700 - node_group.inputs.new('NodeSocketColor', 'Team Color') - spec = node_group.inputs.new( - 'NodeSocketFloat', 'Specular Intensity') + node_group.interface.new_socket(socket_type='NodeSocketColor', + name='Team Color', + in_out='INPUT') + spec = node_group.interface.new_socket(socket_type='NodeSocketFloat', + name='Specular Intensity', + in_out='INPUT') spec.default_value = 0.1 group_out = node('NodeGroupOutput') - node_group.outputs.new('NodeSocketColor', 'Base Color') - node_group.outputs.new('NodeSocketFloat', 'Specular') - node_group.outputs.new('NodeSocketVector', 'Normal') + node_group.interface.new_socket(socket_type='NodeSocketColor', + name='Base Color', + in_out='OUTPUT') + node_group.interface.new_socket(socket_type='NodeSocketFloat', + name='Specular', + in_out='OUTPUT') + node_group.interface.new_socket(socket_type='NodeSocketVector', + name='Normal', + in_out='OUTPUT') base_image_node = node("ShaderNodeTexImage") base_image_node.location.x -= 500 @@ -932,7 +942,6 @@ def read_string_mini_chunk(): def hideObject(object): # set correct area type via context overwrite - context_override = bpy.context.copy() area = None for window in bpy.context.window_manager.windows: screen = window.screen @@ -940,13 +949,11 @@ def hideObject(object): if a.type == 'VIEW_3D': area = a break - - context_override['area'] = area - - bpy.ops.object.select_all(context_override, action='DESELECT') - object.select_set(True) - bpy.ops.object.hide_view_set(context_override) - object.hide_render = True + with context.temp_override(area=area): + bpy.ops.object.select_all(action='DESELECT') + object.select_set(True) + bpy.ops.object.hide_view_set() + object.hide_render = True def hideLODs(): # hides all but the most detailed LOD in Blender From 86c4dee222d29b5e89b0d8c551a3fec640b13723 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 28 Sep 2024 20:52:51 +0200 Subject: [PATCH 085/100] Update import_alo.py --- io_alamo_tools/import_alo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index d498a9c..5b1e4d9 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -587,7 +587,7 @@ def material_group_basic(context, operator, group_name, material): normal_texture = bpy.data.images[material.NormalTexture] normal_texture.alpha_mode = 'CHANNEL_PACKED' normal_image_node.image = normal_texture - normal_image_node.image.colorspace_settings.name = 'Raw' + normal_image_node.image.colorspace_settings.name = 'Non-Color' return node_group From 5698ad8067d602eaeb12a06e67d20bebc0670c2a Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 28 Sep 2024 21:15:43 +0200 Subject: [PATCH 086/100] Update import_alo.py --- io_alamo_tools/import_alo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 5b1e4d9..0ee774a 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -623,8 +623,8 @@ def set_up_textures(material): links.new(mat_group.outputs[0], output.inputs['Surface']) else: bsdf = nodes.new("ShaderNodeBsdfPrincipled") - bsdf.inputs[4].default_value = 0.1 # Set metallic to 0.1 - bsdf.inputs[7].default_value = 0.2 # Set roughness to 0.2 + bsdf.inputs['Metallic'].default_value = 0.1 + bsdf.inputs['Roughness'].default_value = 0.2 bsdf.location.x -= 300.0 links.new(bsdf.outputs['BSDF'], output.inputs['Surface']) my_group = material_group_basic( From 12e9cc4ef3b532e10bbc15bdd77cbcf4263352aa Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 28 Sep 2024 23:11:57 +0200 Subject: [PATCH 087/100] Fix multi material mesh import --- io_alamo_tools/import_alo.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 0ee774a..4141b2f 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -465,7 +465,7 @@ def material_group_additive(context, operator, group_name, material, is_emissive emissive = node_group.interface.new_socket(socket_type='NodeSocketFloat', name='Emissive Strength', in_out='INPUT') - emissive.default_value = 1.0 + emissive.default_value = 100.0 color = node("ShaderNodeEmission") link(group_in.outputs[0], color.inputs[1]) eevee_alpha_fix = node("ShaderNodeInvert") @@ -659,8 +659,13 @@ def create_material(currentSubMesh): mat[texture] = oldMat[texture] obj = bpy.context.object - + materials = [] + for material in obj.data.materials: + if material.name != "DUMMYMATERIAL": + materials.append(material) obj.data.materials.clear() + for material in materials: + obj.data.materials.append(material) obj.data.materials.append(mat) currentSubMesh.material = mat From d6fd74fa5053d51a0201b493069c38407fc4c26b Mon Sep 17 00:00:00 2001 From: yyhrs Date: Wed, 6 Nov 2024 00:13:45 +0100 Subject: [PATCH 088/100] Create a placeholder file so Find Missing Files could work --- io_alamo_tools/import_alo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 4141b2f..442e4b5 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1035,6 +1035,10 @@ def load_image(texture_name): if os.path.isfile(path): img = bpy.data.images.load(path) else: + with open(path, "a") as placeholder: + placeholder.write('placeholder') + img = bpy.data.images.load(path) + os.remove(path) self.report({"WARNING"}, "ALAMO - Couldn't find texture: " + texture_name) return From 68979ca3715ec9450829301eca22878327ba38fb Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 8 Feb 2025 20:56:19 +0100 Subject: [PATCH 089/100] Fix import --- io_alamo_tools/import_alo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 442e4b5..08c4c0c 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -832,9 +832,9 @@ def assign_vertex_groups(animation_mapping, currentMesh): def get_n_objects_n_proxies(): size = read_chunk_length() file.seek(2, 1) - n_objects = struct.unpack("l", file.read(4)) + n_objects = struct.unpack("i", file.read(4)) file.seek(2, 1) - n_proxies = struct.unpack("l", file.read(4)) + n_proxies = struct.unpack("i", file.read(4)) n_objects_proxies = { "n_objects": n_objects[0], "n_proxies": n_proxies[0]} From 9975b81c1414136d96a1ab398f061efdc256853c Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 8 Feb 2025 21:16:27 +0100 Subject: [PATCH 090/100] Transform translation validation into warnings --- io_alamo_tools/validation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/io_alamo_tools/validation.py b/io_alamo_tools/validation.py index 6e2445a..176b455 100644 --- a/io_alamo_tools/validation.py +++ b/io_alamo_tools/validation.py @@ -106,7 +106,7 @@ def checkFaceNumber(object): def checkTranslation(object): # prints warning when translation is not default if object.location != mathutils.Vector((0.0, 0.0, 0.0)) or object.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ'): - return [({'ERROR'}, f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone')] + return [({'WARNING'}, f'ALAMO - {object.name} is not aligned with the world origin; apply translation or bind to bone')] return [] def checkScale(object): # prints warning when scale is not default @@ -150,7 +150,7 @@ def checkTranslationArmature(): # prints warning when translation is not defaul or armature.rotation_euler != mathutils.Euler((0.0, 0.0, 0.0), 'XYZ') or armature.scale != mathutils.Vector((1.0, 1.0, 1.0)) ): - return [({'ERROR'}, f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation')] + return [({'WARNING'}, f'ALAMO - Armature {armature} is not aligned with the world origin; apply translation')] return [] def checkProxyKeyframes(): @@ -182,7 +182,6 @@ def validate(mesh_list): checkShadowMesh, checkUV, checkFaceNumber, - checkTranslation, checkInvalidArmatureModifier, checkScale, checkVertexGroups, From 9feffcf032a8443639d3092c5cbbade7eea4ae32 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 8 Feb 2025 21:48:02 +0100 Subject: [PATCH 091/100] Fix file path issues --- io_alamo_tools/import_ala.py | 4 +--- io_alamo_tools/import_alo.py | 8 +++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/io_alamo_tools/import_ala.py b/io_alamo_tools/import_ala.py index 8aa158e..a4fd75a 100644 --- a/io_alamo_tools/import_ala.py +++ b/io_alamo_tools/import_ala.py @@ -362,9 +362,7 @@ def loadAnimation(self, filePath): data = read_next_chunk(filePath) if(validate(data)): - filePath = filePath[0:-4] - fileNameIndex = filePath.rfind("\\") + 1 - fileName = filePath[fileNameIndex:] + fileName = os.path.basename(filePath)[0:-4] modelName = bpy.context.scene.modelFileName #doesn't always match diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 08c4c0c..47efd1d 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1099,10 +1099,8 @@ def read_float4(material): def loadAnimations(filePath): # remove ending - filePath = filePath[0:-4] - fileNameIndex = filePath.rfind("\\") + 1 - path = filePath[0:fileNameIndex] - fileName = filePath[fileNameIndex:] + path = os.path.dirname(filePath) + fileName = os.path.basename(filePath)[0:-4] bpy.context.scene.modelFileName = fileName animationFiles = [] @@ -1117,7 +1115,7 @@ def loadAnimations(filePath): arm.animation_data_create() for animFile in animationFiles: - importer.loadAnimation(path + animFile) + importer.loadAnimation(os.path.join(path, animFile)) global assignedVertexGroups assignedVertexGroups = [] From 73f38a46ad0d1acc1bb89c8b15e67e4fa5b4641d Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 8 Feb 2025 21:55:27 +0100 Subject: [PATCH 092/100] =?UTF-8?q?(=E2=95=AF=C2=B0=E2=96=A1=C2=B0?= =?UTF-8?q?=EF=BC=89=E2=95=AF=EF=B8=B5=20=E2=94=BB=E2=94=81=E2=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- io_alamo_tools/export_alo.py | 8 -------- io_alamo_tools/import_alo.py | 4 +--- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 98ee715..48f84ff 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -1384,19 +1384,11 @@ def hide_collections(collection_parent, collection_is_hidden_list, counter): def exportAnimations(filePath): - arm = utils.findArmature() if(arm == None or arm.animation_data == None): return - - #remove ending filePath = filePath[0:-4] - fileNameIndex = filePath.rfind("\\") + 1 - path = filePath[0:fileNameIndex] - fileName = filePath[fileNameIndex:] - exporter = export_ala.AnimationExporter() - for action in bpy.data.actions: arm.animation_data.action = action exporter.exportAnimation(filePath + "_" + action.name + ".ALA") diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 47efd1d..593c6ce 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1132,9 +1132,7 @@ def loadAnimations(filePath): meshList = [] global fileName - fileName = self.properties.filepath.split("\\") - fileName = fileName[len(fileName) - 1] - fileName = fileName[:len(fileName) - 4] + fileName = os.path.basename(filePath)[0:-4] global importCollection importCollection = bpy.data.collections.new(fileName) From 53cc54d4e5208d7a313857c7672de9927723bcda Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 8 Feb 2025 22:03:06 +0100 Subject: [PATCH 093/100] =?UTF-8?q?=E0=BC=BC=20=E3=81=A4=20=E2=97=95=5F?= =?UTF-8?q?=E2=97=95=20=E0=BC=BD=E3=81=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- io_alamo_tools/import_alo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 593c6ce..a1f92be 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1132,7 +1132,7 @@ def loadAnimations(filePath): meshList = [] global fileName - fileName = os.path.basename(filePath)[0:-4] + fileName = os.path.basename(self.properties.filepath)[0:-4] global importCollection importCollection = bpy.data.collections.new(fileName) From 3ad98ec6f23af57b5d908b490e168ca5e4da8006 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Sat, 8 Feb 2025 22:44:53 +0100 Subject: [PATCH 094/100] Case insensitive for texture lookup --- io_alamo_tools/import_alo.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index a1f92be..8979930 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -1028,19 +1028,22 @@ def load_image(texture_name): else: path = file.name path = os.path.split(path)[0] - path = os.path.split(path)[0] + "/TEXTURES/" + texture_name - if self.properties.textureOverride != "NONE": - path = textureOverride(path, self.properties.textureOverride, texture_name) - - if os.path.isfile(path): - img = bpy.data.images.load(path) - else: - with open(path, "a") as placeholder: - placeholder.write('placeholder') - img = bpy.data.images.load(path) - os.remove(path) - self.report({"WARNING"}, "ALAMO - Couldn't find texture: " + texture_name) - return + path = os.path.split(path)[0] + for directory in os.listdir(path): + if directory.upper() == 'TEXTURES': + path = f'{path}/{directory}/{texture_name}' + if self.properties.textureOverride != "NONE": + path = textureOverride(path, self.properties.textureOverride, texture_name) + if os.path.exists(path): + img = bpy.data.images.load(path) + else: + with open(path, "a") as placeholder: + placeholder.write('placeholder') + img = bpy.data.images.load(path) + os.remove(path) + self.report({"WARNING"}, "ALAMO - Couldn't find texture: " + texture_name) + return + self.report({"WARNING"}, "ALAMO - Couldn't find: " + path) def validate_material_prop(name): material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization" \ From b1ea4dc2cf5a44370ea7112e319325af00f4b48e Mon Sep 17 00:00:00 2001 From: yyhrs Date: Mon, 10 Feb 2025 23:42:10 +0100 Subject: [PATCH 095/100] Add blender_manifest for new extension API --- .gitignore | 3 +- io_alamo_tools/__init__.py | 8 ++- io_alamo_tools/blender_manifest.toml | 75 ++++++++++++++++++++++++++++ io_alamo_tools/changelog.md | 1 - 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 io_alamo_tools/blender_manifest.toml delete mode 100644 io_alamo_tools/changelog.md diff --git a/.gitignore b/.gitignore index 3e3b88c..739b327 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.lnk \ No newline at end of file +__pycache__ +*.lnk diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 6af1e2c..7d3a957 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -22,8 +22,6 @@ "category": "Import-Export" } -ADDON_FOLDER = 'io_alamo_tools' - modules = ( '.validation', '.UI', @@ -38,8 +36,8 @@ def import_modules(): for mod in modules: - #print('importing with importlib.import_module =' + str(mod) + "=") - importlib.import_module(mod, ADDON_FOLDER) + print('importing with importlib.import_module =' + str(mod) + "=") + importlib.import_module(mod, __package__) def reimport_modules(): ''' @@ -47,7 +45,7 @@ def reimport_modules(): ''' for mod in modules: # Reimporting modules during addon development - want_reload_module = importlib.import_module(mod, ADDON_FOLDER) + want_reload_module = importlib.import_module(mod, __package__) importlib.reload(want_reload_module) from . import validation diff --git a/io_alamo_tools/blender_manifest.toml b/io_alamo_tools/blender_manifest.toml new file mode 100644 index 0000000..66e5897 --- /dev/null +++ b/io_alamo_tools/blender_manifest.toml @@ -0,0 +1,75 @@ +schema_version = "1.0.0" + +# Example of manifest file for a Blender extension +# Change the values according to your extension +id = "io_alamo_tools" +version = "1.0.0" +name = "ALO Importer" +tagline = "This is another extension" +maintainer = "Gaukler, evilbobthebob, inertial, 1138" +# Supported types: "add-on", "theme" +type = "add-on" + +# # Optional: link to documentation, support, source files, etc +# website = "https://extensions.blender.org/add-ons/my-example-package/" + +# # Optional: tag list defined by Blender and server, see: +# # https://docs.blender.org/manual/en/dev/advanced/extensions/tags.html +tags = ["Import-Export"] + +blender_version_min = "4.2.0" +# # Optional: Blender version that the extension does not support, earlier versions are supported. +# # This can be omitted and defined later on the extensions platform if an issue is found. +# blender_version_max = "5.1.0" + +# License conforming to https://spdx.org/licenses/ (use "SPDX: prefix) +# https://docs.blender.org/manual/en/dev/advanced/extensions/licenses.html +license = [ + "SPDX:MIT", +] +# # Optional: required by some licenses. +# copyright = [ +# "2002-2024 Developer Name", +# "1998 Company Name", +# ] + +# # Optional: list of supported platforms. If omitted, the extension will be available in all operating systems. +# platforms = ["windows-x64", "macos-arm64", "linux-x64"] +# # Other supported platforms: "windows-arm64", "macos-x64" + +# # Optional: bundle 3rd party Python modules. +# # https://docs.blender.org/manual/en/dev/advanced/extensions/python_wheels.html +# wheels = [ +# "./wheels/hexdump-3.3-py3-none-any.whl", +# "./wheels/jsmin-3.0.1-py3-none-any.whl", +# ] + +# # Optional: add-ons can list which resources they will require: +# # * files (for access of any filesystem operations) +# # * network (for internet access) +# # * clipboard (to read and/or write the system clipboard) +# # * camera (to capture photos and videos) +# # * microphone (to capture audio) +# # +# # If using network, remember to also check `bpy.app.online_access` +# # https://docs.blender.org/manual/en/dev/advanced/extensions/addons.html#internet-access +# # +# # For each permission it is important to also specify the reason why it is required. +# # Keep this a single short sentence without a period (.) at the end. +# # For longer explanations use the documentation or detail page. +# +# [permissions] +# network = "Need to sync motion-capture data to server" +# files = "Import/export FBX from/to disk" +# clipboard = "Copy and paste bone transforms" + +# # Optional: advanced build settings. +# # https://docs.blender.org/manual/en/dev/advanced/extensions/command_line_arguments.html#command-line-args-extension-build +# [build] +# # These are the default build excluded patterns. +# # You only need to edit them if you want different options. +# paths_exclude_pattern = [ +# "__pycache__/", +# "/.git/", +# "/*.zip", +# ] \ No newline at end of file diff --git a/io_alamo_tools/changelog.md b/io_alamo_tools/changelog.md deleted file mode 100644 index 9697a75..0000000 --- a/io_alamo_tools/changelog.md +++ /dev/null @@ -1 +0,0 @@ -# ` ¯\_(ツ)_/¯ ` \ No newline at end of file From 349042a064ee2f5c082dc399809c7bf562f50148 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Mon, 10 Feb 2025 23:43:06 +0100 Subject: [PATCH 096/100] Update __init__.py --- io_alamo_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/__init__.py b/io_alamo_tools/__init__.py index 7d3a957..c947b1a 100644 --- a/io_alamo_tools/__init__.py +++ b/io_alamo_tools/__init__.py @@ -36,7 +36,7 @@ def import_modules(): for mod in modules: - print('importing with importlib.import_module =' + str(mod) + "=") + # print('importing with importlib.import_module =' + str(mod) + "=") importlib.import_module(mod, __package__) def reimport_modules(): From 1dde1abd04b4c6733a7789efa4a1476995f1f583 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Mon, 10 Mar 2025 22:12:59 +0100 Subject: [PATCH 097/100] Update export_alo.py --- io_alamo_tools/export_alo.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 48f84ff..2e14339 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -64,12 +64,12 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): default="") exportAnimations : BoolProperty( - name="Export Animations", + name="Animations", description="Export all animation actions as .ALA files, into the same directory", default=True, ) exportHiddenObjects : BoolProperty( - name="Export Hidden Objects", + name="Hidden Objects", description="Export all objects, regardless of if they are hidden", default=True, ) @@ -90,23 +90,35 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): items = skeletonEnumCallback, ) + extentionEnum: EnumProperty( + name = "Use Names From", + description = "Whether the exporter should use object or mesh names.", + items=( + ('.alo', ".alo", ""), + ('.ALO', ".ALO", ""), + ), + default = filename_ext, + ) + def draw(self, context): layout = self.layout layout.use_property_split = True - row = layout.row() + row = layout.row(heading="Export") row.prop(self, "exportAnimations") row = layout.row() row.prop(self, "exportHiddenObjects") row = layout.row(heading="Names From") - row.use_property_split = False row.prop(self, "useNamesFrom", expand = True) - row = layout.row() + row = layout.row(heading="Armature") row.prop(bpy.context.scene.ActiveSkeleton, "skeletonEnum") + row = layout.row(heading="Extension") + row.prop(self, "extentionEnum", expand = True) + def execute(self, context): # execute() is called by blender when running the operator. #skeleton and bones @@ -1413,6 +1425,7 @@ def exportAnimations(filePath): global file if os.access(path, os.W_OK) or not os.access(path, os.F_OK): + path = path.replace(self.filename_ext, self.extentionEnum) file = open(path, 'wb') # open file in read binary mode bone_name_per_alo_index = create_skeleton() From 77e00d2e4dfc7b2489d49122db955055533b8421 Mon Sep 17 00:00:00 2001 From: yyhrs Date: Mon, 10 Mar 2025 22:13:38 +0100 Subject: [PATCH 098/100] Update export_alo.py --- io_alamo_tools/export_alo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 2e14339..6c27467 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -91,7 +91,7 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): ) extentionEnum: EnumProperty( - name = "Use Names From", + name = "Extension", description = "Whether the exporter should use object or mesh names.", items=( ('.alo', ".alo", ""), From a330281b635f0b573d0c47edee73d1dff25f563f Mon Sep 17 00:00:00 2001 From: yyhrs Date: Mon, 10 Mar 2025 23:01:30 +0100 Subject: [PATCH 099/100] Update export_alo.py --- io_alamo_tools/export_alo.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 6c27467..86dc52d 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -90,7 +90,10 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): items = skeletonEnumCallback, ) - extentionEnum: EnumProperty( + def extension_update(self, context): + ALO_Exporter.filename_ext = self.extention + + extention: EnumProperty( name = "Extension", description = "Whether the exporter should use object or mesh names.", items=( @@ -98,9 +101,9 @@ class ALO_Exporter(bpy.types.Operator, ExportHelper): ('.ALO', ".ALO", ""), ), default = filename_ext, + update=extension_update, ) - def draw(self, context): layout = self.layout layout.use_property_split = True @@ -117,7 +120,7 @@ def draw(self, context): row.prop(bpy.context.scene.ActiveSkeleton, "skeletonEnum") row = layout.row(heading="Extension") - row.prop(self, "extentionEnum", expand = True) + row.prop(self, "extention", expand = True) def execute(self, context): # execute() is called by blender when running the operator. @@ -1425,7 +1428,6 @@ def exportAnimations(filePath): global file if os.access(path, os.W_OK) or not os.access(path, os.F_OK): - path = path.replace(self.filename_ext, self.extentionEnum) file = open(path, 'wb') # open file in read binary mode bone_name_per_alo_index = create_skeleton() From b1453e155dbca47948490866e9e6ae3b03d5ea16 Mon Sep 17 00:00:00 2001 From: LordPoncho57 Date: Mon, 7 Jul 2025 16:46:07 -0500 Subject: [PATCH 100/100] Custom Shader Support Add my custom shaders to be used in by the tool --- io_alamo_tools/UI_material.py | 16 ++++++++++++++++ io_alamo_tools/export_alo.py | 8 ++++++++ io_alamo_tools/import_alo.py | 4 ++-- io_alamo_tools/settings.py | 16 +++++++++++++--- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/io_alamo_tools/UI_material.py b/io_alamo_tools/UI_material.py index 7f5874e..9348cc1 100644 --- a/io_alamo_tools/UI_material.py +++ b/io_alamo_tools/UI_material.py @@ -194,6 +194,17 @@ def register(): bpy.types.Material.BlendSharpness = bpy.props.FloatProperty( min=0.0, max=255.0, default=0.1 ) + # Custom material parameters + bpy.types.Material.UVOffsetX = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.0 + ) + bpy.types.Material.UVOffsetY = bpy.props.FloatProperty( + min=-255.0, max=255.0, default=0.0 + ) + bpy.types.Material.UVScaleFactor = bpy.props.FloatProperty( + min=0.1, max=255.0, default=1.0 + ) + bpy.types.Material.MaskTexture = bpy.props.StringProperty(default="None") def unregister(): @@ -248,6 +259,11 @@ def unregister(): # tryplanar mapping properties bpy.types.Material.MappingScale bpy.types.Material.BlendSharpness + # Custom material parameters + bpy.types.Material.UVOffsetX + bpy.types.Material.UVOffsetY + bpy.types.Material.UVScaleFactor + bpy.types.Material.MaskTexture if __name__ == "__main__": diff --git a/io_alamo_tools/export_alo.py b/io_alamo_tools/export_alo.py index 86dc52d..6a2fd12 100644 --- a/io_alamo_tools/export_alo.py +++ b/io_alamo_tools/export_alo.py @@ -906,6 +906,14 @@ def create_mat_info_chunks(mat_name, material): chunk += mat_float_chunk("MappingScale", material.MappingScale) elif (parameter == "BlendSharpness"): chunk += mat_float_chunk("BlendSharpness", material.MappingScale) + elif (parameter == "UVOffsetX"): + chunk += mat_float_chunk("UVOffsetX", material.UVOffsetX) + elif (parameter == "UVOffsetY"): + chunk += mat_float_chunk("UVOffsetY", material.UVOffsetY) + elif (parameter == "UVScaleFactor"): + chunk += mat_float_chunk("UVScaleFactor", material.UVScaleFactor) + elif (parameter == "MaskTexture"): + chunk += mat_tex_chunk("MaskTexture", material.MaskTexture) #else: #print("warning: unkown shader parameter: " + parameter) #for debugging return chunk diff --git a/io_alamo_tools/import_alo.py b/io_alamo_tools/import_alo.py index 8979930..ce98033 100644 --- a/io_alamo_tools/import_alo.py +++ b/io_alamo_tools/import_alo.py @@ -652,7 +652,7 @@ def create_material(currentSubMesh): # TODO: Extract set_alamo_shader's shader finder to new function, use that here. material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "DebugColor", "UVOffset", "Color", "UVScrollRate", "DiffuseColor", - "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale", "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture"] + "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale", "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture", "UVOffsetX", "UVOffsetY", "UVScaleFactor", "MaskTexture"] for texture in material_props: if texture in oldMat: @@ -1048,7 +1048,7 @@ def load_image(texture_name): def validate_material_prop(name): material_props = ["BaseTexture", "NormalTexture", "GlossTexture", "WaveTexture", "DistortionTexture", "CloudTexture", "CloudNormalTexture", "Emissive", "Diffuse", "Specular", "Shininess", "Colorization" \ , "DebugColor", "UVOffset", "Color", "UVScrollRate", "DiffuseColor", "EdgeBrightness", "BaseUVScale", "WaveUVScale", "DistortUVScale", "BaseUVScrollRate", "WaveUVScrollRate", "DistortUVScrollRate", "BendScale" \ - , "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture"] + , "Diffuse1", "CloudScrollRate", "CloudScale", "SFreq", "TFreq", "DistortionScale", "Atmosphere", "CityColor", "AtmospherePower", "SpecularTexture", "UVOffsetX", "UVOffsetY", "UVScaleFactor", "MaskTexture"] if(name in material_props): return True diff --git a/io_alamo_tools/settings.py b/io_alamo_tools/settings.py index e25ab63..b26ce2c 100644 --- a/io_alamo_tools/settings.py +++ b/io_alamo_tools/settings.py @@ -46,7 +46,13 @@ "MeshAlphaGloss.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "BaseTexture"], "MeshBumpSpecGlowColorize.fx": ["Emissive", "Diffuse", "Colorization", "UVOffset", "BaseTexture", "NormalTexture", "SpecularTexture"], "MeshBumpReflectColorize.fx": ["Emissive", "Diffuse", "Specular", "Shininess", "Colorization", "UVOffset", - "BaseTexture", "NormalTexture"] + "BaseTexture", "NormalTexture"], + "MeshGlow.fx": ["BaseTexture", "DistortionScale"], + "MeshAdditiveScalar.fx": ["BaseTexture", "UVScrollRate", "Color", "DistortionScale"], + "MeshBumpColorizeTile.fx": ["Emissive", "Diffuse", "Specular", "Colorization", "UVOffsetX", "UVOffsetY", "UVScaleFactor", + "BaseTexture", "NormalTexture", "MaskTexture"], + + } vertex_format_dict = { @@ -84,12 +90,16 @@ "MeshAdditiveVColor.fx" : "alD3dVertNU2C", "MeshAlphaGloss.fx" : "alD3dVertNU2", "MeshBumpSpecGlowColorize.fx" : "alD3dVertNU2C", - "MeshBumpReflectColorize.fx" : "alD3dVertNU2U3U3" + "MeshBumpReflectColorize.fx" : "alD3dVertNU2U3U3", + "MeshGlow.fx" : "alD3dVertNU2", + "MeshAdditiveScalar.fx" : "alD3dVertNU2", + "MeshBumpColorizeTile.fx" : "alD3dVertNU2U3U3", + } billboard_array = {"Disable":0, "Parallel":1, "Face":2, "ZAxis View": 3, "ZAxis Light":4, "ZAxis Wind":5, "Sunlight Glow":6, "Sun":7} -bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpReflectColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx", "MeshBumpSpecColorize.fx"] +bumpMappingList = ['MeshBumpColorize.fx', 'MeshBumpReflectColorize.fx', 'MeshBumpColorizeVertex.fx', 'MeshBumpColorizeDetail.fx', "MeshBumpLight.fx", "Planet.fx", "RSkinBumpColorize.fx", "TerrainMeshBump.fx", "Tree.fx", "MeshBumpSpecColorize.fx", "MeshBumpColorizeTile.fx"] rotation_curve_name = ['].rotation_quaternion', '].rotation_euler']