kopia lustrzana https://github.com/hc-psy/blender-gpt
commit
d68d43d947
|
@ -6,6 +6,9 @@ __pycache__/
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
|
.DS_Store/
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from .gptzh_pnl import BLENDERGPT_PT_PANEL
|
||||||
|
from .gptzh_prf import BLENDERGPT_AddonPreferences
|
||||||
|
from .gptzh_opt import BLENDERGPT_OT_DEL_ALL_MSG, BLENDERGPT_OT_DEL_MSG, BLENDERGPT_OT_GPT_CODE, BLENDERGPT_OT_SEND_MSG
|
||||||
|
|
||||||
|
|
||||||
|
bl_info = {
|
||||||
|
"name": "Blender GPT (ZH) 中文用戶專屬",
|
||||||
|
"author": "Ryvn (@hc-psy) (@@hao-chenglo2049)",
|
||||||
|
"description": "",
|
||||||
|
"blender": (2, 82, 0),
|
||||||
|
"version": (0, 0, 1),
|
||||||
|
"location": "3D View (三維視圖) > UI (使用者介面) > BlenderGptZH",
|
||||||
|
"warning": "",
|
||||||
|
"category": "Object"
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
```"""
|
||||||
|
|
||||||
|
|
||||||
|
Classes = (BLENDERGPT_PT_PANEL, BLENDERGPT_OT_DEL_ALL_MSG, BLENDERGPT_OT_DEL_MSG,
|
||||||
|
BLENDERGPT_OT_GPT_CODE, BLENDERGPT_OT_SEND_MSG, BLENDERGPT_AddonPreferences)
|
||||||
|
|
||||||
|
|
||||||
|
def init_props():
|
||||||
|
bpy.types.Scene.history = bpy.props.CollectionProperty(
|
||||||
|
type=bpy.types.PropertyGroup)
|
||||||
|
|
||||||
|
bpy.types.Scene.model = bpy.props.EnumProperty(
|
||||||
|
name="GPT模型",
|
||||||
|
description="請選擇欲使用的Chat-GPT模型",
|
||||||
|
items=[
|
||||||
|
("gpt3.5", "GPT-3.5 (便宜但較容易出錯)", "使用 GPT-3.5 (便宜但較容易出錯)"),
|
||||||
|
("gpt4", "GPT-4 (昂貴但較詳細準確)", "使用 GPT-4 (昂貴但較詳細準確)"),
|
||||||
|
],
|
||||||
|
default="gpt3.5",
|
||||||
|
)
|
||||||
|
|
||||||
|
bpy.types.Scene.lan = bpy.props.EnumProperty(
|
||||||
|
name="語言",
|
||||||
|
description="請選擇Chat-GPT所回饋的語言",
|
||||||
|
items=[
|
||||||
|
("traditional", "繁體中文", "繁體中文"),
|
||||||
|
("simplified", "简体中文", "简体中文"),
|
||||||
|
("english", "English", "英文"),
|
||||||
|
],
|
||||||
|
default="traditional",
|
||||||
|
)
|
||||||
|
|
||||||
|
bpy.types.Scene.prompt_input = bpy.props.StringProperty(
|
||||||
|
name="指令",
|
||||||
|
description="請輸入你的指令",
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
|
||||||
|
bpy.types.Scene.on_finish = bpy.props.BoolProperty(default=False)
|
||||||
|
bpy.types.PropertyGroup.type = bpy.props.StringProperty()
|
||||||
|
bpy.types.PropertyGroup.content = bpy.props.StringProperty()
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_key(context, addon_name):
|
||||||
|
preferences = context.preferences
|
||||||
|
addon_prefs = preferences.addons[addon_name].preferences
|
||||||
|
return addon_prefs.api_key
|
||||||
|
|
||||||
|
|
||||||
|
def clear_props():
|
||||||
|
del bpy.types.Scene.history
|
||||||
|
del bpy.types.Scene.model
|
||||||
|
del bpy.types.Scene.prompt_input
|
||||||
|
del bpy.types.Scene.on_finish
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in Classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
init_props()
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
for cls in Classes:
|
||||||
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
|
clear_props()
|
|
@ -0,0 +1,120 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from bpy.types import Operator
|
||||||
|
|
||||||
|
|
||||||
|
class BLENDERGPT_OT_DEL_MSG(Operator):
|
||||||
|
bl_idname = "gpt.del_msg"
|
||||||
|
bl_label = "刪除訊息"
|
||||||
|
bl_description = "刪除訊息"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
msg_idx: bpy.props.IntProperty(name="訊息索引", default=0)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
scene = context.scene
|
||||||
|
history = scene.history
|
||||||
|
history.remove(self.msg_idx)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BLENDERGPT_OT_DEL_ALL_MSG(Operator):
|
||||||
|
bl_idname = "gpt.del_all_msg"
|
||||||
|
bl_label = "刪除所有訊息"
|
||||||
|
bl_description = "刪除所有訊息"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
scene = context.scene
|
||||||
|
history = scene.history
|
||||||
|
history.clear()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BLENDERGPT_OT_GPT_CODE(Operator):
|
||||||
|
bl_idname = "gpt.gpt_code"
|
||||||
|
bl_label = "展示GPT程式碼"
|
||||||
|
bl_description = "展示GPT程式碼"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
code: bpy.props.StringProperty(
|
||||||
|
name="GPT程式碼", description="GPT所產生的程式碼", default="")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# text area
|
||||||
|
txt_name = '指令腳本.py'
|
||||||
|
txt = bpy.data.texts.get(txt_name)
|
||||||
|
|
||||||
|
if txt is None:
|
||||||
|
txt = bpy.data.texts.new(txt_name)
|
||||||
|
|
||||||
|
txt.clear()
|
||||||
|
txt.write(self.code)
|
||||||
|
|
||||||
|
txt_edit_area = None
|
||||||
|
for area in bpy.context.screen.areas:
|
||||||
|
if area.type == 'TEXT_EDITOR':
|
||||||
|
txt_edit_area = area
|
||||||
|
break
|
||||||
|
|
||||||
|
cxt_area = context.area
|
||||||
|
for region in cxt_area.regions:
|
||||||
|
if region.type == 'WINDOW':
|
||||||
|
override = {'area': cxt_area, 'region': region}
|
||||||
|
bpy.ops.screen.area_split(
|
||||||
|
override, direction='VERTICAL', factor=0.5)
|
||||||
|
break
|
||||||
|
|
||||||
|
new_area = context.screen.areas[-1]
|
||||||
|
new_area.type = 'TEXT_EDITOR'
|
||||||
|
|
||||||
|
if txt_edit_area is None:
|
||||||
|
txt_edit_area = new_area
|
||||||
|
|
||||||
|
txt_edit_area.spaces.active.text = txt
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class BLENDERGPT_OT_SEND_MSG(Operator):
|
||||||
|
bl_idname = "gpt.send_msg"
|
||||||
|
bl_label = "送出訊息"
|
||||||
|
bl_description = "送出訊息"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
prompt_input: bpy.props.StringProperty(
|
||||||
|
name="指令", description="指令", default="")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# TODO: connect to GPT
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
|
||||||
|
scene.on_finish = True
|
||||||
|
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||||
|
|
||||||
|
blender_code = "print('Hello World')" # TODO: get from GPT
|
||||||
|
|
||||||
|
msg = scene.history.add()
|
||||||
|
msg.type = 'USER'
|
||||||
|
msg.content = scene.prompt_input
|
||||||
|
|
||||||
|
# clear prompt input
|
||||||
|
scene.prompt_input = ""
|
||||||
|
|
||||||
|
if blender_code:
|
||||||
|
msg = scene.history.add()
|
||||||
|
msg.type = 'GPT'
|
||||||
|
msg.content = blender_code
|
||||||
|
|
||||||
|
global_namespace = globals().copy()
|
||||||
|
|
||||||
|
try:
|
||||||
|
exec(blender_code, global_namespace)
|
||||||
|
except Exception as e:
|
||||||
|
self.report({'ERROR'}, f"Error: {e}")
|
||||||
|
scene.on_finish = False
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
scene.on_finish = False
|
||||||
|
return {"FINISHED"}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from bpy.types import Panel
|
||||||
|
|
||||||
|
|
||||||
|
class BLENDERGPT_PT_PANEL(Panel):
|
||||||
|
bl_label = 'Blender GPT ZH'
|
||||||
|
bl_idname = 'GPT_PT_PANEL'
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_category = 'Blender GPT ZH'
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
column = layout.column(align=True)
|
||||||
|
|
||||||
|
# language usage
|
||||||
|
row = column.row(align=True)
|
||||||
|
row.label(text="回饋語言:")
|
||||||
|
row.prop(context.scene, "lan", text="")
|
||||||
|
|
||||||
|
column.separator()
|
||||||
|
|
||||||
|
# history of chat
|
||||||
|
column.label(text="對話歷史紀錄:")
|
||||||
|
box = column.box()
|
||||||
|
for index, message in enumerate(context.scene.history):
|
||||||
|
if message.type == 'GPT':
|
||||||
|
row = box.row()
|
||||||
|
row.label(text="GPT: ")
|
||||||
|
show_code_op = row.operator(
|
||||||
|
"gpt.gpt_code", text="展示程式碼", icon="TEXT")
|
||||||
|
show_code_op.code = message.content
|
||||||
|
delete_message_op = row.operator(
|
||||||
|
'gpt.del_msg', text="", icon='TRASH', emboss=False)
|
||||||
|
delete_message_op.msg_idx = index
|
||||||
|
else:
|
||||||
|
row = box.row()
|
||||||
|
row.label(text=f"USER: {message.content}")
|
||||||
|
delete_message_op = row.operator(
|
||||||
|
'gpt.del_msg', text="", icon='TRASH', emboss=False)
|
||||||
|
delete_message_op.msg_idx = index
|
||||||
|
|
||||||
|
column.separator()
|
||||||
|
|
||||||
|
# model of chat gpt
|
||||||
|
column.label(text="Chat-GPT 模型:")
|
||||||
|
column.prop(context.scene, "model", text="")
|
||||||
|
|
||||||
|
# input of chat
|
||||||
|
column.label(text="指令:")
|
||||||
|
column.prop(context.scene, "prompt_input", text="")
|
||||||
|
|
||||||
|
button_label = "請稍候,模型正在編寫腳本..." if context.scene.on_finish else "送出指令"
|
||||||
|
|
||||||
|
row = column.row(align=True)
|
||||||
|
row.operator("gpt.send_msg", text=button_label)
|
||||||
|
row.operator("gpt.del_all_msg", text="Clear Chat")
|
||||||
|
|
||||||
|
column.separator()
|
|
@ -0,0 +1,17 @@
|
||||||
|
from bpy import props
|
||||||
|
from bpy.types import AddonPreferences
|
||||||
|
|
||||||
|
|
||||||
|
class BLENDERGPT_AddonPreferences(AddonPreferences):
|
||||||
|
bl_idname = __name__
|
||||||
|
print(__name__)
|
||||||
|
api_key: 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")
|
Ładowanie…
Reference in New Issue