From 61c05b86253e5b15452b646d383dfe3bc53f50c4 Mon Sep 17 00:00:00 2001 From: FConstans Date: Fri, 6 May 2022 10:27:32 +0200 Subject: [PATCH] Tag test --- .github/workflows/release.yml | 4 +- repositioning/__init__.py | 60 +++++++++++++++++++----------- repositioning/evaluate_shadow.py | 20 +++++----- repositioning/main_panel.py | 24 ++++++++---- repositioning/manage_textures.py | 35 ++++++++++-------- repositioning/rotate_target.py | 63 ++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 repositioning/rotate_target.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e55efc..c4fb132 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,9 +13,9 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: Build - run: zip -r repositioning.zip repositioning + run: zip -r repositioning-${{github.ref_name}}.zip repositioning - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - files: repositioning.zip + files: repositioning-${{github.ref_name}}.zip diff --git a/repositioning/__init__.py b/repositioning/__init__.py index bcd6bc7..e0aaaee 100644 --- a/repositioning/__init__.py +++ b/repositioning/__init__.py @@ -1,29 +1,10 @@ -from . import add_plane, main_panel, manage_textures, evaluate_shadow -import bpy - -# ------------------------------------------------------------------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------------------------------------------------------------------ -# We will be using RMSE coming from the Sewar package, to evaluate the obtained renders -# Because the version of python that bpy use is "isolated" in Blender, we have to make sure the package is installed inside of Blender: -import subprocess -import sys -import os -from pathlib import Path -py_exec = str(sys.executable) -# Get lib directory -lib = os.path.join(Path(py_exec).parent.parent, "lib") -# Ensure pip is installed -subprocess.call([py_exec, "-m", "ensurepip", "--user"]) -# Update pip (not mandatory) -subprocess.call([py_exec, "-m", "pip", "install", "--upgrade", "pip"]) -# Install packages -subprocess.call([py_exec, "-m", "pip", "install", - f"--target={str(lib)}", "sewar"]) +import bpy # ------------------------------------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------------------------------------ + bl_info = { "name": "Set a shadow baking environment", "author": "Florence Constans", @@ -35,13 +16,49 @@ "doc_url": "", "category": "All", } +# ------------------------------------------------------------------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------------------------------------------------------------------ + +import subprocess +import sys +import os +import importlib +from pathlib import Path + + +# We will be using RMSE coming from the Sewar package, to evaluate the obtained renders +# Because the version of python that bpy use is "isolated" in Blender, we have to make sure the package is installed inside of Blender: + + +if not importlib.util.find_spec("sewar"): + + py_exec = str(sys.executable) + + # # Get lib directory + lib = os.path.join(Path(py_exec).parent.parent, "lib") + # # Ensure pip is installed + # #subprocess.check_call([py_exec, "-m", "ensurepip", "--user"]) + # # Update pip (not mandatory) + # #subprocess.check_call([py_exec, "-m", "pip", "install", "--upgrade", "pip"]) + # # Install packages + subprocess.check_call([py_exec, '-m', 'pip', 'install',f"--target={str(lib)}", 'sewar']) +else : + print("Sewar already installed") + +# ------------------------------------------------------------------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------------------------------------------------------------------ + +from . import add_plane, main_panel, manage_textures, evaluate_shadow, rotate_target + def register(): + main_panel.register() add_plane.register() manage_textures.register() evaluate_shadow.register() + rotate_target.register() def unregister(): @@ -49,6 +66,7 @@ def unregister(): add_plane.unregister() manage_textures.unregister() evaluate_shadow.unregister() + rotate_target.unregister() if __name__ == "__main__": diff --git a/repositioning/evaluate_shadow.py b/repositioning/evaluate_shadow.py index 32b8ee0..6a65d8f 100644 --- a/repositioning/evaluate_shadow.py +++ b/repositioning/evaluate_shadow.py @@ -2,31 +2,31 @@ import bpy -import sewar -import numpy -import PIL +from sewar.full_ref import rmse +from numpy import asarray +from PIL import Image from . manage_textures import bake_shadow, save_image def compare_textures(): object = bpy.context.active_object - object_material = bpy.data.materials[object.name] + object_material = bpy.data.materials[object.name_full] - ref_name = object.name+"_"+"ref_image" - shad_name = object.name+"_"+"shadow_image" + ref_name = object.name_full+"_"+"ref_image" + shad_name = object.name_full+"_"+"shadow_image" - if ref_name in bpy.data.materials[object.name].node_tree.nodes: + if ref_name in bpy.data.materials[object.name_full].node_tree.nodes: save_image(object_material.node_tree.nodes[ref_name]) save_image(bake_shadow()) - ref_image = numpy.asarray(PIL.Image.open( + ref_image = asarray(Image.open( bpy.app.tempdir + ref_name+".png")) - shad_image = numpy.asarray(PIL.Image.open( + shad_image = asarray(Image.open( bpy.app.tempdir + shad_name+".png")) - result = sewar.full_ref.rmse(ref_image, shad_image) + result = rmse(ref_image, shad_image) return(result) else: return("Please, set a reference") diff --git a/repositioning/main_panel.py b/repositioning/main_panel.py index 3a16bc6..6c0ca25 100644 --- a/repositioning/main_panel.py +++ b/repositioning/main_panel.py @@ -22,10 +22,17 @@ def draw(self, context): box = layout.box() box.label(text="Manage textures") - ref_node = obj.name+"_"+"ref_image" - shad_node = obj.name+"_"+"shadow_image" + ref_node = obj.name_full+"_"+"ref_image" + shad_node = obj.name_full+"_"+"shadow_image" - if obj.name not in bpy.data.materials: + result_box = layout.box() + result_box.label(text="Comparison actions") + result_box.operator( + 'object.rotate_target', text='Select object as target').action = 'Select_target' + result_box.operator( + 'object.rotate_target', text='Rotate targeted object').action = 'Rotate_object' + + if obj.name_full not in bpy.data.materials: box.operator('object.manage_textures', text='Add baking dedicated material').action = 'Add_material' else: @@ -35,22 +42,23 @@ def draw(self, context): save_box = box.box() save_box.label(text="Save textures in temp") - if ref_node in bpy.data.materials[obj.name].node_tree.nodes: + if ref_node in bpy.data.materials[obj.name_full].node_tree.nodes: tex_box.operator('object.manage_textures', text='Draw on reference').action = 'Draw_reference' save_box.operator('object.manage_textures', text='Save drawn shadow').action = 'Save_reference' - if shad_node in bpy.data.materials[obj.name].node_tree.nodes: + if shad_node in bpy.data.materials[obj.name_full].node_tree.nodes: save_box.operator('object.manage_textures', text='Save baked shadow').action = 'Save_baked' - result_box = layout.box() - result_box.label(text="Compare") + result_box.operator('object.evaluate_shadow', text='Compare shadows').action = 'Compare' result_box.label(text="Comparison result") score = result_box.box() - score.label(text=bpy.app.driver_namespace["shad_comparison"]) + if "shad_comparison" in bpy.app.driver_namespace: + score.label( + text=bpy.app.driver_namespace["shad_comparison"]) def register(): diff --git a/repositioning/manage_textures.py b/repositioning/manage_textures.py index a5484f3..3194a44 100644 --- a/repositioning/manage_textures.py +++ b/repositioning/manage_textures.py @@ -6,8 +6,8 @@ def add_object_material(): object = bpy.context.active_object - if object.name not in bpy.data.materials: - object_material = bpy.data.materials.new(object.name) + if object.name_full not in bpy.data.materials: + object_material = bpy.data.materials.new(object.name_full) object_material.use_nodes = True # Mandatory for texture manipulation object.data.materials.append(object_material) @@ -18,23 +18,23 @@ def add_object_material(): bsdf.inputs['Base Color'], mix_node.outputs['Color']) # A mix node to see both the baked texture and the drawn shadow at the same time - bpy.data.materials[object.name].node_tree.nodes["Mix"].inputs[0].default_value = 0.8 + bpy.data.materials[object.name_full].node_tree.nodes["Mix"].inputs[0].default_value = 0.8 # Enable texture visibility in the ViewPort if bpy.context.space_data.shading.type != 'MATERIAL': bpy.context.space_data.shading.type = 'MATERIAL' else: - object_material = bpy.data.materials[object.name] + object_material = bpy.data.materials[object.name_full] object.data.materials.append(object_material) def new_blank_image(img_name="tex_image"): object = bpy.context.active_object - name = object.name+"_"+img_name + name = object.name_full+"_"+img_name - object_material = bpy.data.materials[object.name] + object_material = bpy.data.materials[object.name_full] if name not in object_material.node_tree.nodes: image = object_material.node_tree.nodes.new('ShaderNodeTexImage') @@ -53,7 +53,7 @@ def new_blank_image(img_name="tex_image"): # Image on which targeted shadow can be drawn def new_reference_image(name="ref_image"): object = bpy.context.active_object - object_material = bpy.data.materials[object.name] + object_material = bpy.data.materials[object.name_full] reference_image = new_blank_image(name) @@ -68,16 +68,18 @@ def new_reference_image(name="ref_image"): # Save image from a node in temp directory of Blender def save_image(node): - bpy.data.images[node.image.name].filepath_raw = bpy.app.tempdir + \ - node.image.name+".png" - bpy.data.images[node.image.name].file_format = 'PNG' - bpy.data.images[node.image.name].save() + bpy.data.images[node.image.name_full].filepath_raw = bpy.app.tempdir + \ + node.image.name_full+".png" + bpy.data.images[node.image.name_full].file_format = 'PNG' + bpy.data.images[node.image.name_full].save() def bake_shadow(): + global baked_image + object = bpy.context.active_object - object_material = bpy.data.materials[object.name] + object_material = bpy.data.materials[object.name_full] blank_base = new_blank_image("blank") @@ -86,7 +88,8 @@ def bake_shadow(): bsdf.inputs['Base Color'], blank_base.outputs['Color']) baked_image = new_blank_image("shadow_image") - object_material.node_tree.nodes.active = object_material.node_tree.nodes[baked_image.name] + object_material.node_tree.nodes.active = object_material.node_tree.nodes[ + baked_image.name] bpy.context.scene.render.engine = 'CYCLES' bpy.context.scene.cycles.preview_samples = 1 bpy.context.scene.cycles.samples = 1 @@ -107,13 +110,13 @@ def bake_shadow(): def draw_on_ref(): object = bpy.context.active_object - object_material = bpy.data.materials[object.name] + object_material = bpy.data.materials[object.name_full] bpy.context.window.workspace = bpy.data.workspaces['Texture Paint'] # Image dedicated to reference drawing automatically selected back so that the baking texture won't be drawn on object_material.node_tree.nodes.active = object_material.node_tree.nodes[ - object.name+"_ref_image"] + object.name_full+"_ref_image"] class OBJECT_OT_manage_textures(bpy.types.Operator): @@ -155,7 +158,7 @@ def execute(self, context): elif self.action == 'Reference_texture': reference_image = new_reference_image() elif self.action == 'Baking_texture': - baked_image = bake_shadow() + bake_shadow() elif self.action == 'Save_reference': save_image(reference_image) elif self.action == 'Save_baked': diff --git a/repositioning/rotate_target.py b/repositioning/rotate_target.py new file mode 100644 index 0000000..274405e --- /dev/null +++ b/repositioning/rotate_target.py @@ -0,0 +1,63 @@ + +import bpy + +from math import radians +from sewar.full_ref import rmse +from numpy import asarray +from PIL import Image + +from . manage_textures import bake_shadow, save_image +from . evaluate_shadow import compare_textures + +global target +target = None + +# This operator enable the user to chose an object as a target, then go back to select the object on which the target's shadow should be baked. +# It then gives an option to rotate the target object on the Z axis, accordingly to the shadow we expect to cast + + +def select_target_object(): + return (bpy.context.active_object) + + +def rotate_object(target): + target.rotation_euler[2] += radians(10) + return("Rotation of target") + + +class OBJECT_OT_rotate_target(bpy.types.Operator): + bl_idname = "object.rotate_target" + bl_label = "Add materials and textures to a mesh" + bl_description = "This operator rotates a selected object in accordance with its expected casted shadow" + bl_options = {'REGISTER', 'UNDO'} + + action: bpy.props.EnumProperty(items=[ + ('Select_target', 'Select object as target', + 'Set active object as target of positionning'), + ('Rotate_object', 'Launch rotation', 'Launch target object rotation ') + ], name="Target rotation") + + def execute(self, context): + + global target + + if self.action == 'Select_target': + target = select_target_object() + elif self.action == 'Rotate_object': + if target != None: + bpy.app.driver_namespace["shad_comparison"] = rotate_object( + target) + + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(OBJECT_OT_rotate_target) + + +def unregister(): + bpy.utils.unregister_class(OBJECT_OT_rotate_target) + + +if __name__ == "__main__": + register()