feat: 添加 ComfyProjector Blender 插件初始版本
- 新增插件核心文件 __init__.py,包含插件信息和注册逻辑 - 添加工具模块 utils.py,实现 AOV 烘焙和网格视图生成功能 - 包含资产文件:Materials.blend 和 geonodesuvpack.blend - 添加 .gitignore 文件以忽略 Python 缓存目录 - 插件提供 3D 视图面板,包含两个操作按钮
This commit is contained in:
175
utils.py
Normal file
175
utils.py
Normal file
@@ -0,0 +1,175 @@
|
||||
import bpy
|
||||
import bpy.utils.previews
|
||||
from mathutils import Vector
|
||||
import os
|
||||
def property_exists(prop_path, glob, loc):
|
||||
try:
|
||||
eval(prop_path, glob, loc)
|
||||
return True
|
||||
except: # noqa: E722
|
||||
return False
|
||||
|
||||
class Comfy_OT_Aovbake_Ad980(bpy.types.Operator):
|
||||
bl_idname = "comfyui.aovbake_ad980"
|
||||
bl_label = "aovbake"
|
||||
bl_description = ""
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, _context):
|
||||
if bpy.app.version >= (3, 0, 0) and True:
|
||||
cls.poll_message_set(
|
||||
"Please save your Blender file before running the script."
|
||||
)
|
||||
return not ("" == bpy.data.filepath)
|
||||
|
||||
def execute(self, _context):
|
||||
if "" == bpy.data.filepath:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
message="Please save your Blender file before running the script.",
|
||||
)
|
||||
else:
|
||||
if bpy.context.view_layer.objects.active.type == "MESH":
|
||||
bpy.data.scenes["Scene"].render.film_transparent = True
|
||||
bpy.data.scenes["Scene"].render.engine = "CYCLES"
|
||||
bpy.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_z = True
|
||||
bpy.data.scenes["Scene"].cycles.samples = 16
|
||||
bpy.data.scenes["Scene"].render.resolution_x = 1024
|
||||
bpy.data.scenes["Scene"].render.resolution_y = 1024
|
||||
bpy.data.scenes["Scene"].view_layers["ViewLayer"].use_pass_normal = True
|
||||
bpy.context.scene.sna_activeobject = (
|
||||
bpy.context.view_layer.objects.active
|
||||
)
|
||||
bpy.data.scenes["Scene"].use_nodes = True
|
||||
import math
|
||||
|
||||
def show_message_box(message="", title="Message", icon="INFO"):
|
||||
"""Shows a pop-up message in Blender."""
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.label(text=message)
|
||||
|
||||
bpy.context.window_manager.popup_menu(draw, title=title, icon=icon)
|
||||
|
||||
def create_camera_for_object(obj):
|
||||
if obj.type != "MESH":
|
||||
show_message_box("Selected object must be a mesh.", "Error")
|
||||
return None
|
||||
bpy.ops.object.camera_add()
|
||||
cam = bpy.context.object
|
||||
cam.name = "ObjectCamera"
|
||||
bpy.context.scene.camera = cam
|
||||
bpy.context.scene.sna_bakecam = bpy.context.scene.camera
|
||||
bbox_corners = [
|
||||
obj.matrix_world @ Vector(corner) for corner in obj.bound_box
|
||||
]
|
||||
min_x = min(corner.x for corner in bbox_corners)
|
||||
max_x = max(corner.x for corner in bbox_corners)
|
||||
min_y = min(corner.y for corner in bbox_corners)
|
||||
max_y = max(corner.y for corner in bbox_corners)
|
||||
min_z = min(corner.z for corner in bbox_corners)
|
||||
max_z = max(corner.z for corner in bbox_corners)
|
||||
center = (
|
||||
Vector((min_x + max_x, min_y + max_y, min_z + max_z)) / 2
|
||||
).to_3d()
|
||||
size = max(max_x - min_x, max_y - min_y, max_z - min_z) * 1.5
|
||||
cam.location = center + Vector((0, -size, 0))
|
||||
cam.data.type = "ORTHO"
|
||||
cam.data.ortho_scale = size
|
||||
cam.rotation_euler = (math.radians(90), 0, 0)
|
||||
bpy.context.scene.sna_activeobject.select_set(state=True)
|
||||
bpy.ops.view3d.camera_to_view_selected()
|
||||
return cam
|
||||
|
||||
def main():
|
||||
obj = bpy.context.active_object
|
||||
if obj is None:
|
||||
show_message_box("No active object selected.", "Error")
|
||||
return
|
||||
create_camera_for_object(obj)
|
||||
|
||||
main()
|
||||
bpy.context.scene.sna_activeobject.select_set(
|
||||
state=True,
|
||||
)
|
||||
bpy.context.view_layer.objects.active = (
|
||||
bpy.context.scene.sna_activeobject
|
||||
)
|
||||
bpy.data.scenes["Scene"].use_nodes = False
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, _event):
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
class Comfy_OT_Mvgen_B6229(bpy.types.Operator):
|
||||
bl_idname = "comfyui.mvgen_b6229"
|
||||
bl_label = "mvgen"
|
||||
bl_description = ""
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, _context):
|
||||
if bpy.app.version >= (3, 0, 0) and True:
|
||||
cls.poll_message_set(
|
||||
"Please save your Blender file before running the script."
|
||||
)
|
||||
return not ("" == bpy.data.filepath)
|
||||
|
||||
def execute(self, _context):
|
||||
if "" == bpy.data.filepath:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
message="Please save your Blender file before running the script.",
|
||||
)
|
||||
else:
|
||||
if (None is not bpy.context.view_layer.objects.active):
|
||||
if bpy.context.view_layer.objects.active.type == 'MESH':
|
||||
bpy.context.scene.sna_activeobject = bpy.context.view_layer.objects.active
|
||||
bpy.context.scene.sna_activeobject_name = bpy.context.scene.sna_activeobject.name
|
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||
if property_exists("bpy.data.node_groups['UV_New']", globals(), locals()):
|
||||
pass
|
||||
else:
|
||||
before_data = list(bpy.data.node_groups)
|
||||
bpy.ops.wm.append(directory=os.path.join(os.path.dirname(__file__), 'assets', 'geonodesuvpack.blend') + r'\NodeTree', filename='UV_New', link=False)
|
||||
new_data = list(filter(lambda d: d not in before_data, list(bpy.data.node_groups)))
|
||||
appended_4ED0B = None if not new_data else new_data[0]
|
||||
bpy.context.view_layer.objects.active = bpy.context.scene.sna_activeobject
|
||||
bpy.context.scene.sna_activeobject.select_set(state=True, )
|
||||
modifier_5D814 = bpy.context.scene.sna_activeobject.modifiers.new(name='MV_Nodes', type='NODES', )
|
||||
node_group = bpy.data.node_groups.get('UV_New')
|
||||
if node_group is None:
|
||||
self.report({'ERROR'}, message='Missing node group: UV_New')
|
||||
return {"CANCELLED"}
|
||||
bpy.context.scene.sna_activeobject.modifiers['MV_Nodes'].node_group = node_group
|
||||
bpy.ops.object.convert(target='MESH', keep_original=True)
|
||||
bpy.data.objects[bpy.context.scene.sna_activeobject_name].hide_render = True
|
||||
bpy.ops.comfyui.aovbake_ad980('INVOKE_DEFAULT', )
|
||||
bpy.data.objects[bpy.context.scene.sna_activeobject_name].modifiers.clear()
|
||||
bpy.context.view_layer.objects[bpy.context.scene.sna_activeobject_name].hide_set(state=True, )
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, _event):
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
class Comfy_PT_ComfyProjectorPanel(bpy.types.Panel):
|
||||
bl_label = "ComfyProjector"
|
||||
bl_idname = "Comfy_PT_comfyprojector"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "ComfyProjector"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
layout.operator("comfyui.aovbake_ad980", text="AOV Bake")
|
||||
layout.operator("comfyui.mvgen_b6229", text="mvgen")
|
||||
|
||||
|
||||
classes = (
|
||||
Comfy_OT_Aovbake_Ad980,
|
||||
Comfy_OT_Mvgen_B6229,
|
||||
Comfy_PT_ComfyProjectorPanel,
|
||||
)
|
||||
Reference in New Issue
Block a user