feat: Add new Geometry Nodes groups for image relief and shadeless shader
- Implemented the "O Image Relief Plane" Geometry Nodes group to create a fixed-base image relief using depth images and various parameters. - Created the "O Shadeless" shader node group to display color without scene lighting while preserving shadows and reflections. - Introduced a preview feature for node groups, allowing for interactive visualization of node arrangements in an HTML format. - Added a template HTML file for rendering node previews with customizable views and controls.
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
|
||||
from .operators import (
|
||||
ConvertToDepthPlane,
|
||||
ConvertToReliefPlane,
|
||||
ConvertToPlane,
|
||||
GenerateDepthPlane,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"ConvertToDepthPlane",
|
||||
"ConvertToReliefPlane",
|
||||
"ConvertToPlane",
|
||||
"GenerateDepthPlane",
|
||||
)
|
||||
|
||||
@@ -67,8 +67,12 @@ def create_depth_plane_object(
|
||||
material,
|
||||
depth_image,
|
||||
depth_metadata,
|
||||
plane_type,
|
||||
):
|
||||
node_group = load_node_group("O Image Depth Plane")
|
||||
node_group = load_node_group({
|
||||
"DEPTH": "O Image Depth Plane",
|
||||
"RELIEF": "O Image Relief Plane",
|
||||
}[plane_type])
|
||||
source_name = source_object.name
|
||||
bounds = image_empty_bounds(source_object)
|
||||
mesh = create_image_plane_mesh(source_name, bounds)
|
||||
@@ -80,7 +84,7 @@ def create_depth_plane_object(
|
||||
plane = bpy.data.objects.new(source_name, mesh)
|
||||
context.collection.objects.link(plane)
|
||||
plane.matrix_world = build_image_plane_matrix(source_object.matrix_world, bounds)
|
||||
plane["anyimage_mesh_shape"] = "DEPTH_PLANE"
|
||||
plane["anyimage_mesh_shape"] = f"{plane_type}_PLANE"
|
||||
|
||||
modifier = plane.modifiers.new(name=node_group.name, type="NODES")
|
||||
modifier.node_group = node_group
|
||||
@@ -108,6 +112,7 @@ def create_depth_plane_object(
|
||||
|
||||
|
||||
def create_depth_plane_from_result(context, result, operator):
|
||||
normal_space = {"DEPTH": "OBJECT", "RELIEF": "TANGENT"}[operator.plane_type]
|
||||
source_object = require_image_empty(operator.source_object_name)
|
||||
color_image = load_color_result_image(source_object, result.directory)
|
||||
color_image.name = color_data_name(source_object)
|
||||
@@ -122,16 +127,14 @@ def create_depth_plane_from_result(context, result, operator):
|
||||
depth_metadata,
|
||||
)
|
||||
normal_image = load_normal_result_image(
|
||||
result.file("tangent_normal"),
|
||||
result.file(f"{normal_space.lower()}_normal"),
|
||||
source_object,
|
||||
)
|
||||
material = create_image_material(
|
||||
source_object,
|
||||
source_object.data,
|
||||
color_image,
|
||||
0.0,
|
||||
normal_image=normal_image,
|
||||
normal_space="TANGENT",
|
||||
ior=1.2,
|
||||
normal_space=normal_space,
|
||||
scene=context.scene,
|
||||
)
|
||||
create_depth_plane_object(
|
||||
@@ -141,6 +144,7 @@ def create_depth_plane_from_result(context, result, operator):
|
||||
material,
|
||||
depth_image,
|
||||
depth_metadata,
|
||||
operator.plane_type,
|
||||
)
|
||||
except Exception:
|
||||
if material is not None and material.users == 0:
|
||||
@@ -149,6 +153,6 @@ def create_depth_plane_from_result(context, result, operator):
|
||||
if image is not None and image.users == 0:
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
raise
|
||||
label = "Convert to Depth Plane"
|
||||
label = f"Convert to {operator.plane_type.title()} Plane"
|
||||
if "FINISHED" not in bpy.ops.ed.undo_push(message=label):
|
||||
raise RuntimeError(f"Unable to create the {label} Undo step")
|
||||
|
||||
@@ -50,6 +50,11 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
|
||||
input_path: bpy.props.StringProperty(options=_HIDDEN)
|
||||
source_object_name: bpy.props.StringProperty(options=_HIDDEN)
|
||||
plane_type: bpy.props.EnumProperty(
|
||||
items=(("DEPTH", "Depth Plane", ""), ("RELIEF", "Relief Plane", "")),
|
||||
default="DEPTH",
|
||||
options=_HIDDEN,
|
||||
)
|
||||
mesh_detail: bpy.props.IntProperty(default=6, min=0, max=10, options=_HIDDEN)
|
||||
model: bpy.props.StringProperty(default=DEFAULT_MOGE2_MODEL_KEY, options=_HIDDEN)
|
||||
resolution_level: bpy.props.IntProperty(default=5, min=0, max=9, options=_HIDDEN)
|
||||
@@ -71,6 +76,7 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
"video_frames": 1,
|
||||
"start_frame": source_start_frame(self.source_object_name),
|
||||
"max_input_size": int(self.max_input_size),
|
||||
"plane_type": self.plane_type,
|
||||
}
|
||||
parameters.update(
|
||||
moge2_parameters(
|
||||
@@ -85,13 +91,13 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
|
||||
def response(self, context, result):
|
||||
create_depth_plane_from_result(context, result, self)
|
||||
return "Depth Plane is ready"
|
||||
return f"{self.plane_type.title()} Plane is ready"
|
||||
|
||||
def cleanup(self):
|
||||
cleanup_image_input(self.input_path, self.delete_input)
|
||||
|
||||
|
||||
def start_depth_plane_job(operator, context):
|
||||
def start_depth_plane_job(operator, context, plane_type):
|
||||
source_object = context.object
|
||||
if video_total_frames(source_object.data) != 1:
|
||||
operator.report({"ERROR"}, "Depth conversion currently supports still images")
|
||||
@@ -112,6 +118,7 @@ def start_depth_plane_job(operator, context):
|
||||
input_path=str(source_path),
|
||||
source_object_name=source_object.name,
|
||||
mesh_detail=operator.mesh_detail,
|
||||
plane_type=plane_type,
|
||||
model=model,
|
||||
resolution_level=resolution_level,
|
||||
max_input_size=configured_max_ai_input_size(context),
|
||||
@@ -141,9 +148,8 @@ class ConvertToPlane(bpy.types.Operator):
|
||||
source_object
|
||||
)
|
||||
material = create_image_material(
|
||||
source_object,
|
||||
source_object.data,
|
||||
color_image,
|
||||
1.0,
|
||||
frame_start=frame_start,
|
||||
color_frame_offset=frame_offset,
|
||||
color_frame_duration=frame_duration,
|
||||
@@ -157,7 +163,7 @@ class ConvertToPlane(bpy.types.Operator):
|
||||
class ConvertToDepthPlane(bpy.types.Operator):
|
||||
bl_idname = "anyimage.convert_to_depth_plane"
|
||||
bl_label = "Convert to Depth Plane"
|
||||
bl_description = "Convert the complete Image Empty into a Relief or Camera depth plane"
|
||||
bl_description = "Convert the complete Image Empty into a camera-projected depth plane"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
mesh_detail: bpy.props.IntProperty(name="Subdivide", default=6, min=0, max=10)
|
||||
@@ -168,8 +174,23 @@ class ConvertToDepthPlane(bpy.types.Operator):
|
||||
|
||||
return is_image_empty(context.object) and not runtime.server_busy()
|
||||
|
||||
def invoke(self, context, _event):
|
||||
return self.execute(context)
|
||||
def execute(self, context):
|
||||
return start_depth_plane_job(self, context, "DEPTH")
|
||||
|
||||
|
||||
class ConvertToReliefPlane(bpy.types.Operator):
|
||||
bl_idname = "anyimage.convert_to_relief_plane"
|
||||
bl_label = "Convert to Relief Plane"
|
||||
bl_description = "Convert the complete Image Empty into a fixed-base relief plane"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
mesh_detail: bpy.props.IntProperty(name="Subdivide", default=6, min=0, max=10)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
from ...runtime import runtime
|
||||
|
||||
return is_image_empty(context.object) and not runtime.server_busy()
|
||||
|
||||
def execute(self, context):
|
||||
return start_depth_plane_job(self, context)
|
||||
return start_depth_plane_job(self, context, "RELIEF")
|
||||
|
||||
Reference in New Issue
Block a user