Skip to content

Commit

Permalink
Added option to compare targeted and baked shadow
Browse files Browse the repository at this point in the history
  • Loading branch information
FConstans committed May 3, 2022
1 parent 6caf025 commit 1474547
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 99 deletions.
43 changes: 35 additions & 8 deletions repositioning/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,54 @@
from . import manage_plane, panel_manage_plane
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"])

# ------------------------------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------
bl_info = {
"name": "Set a shadow baking environment",
"author": "Florence Constans",
"version": (0, 0, 2),
"version": (0, 0, 3),
"blender": (2, 80, 0),
"location": "View3D > Side Bar",
"description": "Adds a plane on which surrounding objects' shadows are baked and compared to a reference",
"description": "Adds a plane to bake on, or add material and textures on already existing objects to bake shadows on ",
"warning": "",
"doc_url": "",
"category": "Add Mesh",
"category": "All",
}


def register():
panel_manage_plane.register()
manage_plane.register()
main_panel.register()
add_plane.register()
manage_textures.register()
evaluate_shadow.register()


def unregister():
panel_manage_plane.unregister()
manage_plane.unregister()
main_panel.unregister()
add_plane.unregister()
manage_textures.unregister()
evaluate_shadow.unregister()


if __name__ == "__main__":
Expand Down
62 changes: 62 additions & 0 deletions repositioning/add_plane.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import bpy


def set_plane(p_size=0, p_name="Floor", x_location=0, y_location=0, z_location=0, x_rotation=0, y_rotation=0, z_rotation=0):
bpy.ops.mesh.primitive_plane_add(size=p_size, enter_editmode=False, align='WORLD', location=(
x_location, y_location, z_location), rotation=(x_rotation, y_rotation, z_rotation))
plane = bpy.context.active_object
plane.name = p_name

return(plane)


class OBJECT_OT_manage_plane(bpy.types.Operator):

bl_idname = "object.manage_plane"
bl_label = "Place a plane with material to cast shadows on "
bl_description = "This operator permits the addition of a plane to cast shadows on"
bl_options = {'REGISTER', 'UNDO'}

p_size: bpy.props.FloatProperty(name="Size", default=10)

p_name: bpy.props.StringProperty(name="Name", default="Floor")

x_location: bpy.props.FloatProperty(name="location X", default=0)
y_location: bpy.props.FloatProperty(name="Y", default=0)
z_location: bpy.props.FloatProperty(name="Z", default=0)

x_rotation: bpy.props.FloatProperty(name="rotation X", default=0)
y_rotation: bpy.props.FloatProperty(name="Y", default=0)
z_rotation: bpy.props.FloatProperty(name="Z", default=0)

action: bpy.props.EnumProperty(
items=[
('Set_plane', 'Add a plane',
'generate a plane'),
], name="Plane management")

def execute(self, context):

if self.action == 'Set_plane':
set_plane(self.p_size,
self.p_name,
self.x_location,
self.y_location,
self.z_location,
self.x_rotation,
self.y_rotation,
self.z_rotation)

return {'FINISHED'}


def register():
bpy.utils.register_class(OBJECT_OT_manage_plane)


def unregister():
bpy.utils.unregister_class(OBJECT_OT_manage_plane)


if __name__ == "__main__":
register()
71 changes: 71 additions & 0 deletions repositioning/evaluate_shadow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

import bpy


import sewar
import numpy
import PIL

from . manage_textures import bake_shadow, save_image


def compare_textures():
object = bpy.context.active_object
object_material = bpy.data.materials[object.name]

ref_name = object.name+"_"+"ref_image"
shad_name = object.name+"_"+"shadow_image"

if ref_name in bpy.data.materials[object.name].node_tree.nodes:

save_image(object_material.node_tree.nodes[ref_name])
save_image(bake_shadow())

ref_image = numpy.asarray(PIL.Image.open(
bpy.app.tempdir + ref_name+".png"))
shad_image = numpy.asarray(PIL.Image.open(
bpy.app.tempdir + shad_name+".png"))

result = sewar.full_ref.rmse(ref_image, shad_image)
# result = "Testing"
return(result)
else:
return("Please, set a reference image, and bake the shadows on your object")


class OBJECT_OT_evaluate_shadow(bpy.types.Operator):
bl_idname = "object.evaluate_shadow"
bl_label = "Result of targeted and computed shadow comparison"
bl_description = "Give a score indicating whether objects are correctly placed in the scene"
bl_options = {'REGISTER', 'UNDO'}

evaluation_result = "No evaluation yet"

action: bpy.props.EnumProperty(items=[(
'Compare', 'Compare shadows', 'Compare drawn and generated shadows'), ], name="Texture comparisons")

def draw(self, context):
layout = self.layout
row = layout.row()
row.label(text=self.evaluation_result)

def execute(self, context):
if self.action == 'Compare':
self.evaluation_result = str(compare_textures())

# Result of RMSE operation is put in Blender's global dictionnary driver_namespace so that we can access it in any other operator

bpy.app.driver_namespace["shad_comparison"] = self.evaluation_result
return {'FINISHED'}


def register():
bpy.utils.register_class(OBJECT_OT_evaluate_shadow)


def unregister():
bpy.utils.unregister_class(OBJECT_OT_evaluate_shadow)


if __name__ == "__main__":
register()
65 changes: 65 additions & 0 deletions repositioning/main_panel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import bpy


class OBJECT_PT_shadow_comparator_panel (bpy.types.Panel):
bl_idname = 'OBJECT_PT_shadow_comparator'
bl_label = 'Manage elements of scene'
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Repositioning"

def draw(self, context):

obj = bpy.context.active_object

layout = self.layout

layout.operator('object.manage_plane',
text='Set Floor').action = 'Set_plane'

if obj != None and obj.type == 'MESH':

box = layout.box()
box.label(text="Manage textures")

ref_node = obj.name+"_"+"ref_image"
shad_node = obj.name+"_"+"shadow_image"

if obj.name not in bpy.data.materials:
box.operator('object.manage_textures',
text='Add baking dedicated material').action = 'Add_material'
else:
tex_box = box.box()
tex_box.operator('object.manage_textures',
text='Set texture to draw on').action = 'Reference_texture'
save_box = box.box()
save_box.label(text="Save textures in temp")

if ref_node in bpy.data.materials[obj.name].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:
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"])


def register():
bpy.utils.register_class(OBJECT_PT_shadow_comparator_panel)


def unregister():
bpy.utils.unregister_class(OBJECT_PT_shadow_comparator_panel)


if __name__ == "__main__":
register()
Loading

0 comments on commit 1474547

Please sign in to comment.