kopia lustrzana https://github.com/gd3kr/BlenderGPT
248 wiersze
7.5 KiB
Python
248 wiersze
7.5 KiB
Python
import sys
|
|
import os
|
|
import bpy
|
|
import bpy.props
|
|
import re
|
|
|
|
# Add the 'libs' folder to the Python path
|
|
libs_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "lib")
|
|
if libs_path not in sys.path:
|
|
sys.path.append(libs_path)
|
|
|
|
import openai
|
|
|
|
from .utilities import *
|
|
bl_info = {
|
|
"name": "GPT-4 Blender Assistant",
|
|
"blender": (2, 82, 0),
|
|
"category": "Object",
|
|
"author": "Aarya (@gd3kr)",
|
|
"version": (2, 0, 0),
|
|
"location": "3D View > UI > GPT-4 Blender Assistant",
|
|
"description": "Generate Blender Python code using OpenAI's GPT-4 to perform various tasks.",
|
|
"warning": "",
|
|
"wiki_url": "",
|
|
"tracker_url": "",
|
|
}
|
|
|
|
system_prompt = """You are an assistant made for the purposes of helping the user with Blender, the 3D software.
|
|
- Respond with your answers in markdown (```).
|
|
- Preferably import entire modules instead of bits.
|
|
- Do not perform destructive operations on the meshes.
|
|
- Do not use cap_ends. Do not do more than what is asked (setting up render settings, adding cameras, etc)
|
|
- Do not respond with anything that is not Python code.
|
|
|
|
Example:
|
|
|
|
user: create 10 cubes in random locations from -10 to 10
|
|
assistant:
|
|
```
|
|
import bpy
|
|
import random
|
|
bpy.ops.mesh.primitive_cube_add()
|
|
|
|
#how many cubes you want to add
|
|
count = 10
|
|
|
|
for c in range(0,count):
|
|
x = random.randint(-10,10)
|
|
y = random.randint(-10,10)
|
|
z = random.randint(-10,10)
|
|
bpy.ops.mesh.primitive_cube_add(location=(x,y,z))
|
|
```"""
|
|
|
|
|
|
|
|
class GPT4_OT_DeleteMessage(bpy.types.Operator):
|
|
bl_idname = "gpt4.delete_message"
|
|
bl_label = "Delete Message"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
message_index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
context.scene.gpt4_chat_history.remove(self.message_index)
|
|
return {'FINISHED'}
|
|
|
|
class GPT4_OT_ShowCode(bpy.types.Operator):
|
|
bl_idname = "gpt4.show_code"
|
|
bl_label = "Show Code"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
code: bpy.props.StringProperty(
|
|
name="Code",
|
|
description="The generated code",
|
|
default="",
|
|
)
|
|
|
|
def execute(self, context):
|
|
text_name = "GPT4_Generated_Code.py"
|
|
text = bpy.data.texts.get(text_name)
|
|
if text is None:
|
|
text = bpy.data.texts.new(text_name)
|
|
|
|
text.clear()
|
|
text.write(self.code)
|
|
|
|
text_editor_area = None
|
|
for area in context.screen.areas:
|
|
if area.type == 'TEXT_EDITOR':
|
|
text_editor_area = area
|
|
break
|
|
|
|
if text_editor_area is None:
|
|
text_editor_area = split_area_to_text_editor(context)
|
|
|
|
text_editor_area.spaces.active.text = text
|
|
|
|
return {'FINISHED'}
|
|
|
|
class GPT4_PT_Panel(bpy.types.Panel):
|
|
bl_label = "GPT-4 Blender Assistant"
|
|
bl_idname = "GPT4_PT_Panel"
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = 'GPT-4 Assistant'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
column = layout.column(align=True)
|
|
|
|
column.label(text="Chat history:")
|
|
box = column.box()
|
|
for index, message in enumerate(context.scene.gpt4_chat_history):
|
|
if message.type == 'assistant':
|
|
row = box.row()
|
|
row.label(text="Assistant: ")
|
|
show_code_op = row.operator("gpt4.show_code", text="Show Code")
|
|
show_code_op.code = message.content
|
|
delete_message_op = row.operator("gpt4.delete_message", text="", icon="TRASH", emboss=False)
|
|
delete_message_op.message_index = index
|
|
else:
|
|
row = box.row()
|
|
row.label(text=f"User: {message.content}")
|
|
delete_message_op = row.operator("gpt4.delete_message", text="", icon="TRASH", emboss=False)
|
|
delete_message_op.message_index = index
|
|
|
|
column.separator()
|
|
|
|
column.label(text="GPT Model:")
|
|
column.prop(context.scene, "gpt4_model", text="")
|
|
|
|
column.label(text="Enter your message:")
|
|
column.prop(context.scene, "gpt4_chat_input", text="")
|
|
button_label = "Please wait...(this might take some time)" if context.scene.gpt4_button_pressed else "Execute"
|
|
row = column.row(align=True)
|
|
row.operator("gpt4.send_message", text=button_label)
|
|
row.operator("gpt4.clear_chat", text="Clear Chat")
|
|
|
|
column.separator()
|
|
|
|
class GPT4_OT_ClearChat(bpy.types.Operator):
|
|
bl_idname = "gpt4.clear_chat"
|
|
bl_label = "Clear Chat"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
context.scene.gpt4_chat_history.clear()
|
|
return {'FINISHED'}
|
|
|
|
class GPT4_OT_Execute(bpy.types.Operator):
|
|
bl_idname = "gpt4.send_message"
|
|
bl_label = "Send Message"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
natural_language_input: bpy.props.StringProperty(
|
|
name="Command",
|
|
description="Enter the natural language command",
|
|
default="",
|
|
)
|
|
|
|
def execute(self, context):
|
|
openai.api_key = get_api_key(context, __name__)
|
|
# if null then set to env key
|
|
if not openai.api_key:
|
|
openai.api_key = os.getenv("OPENAI_API_KEY")
|
|
|
|
if not openai.api_key:
|
|
self.report({'ERROR'}, "No API key detected. Please set the API key in the addon preferences.")
|
|
return {'CANCELLED'}
|
|
|
|
context.scene.gpt4_button_pressed = True
|
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
|
|
|
blender_code = generate_blender_code(context.scene.gpt4_chat_input, context.scene.gpt4_chat_history, context, system_prompt)
|
|
|
|
message = context.scene.gpt4_chat_history.add()
|
|
message.type = 'user'
|
|
message.content = context.scene.gpt4_chat_input
|
|
|
|
# Clear the chat input field
|
|
context.scene.gpt4_chat_input = ""
|
|
|
|
|
|
if blender_code:
|
|
message = context.scene.gpt4_chat_history.add()
|
|
message.type = 'assistant'
|
|
message.content = blender_code
|
|
|
|
global_namespace = globals().copy()
|
|
|
|
try:
|
|
exec(blender_code, global_namespace)
|
|
except Exception as e:
|
|
self.report({'ERROR'}, f"Error executing generated code: {e}")
|
|
context.scene.gpt4_button_pressed = False
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
context.scene.gpt4_button_pressed = False
|
|
return {'FINISHED'}
|
|
|
|
|
|
def menu_func(self, context):
|
|
self.layout.operator(GPT4_OT_Execute.bl_idname)
|
|
|
|
class GPT4AddonPreferences(bpy.types.AddonPreferences):
|
|
bl_idname = __name__
|
|
|
|
api_key: bpy.props.StringProperty(
|
|
name="API Key",
|
|
description="Enter your OpenAI API Key",
|
|
default="",
|
|
subtype="PASSWORD",
|
|
)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.prop(self, "api_key")
|
|
|
|
def register():
|
|
bpy.utils.register_class(GPT4AddonPreferences)
|
|
bpy.utils.register_class(GPT4_OT_Execute)
|
|
bpy.utils.register_class(GPT4_PT_Panel)
|
|
bpy.utils.register_class(GPT4_OT_ClearChat)
|
|
bpy.utils.register_class(GPT4_OT_ShowCode)
|
|
bpy.utils.register_class(GPT4_OT_DeleteMessage)
|
|
|
|
|
|
bpy.types.VIEW3D_MT_mesh_add.append(menu_func)
|
|
init_props()
|
|
|
|
|
|
def unregister():
|
|
bpy.utils.unregister_class(GPT4AddonPreferences)
|
|
bpy.utils.unregister_class(GPT4_OT_Execute)
|
|
bpy.utils.unregister_class(GPT4_PT_Panel)
|
|
bpy.utils.unregister_class(GPT4_OT_ClearChat)
|
|
bpy.utils.unregister_class(GPT4_OT_ShowCode)
|
|
bpy.utils.unegister_class(GPT4_OT_DeleteMessage)
|
|
|
|
bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)
|
|
clear_props()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|