Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d90fccb014 | ||
|
|
d09119f9d7 | ||
|
|
465a22d5a9 | ||
|
|
cc60a25aca | ||
|
|
355e7b23cf | ||
|
|
622048d573 | ||
|
|
9d09823321 | ||
|
|
eb5843063e | ||
|
|
c7df358fe8 | ||
|
|
21c4fe2117 | ||
|
|
f35fbb95b8 |
@@ -0,0 +1 @@
|
|||||||
|
622048d573ad87c5be84f65e64a7a3290c9305d1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.12
|
||||||
@@ -15,16 +15,20 @@ Video of the previous version : https://vimeo.com/241970235
|
|||||||
<a href="#installation">Installation</a>
|
<a href="#installation">Installation</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#contents">Contents</a>
|
<a href="#create-a-picker-from-scratch">Create a picker from scratch</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#location">Location</a>
|
<a href="#how-to-use-an-existing-picker">How to use an existing picker </a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#precisions">Precisions</a>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
|
||||||
<!-- INSTALLATION -->
|
<!-- INSTALLATION -->
|
||||||
## Installation
|
## Installation
|
||||||
|
### Developper installation
|
||||||
For external user, you can clone the repository using:
|
For external user, you can clone the repository using:
|
||||||
```sh
|
```sh
|
||||||
git clone https://git.autourdeminuit.com/autour_de_minuit/rig_picker.git
|
git clone https://git.autourdeminuit.com/autour_de_minuit/rig_picker.git
|
||||||
@@ -39,10 +43,192 @@ For Internal user:
|
|||||||
```sh
|
```sh
|
||||||
git clone ssh://git@git.autourdeminuit.com:222/autour_de_minuit/rig_picker.git
|
git clone ssh://git@git.autourdeminuit.com:222/autour_de_minuit/rig_picker.git
|
||||||
```
|
```
|
||||||
|
### User installation
|
||||||
|
|
||||||
<!-- CONTENTS -->
|
Addon available on blender 4.X , you can process this way to add this on your blender
|
||||||
## Contents
|
<br/>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_02.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
<!-- Create a picker from scratch -->
|
||||||
|
## Create a picker from scratch
|
||||||
|
|
||||||
|
#### 1. Canvas Creation
|
||||||
|
<br/>
|
||||||
|
1. Once the addon is installed , you'll find the "rigging" tab on the right of your 3D viewport, on it to start your picker first click on the "+" on top here
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/Doc_picker_01.gif" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
It will create a plane inside a "**controller**" collection, this plane will be the background of your picker. The collection is important, you can rename it **but don't delete it.** You can also rename the plane at your liking.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
If you click on this new plane, on the tab you will find (cf menu picture) :
|
||||||
|
- The name of the armature you want to pick
|
||||||
|
- The name of the canvas of your picker
|
||||||
|
- Symmetry
|
||||||
|
- The path needed to save your futur picker
|
||||||
|
- The save button
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_03.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/><br/>
|
||||||
|
#### 2. Picker Controller Creation
|
||||||
|
<br/>
|
||||||
|
1. Next, select the controllers of your armature, and click on the button "Create Shape". It will create empty meshes with the same names as your controllers, it should already be connected to the good controller of your armature, but for more details on that read the rest of this documentation.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/Doc_picker_02.gif" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
2. Create meshes inside the object of the picker's controller freshly generated
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_04.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
3. On those shape, you can add a material with this button in the "rigging" tab, you can add one, delete one if there already is a material here or pick from the scene a material on another element. (it works with a multiple selection)
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/Doc_picker_03.gif" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
You can also create some shapes display to represent the body of your character
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_06.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
#### 3. Controller's Setup
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- Back on the "rigging" tab, you will find buttons :
|
||||||
|
- A create shape button
|
||||||
|
- A mirror shape button
|
||||||
|
- An auto Bone assign button
|
||||||
|
- A "Name from bones" button
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- The last part is where you define to what bone the shape is connected. If you used the button "create shape" the good bone should already be picked, but if you created your own mesh or if you want the mesh to pick another bone instead, you need to pick it from this selection window.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
It is also where you decide if this shape is supposed to be :
|
||||||
|
- A bone : meaning selectable on the picker
|
||||||
|
- A display : only there for a visual purpose (like representing the character's shape)
|
||||||
|
- An Operator : to be connected to a script.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_07.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
#### 4. Final Touch
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- Place all of your meshes on top of the canvas, be careful as it works as photoshops layers, meaning, you need to put the mesh up on the Z axis if you want your shape on top on the 2D picker. The mesh with the vertex that is the higher up on the Z axis will be the top one on the picker display.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/Doc_picker_04.gif" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- When you are all done, save the picker then click on the armature , one the "Sources" window a button to add your picker. Select the path and the file.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_11.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<!-- Howto -->
|
||||||
|
## How to use an existing picker
|
||||||
|
|
||||||
|
- Open a "picker" view, select your armature in pose mode and in the "rigging" tab click on the small "+" sign. That will allow you to choose the picker file that you want. You can "reload" the picker if it doesn't show (or click on the "1" on the top right which center the page of the pickers in the view, in case you have more han one)
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_12.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- You can have more than one page for your picker if you need to detail things a bit more. For exemple in here we have a second canvas (in its own collection), that we'll save with a different name and load it as well.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_13.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
You can select now your controller directly on the picker.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<!-- PRECISIONS -->
|
||||||
|
## Precisions
|
||||||
|
<br/>
|
||||||
|
- In this menu, you will find some features that have actions on your picker's controllers
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_14.png" width=630/>
|
||||||
|
</p>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- If you double click on a controller, it will HIDE the whole layer of this bone
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="attachments/doc_picker_15.png" width=630/>
|
||||||
|
</p>
|
||||||
|
|
||||||
<!-- LOCATION -->
|
|
||||||
## Location
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,18 +8,12 @@ bl_info = {
|
|||||||
"warning": "",
|
"warning": "",
|
||||||
"wiki_url": "",
|
"wiki_url": "",
|
||||||
"tracker_url": "",
|
"tracker_url": "",
|
||||||
"category": "Rigging"}
|
"category": "Rigging",
|
||||||
|
}
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
modules = (
|
modules = (".operators", ".properties", ".ui", ".area", ".gizmo", ".draw_handlers")
|
||||||
'.operators',
|
|
||||||
'.properties',
|
|
||||||
'.ui',
|
|
||||||
'.area',
|
|
||||||
'.gizmo',
|
|
||||||
'.draw_handlers'
|
|
||||||
)
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
@@ -29,6 +23,7 @@ if "bpy" in locals():
|
|||||||
module = importlib.import_module(name, __name__)
|
module = importlib.import_module(name, __name__)
|
||||||
importlib.reload(module)
|
importlib.reload(module)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
if bpy.app.background:
|
if bpy.app.background:
|
||||||
return
|
return
|
||||||
@@ -36,6 +31,7 @@ def register():
|
|||||||
module = importlib.import_module(name, __name__)
|
module = importlib.import_module(name, __name__)
|
||||||
module.register()
|
module.register()
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
if bpy.app.background:
|
if bpy.app.background:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,34 +6,35 @@ from .constants import PICKERS
|
|||||||
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
|
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
|
||||||
class RigPickerTree(NodeTree):
|
class RigPickerTree(NodeTree):
|
||||||
# Description string
|
# Description string
|
||||||
'''A custom node tree type that will show up in the editor type list'''
|
"""A custom node tree type that will show up in the editor type list"""
|
||||||
# Optional identifier string. If not explicitly defined, the python class name is used.
|
# Optional identifier string. If not explicitly defined, the python class name is used.
|
||||||
# Label for nice name display
|
# Label for nice name display
|
||||||
bl_label = "Rig Picker"
|
bl_label = "Rig Picker"
|
||||||
# Icon identifier
|
# Icon identifier
|
||||||
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
bl_icon = "OUTLINER_DATA_ARMATURE"
|
||||||
|
|
||||||
|
|
||||||
class RP_MT_picker(Menu):
|
class RP_MT_picker(Menu):
|
||||||
"""Picker"""
|
"""Picker"""
|
||||||
|
|
||||||
bl_label = "Picker"
|
bl_label = "Picker"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
|
|
||||||
|
layout.operator("rigpicker.reload_picker", icon="FILE_REFRESH")
|
||||||
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH')
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
|
||||||
# Has at least one picker collection in the scene
|
# Has at least one picker collection in the scene
|
||||||
if not [c.rig_picker.enabled for c in scn.collection.children_recursive]:
|
if not [c.rig_picker.enabled for c in scn.collection.children_recursive]:
|
||||||
row.enabled = False
|
row.enabled = False
|
||||||
row.prop(scn.rig_picker, 'use_pick_bone', text='Auto Bone Assign')
|
row.prop(scn.rig_picker, "use_pick_bone", text="Auto Bone Assign")
|
||||||
|
|
||||||
|
|
||||||
class RP_MT_animation(Menu):
|
class RP_MT_animation(Menu):
|
||||||
"""Picker"""
|
"""Picker"""
|
||||||
|
|
||||||
bl_label = "Animation"
|
bl_label = "Animation"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@@ -47,51 +48,62 @@ class RP_MT_animation(Menu):
|
|||||||
row.enabled = False
|
row.enabled = False
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
op = layout.operator("rigpicker.call_operator", text='Select All')
|
op = layout.operator("rigpicker.call_operator", text="Select All")
|
||||||
op.operator = "pose.select_all"
|
op.operator = "pose.select_all"
|
||||||
op.arguments = '{"action": "SELECT"}'
|
op.arguments = '{"action": "SELECT"}'
|
||||||
|
|
||||||
op = layout.operator("rigpicker.call_operator", text='Select All')
|
op = layout.operator("rigpicker.call_operator", text="Select All")
|
||||||
op.operator = "pose.select_all"
|
op.operator = "pose.select_all"
|
||||||
op.arguments = '{"action": "DESELECT"}'
|
op.arguments = '{"action": "DESELECT"}'
|
||||||
|
|
||||||
op = layout.operator("rigpicker.call_operator", text='Frame Selected')
|
op = layout.operator("rigpicker.call_operator", text="Frame Selected")
|
||||||
op.operator = "view3d.view_selected"
|
op.operator = "view3d.view_selected"
|
||||||
op.view_3d = True
|
op.view_3d = True
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("rigpicker.call_operator", text='Insert Keyframe').operator="animtoolbox.insert_keyframe"
|
layout.operator("rigpicker.call_operator", text="Insert Keyframe").operator = (
|
||||||
layout.operator("anim.keyframe_delete_v3d", text='Delete Keyframe')
|
"animtoolbox.insert_keyframe"
|
||||||
|
)
|
||||||
|
layout.operator("anim.keyframe_delete_v3d", text="Delete Keyframe")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
op = layout.operator("rigpicker.call_operator", text='Move')
|
op = layout.operator("rigpicker.call_operator", text="Move")
|
||||||
op.operator="transform.translate"
|
op.operator = "transform.translate"
|
||||||
op.invoke = True
|
op.invoke = True
|
||||||
op.view_3d = True
|
op.view_3d = True
|
||||||
|
|
||||||
layout.operator("node.picker_transform", text='Rotate').mode='ROTATE'
|
layout.operator("node.picker_transform", text="Rotate").mode = "ROTATE"
|
||||||
layout.operator("node.picker_transform", text='Scale').mode='SCALE'
|
layout.operator("node.picker_transform", text="Scale").mode = "SCALE"
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("rigpicker.call_operator", text='Reset Bone').operator="animtoolbox.reset_bone"
|
layout.operator("rigpicker.call_operator", text="Reset Bone").operator = (
|
||||||
layout.operator("rigpicker.call_operator", text='Clear Location').operator='pose.loc_clear'
|
"animtoolbox.reset_bone"
|
||||||
layout.operator("rigpicker.call_operator", text='Clear Rotation').operator='pose.rot_clear'
|
)
|
||||||
layout.operator("rigpicker.call_operator", text='Clear Scale').operator='pose.scale_clear'
|
layout.operator("rigpicker.call_operator", text="Clear Location").operator = (
|
||||||
|
"pose.loc_clear"
|
||||||
|
)
|
||||||
|
layout.operator("rigpicker.call_operator", text="Clear Rotation").operator = (
|
||||||
|
"pose.rot_clear"
|
||||||
|
)
|
||||||
|
layout.operator("rigpicker.call_operator", text="Clear Scale").operator = (
|
||||||
|
"pose.scale_clear"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def draw_header(self, context):
|
def draw_header(self, context):
|
||||||
if not context.space_data.tree_type == 'RigPickerTree':
|
if not context.space_data.tree_type == "RigPickerTree":
|
||||||
self._draw(context)
|
self._draw(context)
|
||||||
return
|
return
|
||||||
|
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
|
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.template_header()
|
layout.template_header()
|
||||||
|
|
||||||
if not context.space_data.node_tree:
|
if not context.space_data.node_tree:
|
||||||
ntree = bpy.data.node_groups.get('.rig_picker')
|
ntree = bpy.data.node_groups.get(".rig_picker")
|
||||||
if not ntree:
|
if not ntree:
|
||||||
ntree = bpy.data.node_groups.new('.rig_picker', 'RigPickerTree')
|
ntree = bpy.data.node_groups.new(".rig_picker", "RigPickerTree")
|
||||||
|
|
||||||
context.space_data.node_tree = ntree
|
context.space_data.node_tree = ntree
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
@@ -99,17 +111,19 @@ def draw_header(self, context):
|
|||||||
row.menu("RP_MT_animation")
|
row.menu("RP_MT_animation")
|
||||||
|
|
||||||
ob = context.object
|
ob = context.object
|
||||||
if not ob or not ob.type == 'ARMATURE':
|
if not ob or not ob.type == "ARMATURE":
|
||||||
return
|
return
|
||||||
|
|
||||||
picker_group = PICKERS.get(ob)
|
picker_group = PICKERS.get(ob)
|
||||||
if not picker_group:
|
if not picker_group:
|
||||||
return
|
return
|
||||||
|
|
||||||
if scn.rig_picker.use_pick_bone:
|
if scn.rig_picker.use_pick_bone:
|
||||||
layout.alert = True
|
layout.alert = True
|
||||||
layout.label(text='Auto Bone Assign')
|
layout.label(text="Auto Bone Assign")
|
||||||
layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False)
|
layout.prop(
|
||||||
|
scn.rig_picker, "use_pick_bone", icon="PANEL_CLOSE", text="", emboss=False
|
||||||
|
)
|
||||||
|
|
||||||
layout.separator_spacer()
|
layout.separator_spacer()
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@@ -120,43 +134,44 @@ def draw_header(self, context):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
|
||||||
for i, picker in enumerate(picker_group.pickers):
|
for i, picker in enumerate(picker_group.pickers):
|
||||||
row.operator('rigpicker.fit_picker', text=f'{i+1}').index=i
|
row.operator("rigpicker.fit_picker", text=f"{i+1}").index = i
|
||||||
|
|
||||||
|
row.operator("rigpicker.fit_picker", text="", icon="FULLSCREEN_ENTER").index = -1
|
||||||
|
|
||||||
row.operator('rigpicker.fit_picker', text='', icon='FULLSCREEN_ENTER').index = -1
|
|
||||||
|
|
||||||
|
|
||||||
def tools_from_context(context, mode=None):
|
def tools_from_context(context, mode=None):
|
||||||
sp = context.space_data
|
sp = context.space_data
|
||||||
if sp and sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree':
|
if sp and sp.type == "NODE_EDITOR" and sp.tree_type == "RigPickerTree":
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
return NODE_PT_tools_active._tools_from_context(context, mode)
|
return NODE_PT_tools_active._tools_from_context(context, mode)
|
||||||
|
|
||||||
|
|
||||||
def tool_set_by_id(self, context):
|
def tool_set_by_id(self, context):
|
||||||
sd = context.space_data
|
sd = context.space_data
|
||||||
if sd and sd.type == 'NODE_EDITOR' and sd.tree_type == 'RigPickerTree':
|
if sd and sd.type == "NODE_EDITOR" and sd.tree_type == "RigPickerTree":
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
else:
|
else:
|
||||||
return self._execute(context)
|
return self._execute(context)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
sp = context.space_data
|
sp = context.space_data
|
||||||
if sp and sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree':
|
if sp and sp.type == "NODE_EDITOR" and sp.tree_type == "RigPickerTree":
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return self._poll(context)
|
return self._poll(context)
|
||||||
|
|
||||||
classes = (
|
|
||||||
RigPickerTree,
|
classes = (RigPickerTree, RP_MT_picker, RP_MT_animation)
|
||||||
RP_MT_picker,
|
|
||||||
RP_MT_animation
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
# Remove the tools inside the picker space
|
# Remove the tools inside the picker space
|
||||||
bpy.types.WM_OT_tool_set_by_id._execute = bpy.types.WM_OT_tool_set_by_id.execute #tool_set_by_id
|
bpy.types.WM_OT_tool_set_by_id._execute = (
|
||||||
|
bpy.types.WM_OT_tool_set_by_id.execute
|
||||||
|
) # tool_set_by_id
|
||||||
bpy.types.WM_OT_tool_set_by_id.execute = tool_set_by_id
|
bpy.types.WM_OT_tool_set_by_id.execute = tool_set_by_id
|
||||||
|
|
||||||
NODE_PT_tools_active._tools_from_context = NODE_PT_tools_active.tools_from_context
|
NODE_PT_tools_active._tools_from_context = NODE_PT_tools_active.tools_from_context
|
||||||
@@ -168,15 +183,16 @@ def register():
|
|||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
bpy.types.WM_OT_tool_set_by_id.execute = bpy.types.WM_OT_tool_set_by_id._execute
|
bpy.types.WM_OT_tool_set_by_id.execute = bpy.types.WM_OT_tool_set_by_id._execute
|
||||||
del bpy.types.WM_OT_tool_set_by_id._execute
|
del bpy.types.WM_OT_tool_set_by_id._execute
|
||||||
|
|
||||||
NODE_PT_tools_active.tools_from_context = NODE_PT_tools_active._tools_from_context
|
NODE_PT_tools_active.tools_from_context = NODE_PT_tools_active._tools_from_context
|
||||||
del NODE_PT_tools_active._tools_from_context
|
del NODE_PT_tools_active._tools_from_context
|
||||||
|
|
||||||
NODE_HT_header.draw = NODE_HT_header._draw
|
NODE_HT_header.draw = NODE_HT_header._draw
|
||||||
del NODE_HT_header._draw
|
del NODE_HT_header._draw
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 6.0 MiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 523 KiB |
|
After Width: | Height: | Size: 504 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 912 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 310 KiB |
|
After Width: | Height: | Size: 406 KiB |
|
After Width: | Height: | Size: 874 KiB |
|
After Width: | Height: | Size: 709 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 656 KiB |
@@ -1,6 +1,7 @@
|
|||||||
import gpu
|
import gpu
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class LazyDict(dict):
|
class LazyDict(dict):
|
||||||
def __getitem__(self, k):
|
def __getitem__(self, k):
|
||||||
v = super().__getitem__(k)
|
v = super().__getitem__(k)
|
||||||
@@ -19,11 +20,32 @@ PICKERS = {}
|
|||||||
|
|
||||||
|
|
||||||
MODULE_DIR = Path(__file__).parent
|
MODULE_DIR = Path(__file__).parent
|
||||||
SHADER_DIR = MODULE_DIR / 'shaders'
|
SHADER_DIR = MODULE_DIR / "shaders"
|
||||||
|
|
||||||
SHADERS = LazyDict()
|
SHADERS = LazyDict()
|
||||||
|
|
||||||
vertex_shader = Path(SHADER_DIR, "dash_shader.vert").read_text(encoding='utf-8')
|
vertex_shader = Path(SHADER_DIR, "dash_shader.vert").read_text(encoding="utf-8")
|
||||||
fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding='utf-8')
|
fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding="utf-8")
|
||||||
|
|
||||||
SHADERS['dashed_line'] = lambda : gpu.types.GPUShader(vertex_shader, fragment_shader)
|
|
||||||
|
def create_dashed_line_shader() -> gpu.types.GPUShader:
|
||||||
|
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||||
|
|
||||||
|
shader_info.vertex_in(0, "VEC2", "pos")
|
||||||
|
shader_info.fragment_out(0, "VEC4", "fragColor")
|
||||||
|
shader_info.push_constant("VEC4", "color")
|
||||||
|
shader_info.push_constant("MAT4", "viewMatrix")
|
||||||
|
shader_info.push_constant("FLOAT", "dashSize")
|
||||||
|
shader_info.push_constant("FLOAT", "gapSize")
|
||||||
|
|
||||||
|
shader_info.vertex_source(vertex_shader)
|
||||||
|
shader_info.fragment_source(fragment_shader)
|
||||||
|
|
||||||
|
shader = gpu.shader.create_from_info(shader_info)
|
||||||
|
del shader_info
|
||||||
|
return shader
|
||||||
|
|
||||||
|
|
||||||
|
SHADERS["dashed_line"] = (
|
||||||
|
create_dashed_line_shader # lambda: gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from .bl_utils import get_mat, get_collection_parents
|
from .bl_utils import get_mat, get_collection_parents
|
||||||
|
|
||||||
|
|
||||||
def get_picker_collection(ob=None):
|
def get_picker_collection(ob: bpy.types.Object | None = None):
|
||||||
"""Return the picker collection of an object"""
|
"""Return the picker collection of an object"""
|
||||||
|
|
||||||
if not ob:
|
if not ob:
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
if not ob:
|
||||||
|
raise Exception("No object in context")
|
||||||
|
|
||||||
for col in ob.users_collection:
|
for col in ob.users_collection:
|
||||||
if col.rig_picker.enabled:
|
if col.rig_picker.enabled:
|
||||||
return col
|
return col
|
||||||
|
|
||||||
if picker_col := next((c for c in get_collection_parents(col) if c.rig_picker.enabled), None):
|
if picker_col := next(
|
||||||
|
(c for c in get_collection_parents(col) if c.rig_picker.enabled), None
|
||||||
|
):
|
||||||
return picker_col
|
return picker_col
|
||||||
|
|
||||||
|
|
||||||
@@ -22,10 +26,10 @@ def is_shape(ob):
|
|||||||
canvas = scn.rig_picker.canvas
|
canvas = scn.rig_picker.canvas
|
||||||
if not canvas or ob.hide_render:
|
if not canvas or ob.hide_render:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
shapes = {ob for col in canvas.users_collection for ob in col.all_objects}
|
shapes = {ob for col in canvas.users_collection for ob in col.all_objects}
|
||||||
|
|
||||||
if ob.type in ('MESH', 'CURVE', 'FONT') and ob in shapes:
|
if ob.type in ("MESH", "CURVE", "FONT") and ob in shapes:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@@ -34,20 +38,20 @@ def is_shape(ob):
|
|||||||
def get_object_color(ob):
|
def get_object_color(ob):
|
||||||
if not ob.data.materials:
|
if not ob.data.materials:
|
||||||
return
|
return
|
||||||
|
|
||||||
mat = get_mat(ob)
|
mat = get_mat(ob)
|
||||||
if not mat or not mat.node_tree or not mat.node_tree.nodes:
|
if not mat or not mat.node_tree or not mat.node_tree.nodes:
|
||||||
return
|
return
|
||||||
|
|
||||||
emit_node = mat.node_tree.nodes.get('Emission')
|
emit_node = mat.node_tree.nodes.get("Emission")
|
||||||
if not emit_node:
|
if not emit_node:
|
||||||
return
|
return
|
||||||
|
|
||||||
return emit_node.inputs['Color'].default_value
|
return emit_node.inputs["Color"].default_value
|
||||||
|
|
||||||
|
|
||||||
def get_operator_from_id(idname):
|
def get_operator_from_id(idname):
|
||||||
if not '.' in idname:
|
if not "." in idname:
|
||||||
return
|
return
|
||||||
|
|
||||||
m, o = idname.split(".")
|
m, o = idname.split(".")
|
||||||
@@ -56,5 +60,5 @@ def get_operator_from_id(idname):
|
|||||||
op.get_rna_type()
|
op.get_rna_type()
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
return op
|
return op
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
@@ -30,94 +29,103 @@ def get_collection_parents(col, root=None, cols=None):
|
|||||||
cols = get_collection_parents(col, root=sub, cols=cols)
|
cols = get_collection_parents(col, root=sub, cols=cols)
|
||||||
return cols
|
return cols
|
||||||
|
|
||||||
|
|
||||||
def get_view_3d_override():
|
def get_view_3d_override():
|
||||||
windows = bpy.context.window_manager.windows
|
windows = bpy.context.window_manager.windows
|
||||||
areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
|
areas = [a for w in windows for a in w.screen.areas if a.type == "VIEW_3D"]
|
||||||
|
|
||||||
if not areas:
|
if not areas:
|
||||||
print('No view 3d found')
|
print("No view 3d found")
|
||||||
return
|
return
|
||||||
|
|
||||||
view_3d = None
|
view_3d = None
|
||||||
for area in areas:
|
for area in areas:
|
||||||
if area.spaces.active.camera:
|
if area.spaces.active.camera:
|
||||||
view_3d = area
|
view_3d = area
|
||||||
|
|
||||||
if not view_3d:
|
|
||||||
view_3d = max(areas, key=lambda x :x.width)
|
|
||||||
|
|
||||||
return {'area': view_3d, 'region': view_3d.regions[-1]}
|
if not view_3d:
|
||||||
|
view_3d = max(areas, key=lambda x: x.width)
|
||||||
|
|
||||||
|
return {"area": view_3d, "region": view_3d.regions[-1]}
|
||||||
|
|
||||||
|
|
||||||
def get_mat(ob):
|
def get_mat(ob):
|
||||||
for sl in ob.material_slots:
|
for sl in ob.material_slots:
|
||||||
if sl.material:
|
if sl.material:
|
||||||
return sl.material
|
return sl.material
|
||||||
|
|
||||||
|
|
||||||
def link_mat_to_object(ob):
|
def link_mat_to_object(ob):
|
||||||
for sl in ob.material_slots:
|
for sl in ob.material_slots:
|
||||||
m = sl.material
|
m = sl.material
|
||||||
sl.link = 'OBJECT'
|
sl.link = "OBJECT"
|
||||||
sl.material = m
|
sl.material = m
|
||||||
|
|
||||||
|
|
||||||
def eval_attr(ob, name):
|
def eval_attr(ob, name):
|
||||||
resolved = ob
|
resolved = ob
|
||||||
for o in name.split("."):
|
for o in name.split("."):
|
||||||
resolved = getattr(resolved, o)
|
resolved = getattr(resolved, o)
|
||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
|
|
||||||
def flip_name(name):
|
def flip_name(name):
|
||||||
if not name:
|
if not name:
|
||||||
return
|
return
|
||||||
|
|
||||||
if name.startswith('[') and name.endswith(']'): #It's a custom property
|
if name.startswith("[") and name.endswith("]"): # It's a custom property
|
||||||
flipped_name = bpy.utils.flip_name(name[:-2][2:])
|
flipped_name = bpy.utils.flip_name(name[:-2][2:])
|
||||||
return f'["{flipped_name}"]'
|
return f'["{flipped_name}"]'
|
||||||
else:
|
else:
|
||||||
return bpy.utils.flip_name(name)
|
return bpy.utils.flip_name(name)
|
||||||
|
|
||||||
def split_path(path) :
|
|
||||||
try :
|
def split_path(path):
|
||||||
|
try:
|
||||||
bone_name = path.split('["')[1].split('"]')[0]
|
bone_name = path.split('["')[1].split('"]')[0]
|
||||||
except Exception:
|
except Exception:
|
||||||
bone_name = None
|
bone_name = None
|
||||||
|
|
||||||
try :
|
try:
|
||||||
prop_name = path.split('["')[2].split('"]')[0]
|
prop_name = path.split('["')[2].split('"]')[0]
|
||||||
except Exception:
|
except Exception:
|
||||||
prop_name = None
|
prop_name = None
|
||||||
|
|
||||||
return bone_name, prop_name
|
return bone_name, prop_name
|
||||||
|
|
||||||
|
|
||||||
def hide_layers(args):
|
def hide_layers(args):
|
||||||
""" """
|
""" """
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
|
|
||||||
layers = []
|
layers = []
|
||||||
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||||
for i,l in enumerate(bone.bone.layers):
|
for i, l in enumerate(bone.bone.layers):
|
||||||
if l and i not in layers:
|
if l and i not in layers:
|
||||||
layers.append(i)
|
layers.append(i)
|
||||||
|
|
||||||
for i in layers:
|
for i in layers:
|
||||||
ob.data.layers[i] = not ob.data.layers[i]
|
ob.data.layers[i] = not ob.data.layers[i]
|
||||||
|
|
||||||
|
|
||||||
def select_layer(args):
|
def select_layer(args):
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
|
|
||||||
layers =[]
|
layers = []
|
||||||
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||||
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
bone_layers = [i for i, l in enumerate(bone.bone.layers) if l]
|
||||||
|
|
||||||
for l in bone_layers:
|
for l in bone_layers:
|
||||||
if l not in layers:
|
if l not in layers:
|
||||||
layers.append(l)
|
layers.append(l)
|
||||||
|
|
||||||
for bone in ob.pose.bones:
|
for bone in ob.pose.bones:
|
||||||
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
bone_layers = [i for i, l in enumerate(bone.bone.layers) if l]
|
||||||
|
|
||||||
if len(set(bone_layers).intersection(layers)):
|
if len(set(bone_layers).intersection(layers)):
|
||||||
bone.bone.select = True
|
bone.bone.select = True
|
||||||
|
|
||||||
|
|
||||||
def hide_bones(args):
|
def hide_bones(args):
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
selected_bone = [b for b in ob.pose.bones if b.bone.select]
|
selected_bone = [b for b in ob.pose.bones if b.bone.select]
|
||||||
@@ -129,10 +137,11 @@ def hide_bones(args):
|
|||||||
for bone in selected_bone:
|
for bone in selected_bone:
|
||||||
bone.bone.hide = visibility
|
bone.bone.hide = visibility
|
||||||
|
|
||||||
|
|
||||||
def select_all(args):
|
def select_all(args):
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
shapes = ob.data.rig_picker['shapes']
|
shapes = ob.data.rig_picker["shapes"]
|
||||||
bones = [s['bone'] for s in shapes if s['shape_type']=='BONE']
|
bones = [s["bone"] for s in shapes if s["shape_type"] == "BONE"]
|
||||||
|
|
||||||
for bone_name in bones:
|
for bone_name in bones:
|
||||||
bone = ob.pose.bones.get(bone_name)
|
bone = ob.pose.bones.get(bone_name)
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from mathutils.geometry import intersect_line_line_2d
|
from mathutils.geometry import intersect_line_line_2d
|
||||||
|
|
||||||
|
|
||||||
def is_over_region(self,context,event):
|
def is_over_region(self, context, event):
|
||||||
inside = 2 < event.mouse_region_x < context.region.width -2 and \
|
inside = (
|
||||||
2 < event.mouse_region_y < context.region.height -2 and \
|
2 < event.mouse_region_x < context.region.width - 2
|
||||||
[a for a in context.screen.areas if a.as_pointer()==self.adress] and \
|
and 2 < event.mouse_region_y < context.region.height - 2
|
||||||
not context.screen.show_fullscreen
|
and [a for a in context.screen.areas if a.as_pointer() == self.adress]
|
||||||
|
and not context.screen.show_fullscreen
|
||||||
|
)
|
||||||
|
|
||||||
return inside
|
return inside
|
||||||
|
|
||||||
|
|
||||||
def bound_box_center(ob):
|
def bound_box_center(ob):
|
||||||
points = [ob.matrix_world@Vector(p) for p in ob.bound_box]
|
points = [ob.matrix_world @ Vector(p) for p in ob.bound_box]
|
||||||
|
|
||||||
x = [v[0] for v in points]
|
x = [v[0] for v in points]
|
||||||
y = [v[1] for v in points]
|
y = [v[1] for v in points]
|
||||||
z = [v[2] for v in points]
|
z = [v[2] for v in points]
|
||||||
|
|
||||||
return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points))
|
return (sum(x) / len(points), sum(y) / len(points), sum(z) / len(points))
|
||||||
|
|
||||||
|
|
||||||
def bounding_rect(points):
|
def bounding_rect(points):
|
||||||
x_points = sorted(p[0] for p in points)
|
x_points = sorted(p[0] for p in points)
|
||||||
y_points = sorted(p[1] for p in points)
|
y_points = sorted(p[1] for p in points)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -32,28 +35,31 @@ def bounding_rect(points):
|
|||||||
Vector((x_points[0], y_points[0])),
|
Vector((x_points[0], y_points[0])),
|
||||||
]
|
]
|
||||||
|
|
||||||
def intersect_rects(rect, border): # returns None if rectangles don't intersect
|
|
||||||
dx = min(border[1][0],rect[1][0]) - max(border[0][0],rect[0][0])
|
|
||||||
dy = min(border[0][1],rect[0][1]) - max(border[2][1],rect[2][1])
|
|
||||||
|
|
||||||
if (dx>=0) and (dy>=0):
|
def intersect_rects(rect, border): # returns None if rectangles don't intersect
|
||||||
return dx*dy
|
dx = min(border[1][0], rect[1][0]) - max(border[0][0], rect[0][0])
|
||||||
|
dy = min(border[0][1], rect[0][1]) - max(border[2][1], rect[2][1])
|
||||||
|
|
||||||
|
if (dx >= 0) and (dy >= 0):
|
||||||
|
return dx * dy
|
||||||
|
|
||||||
|
|
||||||
def point_inside_rect(point, rect):
|
def point_inside_rect(point, rect):
|
||||||
return rect[0][0]< point[0]< rect[1][0] and rect[2][1]< point[1]< rect[0][1]
|
return rect[0][0] < point[0] < rect[1][0] and rect[2][1] < point[1] < rect[0][1]
|
||||||
|
|
||||||
def point_over_shape(point,verts,loops,outside_point=(-1,-1)):
|
|
||||||
|
def point_over_shape(point, verts, loops, outside_point=(-1, -1)):
|
||||||
out = Vector(outside_point)
|
out = Vector(outside_point)
|
||||||
pt = Vector(point)
|
pt = Vector(point)
|
||||||
|
|
||||||
intersections = 0
|
intersections = 0
|
||||||
for loop in loops:
|
for loop in loops:
|
||||||
for i,p in enumerate(loop):
|
for i, p in enumerate(loop):
|
||||||
a = Vector(verts[loop[i-1]])
|
a = Vector(verts[loop[i - 1]])
|
||||||
b = Vector(verts[p])
|
b = Vector(verts[p])
|
||||||
|
|
||||||
if intersect_line_line_2d(pt,out,a,b):
|
if intersect_line_line_2d(pt, out, a, b):
|
||||||
intersections += 1
|
intersections += 1
|
||||||
|
|
||||||
if intersections%2 == 1: #chek if the nb of intersection is odd
|
if intersections % 2 == 1: # chek if the nb of intersection is odd
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import gpu
|
import gpu
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
import blf
|
import blf
|
||||||
from mathutils import bvhtree, Vector, Color
|
from mathutils import bvhtree, Vector, Color
|
||||||
from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d,
|
from mathutils.geometry import (
|
||||||
intersect_tri_tri_2d)
|
intersect_point_quad_2d,
|
||||||
|
intersect_point_tri_2d,
|
||||||
|
intersect_tri_tri_2d,
|
||||||
|
)
|
||||||
from ..constants import PICKERS
|
from ..constants import PICKERS
|
||||||
from .addon_utils import get_operator_from_id
|
from .addon_utils import get_operator_from_id
|
||||||
from .geometry_utils import bounding_rect
|
from .geometry_utils import bounding_rect
|
||||||
@@ -15,13 +17,21 @@ import re
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
|
||||||
class Shape:
|
class Shape:
|
||||||
def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''):
|
def __init__(
|
||||||
|
self,
|
||||||
self.type = 'display'
|
picker,
|
||||||
|
points,
|
||||||
|
polygons=None,
|
||||||
|
edges=None,
|
||||||
|
tooltip="",
|
||||||
|
color=None,
|
||||||
|
source_name="",
|
||||||
|
):
|
||||||
|
self.type = "display"
|
||||||
self.picker = picker
|
self.picker = picker
|
||||||
self.rig = picker.rig
|
self.rig = picker.rig
|
||||||
self.source_name = source_name
|
self.source_name = source_name
|
||||||
@@ -29,11 +39,11 @@ class Shape:
|
|||||||
self.hover = False
|
self.hover = False
|
||||||
self.press = False
|
self.press = False
|
||||||
|
|
||||||
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
self.shader.bind()
|
self.shader.bind()
|
||||||
|
|
||||||
#self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
# self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
#self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
|
# self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
|
||||||
|
|
||||||
self.color = color
|
self.color = color
|
||||||
self.hover_color = self.brighten_color(self.color)
|
self.hover_color = self.brighten_color(self.color)
|
||||||
@@ -43,15 +53,19 @@ class Shape:
|
|||||||
self.polygons = polygons or []
|
self.polygons = polygons or []
|
||||||
self.edges = edges or []
|
self.edges = edges or []
|
||||||
|
|
||||||
self.p_batch = batch_for_shader(self.shader, 'TRIS', {"pos": self.points}, indices=self.polygons)
|
self.p_batch = batch_for_shader(
|
||||||
self.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges)
|
self.shader, "TRIS", {"pos": self.points}, indices=self.polygons
|
||||||
|
)
|
||||||
|
self.e_batch = batch_for_shader(
|
||||||
|
self.shader, "LINES", {"pos": self.points}, indices=self.edges
|
||||||
|
)
|
||||||
|
|
||||||
self.rect = bounding_rect(points)
|
self.rect = bounding_rect(points)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self):
|
def color(self):
|
||||||
return self._color
|
return self._color
|
||||||
|
|
||||||
def brighten_color(self, color):
|
def brighten_color(self, color):
|
||||||
brighten_color = Color(color[:3])
|
brighten_color = Color(color[:3])
|
||||||
brighten_color.v += 0.05
|
brighten_color.v += 0.05
|
||||||
@@ -70,16 +84,15 @@ class Shape:
|
|||||||
elif len(color) == 4:
|
elif len(color) == 4:
|
||||||
color = list(color)
|
color = list(color)
|
||||||
else:
|
else:
|
||||||
raise Exception('color must have a len of 3 or 4')
|
raise Exception("color must have a len of 3 or 4")
|
||||||
else:
|
else:
|
||||||
raise Exception(f'color is {type(color)} must be None or (tuple, list)')
|
raise Exception(f"color is {type(color)} must be None or (tuple, list)")
|
||||||
|
|
||||||
#self.shader.uniform_float("color", color)
|
# self.shader.uniform_float("color", color)
|
||||||
self._color = color
|
self._color = color
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
# self.shader.bind()
|
||||||
#self.shader.bind()
|
|
||||||
self.shader.uniform_float("color", self.color)
|
self.shader.uniform_float("color", self.color)
|
||||||
|
|
||||||
if self.polygons:
|
if self.polygons:
|
||||||
@@ -92,28 +105,45 @@ class Shape:
|
|||||||
if not intersect_point_quad_2d(location, *self.rect):
|
if not intersect_point_quad_2d(location, *self.rect):
|
||||||
self.hover = False
|
self.hover = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for p in self.polygons:
|
for p in self.polygons:
|
||||||
if intersect_point_tri_2d(location, *[self.points[i] for i in p]):
|
if intersect_point_tri_2d(location, *[self.points[i] for i in p]):
|
||||||
self.hover = True
|
self.hover = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.hover = False
|
self.hover = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def press_event(self, mode='SET'):
|
def press_event(self, mode="SET"):
|
||||||
self.press = True
|
self.press = True
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode="SET"):
|
||||||
self.press = False
|
self.press = False
|
||||||
|
|
||||||
|
|
||||||
class BoneShape(Shape):
|
class BoneShape(Shape):
|
||||||
def __init__(self, picker, points, polygons, edges, bone, tooltip='', color=None, source_name=''):
|
def __init__(
|
||||||
super().__init__(picker, points=points, polygons=polygons, edges=edges,
|
self,
|
||||||
tooltip=tooltip, color=color, source_name=source_name)
|
picker,
|
||||||
|
points,
|
||||||
|
polygons,
|
||||||
|
edges,
|
||||||
|
bone,
|
||||||
|
tooltip="",
|
||||||
|
color=None,
|
||||||
|
source_name="",
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
picker,
|
||||||
|
points=points,
|
||||||
|
polygons=polygons,
|
||||||
|
edges=edges,
|
||||||
|
tooltip=tooltip,
|
||||||
|
color=color,
|
||||||
|
source_name=source_name,
|
||||||
|
)
|
||||||
|
|
||||||
self.type = 'bone'
|
self.type = "bone"
|
||||||
self.bone = bone
|
self.bone = bone
|
||||||
self.active_color = [1, 1, 1, 0.1]
|
self.active_color = [1, 1, 1, 0.1]
|
||||||
self.select_color = self.brighten_color(self.hover_color)
|
self.select_color = self.brighten_color(self.hover_color)
|
||||||
@@ -125,23 +155,29 @@ class BoneShape(Shape):
|
|||||||
if not self.bone:
|
if not self.bone:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return self.bone in (bpy.context.selected_pose_bones or []) #self.bone.bone.select
|
return self.bone in (
|
||||||
|
bpy.context.selected_pose_bones or []
|
||||||
|
) # self.bone.bone.select
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active(self):
|
def active(self):
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return self.bone == bpy.context.active_pose_bone #self.rig.data.bones.active == self.bone.bone
|
return (
|
||||||
|
self.bone == bpy.context.active_pose_bone
|
||||||
|
) # self.rig.data.bones.active == self.bone.bone
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hide(self):
|
def hide(self):
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#return self.bone not in (bpy.context.visible_pose_bones or [])
|
# return self.bone not in (bpy.context.visible_pose_bones or [])
|
||||||
|
|
||||||
return self.bone.bone.hide or not any(l.is_visible for l in self.bone.bone.collections)
|
return self.bone.bone.hide or not any(
|
||||||
|
l.is_visible for l in self.bone.bone.collections
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bone_color(self):
|
def bone_color(self):
|
||||||
@@ -149,45 +185,44 @@ class BoneShape(Shape):
|
|||||||
return [0, 0, 0, 1]
|
return [0, 0, 0, 1]
|
||||||
|
|
||||||
if self.select and self.active:
|
if self.select and self.active:
|
||||||
return self.bone_colors['active']
|
return self.bone_colors["active"]
|
||||||
elif self.select:
|
elif self.select:
|
||||||
return self.bone_colors['select']
|
return self.bone_colors["select"]
|
||||||
elif self.hide:
|
elif self.hide:
|
||||||
return self.bone_colors['hide']
|
return self.bone_colors["hide"]
|
||||||
else:
|
else:
|
||||||
return self.bone_colors['normal']
|
return self.bone_colors["normal"]
|
||||||
|
|
||||||
def get_bone_colors(self):
|
def get_bone_colors(self):
|
||||||
|
theme = bpy.context.preferences.themes["Default"]
|
||||||
theme = bpy.context.preferences.themes['Default']
|
|
||||||
bone_colors = {
|
bone_colors = {
|
||||||
'select': [*theme.view_3d.bone_pose, 1],
|
"select": [*theme.view_3d.bone_pose, 1],
|
||||||
'normal': [0.05, 0.05, 0.05, 1],
|
"normal": [0.05, 0.05, 0.05, 1],
|
||||||
'active': [*theme.view_3d.bone_pose_active, 1],
|
"active": [*theme.view_3d.bone_pose_active, 1],
|
||||||
'hide': [0.85, 0.85, 0.85, 0.2],
|
"hide": [0.85, 0.85, 0.85, 0.2],
|
||||||
}
|
}
|
||||||
|
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return bone_colors
|
return bone_colors
|
||||||
|
|
||||||
if self.bone.color.palette == 'CUSTOM':
|
if self.bone.color.palette == "CUSTOM":
|
||||||
bone_color = self.bone
|
bone_color = self.bone
|
||||||
|
|
||||||
elif self.bone.color.palette == 'DEFAULT':
|
elif self.bone.color.palette == "DEFAULT":
|
||||||
bone_color = self.bone.bone
|
bone_color = self.bone.bone
|
||||||
|
|
||||||
normal_color = bone_color.color.custom.normal.copy()
|
normal_color = bone_color.color.custom.normal.copy()
|
||||||
normal_color.s *= 0.75
|
normal_color.s *= 0.75
|
||||||
|
|
||||||
bone_colors['normal'] = [*normal_color, 1]
|
bone_colors["normal"] = [*normal_color, 1]
|
||||||
bone_colors['select'] = [*bone_color.color.custom.select, 1]
|
bone_colors["select"] = [*bone_color.color.custom.select, 1]
|
||||||
bone_colors['active'] = [*bone_color.color.custom.active, 1]
|
bone_colors["active"] = [*bone_color.color.custom.active, 1]
|
||||||
bone_colors['hide'] = [*normal_color, 0.1]
|
bone_colors["hide"] = [*normal_color, 0.1]
|
||||||
|
|
||||||
return bone_colors
|
return bone_colors
|
||||||
|
|
||||||
def draw_hided(self):
|
def draw_hided(self):
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set("ALPHA")
|
||||||
gpu.state.line_width_set(1.0)
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
line_color = (1, 1, 1, 0.1)
|
line_color = (1, 1, 1, 0.1)
|
||||||
@@ -200,17 +235,17 @@ class BoneShape(Shape):
|
|||||||
|
|
||||||
self.shader.uniform_float("color", (*color[:3], 0.5))
|
self.shader.uniform_float("color", (*color[:3], 0.5))
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
self.shader.uniform_float("color", line_color)
|
self.shader.uniform_float("color", line_color)
|
||||||
self.e_batch.draw(self.shader)
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set("NONE")
|
||||||
|
|
||||||
def draw_zero_scaled(self):
|
def draw_zero_scaled(self):
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set("ALPHA")
|
||||||
gpu.state.line_width_set(1.0)
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
line_color = (*self.bone_colors['normal'][:3], 0.2)
|
line_color = (*self.bone_colors["normal"][:3], 0.2)
|
||||||
color = Color(self.color[:3])
|
color = Color(self.color[:3])
|
||||||
if self.hover or self.select:
|
if self.hover or self.select:
|
||||||
color.v += 0.05
|
color.v += 0.05
|
||||||
@@ -219,14 +254,14 @@ class BoneShape(Shape):
|
|||||||
|
|
||||||
self.shader.uniform_float("color", (*color[:3], 0.5))
|
self.shader.uniform_float("color", (*color[:3], 0.5))
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
self.shader.uniform_float("color", line_color)
|
self.shader.uniform_float("color", line_color)
|
||||||
self.e_batch.draw(self.shader)
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set("NONE")
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set("ALPHA")
|
||||||
gpu.state.line_width_set(1.0)
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
if self.hide:
|
if self.hide:
|
||||||
@@ -244,9 +279,9 @@ class BoneShape(Shape):
|
|||||||
|
|
||||||
self.shader.uniform_float("color", color)
|
self.shader.uniform_float("color", color)
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
# Draw Outline
|
# Draw Outline
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set("NONE")
|
||||||
if self.select or self.active:
|
if self.select or self.active:
|
||||||
gpu.state.line_width_set(2.0)
|
gpu.state.line_width_set(2.0)
|
||||||
|
|
||||||
@@ -254,51 +289,48 @@ class BoneShape(Shape):
|
|||||||
self.e_batch.draw(self.shader)
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
gpu.state.line_width_set(1.0)
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
def assign_bone_event(self):
|
|
||||||
|
|
||||||
#print('assign_bone_event', self)
|
def assign_bone_event(self):
|
||||||
|
# print('assign_bone_event', self)
|
||||||
|
|
||||||
scn = bpy.context.scene
|
scn = bpy.context.scene
|
||||||
rig = self.picker.rig
|
rig = self.picker.rig
|
||||||
source_object = scn.objects.get(self.source_name)
|
source_object = scn.objects.get(self.source_name)
|
||||||
if not source_object:
|
if not source_object:
|
||||||
print(f'Source object {self.source_name} not found')
|
print(f"Source object {self.source_name} not found")
|
||||||
return
|
|
||||||
|
|
||||||
active_bone = rig.data.bones.active
|
|
||||||
if not active_bone:
|
|
||||||
print('You need to have an active bone')
|
|
||||||
return
|
return
|
||||||
|
|
||||||
#print(active_bone, source_object)
|
active_bone = rig.data.bones.active
|
||||||
#print(rig)
|
if not active_bone:
|
||||||
|
print("You need to have an active bone")
|
||||||
|
return
|
||||||
|
|
||||||
|
# print(active_bone, source_object)
|
||||||
|
# print(rig)
|
||||||
source_object.name = rig.data.bones.active.name
|
source_object.name = rig.data.bones.active.name
|
||||||
source_object.rig_picker.name = rig.data.bones.active.name
|
source_object.rig_picker.name = rig.data.bones.active.name
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode="SET"):
|
||||||
super().release_event(mode)
|
super().release_event(mode)
|
||||||
|
|
||||||
if self.hide or not self.bone:
|
if self.hide or not self.bone:
|
||||||
return
|
return
|
||||||
|
|
||||||
select = True
|
select = True
|
||||||
if mode == 'SUBSTRACT':
|
if mode == "SUBSTRACT":
|
||||||
select = False
|
select = False
|
||||||
|
|
||||||
self.bone.bone.select = select
|
self.rig.pose.bones.get(self.bone.bone.name).select = select
|
||||||
if self.hover:
|
if self.hover:
|
||||||
|
if mode != "SUBSTRACT":
|
||||||
if mode != 'SUBSTRACT':
|
|
||||||
self.rig.data.bones.active = self.bone.bone
|
self.rig.data.bones.active = self.bone.bone
|
||||||
|
|
||||||
|
def border_select(self, border, mode="SET"):
|
||||||
def border_select(self, border, mode='SET'):
|
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.hide:
|
if self.hide:
|
||||||
self.bone.bone.select = False
|
self.rig.pose.bones.get(self.bone.bone.name).select = False
|
||||||
return
|
return
|
||||||
|
|
||||||
rect_tri1 = self.rect[0], self.rect[1], self.rect[2]
|
rect_tri1 = self.rect[0], self.rect[1], self.rect[2]
|
||||||
@@ -307,77 +339,79 @@ class BoneShape(Shape):
|
|||||||
border_tri1 = border[0], border[1], border[2]
|
border_tri1 = border[0], border[1], border[2]
|
||||||
border_tri2 = border[2], border[3], border[0]
|
border_tri2 = border[2], border[3], border[0]
|
||||||
|
|
||||||
if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and
|
if (
|
||||||
not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and
|
not intersect_tri_tri_2d(*border_tri1, *rect_tri1)
|
||||||
not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and
|
and not intersect_tri_tri_2d(*border_tri1, *rect_tri2)
|
||||||
not intersect_tri_tri_2d(*border_tri2, *rect_tri2)):
|
and not intersect_tri_tri_2d(*border_tri2, *rect_tri1)
|
||||||
|
and not intersect_tri_tri_2d(*border_tri2, *rect_tri2)
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
select = True
|
select = True
|
||||||
if mode == 'SUBSTRACT':
|
if mode == "SUBSTRACT":
|
||||||
select = False
|
select = False
|
||||||
|
|
||||||
for polygon in self.polygons:
|
for polygon in self.polygons:
|
||||||
points = [self.points[i] for i in polygon]
|
points = [self.points[i] for i in polygon]
|
||||||
|
|
||||||
if intersect_tri_tri_2d(*border_tri1, *points):
|
if intersect_tri_tri_2d(*border_tri1, *points):
|
||||||
self.bone.bone.select = select
|
self.rig.pose.bones.get(self.bone.bone.name).select = select
|
||||||
return
|
return
|
||||||
|
|
||||||
if intersect_tri_tri_2d(*border_tri2, *points):
|
if intersect_tri_tri_2d(*border_tri2, *points):
|
||||||
self.bone.bone.select = select
|
self.rig.pose.bones.get(self.bone.bone.name).select = select
|
||||||
return
|
return
|
||||||
|
|
||||||
'''
|
|
||||||
for b in border:
|
|
||||||
if intersect_point_tri_2d(b, *points):
|
|
||||||
self.bone.bone.select = select
|
|
||||||
return
|
|
||||||
|
|
||||||
for p in points:
|
|
||||||
if intersect_point_quad_2d(p, *border):
|
|
||||||
self.bone.bone.select = select
|
|
||||||
return
|
|
||||||
'''
|
|
||||||
|
|
||||||
class OperatorShape(Shape):
|
class OperatorShape(Shape):
|
||||||
def __init__(self, picker, points, polygons, operator, tooltip='', color=None, source_name=''):
|
def __init__(
|
||||||
super().__init__(picker, points=points, polygons=polygons, tooltip=tooltip,
|
self, picker, points, polygons, operator, tooltip="", color=None, source_name=""
|
||||||
color=color, source_name=source_name)
|
):
|
||||||
|
super().__init__(
|
||||||
|
picker,
|
||||||
|
points=points,
|
||||||
|
polygons=polygons,
|
||||||
|
tooltip=tooltip,
|
||||||
|
color=color,
|
||||||
|
source_name=source_name,
|
||||||
|
)
|
||||||
|
|
||||||
self.type = 'operator'
|
self.type = "operator"
|
||||||
self.active_color = [1, 1, 1, 0.15]
|
self.active_color = [1, 1, 1, 0.15]
|
||||||
self.press_color = [0, 0, 0, 0.25]
|
self.press_color = [0, 0, 0, 0.25]
|
||||||
self.operator = operator
|
self.operator = operator
|
||||||
#self.arguments = arguments#{k:eval(v)}
|
# self.arguments = arguments#{k:eval(v)}
|
||||||
#self.operator = get_operator_from_id(self.operator)
|
# self.operator = get_operator_from_id(self.operator)
|
||||||
|
|
||||||
if not tooltip:
|
if not tooltip:
|
||||||
self.tooltip = self.operator.replace('bpy.ops.', '').replace("'INVOKE_DEFAULT', ", '')
|
self.tooltip = self.operator.replace("bpy.ops.", "").replace(
|
||||||
|
"'INVOKE_DEFAULT', ", ""
|
||||||
|
)
|
||||||
|
|
||||||
#self.reg_args = re.compile(r'(\w+)=')
|
# self.reg_args = re.compile(r'(\w+)=')
|
||||||
'''
|
|
||||||
|
"""
|
||||||
def parse_args(self):
|
def parse_args(self):
|
||||||
|
|
||||||
args = self.reg_args.split(self.arguments)[1:]
|
args = self.reg_args.split(self.arguments)[1:]
|
||||||
#print(args, zip(args[::2], args[1::2]))
|
#print(args, zip(args[::2], args[1::2]))
|
||||||
return {k: eval(v) for k, v in zip(args[::2], args[1::2])}
|
return {k: eval(v) for k, v in zip(args[::2], args[1::2])}
|
||||||
#return {k:eval(v) for k, v in self.reg_args.split(self.arguments)}
|
#return {k:eval(v) for k, v in self.reg_args.split(self.arguments)}
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode="SET"):
|
||||||
super().release_event(mode)
|
super().release_event(mode)
|
||||||
|
|
||||||
#args = self.parse_args()
|
# args = self.parse_args()
|
||||||
if not self.operator:
|
if not self.operator:
|
||||||
return
|
return
|
||||||
|
|
||||||
exec(self.operator)
|
exec(self.operator)
|
||||||
#f'bpy.ops;{idname}'
|
# f'bpy.ops;{idname}'
|
||||||
|
|
||||||
#print(self.idname)
|
# print(self.idname)
|
||||||
#print(self.arguments)
|
# print(self.arguments)
|
||||||
#else:
|
# else:
|
||||||
# self.bone.bone.select = False
|
# self.bone.bone.select = False
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
@@ -390,10 +424,10 @@ class OperatorShape(Shape):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set("ALPHA")
|
||||||
self.shader.uniform_float("color", color)
|
self.shader.uniform_float("color", color)
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set("NONE")
|
||||||
|
|
||||||
|
|
||||||
class Picker:
|
class Picker:
|
||||||
@@ -406,48 +440,48 @@ class Picker:
|
|||||||
self.shapes = []
|
self.shapes = []
|
||||||
self.box_select = None
|
self.box_select = None
|
||||||
self.hover_shape = None
|
self.hover_shape = None
|
||||||
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
|
|
||||||
self.mouse = None
|
self.mouse = None
|
||||||
|
|
||||||
for shape_data in shapes:
|
for shape_data in shapes:
|
||||||
if not shape_data['points']:
|
if not shape_data["points"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if shape_data['type'] in ('CANVAS', 'DISPLAY'):
|
if shape_data["type"] in ("CANVAS", "DISPLAY"):
|
||||||
shape = Shape(
|
shape = Shape(
|
||||||
self,
|
self,
|
||||||
points=shape_data['points'],
|
points=shape_data["points"],
|
||||||
polygons=shape_data['polygons'],
|
polygons=shape_data["polygons"],
|
||||||
edges=shape_data['edges'],
|
edges=shape_data["edges"],
|
||||||
color=shape_data['color']
|
color=shape_data["color"],
|
||||||
)
|
)
|
||||||
|
|
||||||
elif shape_data['type'] == 'BONE':
|
elif shape_data["type"] == "BONE":
|
||||||
bone = rig.pose.bones.get(shape_data['bone'])
|
bone = rig.pose.bones.get(shape_data["bone"])
|
||||||
#if not bone:
|
# if not bone:
|
||||||
# print(f'Bone {shape_data["bone"]} not exist')
|
# print(f'Bone {shape_data["bone"]} not exist')
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
shape = BoneShape(
|
shape = BoneShape(
|
||||||
self,
|
self,
|
||||||
source_name=shape_data['source_name'],
|
source_name=shape_data["source_name"],
|
||||||
points=shape_data['points'],
|
points=shape_data["points"],
|
||||||
polygons=shape_data['polygons'],
|
polygons=shape_data["polygons"],
|
||||||
edges=shape_data['edges'],
|
edges=shape_data["edges"],
|
||||||
bone=bone,
|
bone=bone,
|
||||||
color=shape_data['color']
|
color=shape_data["color"],
|
||||||
)
|
)
|
||||||
|
|
||||||
elif shape_data['type'] == 'OPERATOR':
|
elif shape_data["type"] == "OPERATOR":
|
||||||
shape = OperatorShape(
|
shape = OperatorShape(
|
||||||
self,
|
self,
|
||||||
source_name=shape_data['source_name'],
|
source_name=shape_data["source_name"],
|
||||||
points=shape_data['points'],
|
points=shape_data["points"],
|
||||||
polygons=shape_data['polygons'],
|
polygons=shape_data["polygons"],
|
||||||
operator=shape_data['operator'],
|
operator=shape_data["operator"],
|
||||||
color=shape_data['color'],
|
color=shape_data["color"],
|
||||||
tooltip=shape_data['tooltip'],
|
tooltip=shape_data["tooltip"],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.shapes.append(shape)
|
self.shapes.append(shape)
|
||||||
@@ -456,36 +490,34 @@ class Picker:
|
|||||||
|
|
||||||
def assign_bone_event(self):
|
def assign_bone_event(self):
|
||||||
for shape in self.shapes:
|
for shape in self.shapes:
|
||||||
if shape.type == 'bone' and shape.hover:
|
if shape.type == "bone" and shape.hover:
|
||||||
shape.assign_bone_event()
|
shape.assign_bone_event()
|
||||||
|
|
||||||
bpy.ops.rigpicker.save_picker(index=self.index)
|
bpy.ops.rigpicker.save_picker(index=self.index)
|
||||||
|
|
||||||
def press_event(self, mode='SET'):
|
def press_event(self, mode="SET"):
|
||||||
for shape in self.shapes:
|
for shape in self.shapes:
|
||||||
#print(s)
|
# print(s)
|
||||||
if shape.hover:
|
if shape.hover:
|
||||||
shape.press_event(mode)
|
shape.press_event(mode)
|
||||||
else:
|
else:
|
||||||
shape.press = False
|
shape.press = False
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode="SET"):
|
||||||
|
# bpy.ops.pose.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
# print('PICKER release event', mode)
|
||||||
#bpy.ops.pose.select_all(action='DESELECT')
|
# print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
#print('PICKER release event', mode)
|
|
||||||
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
|
||||||
|
|
||||||
for shape in self.shapes:
|
for shape in self.shapes:
|
||||||
if shape.hover:
|
if shape.hover:
|
||||||
shape.release_event(mode)
|
shape.release_event(mode)
|
||||||
|
|
||||||
shape.press = False
|
|
||||||
|
|
||||||
#bpy.context.area.tag_redraw()
|
|
||||||
|
|
||||||
'''
|
shape.press = False
|
||||||
|
|
||||||
|
# bpy.context.area.tag_redraw()
|
||||||
|
|
||||||
|
"""
|
||||||
picker.tooltip_event(event='SHOW')
|
picker.tooltip_event(event='SHOW')
|
||||||
region.tag_redraw()
|
region.tag_redraw()
|
||||||
|
|
||||||
@@ -495,8 +527,9 @@ class Picker:
|
|||||||
|
|
||||||
picker.tooltip_event(event='HIDE')
|
picker.tooltip_event(event='HIDE')
|
||||||
bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
|
bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
|
||||||
'''
|
"""
|
||||||
'''
|
|
||||||
|
"""
|
||||||
def tooltip_event(self, event):
|
def tooltip_event(self, event):
|
||||||
|
|
||||||
|
|
||||||
@@ -508,7 +541,7 @@ class Picker:
|
|||||||
self.tooltip = self.hover_shape.bone.name
|
self.tooltip = self.hover_shape.bone.name
|
||||||
|
|
||||||
#bpy.context.region.tag_redraw()
|
#bpy.context.region.tag_redraw()
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def border_select(self, border, mode):
|
def border_select(self, border, mode):
|
||||||
border = [Vector(b) - Vector(self.translation) for b in border]
|
border = [Vector(b) - Vector(self.translation) for b in border]
|
||||||
@@ -516,7 +549,7 @@ class Picker:
|
|||||||
for shape in self.shapes:
|
for shape in self.shapes:
|
||||||
shape.press = False
|
shape.press = False
|
||||||
shape.hover = False
|
shape.hover = False
|
||||||
if shape.type != 'bone':
|
if shape.type != "bone":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
shape.border_select(border, mode)
|
shape.border_select(border, mode)
|
||||||
@@ -531,16 +564,16 @@ class Picker:
|
|||||||
elif shape.move_event(self.mouse):
|
elif shape.move_event(self.mouse):
|
||||||
self.hover_shape = shape
|
self.hover_shape = shape
|
||||||
|
|
||||||
#if point_inside_rect(self.end, rect):
|
# if point_inside_rect(self.end, rect):
|
||||||
# over = point_over_shape(self.end,points, edges)
|
# over = point_over_shape(self.end,points, edges)
|
||||||
|
|
||||||
#if bpy.app.timers.is_registered(self.tooltip_event):
|
# if bpy.app.timers.is_registered(self.tooltip_event):
|
||||||
#try:
|
# try:
|
||||||
# bpy.app.timers.unregister(self.tooltip_event)
|
# bpy.app.timers.unregister(self.tooltip_event)
|
||||||
#except:
|
# except:
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
#bpy.app.timers.register(self.tooltip_event, first_interval=1)
|
# bpy.app.timers.register(self.tooltip_event, first_interval=1)
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
with gpu.matrix.push_pop():
|
with gpu.matrix.push_pop():
|
||||||
@@ -564,8 +597,7 @@ class Picker:
|
|||||||
|
|
||||||
class PickerGroup:
|
class PickerGroup:
|
||||||
def __init__(self, rig, pickers):
|
def __init__(self, rig, pickers):
|
||||||
|
# self.view_location = Vector((0, 0))
|
||||||
#self.view_location = Vector((0, 0))
|
|
||||||
self.region = bpy.context.region
|
self.region = bpy.context.region
|
||||||
self.rig = rig
|
self.rig = rig
|
||||||
self.pickers = []
|
self.pickers = []
|
||||||
@@ -577,13 +609,13 @@ class PickerGroup:
|
|||||||
|
|
||||||
self.tooltip_shape = None
|
self.tooltip_shape = None
|
||||||
self.tooltip_mouse = Vector((0, 0))
|
self.tooltip_mouse = Vector((0, 0))
|
||||||
self.tooltip = ''
|
self.tooltip = ""
|
||||||
|
|
||||||
self.timer = None
|
self.timer = None
|
||||||
|
|
||||||
for picker in pickers:
|
for picker in pickers:
|
||||||
self.add_picker(picker)
|
self.add_picker(picker)
|
||||||
|
|
||||||
def add_picker(self, picker):
|
def add_picker(self, picker):
|
||||||
self.pickers.append(Picker(self, self.rig, picker))
|
self.pickers.append(Picker(self, self.rig, picker))
|
||||||
|
|
||||||
@@ -592,12 +624,12 @@ class PickerGroup:
|
|||||||
for picker in self.pickers:
|
for picker in self.pickers:
|
||||||
height = picker.rect[1][1] - picker.rect[-1][1]
|
height = picker.rect[1][1] - picker.rect[-1][1]
|
||||||
|
|
||||||
picker.translation = Vector((0, -y-height*0.5))
|
picker.translation = Vector((0, -y - height * 0.5))
|
||||||
picker.draw()
|
picker.draw()
|
||||||
|
|
||||||
y += height + 50
|
y += height + 50
|
||||||
|
|
||||||
#break #TODO for now only draw first picker
|
# break #TODO for now only draw first picker
|
||||||
|
|
||||||
def move_event(self, mouse):
|
def move_event(self, mouse):
|
||||||
self.mouse = mouse
|
self.mouse = mouse
|
||||||
@@ -618,14 +650,14 @@ class PickerGroup:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
picker.move_event(self.location)
|
picker.move_event(self.location)
|
||||||
|
|
||||||
if picker.hover_shape:
|
if picker.hover_shape:
|
||||||
hover_shape = picker.hover_shape
|
hover_shape = picker.hover_shape
|
||||||
|
|
||||||
self.hover_shape = hover_shape
|
self.hover_shape = hover_shape
|
||||||
|
|
||||||
if self.tooltip_shape is not self.hover_shape:
|
if self.tooltip_shape is not self.hover_shape:
|
||||||
self.tooltip = ''
|
self.tooltip = ""
|
||||||
|
|
||||||
if self.timer:
|
if self.timer:
|
||||||
self.timer.cancel()
|
self.timer.cancel()
|
||||||
@@ -633,8 +665,8 @@ class PickerGroup:
|
|||||||
self.timer = threading.Timer(0.4, self.tooltip_event)
|
self.timer = threading.Timer(0.4, self.tooltip_event)
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
|
|
||||||
def press_event(self, mode='SET'):
|
def press_event(self, mode="SET"):
|
||||||
#self.clear_tooltip()
|
# self.clear_tooltip()
|
||||||
|
|
||||||
for picker in self.pickers:
|
for picker in self.pickers:
|
||||||
if picker.under_mouse(self.location):
|
if picker.under_mouse(self.location):
|
||||||
@@ -643,9 +675,9 @@ class PickerGroup:
|
|||||||
for shape in picker.shapes:
|
for shape in picker.shapes:
|
||||||
shape.press = False
|
shape.press = False
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode="SET"):
|
||||||
if mode == 'SET':
|
if mode == "SET":
|
||||||
for bone in self.rig.data.bones:
|
for bone in self.rig.pose.bones:
|
||||||
bone.select = False
|
bone.select = False
|
||||||
|
|
||||||
for picker in self.pickers:
|
for picker in self.pickers:
|
||||||
@@ -663,45 +695,46 @@ class PickerGroup:
|
|||||||
def border_select(self, border, mode):
|
def border_select(self, border, mode):
|
||||||
border = [self.region.view2d.region_to_view(*b) for b in border]
|
border = [self.region.view2d.region_to_view(*b) for b in border]
|
||||||
|
|
||||||
if mode == 'SET':
|
if mode == "SET":
|
||||||
for bone in self.rig.data.bones:
|
for bone in self.rig.pose.bones:
|
||||||
bone.select = False
|
bone.select = False
|
||||||
|
|
||||||
for picker in self.pickers:
|
for picker in self.pickers:
|
||||||
picker.border_select(border, mode)
|
picker.border_select(border, mode)
|
||||||
|
|
||||||
def clear_tooltip(self):
|
def clear_tooltip(self):
|
||||||
self.tooltip = ''
|
self.tooltip = ""
|
||||||
self.tooltip_shape = None
|
self.tooltip_shape = None
|
||||||
self.timer.cancel()
|
self.timer.cancel()
|
||||||
self.region.tag_redraw()
|
self.region.tag_redraw()
|
||||||
|
|
||||||
def tooltip_event(self):
|
def tooltip_event(self):
|
||||||
#print('Tooltip Event', self)
|
# print('Tooltip Event', self)
|
||||||
|
|
||||||
#print(self.hover_shape, self.hover_shape.type)
|
# print(self.hover_shape, self.hover_shape.type)
|
||||||
|
|
||||||
if self.hover_shape and self.hover_shape.type != 'display':
|
if self.hover_shape and self.hover_shape.type != "display":
|
||||||
if self.hover_shape.type == 'bone' and self.hover_shape.bone:
|
if self.hover_shape.type == "bone" and self.hover_shape.bone:
|
||||||
self.tooltip = self.hover_shape.bone.name
|
self.tooltip = self.hover_shape.bone.name
|
||||||
else:
|
else:
|
||||||
self.tooltip = self.hover_shape.tooltip
|
self.tooltip = self.hover_shape.tooltip
|
||||||
self.tooltip_shape = self.hover_shape
|
self.tooltip_shape = self.hover_shape
|
||||||
else:
|
else:
|
||||||
return self.clear_tooltip()
|
return self.clear_tooltip()
|
||||||
|
|
||||||
|
|
||||||
self.tooltip_mouse = self.mouse
|
self.tooltip_mouse = self.mouse
|
||||||
|
|
||||||
self.timer.cancel()
|
self.timer.cancel()
|
||||||
|
|
||||||
#print(self.tooltip)
|
# print(self.tooltip)
|
||||||
|
|
||||||
self.region.tag_redraw()
|
self.region.tag_redraw()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rect(self):
|
def rect(self):
|
||||||
return bounding_rect([co - Vector(p.translation) for p in self.pickers for co in p.rect])
|
return bounding_rect(
|
||||||
|
[co - Vector(p.translation) for p in self.pickers for co in p.rect]
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def center(self):
|
def center(self):
|
||||||
@@ -710,23 +743,27 @@ class PickerGroup:
|
|||||||
return center
|
return center
|
||||||
|
|
||||||
|
|
||||||
def load_picker_data(rig):
|
def load_picker_data(rig: bpy.types.Object):
|
||||||
if 'pickers' in rig.data.rig_picker:
|
if "pickers" in rig.data.rig_picker:
|
||||||
picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']]
|
picker_datas = [
|
||||||
|
[s.to_dict() for s in p] for p in rig.data.rig_picker["pickers"]
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
picker_datas = []
|
picker_datas = []
|
||||||
|
|
||||||
for picker in rig.data.rig_picker.sources:
|
for picker in rig.data.rig_picker.sources:
|
||||||
picker_path = Path(bpy.path.abspath(picker.source, library=rig.data.library))
|
picker_path = Path(
|
||||||
|
bpy.path.abspath(picker.source, library=rig.data.library)
|
||||||
|
)
|
||||||
|
|
||||||
if not picker_path.exists():
|
if not picker_path.exists():
|
||||||
print(f'Picker path not exists: {picker_path.resolve()}')
|
print(f"Picker path not exists: {picker_path.resolve()}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print('Load picker from', picker_path.resolve())
|
print("Load picker from", picker_path.resolve())
|
||||||
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
picker_data = json.loads(picker_path.read_text(encoding="utf-8"))
|
||||||
picker_datas.append(picker_data)
|
picker_datas.append(picker_data)
|
||||||
|
|
||||||
PICKERS[rig] = PickerGroup(rig, picker_datas)
|
PICKERS[rig] = PickerGroup(rig, picker_datas)
|
||||||
|
|
||||||
|
|
||||||
@@ -736,24 +773,24 @@ def get_picker_path(rig, source, start=None):
|
|||||||
|
|
||||||
|
|
||||||
def pack_picker(rig, start=None):
|
def pack_picker(rig, start=None):
|
||||||
if not 'rig_picker' in rig.data:
|
if not "rig_picker" in rig.data:
|
||||||
return
|
return
|
||||||
|
|
||||||
pickers = []
|
pickers = []
|
||||||
for picker_source in rig.data['rig_picker'].get('sources', []):
|
for picker_source in rig.data["rig_picker"].get("sources", []):
|
||||||
picker_path = get_picker_path(rig, picker_source['source'], start)
|
picker_path = get_picker_path(rig, picker_source["source"], start)
|
||||||
if not picker_path.exists():
|
if not picker_path.exists():
|
||||||
print(f'{picker_path} not exists')
|
print(f"{picker_path} not exists")
|
||||||
continue
|
continue
|
||||||
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
picker_data = json.loads(picker_path.read_text(encoding="utf-8"))
|
||||||
pickers.append(picker_data)
|
pickers.append(picker_data)
|
||||||
|
|
||||||
rig.data['rig_picker']['pickers'] = pickers
|
rig.data["rig_picker"]["pickers"] = pickers
|
||||||
|
|
||||||
|
|
||||||
def unpack_picker(rig):
|
def unpack_picker(rig):
|
||||||
if 'rig_picker' not in rig.data.keys():
|
if "rig_picker" not in rig.data.keys():
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'pickers' in rig.data['rig_picker'].keys():
|
if "pickers" in rig.data["rig_picker"].keys():
|
||||||
del rig.data['rig_picker']['pickers']
|
del rig.data["rig_picker"]["pickers"]
|
||||||
|
|||||||
@@ -6,30 +6,29 @@ from .geometry_utils import bound_box_center
|
|||||||
from .addon_utils import get_object_color
|
from .addon_utils import get_object_color
|
||||||
|
|
||||||
|
|
||||||
|
def border_over_shape(border, verts, loops):
|
||||||
def border_over_shape(border,verts,loops):
|
|
||||||
for loop in loops:
|
for loop in loops:
|
||||||
for i,p in enumerate(loop):
|
for i, p in enumerate(loop):
|
||||||
a = Vector(verts[loop[i-1]])
|
a = Vector(verts[loop[i - 1]])
|
||||||
b = Vector(verts[p])
|
b = Vector(verts[p])
|
||||||
|
|
||||||
for j in range(0,4):
|
for j in range(0, 4):
|
||||||
c = border[j-1]
|
c = border[j - 1]
|
||||||
d = border[j]
|
d = border[j]
|
||||||
if intersect_line_line_2d(a,b,c,d):
|
if intersect_line_line_2d(a, b, c, d):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for point in verts:
|
for point in verts:
|
||||||
if point_inside_rect(point,border):
|
if point_inside_rect(point, border):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for point in border:
|
for point in border:
|
||||||
if point_over_shape(point,verts,loops):
|
if point_over_shape(point, verts, loops):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def border_loop(vert, loop):
|
def border_loop(vert, loop):
|
||||||
border_edge =[e for e in vert.link_edges if e.is_rectary]
|
border_edge = [e for e in vert.link_edges if e.is_rectary]
|
||||||
|
|
||||||
if border_edge:
|
if border_edge:
|
||||||
for edge in border_edge:
|
for edge in border_edge:
|
||||||
@@ -43,6 +42,7 @@ def border_loop(vert, loop):
|
|||||||
else:
|
else:
|
||||||
return [vert]
|
return [vert]
|
||||||
|
|
||||||
|
|
||||||
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
||||||
loops = loops or []
|
loops = loops or []
|
||||||
vert_indices = vert_indices or [v.index for v in bm.verts]
|
vert_indices = vert_indices or [v.index for v in bm.verts]
|
||||||
@@ -50,7 +50,7 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
|||||||
bm.verts.ensure_lookup_table()
|
bm.verts.ensure_lookup_table()
|
||||||
|
|
||||||
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
|
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
|
||||||
if len(loop) >1:
|
if len(loop) > 1:
|
||||||
loops.append(loop)
|
loops.append(loop)
|
||||||
|
|
||||||
for v in loop:
|
for v in loop:
|
||||||
@@ -75,14 +75,13 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
|
|||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.from_mesh(mesh)
|
bm.from_mesh(mesh)
|
||||||
|
|
||||||
|
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
|
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
|
||||||
|
|
||||||
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
|
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
|
||||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||||
|
|
||||||
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
|
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces) <= 1]
|
||||||
bm.to_mesh(mesh)
|
bm.to_mesh(mesh)
|
||||||
mesh.update()
|
mesh.update()
|
||||||
bm.clear()
|
bm.clear()
|
||||||
@@ -96,11 +95,11 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
|
|||||||
points.append([round(co[0], 1), round(co[1], 1)])
|
points.append([round(co[0], 1), round(co[1], 1)])
|
||||||
|
|
||||||
depths += [co[2]]
|
depths += [co[2]]
|
||||||
|
|
||||||
if depths:
|
if depths:
|
||||||
depth = max(depths)
|
depth = max(depths)
|
||||||
else:
|
else:
|
||||||
print(f'{ob.name} has no vertices')
|
print(f"{ob.name} has no vertices")
|
||||||
depth = 0
|
depth = 0
|
||||||
|
|
||||||
for face in mesh.polygons:
|
for face in mesh.polygons:
|
||||||
@@ -113,67 +112,70 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
|
|||||||
color = [0.5, 0.5, 0.5]
|
color = [0.5, 0.5, 0.5]
|
||||||
|
|
||||||
shape = {
|
shape = {
|
||||||
'source_name': ob.name,
|
"source_name": ob.name,
|
||||||
'tooltip': ob.rig_picker.name,
|
"tooltip": ob.rig_picker.name,
|
||||||
'depth': depth,
|
"depth": depth,
|
||||||
'points': points,
|
"points": points,
|
||||||
'polygons': polygons,
|
"polygons": polygons,
|
||||||
'edges': edges,
|
"edges": edges,
|
||||||
'color': color,
|
"color": color,
|
||||||
'type': ob.rig_picker.shape_type
|
"type": ob.rig_picker.shape_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
if shape['type'] =='OPERATOR':
|
if shape["type"] == "OPERATOR":
|
||||||
shape['operator'] = ob.rig_picker.operator
|
shape["operator"] = ob.rig_picker.operator
|
||||||
shape['shortcut'] = ob.rig_picker.shortcut
|
shape["shortcut"] = ob.rig_picker.shortcut
|
||||||
|
|
||||||
|
elif shape["type"] == "BONE":
|
||||||
|
shape["bone"] = ob.rig_picker.name
|
||||||
|
|
||||||
elif shape['type'] =='BONE':
|
|
||||||
shape['bone'] = ob.rig_picker.name
|
|
||||||
|
|
||||||
eval_ob.to_mesh_clear()
|
eval_ob.to_mesh_clear()
|
||||||
|
|
||||||
return shape
|
return shape
|
||||||
|
|
||||||
|
|
||||||
def get_picker_data(collection):
|
def get_picker_data(collection):
|
||||||
picker_data = []
|
picker_data = []
|
||||||
|
|
||||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
canvas = collection.rig_picker.canvas
|
canvas = collection.rig_picker.canvas
|
||||||
|
|
||||||
if canvas.type == 'CURVE':
|
if canvas.type == "CURVE":
|
||||||
canvas_points = canvas.data.splines[0].points
|
canvas_points = canvas.data.splines[0].points
|
||||||
else:
|
else:
|
||||||
canvas_points = canvas.data.vertices
|
canvas_points = canvas.data.vertices
|
||||||
|
|
||||||
canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]
|
canvas_coords = [canvas.matrix_world @ Vector((p.co)) for p in canvas_points]
|
||||||
|
|
||||||
height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords))
|
height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords))
|
||||||
width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords))
|
width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords))
|
||||||
|
|
||||||
center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
||||||
scale = 2048 / max(height, width)# Reference height for the canvas
|
scale = 2048 / max(height, width) # Reference height for the canvas
|
||||||
|
|
||||||
matrix = Matrix.Translation(-center)
|
matrix = Matrix.Translation(-center)
|
||||||
matrix = Matrix.Scale(scale, 4) @ matrix
|
matrix = Matrix.Scale(scale, 4) @ matrix
|
||||||
|
|
||||||
#sorted by their z axes
|
# sorted by their z axes
|
||||||
for ob in collection.all_objects:
|
for ob in collection.all_objects:
|
||||||
if ob.instance_collection:
|
if ob.instance_collection:
|
||||||
for shape in ob.instance_collection.all_objects:
|
for shape in ob.instance_collection.all_objects:
|
||||||
picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph))
|
picker_data.append(
|
||||||
|
get_shape_data(
|
||||||
elif ob.type in ('MESH', 'CURVE', 'FONT'):
|
shape, matrix=matrix @ ob.matrix_world, depsgraph=depsgraph
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif ob.type in ("MESH", "CURVE", "FONT"):
|
||||||
picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))
|
picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
#print(f'{ob.name} of type {ob.type} not supported')
|
# print(f'{ob.name} of type {ob.type} not supported')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
picker_data.sort(key=lambda x : x['depth'])
|
picker_data.sort(key=lambda x: x["depth"])
|
||||||
|
|
||||||
#print(picker_datas)
|
# print(picker_datas)
|
||||||
|
|
||||||
return picker_data
|
return picker_data
|
||||||
#rig.data.rig_picker['shapes'] = picker_datas
|
# rig.data.rig_picker['shapes'] = picker_datas
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import gpu
|
import gpu
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
@@ -20,12 +19,13 @@ def draw_rect_2d(position, width, height, color):
|
|||||||
:arg height: Height of the rect.
|
:arg height: Height of the rect.
|
||||||
:type height: float
|
:type height: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
coords = ((0, 0), (1, 0), (1, 1), (0, 1))
|
coords = ((0, 0), (1, 0), (1, 1), (0, 1))
|
||||||
|
|
||||||
shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
batch = batch_for_shader(
|
batch = batch_for_shader(
|
||||||
shader, 'TRI_FAN',
|
shader,
|
||||||
|
"TRI_FAN",
|
||||||
{"pos": coords},
|
{"pos": coords},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ def draw_rect_2d(position, width, height, color):
|
|||||||
def draw_callback_view():
|
def draw_callback_view():
|
||||||
space_data = bpy.context.space_data
|
space_data = bpy.context.space_data
|
||||||
|
|
||||||
if not space_data or not space_data.tree_type == 'RigPickerTree':
|
if not space_data or not space_data.tree_type == "RigPickerTree":
|
||||||
return
|
return
|
||||||
|
|
||||||
# Use the pin to know if this is the first time this picker window in created to hide the n panel
|
# Use the pin to know if this is the first time this picker window in created to hide the n panel
|
||||||
@@ -49,19 +49,19 @@ def draw_callback_view():
|
|||||||
space_data.show_region_ui = False
|
space_data.show_region_ui = False
|
||||||
|
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.sources:
|
if not ob or ob.type != "ARMATURE" or not ob.data.rig_picker.sources:
|
||||||
return
|
return
|
||||||
|
|
||||||
if ob not in PICKERS:
|
if ob not in PICKERS:
|
||||||
load_picker_data(ob)
|
load_picker_data(ob)
|
||||||
|
|
||||||
picker_group = PICKERS[ob]
|
picker_group = PICKERS[ob]
|
||||||
picker_group.draw()
|
picker_group.draw()
|
||||||
|
|
||||||
|
|
||||||
def draw_callback_px():
|
def draw_callback_px():
|
||||||
sp = bpy.context.space_data
|
sp = bpy.context.space_data
|
||||||
if not sp.tree_type == 'RigPickerTree':
|
if not sp.tree_type == "RigPickerTree":
|
||||||
return
|
return
|
||||||
|
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
@@ -74,37 +74,36 @@ def draw_callback_px():
|
|||||||
text = picker_group.tooltip
|
text = picker_group.tooltip
|
||||||
ui_scale = bpy.context.preferences.system.ui_scale
|
ui_scale = bpy.context.preferences.system.ui_scale
|
||||||
|
|
||||||
#print('Draw text', text)
|
# print('Draw text', text)
|
||||||
font_id = 0
|
font_id = 0
|
||||||
blf.size(font_id, int(13 * ui_scale))
|
blf.size(font_id, int(13 * ui_scale))
|
||||||
|
|
||||||
margins = [12, 5]
|
margins = [12, 5]
|
||||||
text_size = blf.dimensions(font_id, text)
|
text_size = blf.dimensions(font_id, text)
|
||||||
text_pos = (region.width - text_size[0]-margins[0], margins[1])
|
text_pos = (region.width - text_size[0] - margins[0], margins[1])
|
||||||
|
|
||||||
bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1]-1)
|
bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1] - 1)
|
||||||
bg_size = (text_size[0] + 2*margins[0], text_size[1] + 2*margins[1])
|
bg_size = (text_size[0] + 2 * margins[0], text_size[1] + 2 * margins[1])
|
||||||
|
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set("ALPHA")
|
||||||
draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.66))
|
draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.66))
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set("NONE")
|
||||||
|
|
||||||
|
# blf.dimensions(font_id, text)
|
||||||
#blf.dimensions(font_id, text)
|
|
||||||
blf.enable(font_id, blf.SHADOW)
|
blf.enable(font_id, blf.SHADOW)
|
||||||
|
|
||||||
|
|
||||||
# BLF drawing routine
|
# BLF drawing routine
|
||||||
blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0)
|
blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0)
|
||||||
blf.color(font_id, 0.85, 0.85, 0.85, 1)
|
blf.color(font_id, 0.85, 0.85, 0.85, 1)
|
||||||
|
|
||||||
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
blf.shadow(font_id, 5, 0.0, 0.0, 0.0, 1)
|
||||||
blf.shadow_offset(font_id, 2, -2)
|
blf.shadow_offset(font_id, 2, -2)
|
||||||
|
|
||||||
blf.draw(font_id, text)
|
blf.draw(font_id, text)
|
||||||
|
|
||||||
blf.disable(font_id, blf.SHADOW)
|
blf.disable(font_id, blf.SHADOW)
|
||||||
|
|
||||||
|
|
||||||
handle_view = None
|
handle_view = None
|
||||||
handle_pixel = None
|
handle_pixel = None
|
||||||
|
|
||||||
@@ -113,15 +112,20 @@ def register():
|
|||||||
global handle_view
|
global handle_view
|
||||||
global handle_pixel
|
global handle_pixel
|
||||||
|
|
||||||
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
|
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(
|
||||||
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
|
draw_callback_view, (), "WINDOW", "POST_VIEW"
|
||||||
|
)
|
||||||
|
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(
|
||||||
|
draw_callback_px, (), "WINDOW", "POST_PIXEL"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
global handle_view
|
global handle_view
|
||||||
global handle_pixel
|
global handle_pixel
|
||||||
|
|
||||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW')
|
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, "WINDOW")
|
||||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW')
|
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, "WINDOW")
|
||||||
|
|
||||||
handle_view = None
|
handle_view = None
|
||||||
handle_pixel = None
|
handle_pixel = None
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from bpy.props import (IntProperty, EnumProperty, BoolProperty)
|
from bpy.props import IntProperty, EnumProperty, BoolProperty
|
||||||
from bpy.types import (AddonPreferences, GizmoGroup, Operator, Gizmo)
|
from bpy.types import AddonPreferences, GizmoGroup, Operator, Gizmo
|
||||||
|
|
||||||
from mathutils import Vector, Matrix, Euler
|
from mathutils import Vector, Matrix, Euler
|
||||||
from .constants import PICKERS
|
from .constants import PICKERS
|
||||||
from .core.picker import Picker
|
from .core.picker import Picker
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
class RP_OT_simple_operator(bpy.types.Operator):
|
class RP_OT_simple_operator(bpy.types.Operator):
|
||||||
"""Tooltip"""
|
"""Tooltip"""
|
||||||
@@ -40,10 +37,6 @@ class RP_OT_simple_operator(bpy.types.Operator):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RP_GT_gizmo(Gizmo):
|
class RP_GT_gizmo(Gizmo):
|
||||||
|
|
||||||
def test_select(self, context, location):
|
def test_select(self, context, location):
|
||||||
@@ -55,27 +48,25 @@ class RP_GT_gizmo(Gizmo):
|
|||||||
|
|
||||||
picker.move_event(location)
|
picker.move_event(location)
|
||||||
|
|
||||||
#if bpy.app.timers.is_registered(tooltip):
|
# if bpy.app.timers.is_registered(tooltip):
|
||||||
# bpy.app.timers.unregister(tooltip)
|
# bpy.app.timers.unregister(tooltip)
|
||||||
#context.region.tag_redraw()
|
# context.region.tag_redraw()
|
||||||
|
|
||||||
#picker.tooltip_event(event='START')
|
|
||||||
#bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
|
|
||||||
|
|
||||||
#print(location)
|
|
||||||
|
|
||||||
|
# picker.tooltip_event(event='START')
|
||||||
|
# bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
|
||||||
|
|
||||||
|
# print(location)
|
||||||
|
|
||||||
context.region.tag_redraw()
|
context.region.tag_redraw()
|
||||||
|
|
||||||
#print(location)
|
# print(location)
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
return
|
return
|
||||||
'''
|
|
||||||
|
"""
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
@@ -88,39 +79,35 @@ class RP_GT_gizmo(Gizmo):
|
|||||||
def exit(self, context, cancel):
|
def exit(self, context, cancel):
|
||||||
print('EXIT')
|
print('EXIT')
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RP_GT_gizmogroup(GizmoGroup):
|
class RP_GT_gizmogroup(GizmoGroup):
|
||||||
""" test gizmo button 2d """
|
"""test gizmo button 2d"""
|
||||||
|
|
||||||
bl_idname = "view3d.gizmo_button_2d"
|
bl_idname = "view3d.gizmo_button_2d"
|
||||||
bl_label = "Test button 2d"
|
bl_label = "Test button 2d"
|
||||||
bl_space_type = 'NODE_EDITOR'
|
bl_space_type = "NODE_EDITOR"
|
||||||
bl_region_type = 'WINDOW'
|
bl_region_type = "WINDOW"
|
||||||
bl_options = {'PERSISTENT'}
|
bl_options = {"PERSISTENT"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.space_data.tree_type == 'RigPickerTree'
|
return context.space_data.tree_type == "RigPickerTree"
|
||||||
|
|
||||||
def setup(self, context):
|
def setup(self, context):
|
||||||
self.gizmo = self.gizmos.new("RP_GT_gizmo")
|
self.gizmo = self.gizmos.new("RP_GT_gizmo")
|
||||||
|
|
||||||
|
|
||||||
|
classes = (RP_GT_gizmo, RP_GT_gizmogroup)
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
|
||||||
RP_GT_gizmo,
|
|
||||||
RP_GT_gizmogroup
|
|
||||||
)
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
from . import material
|
from . import material
|
||||||
from . import picker
|
from . import picker
|
||||||
from . import shape
|
from . import shape
|
||||||
|
|
||||||
|
|
||||||
modules = (
|
modules = (material, picker, shape)
|
||||||
material,
|
|
||||||
picker,
|
|
||||||
shape
|
|
||||||
)
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for mod in modules:
|
for mod in modules:
|
||||||
mod.register()
|
mod.register()
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for mod in reversed(modules):
|
for mod in reversed(modules):
|
||||||
mod.unregister()
|
mod.unregister()
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
|
|
||||||
@@ -6,56 +5,55 @@ from bpy_extras import view3d_utils
|
|||||||
class RP_OT_add_material(bpy.types.Operator):
|
class RP_OT_add_material(bpy.types.Operator):
|
||||||
bl_label = "Add Ui Material"
|
bl_label = "Add Ui Material"
|
||||||
bl_idname = "rigpicker.add_mat"
|
bl_idname = "rigpicker.add_mat"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
|
|
||||||
mat = bpy.data.materials.new('UI')
|
mat = bpy.data.materials.new("UI")
|
||||||
mat.use_nodes = True
|
mat.use_nodes = True
|
||||||
|
|
||||||
for node in mat.node_tree.nodes:
|
for node in mat.node_tree.nodes:
|
||||||
if node.type == 'OUTPUT_MATERIAL':
|
if node.type == "OUTPUT_MATERIAL":
|
||||||
mat_output = node
|
mat_output = node
|
||||||
else:
|
else:
|
||||||
mat.node_tree.nodes.remove(node)
|
mat.node_tree.nodes.remove(node)
|
||||||
|
|
||||||
emission = mat.node_tree.nodes.new('ShaderNodeEmission')
|
emission = mat.node_tree.nodes.new("ShaderNodeEmission")
|
||||||
mat.node_tree.links.new(emission.outputs[0],mat_output.inputs[0])
|
mat.node_tree.links.new(emission.outputs[0], mat_output.inputs[0])
|
||||||
|
|
||||||
|
|
||||||
if not context.object.data.materials:
|
if not context.object.data.materials:
|
||||||
context.object.data.materials.append(mat)
|
context.object.data.materials.append(mat)
|
||||||
else:
|
else:
|
||||||
context.object.material_slots[0].material = mat
|
context.object.material_slots[0].material = mat
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_remove_material(bpy.types.Operator):
|
class RP_OT_remove_material(bpy.types.Operator):
|
||||||
bl_label = "Remove Ui Material"
|
bl_label = "Remove Ui Material"
|
||||||
bl_idname = "rigpicker.remove_mat"
|
bl_idname = "rigpicker.remove_mat"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
#print(self.shape_type)
|
# print(self.shape_type)
|
||||||
for mat in context.object.data.materials:
|
for mat in context.object.data.materials:
|
||||||
bpy.data.materials.remove(mat)
|
bpy.data.materials.remove(mat)
|
||||||
context.area.tag_redraw()
|
context.area.tag_redraw()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_eyedropper_material(bpy.types.Operator):
|
class RP_OT_eyedropper_material(bpy.types.Operator):
|
||||||
"""Tooltip"""
|
"""Tooltip"""
|
||||||
|
|
||||||
bl_idname = "rigpicker.eyedropper_mat"
|
bl_idname = "rigpicker.eyedropper_mat"
|
||||||
bl_label = "Eye Dropper mat"
|
bl_label = "Eye Dropper mat"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
#first_mouse_x = IntProperty()
|
|
||||||
#first_value = FloatProperty()
|
|
||||||
|
|
||||||
|
# first_mouse_x = IntProperty()
|
||||||
|
# first_value = FloatProperty()
|
||||||
|
|
||||||
def modal(self, context, event):
|
def modal(self, context, event):
|
||||||
context.area.tag_redraw()
|
context.area.tag_redraw()
|
||||||
@@ -66,7 +64,7 @@ class RP_OT_eyedropper_material(bpy.types.Operator):
|
|||||||
region = context.region
|
region = context.region
|
||||||
rv3d = context.region_data
|
rv3d = context.region_data
|
||||||
|
|
||||||
if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
|
if event.type == "LEFTMOUSE" and event.value == "RELEASE":
|
||||||
self.mouse = event.mouse_region_x, event.mouse_region_y
|
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
|
|
||||||
dg = context.evaluated_depsgraph_get()
|
dg = context.evaluated_depsgraph_get()
|
||||||
@@ -76,47 +74,48 @@ class RP_OT_eyedropper_material(bpy.types.Operator):
|
|||||||
|
|
||||||
raycast = scene.ray_cast(dg, ray_origin, view_vector)
|
raycast = scene.ray_cast(dg, ray_origin, view_vector)
|
||||||
|
|
||||||
if raycast[0]==True:
|
if raycast[0] == True:
|
||||||
ob = raycast[4]
|
ob = raycast[4]
|
||||||
|
|
||||||
if ob.data.materials:
|
if ob.data.materials:
|
||||||
mat = ob.data.materials[0]
|
mat = ob.data.materials[0]
|
||||||
for shape in [o for o in context.selected_objects if o.type in ('MESH','CURVE','FONT')]:
|
for shape in [
|
||||||
|
o
|
||||||
|
for o in context.selected_objects
|
||||||
|
if o.type in ("MESH", "CURVE", "FONT")
|
||||||
|
]:
|
||||||
if not shape.data.materials:
|
if not shape.data.materials:
|
||||||
shape.data.materials.append(mat)
|
shape.data.materials.append(mat)
|
||||||
else:
|
else:
|
||||||
shape.material_slots[0].material = mat
|
shape.material_slots[0].material = mat
|
||||||
|
|
||||||
|
# context.space_data.draw_handler_remove(self._handle, 'WINDOW')
|
||||||
#context.space_data.draw_handler_remove(self._handle, 'WINDOW')
|
|
||||||
|
|
||||||
context.window.cursor_modal_restore()
|
context.window.cursor_modal_restore()
|
||||||
|
|
||||||
for ob in self.temp_ob:
|
for ob in self.temp_ob:
|
||||||
bpy.data.objects.remove(ob)
|
bpy.data.objects.remove(ob)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# return {'FINISHED'}
|
||||||
|
|
||||||
#return {'FINISHED'}
|
elif event.type in {"RIGHTMOUSE", "ESC"}:
|
||||||
|
# context.object.location.x = self.first_value
|
||||||
elif event.type in {'RIGHTMOUSE', 'ESC'}:
|
|
||||||
#context.object.location.x = self.first_value
|
|
||||||
context.window.cursor_modal_restore()
|
context.window.cursor_modal_restore()
|
||||||
|
|
||||||
for ob in self.temp_ob:
|
for ob in self.temp_ob:
|
||||||
bpy.data.objects.remove(ob)
|
bpy.data.objects.remove(ob)
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
|
||||||
|
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
#self.local_cursor = tuple(context.space_data.cursor.location)
|
# self.local_cursor = tuple(context.space_data.cursor.location)
|
||||||
#self.cursor = tuple(context.scene.cursor.location)
|
# self.cursor = tuple(context.scene.cursor.location)
|
||||||
|
|
||||||
curves =[o for o in context.visible_objects if o.type in ('CURVE', 'FONT')]
|
curves = [o for o in context.visible_objects if o.type in ("CURVE", "FONT")]
|
||||||
|
|
||||||
self.temp_ob = []
|
self.temp_ob = []
|
||||||
|
|
||||||
@@ -124,17 +123,17 @@ class RP_OT_eyedropper_material(bpy.types.Operator):
|
|||||||
|
|
||||||
for c in curves:
|
for c in curves:
|
||||||
mesh = bpy.data.meshes.new_from_object(c.evaluated_get(dg))
|
mesh = bpy.data.meshes.new_from_object(c.evaluated_get(dg))
|
||||||
copy = bpy.data.objects.new(c.name+'_tmp', mesh)
|
copy = bpy.data.objects.new(c.name + "_tmp", mesh)
|
||||||
copy.matrix_world = c.matrix_world
|
copy.matrix_world = c.matrix_world
|
||||||
for mat in c.data.materials:
|
for mat in c.data.materials:
|
||||||
copy.data.materials.append(mat)
|
copy.data.materials.append(mat)
|
||||||
scene.collection.objects.link(copy)
|
scene.collection.objects.link(copy)
|
||||||
self.temp_ob.append(copy)
|
self.temp_ob.append(copy)
|
||||||
#args = (self,context)
|
# args = (self,context)
|
||||||
#self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
|
# self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
|
||||||
context.window_manager.modal_handler_add(self)
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
@@ -143,4 +142,4 @@ classes = (
|
|||||||
RP_OT_eyedropper_material,
|
RP_OT_eyedropper_material,
|
||||||
)
|
)
|
||||||
|
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ from ..core.addon_utils import get_picker_collection
|
|||||||
from ..core.bl_utils import get_view_3d_override, split_path, eval_attr
|
from ..core.bl_utils import get_view_3d_override, split_path, eval_attr
|
||||||
from ..core.geometry_utils import bounding_rect
|
from ..core.geometry_utils import bounding_rect
|
||||||
from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data
|
from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data
|
||||||
#from .func_bgl import draw_callback_px
|
|
||||||
#from .func_bgl import select_bone
|
# from .func_bgl import draw_callback_px
|
||||||
#from core.picker import *
|
# from .func_bgl import select_bone
|
||||||
#from .utils import is_over_region
|
# from core.picker import *
|
||||||
|
# from .utils import is_over_region
|
||||||
import gpu
|
import gpu
|
||||||
from mathutils import Vector, Euler, Matrix
|
from mathutils import Vector, Euler, Matrix
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
@@ -18,34 +19,36 @@ import json
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def is_picker_space(space_data=None):
|
def is_picker_space(space_data=None):
|
||||||
if not space_data:
|
if not space_data:
|
||||||
space_data = bpy.context.space_data
|
space_data = bpy.context.space_data
|
||||||
if space_data and (space_data.type == 'NODE_EDITOR' and space_data.tree_type == 'RigPickerTree'):
|
if space_data and (
|
||||||
|
space_data.type == "NODE_EDITOR" and space_data.tree_type == "RigPickerTree"
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_box_select(Operator):
|
class RP_OT_box_select(Operator):
|
||||||
"""Box Select bones in the picker view"""
|
"""Box Select bones in the picker view"""
|
||||||
|
|
||||||
bl_idname = "node.rp_box_select"
|
bl_idname = "node.rp_box_select"
|
||||||
bl_label = "Picker Box Select"
|
bl_label = "Picker Box Select"
|
||||||
|
|
||||||
mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
|
mode: EnumProperty(
|
||||||
|
items=[(i, i.title(), "") for i in ("SET", "EXTEND", "SUBSTRACT")]
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not is_picker_space(context.space_data):
|
if not is_picker_space(context.space_data):
|
||||||
return
|
return
|
||||||
|
|
||||||
ob = context.object
|
ob = context.object
|
||||||
return ob and ob in PICKERS
|
return ob and ob in PICKERS
|
||||||
|
|
||||||
'''
|
"""
|
||||||
def mode_from_event(self, event):
|
def mode_from_event(self, event):
|
||||||
if event.alt:
|
if event.alt:
|
||||||
return 'SUBSTRACT'
|
return 'SUBSTRACT'
|
||||||
@@ -53,17 +56,17 @@ class RP_OT_box_select(Operator):
|
|||||||
return 'EXTEND'
|
return 'EXTEND'
|
||||||
else:
|
else:
|
||||||
return 'SET'
|
return 'SET'
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def draw_callback(self):
|
def draw_callback(self):
|
||||||
#print('draw callback border')
|
# print('draw callback border')
|
||||||
if not self.draw_border:
|
if not self.draw_border:
|
||||||
return
|
return
|
||||||
|
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set("ALPHA")
|
||||||
|
|
||||||
|
# print('DRAW BORDER')
|
||||||
|
|
||||||
#print('DRAW BORDER')
|
|
||||||
|
|
||||||
self.color_shader.bind()
|
self.color_shader.bind()
|
||||||
self.color_shader.uniform_float("color", self.bg_color)
|
self.color_shader.uniform_float("color", self.bg_color)
|
||||||
self.bg_batch.draw(self.color_shader)
|
self.bg_batch.draw(self.color_shader)
|
||||||
@@ -75,93 +78,100 @@ class RP_OT_box_select(Operator):
|
|||||||
self.dash_shader.uniform_float("viewMatrix", matrix)
|
self.dash_shader.uniform_float("viewMatrix", matrix)
|
||||||
self.dash_shader.uniform_float("dashSize", 5)
|
self.dash_shader.uniform_float("dashSize", 5)
|
||||||
self.dash_shader.uniform_float("gapSize", 4)
|
self.dash_shader.uniform_float("gapSize", 4)
|
||||||
|
|
||||||
self.contour_batch.draw(self.dash_shader)
|
self.contour_batch.draw(self.dash_shader)
|
||||||
|
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set("NONE")
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
#print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
# print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
if context.object.mode != 'POSE':
|
if context.object.mode != "POSE":
|
||||||
bpy.ops.object.posemode_toggle()
|
bpy.ops.object.posemode_toggle()
|
||||||
|
|
||||||
self.timer = None
|
self.timer = None
|
||||||
#self.mode = self.mode_from_event(event)
|
# self.mode = self.mode_from_event(event)
|
||||||
#self.invoke_event = event.copy()
|
# self.invoke_event = event.copy()
|
||||||
self.region = context.region
|
self.region = context.region
|
||||||
self.draw_border = False
|
self.draw_border = False
|
||||||
|
|
||||||
self.picker = PICKERS[context.object]
|
self.picker = PICKERS[context.object]
|
||||||
|
|
||||||
self.start_mouse = event.mouse_region_x, event.mouse_region_y
|
self.start_mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
#self.shader = line_strip_shader
|
# self.shader = line_strip_shader
|
||||||
self.border_color = [1, 1, 1, 1]
|
self.border_color = [1, 1, 1, 1]
|
||||||
self.bg_color = [1, 1, 1, 0.05]
|
self.bg_color = [1, 1, 1, 0.05]
|
||||||
|
|
||||||
#args = (self, context)
|
# args = (self, context)
|
||||||
self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
self.color_shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
self.dash_shader = SHADERS['dashed_line']
|
self.dash_shader = SHADERS["dashed_line"]
|
||||||
self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
|
self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(
|
||||||
|
self.draw_callback, (), "WINDOW", "POST_PIXEL"
|
||||||
|
)
|
||||||
|
|
||||||
context.window_manager.modal_handler_add(self)
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
self.picker.press_event(self.mode)
|
self.picker.press_event(self.mode)
|
||||||
self.region.tag_redraw()
|
self.region.tag_redraw()
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
def modal(self, context, event):
|
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
self.mouse = event.mouse_region_x, event.mouse_region_y
|
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
|
|
||||||
self.border = bounding_rect((self.start_mouse, self.mouse))
|
self.border = bounding_rect((self.start_mouse, self.mouse))
|
||||||
|
|
||||||
self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border})
|
self.bg_batch = batch_for_shader(
|
||||||
self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border})
|
self.color_shader, "TRI_FAN", {"pos": self.border}
|
||||||
|
)
|
||||||
|
self.contour_batch = batch_for_shader(
|
||||||
|
self.dash_shader, "LINE_LOOP", {"pos": self.border}
|
||||||
|
)
|
||||||
|
|
||||||
self.draw_border = True
|
self.draw_border = True
|
||||||
|
|
||||||
self.region.tag_redraw()
|
self.region.tag_redraw()
|
||||||
|
|
||||||
if event.value == 'RELEASE':
|
if event.value == "RELEASE":
|
||||||
return self.release_event(context)
|
return self.release_event(context)
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
def release_event(self, context):
|
def release_event(self, context):
|
||||||
|
|
||||||
if get_picker_collection():
|
if get_picker_collection():
|
||||||
self.picker.assign_bone_event()
|
self.picker.assign_bone_event()
|
||||||
|
|
||||||
elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]):
|
elif (
|
||||||
|
self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]
|
||||||
|
):
|
||||||
self.picker.border_select(self.border, self.mode)
|
self.picker.border_select(self.border, self.mode)
|
||||||
else:
|
else:
|
||||||
self.picker.move_event(self.mouse)
|
self.picker.move_event(self.mouse)
|
||||||
self.picker.release_event(self.mode)
|
self.picker.release_event(self.mode)
|
||||||
|
|
||||||
bpy.ops.ed.undo_push(message="Box Select")
|
bpy.ops.ed.undo_push(message="Box Select")
|
||||||
|
|
||||||
return self.exit(context)
|
return self.exit(context)
|
||||||
|
|
||||||
def exit(self, context):
|
def exit(self, context):
|
||||||
bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
|
bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, "WINDOW")
|
||||||
context.region.tag_redraw()
|
context.region.tag_redraw()
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_picker_transform(Operator):
|
class RP_OT_picker_transform(Operator):
|
||||||
"""Transform Bones in the picker view"""
|
"""Transform Bones in the picker view"""
|
||||||
|
|
||||||
bl_idname = "node.picker_transform"
|
bl_idname = "node.picker_transform"
|
||||||
bl_label = "Transform Bone in Picker View"
|
bl_label = "Transform Bone in Picker View"
|
||||||
|
|
||||||
mode : EnumProperty(items=[(m, m.title(), '') for m in ('ROTATE', 'SCALE')])
|
mode: EnumProperty(items=[(m, m.title(), "") for m in ("ROTATE", "SCALE")])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.selected_pose_bones and is_picker_space(context.space_data)
|
return context.selected_pose_bones and is_picker_space(context.space_data)
|
||||||
|
|
||||||
'''
|
"""
|
||||||
def draw_callback(self):
|
def draw_callback(self):
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set('ALPHA')
|
||||||
gpu.state.line_width_set(2)
|
gpu.state.line_width_set(2)
|
||||||
@@ -179,18 +189,20 @@ class RP_OT_picker_transform(Operator):
|
|||||||
|
|
||||||
gpu.state.line_width_set(1)
|
gpu.state.line_width_set(1)
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set('NONE')
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
self.override = get_view_3d_override()
|
self.override = get_view_3d_override()
|
||||||
|
|
||||||
self.view_center = Vector((int(context.region.width / 2), int(context.region.height / 2)))
|
self.view_center = Vector(
|
||||||
|
(int(context.region.width / 2), int(context.region.height / 2))
|
||||||
|
)
|
||||||
self.mouse_start = Vector((event.mouse_region_x, event.mouse_region_y))
|
self.mouse_start = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||||
self.mouse = Vector((0, 0))
|
self.mouse = Vector((0, 0))
|
||||||
|
|
||||||
transform_type = context.scene.tool_settings.transform_pivot_point
|
transform_type = context.scene.tool_settings.transform_pivot_point
|
||||||
self.center = context.active_pose_bone.matrix.to_translation()
|
self.center = context.active_pose_bone.matrix.to_translation()
|
||||||
if transform_type == 'MEDIAN_POINT':
|
if transform_type == "MEDIAN_POINT":
|
||||||
origins = [b.matrix.to_translation() for b in context.selected_pose_bones]
|
origins = [b.matrix.to_translation() for b in context.selected_pose_bones]
|
||||||
self.center = sum(origins, Vector()) / len(context.selected_pose_bones)
|
self.center = sum(origins, Vector()) / len(context.selected_pose_bones)
|
||||||
# self.bone_data = {}
|
# self.bone_data = {}
|
||||||
@@ -198,38 +210,36 @@ class RP_OT_picker_transform(Operator):
|
|||||||
# self.bone_data[bone] = bone.matrix.copy()
|
# self.bone_data[bone] = bone.matrix.copy()
|
||||||
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
|
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
|
||||||
|
|
||||||
space = self.override['area'].spaces.active
|
space = self.override["area"].spaces.active
|
||||||
|
|
||||||
view_matrix = space.region_3d.view_matrix
|
view_matrix = space.region_3d.view_matrix
|
||||||
if space.region_3d.view_perspective == 'CAMERA':
|
if space.region_3d.view_perspective == "CAMERA":
|
||||||
view_matrix = context.scene.camera.matrix_world
|
view_matrix = context.scene.camera.matrix_world
|
||||||
|
|
||||||
context.window.cursor_modal_set('MOVE_X')
|
context.window.cursor_modal_set("MOVE_X")
|
||||||
self.view_vector = Vector((0, 0, 1))
|
self.view_vector = Vector((0, 0, 1))
|
||||||
if self.mode == 'ROTATE':
|
if self.mode == "ROTATE":
|
||||||
self.view_vector.rotate(view_matrix)
|
self.view_vector.rotate(view_matrix)
|
||||||
|
|
||||||
|
|
||||||
elif self.mode == 'SCALE':
|
elif self.mode == "SCALE":
|
||||||
self.view_vector = Vector((0, 0, 0))
|
self.view_vector = Vector((0, 0, 0))
|
||||||
|
|
||||||
self.transform_type = "VIEW"
|
self.transform_type = "VIEW"
|
||||||
self.transform_types = ["VIEW", 'GLOBAL', 'LOCAL']
|
self.transform_types = ["VIEW", "GLOBAL", "LOCAL"]
|
||||||
if context.scene.transform_orientation_slots[0].type == 'LOCAL':
|
if context.scene.transform_orientation_slots[0].type == "LOCAL":
|
||||||
self.transform_types = ["VIEW", 'LOCAL', 'GLOBAL']
|
self.transform_types = ["VIEW", "LOCAL", "GLOBAL"]
|
||||||
|
|
||||||
self.transform_orientation = context.scene.transform_orientation_slots[0].type
|
self.transform_orientation = context.scene.transform_orientation_slots[0].type
|
||||||
|
|
||||||
#self.dash_shader = SHADERS['dashed_line']
|
# self.dash_shader = SHADERS['dashed_line']
|
||||||
|
|
||||||
|
|
||||||
#self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
|
# self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
|
||||||
context.window_manager.modal_handler_add(self)
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
def change_transform_type(self):
|
def change_transform_type(self):
|
||||||
indices = {0: 1, 1:2, 2:1}
|
indices = {0: 1, 1: 2, 2: 1}
|
||||||
index = self.transform_types.index(self.transform_type)
|
index = self.transform_types.index(self.transform_type)
|
||||||
new_index = indices[index]
|
new_index = indices[index]
|
||||||
self.transform_type = self.transform_types[new_index]
|
self.transform_type = self.transform_types[new_index]
|
||||||
@@ -237,73 +247,73 @@ class RP_OT_picker_transform(Operator):
|
|||||||
def release_event(self, context):
|
def release_event(self, context):
|
||||||
scn = bpy.context.scene
|
scn = bpy.context.scene
|
||||||
|
|
||||||
# Insert keyframe
|
# Insert keyframe
|
||||||
#bpy.ops.ed.undo_push(message="Transform")
|
# bpy.ops.ed.undo_push(message="Transform")
|
||||||
if scn.tool_settings.use_keyframe_insert_auto:
|
if scn.tool_settings.use_keyframe_insert_auto:
|
||||||
try:
|
try:
|
||||||
bpy.ops.animtoolbox.insert_keyframe()
|
bpy.ops.animtoolbox.insert_keyframe()
|
||||||
except Exception:
|
except Exception:
|
||||||
self.report({"WARNING"}, 'You need animtoolbox addon for inserting keyframe')
|
self.report(
|
||||||
|
{"WARNING"}, "You need animtoolbox addon for inserting keyframe"
|
||||||
|
)
|
||||||
|
|
||||||
self.exit(context)
|
self.exit(context)
|
||||||
|
|
||||||
def exit(self, context):
|
def exit(self, context):
|
||||||
#bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
|
# bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
|
||||||
context.window.cursor_modal_restore()
|
context.window.cursor_modal_restore()
|
||||||
#context.region.tag_redraw()
|
# context.region.tag_redraw()
|
||||||
|
|
||||||
def modal(self, context, event):
|
def modal(self, context, event):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
self.mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
self.mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||||
|
|
||||||
if self.mode == 'ROTATE':
|
if self.mode == "ROTATE":
|
||||||
delta = (event.mouse_region_x - self.mouse_start[0]) / 100
|
delta = (event.mouse_region_x - self.mouse_start[0]) / 100
|
||||||
elif self.mode == 'SCALE':
|
elif self.mode == "SCALE":
|
||||||
delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100
|
delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100
|
||||||
#delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length
|
# delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length
|
||||||
|
|
||||||
|
|
||||||
transform_type = self.transform_type
|
transform_type = self.transform_type
|
||||||
#self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]})
|
# self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]})
|
||||||
#context.area.tag_redraw()
|
# context.area.tag_redraw()
|
||||||
|
|
||||||
#print(event.type, event.value)
|
# print(event.type, event.value)
|
||||||
if self.mode == 'ROTATE' and event.type == "R" and event.value == 'PRESS':
|
if self.mode == "ROTATE" and event.type == "R" and event.value == "PRESS":
|
||||||
with context.temp_override(**self.override):
|
with context.temp_override(**self.override):
|
||||||
self.exit(context)
|
self.exit(context)
|
||||||
bpy.ops.transform.trackball('INVOKE_DEFAULT')
|
bpy.ops.transform.trackball("INVOKE_DEFAULT")
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
if event.type == "LEFTMOUSE" and event.value == 'RELEASE':
|
if event.type == "LEFTMOUSE" and event.value == "RELEASE":
|
||||||
self.release_event(context)
|
self.release_event(context)
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
if event.type in {"RIGHTMOUSE", "ESC"}:
|
if event.type in {"RIGHTMOUSE", "ESC"}:
|
||||||
for bone, matrix in self.bone_data.items():
|
for bone, matrix in self.bone_data.items():
|
||||||
bone.matrix = matrix
|
bone.matrix = matrix
|
||||||
self.exit(context)
|
self.exit(context)
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
elif event.type == "X" and event.value == 'PRESS':
|
elif event.type == "X" and event.value == "PRESS":
|
||||||
self.change_transform_type()
|
self.change_transform_type()
|
||||||
self.view_vector = Vector((1, 0, 0))
|
self.view_vector = Vector((1, 0, 0))
|
||||||
#transform_type = self.transform_orientation
|
# transform_type = self.transform_orientation
|
||||||
|
|
||||||
elif event.type == "Y" and event.value == 'PRESS':
|
elif event.type == "Y" and event.value == "PRESS":
|
||||||
self.change_transform_type()
|
self.change_transform_type()
|
||||||
self.view_vector = Vector((0, 1, 0))
|
self.view_vector = Vector((0, 1, 0))
|
||||||
#transform_type = self.transform_orientation
|
# transform_type = self.transform_orientation
|
||||||
|
|
||||||
elif event.type == "Z" and event.value == 'PRESS':
|
elif event.type == "Z" and event.value == "PRESS":
|
||||||
self.change_transform_type()
|
self.change_transform_type()
|
||||||
self.view_vector = Vector((0, 0, 1))
|
self.view_vector = Vector((0, 0, 1))
|
||||||
#transform_type = self.transform_orientation
|
# transform_type = self.transform_orientation
|
||||||
|
|
||||||
elif event.type == "MOUSEMOVE":
|
elif event.type == "MOUSEMOVE":
|
||||||
|
if self.mode == "ROTATE":
|
||||||
if self.mode == 'ROTATE':
|
|
||||||
transform_matrix = Matrix.Rotation(delta, 4, self.view_vector)
|
transform_matrix = Matrix.Rotation(delta, 4, self.view_vector)
|
||||||
elif self.mode == 'SCALE':
|
elif self.mode == "SCALE":
|
||||||
if self.view_vector.length:
|
if self.view_vector.length:
|
||||||
transform_matrix = Matrix.Scale(delta, 4, self.view_vector)
|
transform_matrix = Matrix.Scale(delta, 4, self.view_vector)
|
||||||
else:
|
else:
|
||||||
@@ -314,14 +324,14 @@ class RP_OT_picker_transform(Operator):
|
|||||||
if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS":
|
if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS":
|
||||||
center = matrix.to_translation()
|
center = matrix.to_translation()
|
||||||
|
|
||||||
if self.mode == 'ROTATE':
|
if self.mode == "ROTATE":
|
||||||
if transform_type == 'LOCAL':
|
if transform_type == "LOCAL":
|
||||||
view_vector = self.view_vector.copy()
|
view_vector = self.view_vector.copy()
|
||||||
view_vector.rotate(matrix)
|
view_vector.rotate(matrix)
|
||||||
transform_matrix = Matrix.Rotation(delta, 4, view_vector)
|
transform_matrix = Matrix.Rotation(delta, 4, view_vector)
|
||||||
|
|
||||||
elif self.mode == 'SCALE':
|
elif self.mode == "SCALE":
|
||||||
if transform_type == 'LOCAL':
|
if transform_type == "LOCAL":
|
||||||
view_vector = self.view_vector.copy()
|
view_vector = self.view_vector.copy()
|
||||||
view_vector.rotate(matrix)
|
view_vector.rotate(matrix)
|
||||||
transform_matrix = Matrix.Scale(delta, 4, view_vector)
|
transform_matrix = Matrix.Scale(delta, 4, view_vector)
|
||||||
@@ -335,40 +345,40 @@ class RP_OT_picker_transform(Operator):
|
|||||||
|
|
||||||
bone.matrix = mat
|
bone.matrix = mat
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_toogle_property(Operator):
|
class RP_OT_toogle_property(Operator):
|
||||||
"""Invert a bone custom property"""
|
"""Invert a bone custom property"""
|
||||||
|
|
||||||
bl_idname = "rigpicker.toogle_property"
|
bl_idname = "rigpicker.toogle_property"
|
||||||
bl_label = 'Toogle Property'
|
bl_label = "Toogle Property"
|
||||||
|
|
||||||
data_path : StringProperty()
|
data_path: StringProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return is_picker_space(context.space_data)
|
return is_picker_space(context.space_data)
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
ob = context.object
|
ob = context.object
|
||||||
|
|
||||||
try:
|
try:
|
||||||
value = ob.path_resolve(self.data_path)
|
value = ob.path_resolve(self.data_path)
|
||||||
except Exception:
|
except Exception:
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
data = ob
|
data = ob
|
||||||
prop_name = self.data_path
|
prop_name = self.data_path
|
||||||
#if '.' in self.data_path:
|
# if '.' in self.data_path:
|
||||||
# data, prop = prop_name.rsplit('.', 1)
|
# data, prop = prop_name.rsplit('.', 1)
|
||||||
|
|
||||||
value = type(value)(not value)
|
|
||||||
exec(f'{repr(ob)}.{self.data_path} = {value}')
|
|
||||||
|
|
||||||
#setattr(data, prop_name, value)
|
value = type(value)(not value)
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
exec(f"{repr(ob)}.{self.data_path} = {value}")
|
||||||
bpy.ops.object.mode_set(mode='POSE')
|
|
||||||
|
# setattr(data, prop_name, value)
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
bpy.ops.object.mode_set(mode="POSE")
|
||||||
|
|
||||||
if context.scene.tool_settings.use_keyframe_insert_auto:
|
if context.scene.tool_settings.use_keyframe_insert_auto:
|
||||||
bone_name, _ = split_path(self.data_path)
|
bone_name, _ = split_path(self.data_path)
|
||||||
@@ -376,42 +386,45 @@ class RP_OT_toogle_property(Operator):
|
|||||||
|
|
||||||
context.area.tag_redraw()
|
context.area.tag_redraw()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_reload_picker(Operator):
|
class RP_OT_reload_picker(Operator):
|
||||||
"""Reload the picker shapes"""
|
"""Reload the picker shapes"""
|
||||||
|
|
||||||
bl_idname = "rigpicker.reload_picker"
|
bl_idname: str = "rigpicker.reload_picker"
|
||||||
bl_label = 'Reload Picker'
|
bl_label: str = "Reload Picker"
|
||||||
|
|
||||||
#@classmethod
|
# @classmethod
|
||||||
#def poll(cls, context):
|
# def poll(cls, context):
|
||||||
# if not is_picker_space(context):
|
# if not is_picker_space(context):
|
||||||
# return
|
# return
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context: bpy.types.Context):
|
||||||
if context.object.type == 'ARMATURE':
|
if not context.object:
|
||||||
|
return
|
||||||
|
if context.object.type == "ARMATURE":
|
||||||
rig = context.object
|
rig = context.object
|
||||||
else:
|
else:
|
||||||
collection = get_picker_collection(context.object)
|
collection = get_picker_collection(context.object)
|
||||||
rig = collection.rig_picker.rig
|
rig = collection.rig_picker.rig
|
||||||
|
|
||||||
#print('Reload', rig)
|
# print('Reload', rig)
|
||||||
load_picker_data(rig)
|
load_picker_data(rig)
|
||||||
|
|
||||||
for area in context.screen.areas:
|
if context.screen:
|
||||||
if is_picker_space(area.spaces.active):
|
for area in context.screen.areas:
|
||||||
area.tag_redraw()
|
if is_picker_space(area.spaces.active):
|
||||||
|
area.tag_redraw()
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_toogle_bone_layer(Operator):
|
class RP_OT_toogle_bone_layer(Operator):
|
||||||
"""Toogle bone layer visibility when double clicking on a bone"""
|
"""Toogle bone layer visibility when double clicking on a bone"""
|
||||||
bl_idname = "rigpicker.toogle_bone_layer"
|
|
||||||
bl_label = 'Toogle Bone Layer'
|
bl_idname: str = "rigpicker.toogle_bone_layer"
|
||||||
|
bl_label: str = "Toogle Bone Layer"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -421,7 +434,7 @@ class RP_OT_toogle_bone_layer(Operator):
|
|||||||
ob = context.object
|
ob = context.object
|
||||||
picker = PICKERS.get(ob)
|
picker = PICKERS.get(ob)
|
||||||
|
|
||||||
if picker.hover_shape and picker.hover_shape.type == 'bone':
|
if picker.hover_shape and picker.hover_shape.type == "bone":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -442,62 +455,64 @@ class RP_OT_toogle_bone_layer(Operator):
|
|||||||
|
|
||||||
class RP_OT_context_menu_picker(Operator):
|
class RP_OT_context_menu_picker(Operator):
|
||||||
"""Display Menu with the custom properties of the hovered bone if any or the active bone"""
|
"""Display Menu with the custom properties of the hovered bone if any or the active bone"""
|
||||||
|
|
||||||
bl_idname = "node.context_menu_picker"
|
bl_idname = "node.context_menu_picker"
|
||||||
bl_label = 'Context Menu Picker'
|
bl_label = "Context Menu Picker"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return is_picker_space(context.space_data)
|
return is_picker_space(context.space_data)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.ops.wm.call_menu(name='RP_MT_context_menu')
|
bpy.ops.wm.call_menu(name="RP_MT_context_menu")
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_pack_picker(Operator):
|
class RP_OT_pack_picker(Operator):
|
||||||
"""Pack Unpack the picker on the rig"""
|
"""Pack Unpack the picker on the rig"""
|
||||||
|
|
||||||
bl_idname = "rigpicker.pack_picker"
|
bl_idname = "rigpicker.pack_picker"
|
||||||
bl_label = 'Pack Picker'
|
bl_label = "Pack Picker"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
ob = context.object
|
ob = context.object
|
||||||
return (ob and ob.type == 'ARMATURE' and ob.data.rig_picker.sources)
|
return ob and ob.type == "ARMATURE" and ob.data.rig_picker.sources
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print('Pack Pickers...')
|
print("Pack Pickers...")
|
||||||
|
|
||||||
rig = context.object
|
rig = context.object
|
||||||
|
|
||||||
if 'pickers' in rig.data.rig_picker.keys():
|
if "pickers" in rig.data.rig_picker.keys():
|
||||||
unpack_picker(rig)
|
unpack_picker(rig)
|
||||||
self.report({"INFO"}, f'The picker is unpacked')
|
self.report({"INFO"}, f"The picker is unpacked")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
pack_picker(rig)
|
pack_picker(rig)
|
||||||
|
|
||||||
if not rig.data.rig_picker['pickers']:
|
if not rig.data.rig_picker["pickers"]:
|
||||||
self.report({"ERROR"}, f'No picker have been packed')
|
self.report({"ERROR"}, f"No picker have been packed")
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
elif len(rig.data.rig_picker['pickers']) < len(rig.data.rig_picker.sources):
|
elif len(rig.data.rig_picker["pickers"]) < len(rig.data.rig_picker.sources):
|
||||||
self.report({"WARNING"}, f'No all pickers have been packed')
|
self.report({"WARNING"}, f"No all pickers have been packed")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
self.report({"INFO"}, f'The picker is packed')
|
self.report({"INFO"}, f"The picker is packed")
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_call_operator(Operator):
|
class RP_OT_call_operator(Operator):
|
||||||
bl_idname = "rigpicker.call_operator"
|
bl_idname = "rigpicker.call_operator"
|
||||||
bl_label = 'Call operator'
|
bl_label = "Call operator"
|
||||||
|
|
||||||
operator : StringProperty()
|
operator: StringProperty()
|
||||||
arguments : StringProperty()
|
arguments: StringProperty()
|
||||||
invoke: BoolProperty()
|
invoke: BoolProperty()
|
||||||
view_3d : BoolProperty()
|
view_3d: BoolProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -509,30 +524,30 @@ class RP_OT_call_operator(Operator):
|
|||||||
op = eval_attr(bpy.ops, properties.operator).get_rna_type()
|
op = eval_attr(bpy.ops, properties.operator).get_rna_type()
|
||||||
return op.description
|
return op.description
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return 'Call operator'
|
return "Call operator"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
#context.area.tag_redraw()
|
# context.area.tag_redraw()
|
||||||
|
|
||||||
override = {}
|
override = {}
|
||||||
if self.view_3d:
|
if self.view_3d:
|
||||||
override = get_view_3d_override()
|
override = get_view_3d_override()
|
||||||
|
|
||||||
arguments = json.loads(self.arguments or "{}")
|
arguments = json.loads(self.arguments or "{}")
|
||||||
|
|
||||||
with context.temp_override(**override):
|
with context.temp_override(**override):
|
||||||
try:
|
try:
|
||||||
ops = eval_attr(bpy.ops, self.operator)
|
ops = eval_attr(bpy.ops, self.operator)
|
||||||
if self.invoke:
|
if self.invoke:
|
||||||
ops('INVOKE_DEFAULT', **arguments)
|
ops("INVOKE_DEFAULT", **arguments)
|
||||||
else:
|
else:
|
||||||
ops(**arguments)
|
ops(**arguments)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
self.report({"ERROR"}, f'The operator {self.operator} cannot be called')
|
self.report({"ERROR"}, f"The operator {self.operator} cannot be called")
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
context.area.tag_redraw()
|
context.area.tag_redraw()
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -545,72 +560,77 @@ class RP_MT_context_menu(Menu):
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.operator_context = 'INVOKE_DEFAULT'
|
col.operator_context = "INVOKE_DEFAULT"
|
||||||
#col.use_property_split = True
|
# col.use_property_split = True
|
||||||
|
|
||||||
ob = context.object
|
ob = context.object
|
||||||
picker = PICKERS.get(ob)
|
picker = PICKERS.get(ob)
|
||||||
|
|
||||||
if picker.hover_shape and picker.hover_shape.type == 'bone':
|
if picker.hover_shape and picker.hover_shape.type == "bone":
|
||||||
bone = picker.hover_shape.bone
|
bone = picker.hover_shape.bone
|
||||||
else:
|
else:
|
||||||
bone = context.active_pose_bone
|
bone = context.active_pose_bone
|
||||||
|
|
||||||
# Draw Space Switch Operator
|
# Draw Space Switch Operator
|
||||||
if getattr(ob.data, 'space_switch'):
|
if getattr(ob.data, "space_switch"):
|
||||||
space_switch = ob.data.space_switch
|
space_switch = ob.data.space_switch
|
||||||
|
|
||||||
data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()]
|
data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()]
|
||||||
space_bone = next((s for s in space_switch.bones if s.data_path in data_paths), None)
|
space_bone = next(
|
||||||
|
(s for s in space_switch.bones if s.data_path in data_paths), None
|
||||||
|
)
|
||||||
if space_bone:
|
if space_bone:
|
||||||
|
|
||||||
index = list(space_switch.bones).index(space_bone)
|
index = list(space_switch.bones).index(space_bone)
|
||||||
value = ob.path_resolve(space_bone.data_path)
|
value = ob.path_resolve(space_bone.data_path)
|
||||||
space = next((s.name for s in space_bone.spaces if s.value == value), None)
|
space = next(
|
||||||
|
(s.name for s in space_bone.spaces if s.value == value), None
|
||||||
|
)
|
||||||
|
|
||||||
op = col.operator("spaceswitch.change_space", text=f'({space})', icon='PINNED')
|
op = col.operator(
|
||||||
op.index=index
|
"spaceswitch.change_space", text=f"({space})", icon="PINNED"
|
||||||
|
)
|
||||||
|
op.index = index
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
||||||
if bone:
|
if bone:
|
||||||
for key in bone.keys():
|
for key in bone.keys():
|
||||||
col.prop(bone,f'["{key}"]', slider=True)
|
col.prop(bone, f'["{key}"]', slider=True)
|
||||||
|
|
||||||
#layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
|
# layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
|
||||||
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
|
# layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_add_picker_collection(Operator):
|
class RP_OT_add_picker_collection(Operator):
|
||||||
bl_idname = "rigpicker.add_picker_collection"
|
bl_idname = "rigpicker.add_picker_collection"
|
||||||
bl_label = "Add a Picker Collection"
|
bl_label = "Add a Picker Collection"
|
||||||
bl_description = "Add a Picker Collection"
|
bl_description = "Add a Picker Collection"
|
||||||
bl_options = {'UNDO', 'REGISTER'}
|
bl_options = {"UNDO", "REGISTER"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
ob = context.object
|
ob = context.object
|
||||||
return (ob and ob.type == 'ARMATURE')
|
return ob and ob.type == "ARMATURE"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
ob = context.object
|
ob = context.object
|
||||||
|
|
||||||
name = ob.name
|
name = ob.name
|
||||||
if name.endswith('_rig'):
|
if name.endswith("_rig"):
|
||||||
name = name[:-4]
|
name = name[:-4]
|
||||||
|
|
||||||
canvas_name = f'canevas_{name}'
|
canvas_name = f"canevas_{name}"
|
||||||
canvas_points = [(-0.5, 0.5, 0), (0.5, 0.5, 0), (0.5, -0.5, 0), (-0.5, -0.5, 0)]
|
canvas_points = [(-0.5, 0.5, 0), (0.5, 0.5, 0), (0.5, -0.5, 0), (-0.5, -0.5, 0)]
|
||||||
canvas_faces = [(0, 1, 2, 3)]
|
canvas_faces = [(0, 1, 2, 3)]
|
||||||
|
|
||||||
canvas_mesh = bpy.data.meshes.new(canvas_name)
|
canvas_mesh = bpy.data.meshes.new(canvas_name)
|
||||||
canvas_mesh.from_pydata(canvas_points, [], canvas_faces)
|
canvas_mesh.from_pydata(canvas_points, [], canvas_faces)
|
||||||
canvas_mesh.update(calc_edges=True)
|
canvas_mesh.update(calc_edges=True)
|
||||||
|
|
||||||
canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh)
|
canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh)
|
||||||
canvas_ob.rig_picker.shape_type = 'DISPLAY'
|
canvas_ob.rig_picker.shape_type = "DISPLAY"
|
||||||
|
|
||||||
col = bpy.data.collections.new(f'Picker {name}')
|
col = bpy.data.collections.new(f"Picker {name}")
|
||||||
col.rig_picker.enabled = True
|
col.rig_picker.enabled = True
|
||||||
col.rig_picker.rig = ob
|
col.rig_picker.rig = ob
|
||||||
col.rig_picker.canvas = canvas_ob
|
col.rig_picker.canvas = canvas_ob
|
||||||
@@ -621,44 +641,44 @@ class RP_OT_add_picker_collection(Operator):
|
|||||||
|
|
||||||
self.report({"INFO"}, f"New Picker Collection {col.name} Created")
|
self.report({"INFO"}, f"New Picker Collection {col.name} Created")
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_add_picker_source(Operator):
|
class RP_OT_add_picker_source(Operator):
|
||||||
bl_idname = "rigpicker.add_picker_source"
|
bl_idname = "rigpicker.add_picker_source"
|
||||||
bl_label = "Add a Picker source"
|
bl_label = "Add a Picker source"
|
||||||
bl_description = "Add a Picker source"
|
bl_description = "Add a Picker source"
|
||||||
bl_options = {'UNDO', 'REGISTER'}
|
bl_options = {"UNDO", "REGISTER"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
arm = context.object.data
|
arm = context.object.data
|
||||||
arm.rig_picker.sources.add()
|
arm.rig_picker.sources.add()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_remove_picker_source(Operator):
|
class RP_OT_remove_picker_source(Operator):
|
||||||
bl_idname = "rigpicker.remove_picker_source"
|
bl_idname = "rigpicker.remove_picker_source"
|
||||||
bl_label = "Delete a Picker source"
|
bl_label = "Delete a Picker source"
|
||||||
bl_description = "Delete a Picker source"
|
bl_description = "Delete a Picker source"
|
||||||
bl_options = {'UNDO', 'REGISTER'}
|
bl_options = {"UNDO", "REGISTER"}
|
||||||
|
|
||||||
index : IntProperty(default=-1)
|
index: IntProperty(default=-1)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
arm = context.object.data
|
arm = context.object.data
|
||||||
arm.rig_picker.sources.remove(self.index)
|
arm.rig_picker.sources.remove(self.index)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_fit_picker(Operator):
|
class RP_OT_fit_picker(Operator):
|
||||||
bl_idname = "rigpicker.fit_picker"
|
bl_idname = "rigpicker.fit_picker"
|
||||||
bl_label = "Fit Picker"
|
bl_label = "Fit Picker"
|
||||||
bl_description = "Fit Picker in 2d view"
|
bl_description = "Fit Picker in 2d view"
|
||||||
bl_options = {'UNDO', 'REGISTER'}
|
bl_options = {"UNDO", "REGISTER"}
|
||||||
|
|
||||||
index : IntProperty()
|
index: IntProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -675,17 +695,22 @@ class RP_OT_fit_picker(Operator):
|
|||||||
view2d = context.region.view2d
|
view2d = context.region.view2d
|
||||||
|
|
||||||
if self.index == -1:
|
if self.index == -1:
|
||||||
#print('Picker Group')
|
# print('Picker Group')
|
||||||
picker = picker_group
|
picker = picker_group
|
||||||
|
|
||||||
view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect]
|
view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect]
|
||||||
bpy.ops.view2d.zoom_border(
|
bpy.ops.view2d.zoom_border(
|
||||||
xmin=round(view_rect[3][0]), xmax=round(view_rect[1][0]),
|
xmin=round(view_rect[3][0]),
|
||||||
ymin=round(view_rect[3][1]), ymax=round(view_rect[1][1]),
|
xmax=round(view_rect[1][0]),
|
||||||
wait_for_input=False, zoom_out=False
|
ymin=round(view_rect[3][1]),
|
||||||
|
ymax=round(view_rect[1][1]),
|
||||||
|
wait_for_input=False,
|
||||||
|
zoom_out=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
region_center = Vector((context.region.width * 0.5, context.region.height * 0.5))
|
region_center = Vector(
|
||||||
|
(context.region.width * 0.5, context.region.height * 0.5)
|
||||||
|
)
|
||||||
view_center = view2d.region_to_view(*region_center)
|
view_center = view2d.region_to_view(*region_center)
|
||||||
|
|
||||||
view_offset = Vector(view_center) + (picker.center - Vector(view_center))
|
view_offset = Vector(view_center) + (picker.center - Vector(view_center))
|
||||||
@@ -695,21 +720,27 @@ class RP_OT_fit_picker(Operator):
|
|||||||
|
|
||||||
bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1]))
|
bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1]))
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
keymaps = []
|
keymaps = []
|
||||||
|
|
||||||
|
|
||||||
def register_keymaps():
|
def register_keymaps():
|
||||||
wm = bpy.context.window_manager
|
wm = bpy.context.window_manager
|
||||||
|
|
||||||
km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")
|
km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")
|
||||||
|
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
kmi = km.keymap_items.new("rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS")
|
kmi = km.keymap_items.new(
|
||||||
|
"rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS"
|
||||||
|
)
|
||||||
kmi.properties.index = i - 1
|
kmi.properties.index = i - 1
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS")
|
kmi = km.keymap_items.new(
|
||||||
|
"rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS"
|
||||||
|
)
|
||||||
kmi.properties.operator = "view3d.view_selected"
|
kmi.properties.operator = "view3d.view_selected"
|
||||||
kmi.properties.view_3d = True
|
kmi.properties.view_3d = True
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
@@ -719,12 +750,16 @@ def register_keymaps():
|
|||||||
kmi.properties.arguments = '{"action": "SELECT"}'
|
kmi.properties.arguments = '{"action": "SELECT"}'
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS", alt=True)
|
kmi = km.keymap_items.new(
|
||||||
|
"rigpicker.call_operator", type="A", value="PRESS", alt=True
|
||||||
|
)
|
||||||
kmi.properties.operator = "pose.select_all"
|
kmi.properties.operator = "pose.select_all"
|
||||||
kmi.properties.arguments = '{"action": "DESELECT"}'
|
kmi.properties.arguments = '{"action": "DESELECT"}'
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
|
kmi = km.keymap_items.new(
|
||||||
|
"rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK"
|
||||||
|
)
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
|
kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
|
||||||
@@ -735,55 +770,69 @@ def register_keymaps():
|
|||||||
kmi.properties.operator = "animtoolbox.insert_keyframe"
|
kmi.properties.operator = "animtoolbox.insert_keyframe"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True)
|
kmi = km.keymap_items.new(
|
||||||
|
"anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True
|
||||||
|
)
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS")
|
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS")
|
||||||
kmi.properties.operator = 'transform.translate'
|
kmi.properties.operator = "transform.translate"
|
||||||
kmi.properties.view_3d = True
|
kmi.properties.view_3d = True
|
||||||
kmi.properties.invoke = True
|
kmi.properties.invoke = True
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS", alt=True)
|
kmi = km.keymap_items.new(
|
||||||
kmi.properties.operator = 'pose.loc_clear'
|
"rigpicker.call_operator", type="G", value="PRESS", alt=True
|
||||||
|
)
|
||||||
|
kmi.properties.operator = "pose.loc_clear"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
|
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
|
||||||
kmi.properties.mode = 'ROTATE'
|
kmi.properties.mode = "ROTATE"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="R", value="PRESS", alt=True)
|
kmi = km.keymap_items.new(
|
||||||
kmi.properties.operator = 'pose.rot_clear'
|
"rigpicker.call_operator", type="R", value="PRESS", alt=True
|
||||||
|
)
|
||||||
|
kmi.properties.operator = "pose.rot_clear"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
|
kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
|
||||||
kmi.properties.mode = 'SCALE'
|
kmi.properties.mode = "SCALE"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="S", value="PRESS", alt=True)
|
kmi = km.keymap_items.new(
|
||||||
kmi.properties.operator = 'pose.scale_clear'
|
"rigpicker.call_operator", type="S", value="PRESS", alt=True
|
||||||
|
)
|
||||||
|
kmi.properties.operator = "pose.scale_clear"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS")
|
kmi = km.keymap_items.new(
|
||||||
|
"node.context_menu_picker", type="RIGHTMOUSE", value="PRESS"
|
||||||
|
)
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
|
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
|
||||||
kmi.properties.mode = 'SET'
|
kmi.properties.mode = "SET"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True)
|
kmi = km.keymap_items.new(
|
||||||
kmi.properties.mode = 'EXTEND'
|
"node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True
|
||||||
|
)
|
||||||
|
kmi.properties.mode = "EXTEND"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
#kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True)
|
# kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True)
|
||||||
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True)
|
kmi = km.keymap_items.new(
|
||||||
kmi.properties.mode = 'SUBSTRACT'
|
"node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True
|
||||||
|
)
|
||||||
|
kmi.properties.mode = "SUBSTRACT"
|
||||||
keymaps.append((km, kmi))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
#km = wm.keyconfigs.addon.keymaps.new(name="View2D")
|
# km = wm.keyconfigs.addon.keymaps.new(name="View2D")
|
||||||
#kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
|
# kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
|
||||||
#kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
|
# kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
|
||||||
#keymaps.append((km, kmi))
|
# keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
|
||||||
def unregister_keymaps():
|
def unregister_keymaps():
|
||||||
@@ -791,6 +840,7 @@ def unregister_keymaps():
|
|||||||
km.keymap_items.remove(kmi)
|
km.keymap_items.remove(kmi)
|
||||||
keymaps.clear()
|
keymaps.clear()
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
RP_OT_box_select,
|
RP_OT_box_select,
|
||||||
RP_OT_toogle_property,
|
RP_OT_toogle_property,
|
||||||
@@ -804,19 +854,19 @@ classes = (
|
|||||||
RP_OT_add_picker_source,
|
RP_OT_add_picker_source,
|
||||||
RP_OT_remove_picker_source,
|
RP_OT_remove_picker_source,
|
||||||
RP_OT_fit_picker,
|
RP_OT_fit_picker,
|
||||||
RP_OT_add_picker_collection
|
RP_OT_add_picker_collection,
|
||||||
#RP_OT_ui_draw
|
# RP_OT_ui_draw
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
register_keymaps()
|
register_keymaps()
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
unregister_keymaps()
|
unregister_keymaps()
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
from os.path import abspath
|
from os.path import abspath
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -13,23 +12,23 @@ from ..core.bl_utils import link_mat_to_object, flip_name
|
|||||||
|
|
||||||
|
|
||||||
class RP_OT_create_shape(Operator):
|
class RP_OT_create_shape(Operator):
|
||||||
bl_label = 'Create UI shape'
|
bl_label = "Create UI shape"
|
||||||
bl_idname = 'rigpicker.create_shape'
|
bl_idname = "rigpicker.create_shape"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return (context.object and context.object.mode == 'POSE')
|
return context.object and context.object.mode == "POSE"
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return context.window_manager.invoke_props_dialog(self)
|
return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
for col in bpy.data.collections:
|
for col in bpy.data.collections:
|
||||||
if col.rig_picker.enabled:
|
if col.rig_picker.enabled:
|
||||||
self.layout.prop(col.rig_picker, 'link_shape', text=col.name)
|
self.layout.prop(col.rig_picker, "link_shape", text=col.name)
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
vl = context.view_layer
|
vl = context.view_layer
|
||||||
|
|
||||||
@@ -42,17 +41,18 @@ class RP_OT_create_shape(Operator):
|
|||||||
|
|
||||||
ob.rig_picker.name = name
|
ob.rig_picker.name = name
|
||||||
|
|
||||||
verts=[(0,0,0)]
|
verts = [(0, 0, 0)]
|
||||||
edges = []
|
edges = []
|
||||||
faces =[]
|
faces = []
|
||||||
|
|
||||||
mesh.from_pydata(verts, edges, faces)
|
mesh.from_pydata(verts, edges, faces)
|
||||||
|
|
||||||
for col in bpy.data.collections:
|
picker_selected_cols = [
|
||||||
if col.rig_picker.link_shape:
|
col for col in bpy.data.collections if col.rig_picker.link_shape
|
||||||
col.objects.link(ob)
|
]
|
||||||
else:
|
|
||||||
scn.collection.objects.link(ob)
|
for col in picker_selected_cols or [scn.collection]:
|
||||||
|
col.objects.link(ob)
|
||||||
|
|
||||||
ob.location.z = 0.05
|
ob.location.z = 0.05
|
||||||
ob.location.x = offset
|
ob.location.x = offset
|
||||||
@@ -65,36 +65,36 @@ class RP_OT_create_shape(Operator):
|
|||||||
|
|
||||||
offset += 0.05
|
offset += 0.05
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_name_from_bone(Operator):
|
class RP_OT_name_from_bone(Operator):
|
||||||
bl_label = 'Name Shape from selected bones'
|
bl_label = "Name Shape from selected bones"
|
||||||
bl_idname = 'rigpicker.name_from_bone'
|
bl_idname = "rigpicker.name_from_bone"
|
||||||
bl_description = 'Rename all shapes from related bones name'
|
bl_description = "Rename all shapes from related bones name"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
|
|
||||||
col = get_picker_collection(context.object)
|
col = get_picker_collection(context.object)
|
||||||
|
|
||||||
for ob in col.all_objects:
|
for ob in col.all_objects:
|
||||||
if ob.rig_picker.shape_type == 'BONE':
|
if ob.rig_picker.shape_type == "BONE":
|
||||||
ob.name = ob.rig_picker.name
|
ob.name = ob.rig_picker.name
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_mirror_shape(Operator):
|
class RP_OT_mirror_shape(Operator):
|
||||||
bl_label = 'Mirror UI shape'
|
bl_label = "Mirror UI shape"
|
||||||
bl_idname = 'rigpicker.mirror_shape'
|
bl_idname = "rigpicker.mirror_shape"
|
||||||
#bl_options = g
|
# bl_options = g
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))
|
return context.object and context.object.type in ("MESH", "CURVE", "TEXT")
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
collection = get_picker_collection(context.object)
|
collection = get_picker_collection(context.object)
|
||||||
objects = context.selected_objects
|
objects = context.selected_objects
|
||||||
|
|
||||||
@@ -104,9 +104,13 @@ class RP_OT_mirror_shape(Operator):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for mod in ob.modifiers:
|
for mod in ob.modifiers:
|
||||||
if (mod.type == 'NODES' and mod.node_group and mod.node_group.name == 'Symmetrize' and
|
if (
|
||||||
mod.get('Socket_2') in objects):
|
mod.type == "NODES"
|
||||||
|
and mod.node_group
|
||||||
|
and mod.node_group.name == "Symmetrize"
|
||||||
|
and mod.get("Socket_2") in objects
|
||||||
|
):
|
||||||
|
|
||||||
bpy.data.objects.remove(ob)
|
bpy.data.objects.remove(ob)
|
||||||
|
|
||||||
if collection.rig_picker.symmetry:
|
if collection.rig_picker.symmetry:
|
||||||
@@ -117,14 +121,14 @@ class RP_OT_mirror_shape(Operator):
|
|||||||
for ob in objects:
|
for ob in objects:
|
||||||
flipped_name = flip_name(ob.name)
|
flipped_name = flip_name(ob.name)
|
||||||
if flipped_name == ob.name:
|
if flipped_name == ob.name:
|
||||||
suffix = '.L'
|
suffix = ".L"
|
||||||
flipped_suffix = '.R'
|
flipped_suffix = ".R"
|
||||||
# Determine side of the object
|
# Determine side of the object
|
||||||
if ob.matrix_world.to_translation()[0] < x_axis:
|
if ob.matrix_world.to_translation()[0] < x_axis:
|
||||||
suffix, flipped_suffix = flipped_suffix, suffix
|
suffix, flipped_suffix = flipped_suffix, suffix
|
||||||
|
|
||||||
flipped_name = f'{ob.name}{flipped_suffix}'
|
flipped_name = f"{ob.name}{flipped_suffix}"
|
||||||
ob.name = f'{ob.name}{suffix}'
|
ob.name = f"{ob.name}{suffix}"
|
||||||
|
|
||||||
flipped_object = ob.copy()
|
flipped_object = ob.copy()
|
||||||
flipped_object.name = flipped_name
|
flipped_object.name = flipped_name
|
||||||
@@ -134,20 +138,20 @@ class RP_OT_mirror_shape(Operator):
|
|||||||
flipped_object.modifiers.remove(mod)
|
flipped_object.modifiers.remove(mod)
|
||||||
|
|
||||||
# Add symmetrize modifier TODO add it into a resource
|
# Add symmetrize modifier TODO add it into a resource
|
||||||
mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES')
|
mod = flipped_object.modifiers.new(name="Symmetrize", type="NODES")
|
||||||
mod.node_group = bpy.data.node_groups['Symmetrize']
|
mod.node_group = bpy.data.node_groups["Symmetrize"]
|
||||||
mod['Socket_2'] = ob
|
mod["Socket_2"] = ob
|
||||||
mod['Socket_3'] = collection.rig_picker.symmetry
|
mod["Socket_3"] = collection.rig_picker.symmetry
|
||||||
|
|
||||||
for col in ob.users_collection:
|
for col in ob.users_collection:
|
||||||
col.objects.link(flipped_object)
|
col.objects.link(flipped_object)
|
||||||
|
|
||||||
if ob.rig_picker.shape_type == 'BONE':
|
if ob.rig_picker.shape_type == "BONE":
|
||||||
flipped_object.rig_picker.name = flip_name(ob.rig_picker.name)
|
flipped_object.rig_picker.name = flip_name(ob.rig_picker.name)
|
||||||
|
|
||||||
elif ob.rig_picker.shape_type == 'FUNCTION':
|
elif ob.rig_picker.shape_type == "FUNCTION":
|
||||||
args = {}
|
args = {}
|
||||||
for key,value in eval(ob.rig_picker.arguments).items():
|
for key, value in eval(ob.rig_picker.arguments).items():
|
||||||
if type(value) == list:
|
if type(value) == list:
|
||||||
mirrored_value = []
|
mirrored_value = []
|
||||||
for item in value:
|
for item in value:
|
||||||
@@ -159,32 +163,37 @@ class RP_OT_mirror_shape(Operator):
|
|||||||
|
|
||||||
flipped_object.rig_picker.arguments = str(args)
|
flipped_object.rig_picker.arguments = str(args)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_select_shape_type(Operator):
|
class RP_OT_select_shape_type(Operator):
|
||||||
bl_label = 'Select Shape by Type'
|
bl_label = "Select Shape by Type"
|
||||||
bl_idname = 'rigpicker.select_shape_type'
|
bl_idname = "rigpicker.select_shape_type"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
shape_type = bpy.props.EnumProperty(items =[(i.upper(), i, '') for i in ('Bone', 'Display', 'Function')])
|
shape_type = bpy.props.EnumProperty(
|
||||||
|
items=[(i.upper(), i, "") for i in ("Bone", "Display", "Function")]
|
||||||
|
)
|
||||||
|
|
||||||
def draw(self,context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(self,'shape_type', expand=True)
|
col.prop(self, "shape_type", expand=True)
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self, context):
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
#print(self.shape_type)
|
# print(self.shape_type)
|
||||||
canvas = context.scene.rig_picker.canvas
|
canvas = context.scene.rig_picker.canvas
|
||||||
if canvas:
|
if canvas:
|
||||||
for ob in [o for o in bpy.data.objects if o.layers==canvas.layers]:
|
for ob in [o for o in bpy.data.objects if o.layers == canvas.layers]:
|
||||||
if ob.type in ['MESH', 'CURVE', 'FONT'] and ob.rig_picker.shape_type == self.shape_type:
|
if (
|
||||||
|
ob.type in ["MESH", "CURVE", "FONT"]
|
||||||
|
and ob.rig_picker.shape_type == self.shape_type
|
||||||
|
):
|
||||||
ob.select = True
|
ob.select = True
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
wm = context.window_manager
|
wm = context.window_manager
|
||||||
@@ -192,10 +201,10 @@ class RP_OT_select_shape_type(Operator):
|
|||||||
|
|
||||||
|
|
||||||
class RP_OT_save_picker(Operator):
|
class RP_OT_save_picker(Operator):
|
||||||
bl_label = 'Store UI Data'
|
bl_label = "Store UI Data"
|
||||||
bl_idname = 'rigpicker.save_picker'
|
bl_idname = "rigpicker.save_picker"
|
||||||
|
|
||||||
index : IntProperty(default=-1)
|
index: IntProperty(default=-1)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
@@ -206,39 +215,51 @@ class RP_OT_save_picker(Operator):
|
|||||||
else:
|
else:
|
||||||
source = context.active_object.data.rig_picker.sources[self.index].source
|
source = context.active_object.data.rig_picker.sources[self.index].source
|
||||||
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
|
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
|
||||||
collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled
|
collection = next(
|
||||||
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
|
(
|
||||||
|
c
|
||||||
|
for c in set(scn.collection.children_recursive)
|
||||||
|
if c.rig_picker.enabled
|
||||||
|
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve()
|
||||||
|
== source
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
if not collection:
|
if not collection:
|
||||||
self.report({"ERROR"}, 'No Picker found')
|
self.report({"ERROR"}, "No Picker found")
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
canvas = collection.rig_picker.canvas
|
canvas = collection.rig_picker.canvas
|
||||||
#rig = collection.rig_picker.rig
|
# rig = collection.rig_picker.rig
|
||||||
shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')]
|
shapes = [
|
||||||
|
o
|
||||||
|
for o in collection.all_objects
|
||||||
|
if o != canvas and o.type in ("MESH", "CURVE", "FONT")
|
||||||
|
]
|
||||||
|
|
||||||
if not canvas:
|
if not canvas:
|
||||||
self.report({'ERROR'}, 'Choose a Canvas')
|
self.report({"ERROR"}, "Choose a Canvas")
|
||||||
return {'CANCELLED'}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
|
||||||
picker_data = get_picker_data(collection)
|
picker_data = get_picker_data(collection)
|
||||||
|
|
||||||
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))
|
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))
|
||||||
|
|
||||||
print(f'Save Picker to {picker_path}')
|
print(f"Save Picker to {picker_path}")
|
||||||
picker_path.write_text(json.dumps(picker_data))
|
picker_path.write_text(json.dumps(picker_data))
|
||||||
|
|
||||||
bpy.ops.rigpicker.reload_picker()
|
bpy.ops.rigpicker.reload_picker()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
RP_OT_create_shape,
|
RP_OT_create_shape,
|
||||||
RP_OT_name_from_bone,
|
RP_OT_name_from_bone,
|
||||||
RP_OT_mirror_shape,
|
RP_OT_mirror_shape,
|
||||||
RP_OT_select_shape_type,
|
RP_OT_select_shape_type,
|
||||||
RP_OT_save_picker
|
RP_OT_save_picker,
|
||||||
)
|
)
|
||||||
|
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.props import (EnumProperty, StringProperty, PointerProperty, BoolProperty,
|
from bpy.props import (
|
||||||
CollectionProperty, IntProperty)
|
EnumProperty,
|
||||||
|
StringProperty,
|
||||||
|
PointerProperty,
|
||||||
|
BoolProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
IntProperty,
|
||||||
|
)
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
'''
|
"""
|
||||||
def get_operator_items(self,context):
|
def get_operator_items(self,context):
|
||||||
items = []
|
items = []
|
||||||
from . import func_picker as mod
|
from . import func_picker as mod
|
||||||
@@ -14,55 +20,58 @@ def get_operator_items(self,context):
|
|||||||
items.append((name,name,""))
|
items.append((name,name,""))
|
||||||
|
|
||||||
return items
|
return items
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def bones_item(self,context):
|
|
||||||
|
def bones_item(self, context):
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
if context.scene.rig_picker.rig:
|
if context.scene.rig_picker.rig:
|
||||||
for bone in context.scene.rig_picker.rig.pose.bones:
|
for bone in context.scene.rig_picker.rig.pose.bones:
|
||||||
items.append((bone.name,bone.name,''))
|
items.append((bone.name, bone.name, ""))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_picker_source(bpy.types.PropertyGroup):
|
class RP_PG_picker_source(bpy.types.PropertyGroup):
|
||||||
source : StringProperty(subtype='FILE_PATH')
|
source: StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_armature_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_armature_ui_settings(bpy.types.PropertyGroup):
|
||||||
name: StringProperty()
|
name: StringProperty()
|
||||||
source: StringProperty(subtype='FILE_PATH')
|
source: StringProperty(subtype="FILE_PATH")
|
||||||
sources: CollectionProperty(type=RP_PG_picker_source)
|
sources: CollectionProperty(type=RP_PG_picker_source)
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
||||||
shape_type: EnumProperty(items=[(i.upper(), i, "") for i in ('Bone', 'Display', 'Operator')])
|
shape_type: EnumProperty(
|
||||||
#idname: StringProperty()
|
items=[(i.upper(), i, "") for i in ("Bone", "Display", "Operator")]
|
||||||
#arguments: StringProperty()
|
)
|
||||||
|
# idname: StringProperty()
|
||||||
|
# arguments: StringProperty()
|
||||||
operator: StringProperty()
|
operator: StringProperty()
|
||||||
shortcut: StringProperty()
|
shortcut: StringProperty()
|
||||||
name: StringProperty()
|
name: StringProperty()
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_collection_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_collection_ui_settings(bpy.types.PropertyGroup):
|
||||||
enabled : BoolProperty(default=False)
|
enabled: BoolProperty(default=False)
|
||||||
rig: PointerProperty(type=bpy.types.Object)
|
rig: PointerProperty(type=bpy.types.Object)
|
||||||
canvas: PointerProperty(type=bpy.types.Object)
|
canvas: PointerProperty(type=bpy.types.Object)
|
||||||
symmetry: PointerProperty(type=bpy.types.Object)
|
symmetry: PointerProperty(type=bpy.types.Object)
|
||||||
destination: StringProperty(subtype='FILE_PATH')
|
destination: StringProperty(subtype="FILE_PATH")
|
||||||
use_pick_bone : BoolProperty(default=False)
|
use_pick_bone: BoolProperty(default=False)
|
||||||
link_shape : BoolProperty(default=False)
|
link_shape: BoolProperty(default=False)
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
|
||||||
use_pick_bone : BoolProperty(default=False)
|
use_pick_bone: BoolProperty(default=False)
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_operator_selector(bpy.types.Operator):
|
class RP_OT_operator_selector(bpy.types.Operator):
|
||||||
bl_label = "Select function"
|
bl_label = "Select function"
|
||||||
bl_idname = "rigpicker.operator_selector"
|
bl_idname = "rigpicker.operator_selector"
|
||||||
bl_property = "idname"
|
bl_property = "idname"
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
# bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
idname: EnumProperty(items=[])
|
idname: EnumProperty(items=[])
|
||||||
|
|
||||||
@@ -70,12 +79,12 @@ class RP_OT_operator_selector(bpy.types.Operator):
|
|||||||
ob = context.object
|
ob = context.object
|
||||||
ob.rig_picker.idname = self.idname
|
ob.rig_picker.idname = self.idname
|
||||||
context.area.tag_redraw()
|
context.area.tag_redraw()
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
wm = context.window_manager
|
wm = context.window_manager
|
||||||
wm.invoke_search_popup(self)
|
wm.invoke_search_popup(self)
|
||||||
return {'FINISHED'}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
@@ -87,15 +96,23 @@ classes = (
|
|||||||
RP_OT_operator_selector,
|
RP_OT_operator_selector,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings)
|
bpy.types.Armature.rig_picker = bpy.props.PointerProperty(
|
||||||
bpy.types.Object.rig_picker = bpy.props.PointerProperty(type=RP_PG_object_ui_settings)
|
type=RP_PG_armature_ui_settings
|
||||||
bpy.types.Collection.rig_picker = bpy.props.PointerProperty(type=RP_PG_collection_ui_settings)
|
)
|
||||||
|
bpy.types.Object.rig_picker = bpy.props.PointerProperty(
|
||||||
|
type=RP_PG_object_ui_settings
|
||||||
|
)
|
||||||
|
bpy.types.Collection.rig_picker = bpy.props.PointerProperty(
|
||||||
|
type=RP_PG_collection_ui_settings
|
||||||
|
)
|
||||||
bpy.types.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings)
|
bpy.types.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Scene.rig_picker
|
del bpy.types.Scene.rig_picker
|
||||||
del bpy.types.Collection.rig_picker
|
del bpy.types.Collection.rig_picker
|
||||||
@@ -104,4 +121,3 @@ def unregister():
|
|||||||
|
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[project]
|
||||||
|
name = "rig-picker"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"black>=25.11.0",
|
||||||
|
"fake-bpy-module>=20251003",
|
||||||
|
]
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
flat in vec2 startPos;
|
flat in vec2 startPos;
|
||||||
in vec2 vertPos;
|
in vec2 vertPos;
|
||||||
|
|
||||||
out vec4 fragColor;
|
// out vec4 fragColor;
|
||||||
|
|
||||||
uniform vec4 color;
|
// uniform vec4 color;
|
||||||
uniform float dashSize;
|
// uniform float dashSize;
|
||||||
uniform float gapSize;
|
// uniform float gapSize;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
@@ -16,4 +16,4 @@ void main()
|
|||||||
if (fract(dist / (dashSize + gapSize)) > dashSize/(dashSize + gapSize))
|
if (fract(dist / (dashSize + gapSize)) > dashSize/(dashSize + gapSize))
|
||||||
discard;
|
discard;
|
||||||
fragColor = color;
|
fragColor = color;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
layout (location = 0) in vec2 pos;
|
// layout (location = 0) in vec2 pos;
|
||||||
|
|
||||||
flat out vec2 startPos;
|
flat out vec2 startPos;
|
||||||
out vec2 vertPos;
|
out vec2 vertPos;
|
||||||
|
|
||||||
uniform mat4 viewMatrix;
|
// uniform mat4 viewMatrix;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
@@ -12,4 +12,4 @@ void main()
|
|||||||
gl_Position = outPos;
|
gl_Position = outPos;
|
||||||
vertPos = pos.xy / outPos.w;
|
vertPos = pos.xy / outPos.w;
|
||||||
startPos = vertPos;
|
startPos = vertPos;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.types import UIList
|
from bpy.types import UIList
|
||||||
#import collections
|
|
||||||
#import inspect
|
# import collections
|
||||||
|
# import inspect
|
||||||
from .core.addon_utils import get_operator_from_id, get_picker_collection
|
from .core.addon_utils import get_operator_from_id, get_picker_collection
|
||||||
from .core.bl_utils import get_mat
|
from .core.bl_utils import get_mat
|
||||||
|
|
||||||
@@ -20,17 +21,19 @@ import re
|
|||||||
|
|
||||||
|
|
||||||
class RP_PT_picker_maker_panel(bpy.types.Panel):
|
class RP_PT_picker_maker_panel(bpy.types.Panel):
|
||||||
bl_label = 'Rig Picker'
|
bl_label = "Rig Picker"
|
||||||
bl_category = 'Rigging'
|
bl_category = "Rigging"
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = "VIEW_3D"
|
||||||
bl_region_type = 'UI'
|
bl_region_type = "UI"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.object
|
return context.object
|
||||||
|
|
||||||
def draw_header_preset(self, context):
|
def draw_header_preset(self, context):
|
||||||
self.layout.operator('rigpicker.add_picker_collection', text="", icon='ADD', emboss=False)
|
self.layout.operator(
|
||||||
|
"rigpicker.add_picker_collection", text="", icon="ADD", emboss=False
|
||||||
|
)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
ob = context.object
|
ob = context.object
|
||||||
@@ -41,11 +44,15 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
|
|||||||
col = layout.column(align=False)
|
col = layout.column(align=False)
|
||||||
|
|
||||||
if collection:
|
if collection:
|
||||||
col.prop_search(collection.rig_picker, 'rig', scn, 'objects', text='Rig ')
|
col.prop_search(collection.rig_picker, "rig", scn, "objects", text="Rig ")
|
||||||
col.prop_search(collection.rig_picker, 'canvas', scn, 'objects', text='Canvas ')
|
col.prop_search(
|
||||||
col.prop_search(collection.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ')
|
collection.rig_picker, "canvas", scn, "objects", text="Canvas "
|
||||||
|
)
|
||||||
|
col.prop_search(
|
||||||
|
collection.rig_picker, "symmetry", scn, "objects", text="Symmetry "
|
||||||
|
)
|
||||||
|
|
||||||
if ob.type == 'ARMATURE':
|
if ob.type == "ARMATURE":
|
||||||
box = col.box()
|
box = col.box()
|
||||||
box.enabled = not ob.data.library
|
box.enabled = not ob.data.library
|
||||||
col = box.column(align=False)
|
col = box.column(align=False)
|
||||||
@@ -54,27 +61,41 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
|
|||||||
row.separator(factor=0.5)
|
row.separator(factor=0.5)
|
||||||
row.label(text="Sources")
|
row.label(text="Sources")
|
||||||
|
|
||||||
is_packed = ('pickers' in ob.data.rig_picker.keys())
|
is_packed = "pickers" in ob.data.rig_picker.keys()
|
||||||
row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='', emboss=False)
|
row.operator(
|
||||||
row.operator("rigpicker.add_picker_source", icon ='ADD', text="", emboss=False)
|
"rigpicker.pack_picker",
|
||||||
|
icon="PACKAGE" if is_packed else "UGLYPACKAGE",
|
||||||
|
text="",
|
||||||
|
emboss=False,
|
||||||
|
)
|
||||||
|
row.operator(
|
||||||
|
"rigpicker.add_picker_source", icon="ADD", text="", emboss=False
|
||||||
|
)
|
||||||
|
|
||||||
for i, item in enumerate(ob.data.rig_picker.sources):
|
for i, item in enumerate(ob.data.rig_picker.sources):
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.enabled = not is_packed
|
row.enabled = not is_packed
|
||||||
row.prop(item, 'source', text='')
|
row.prop(item, "source", text="")
|
||||||
row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i
|
row.operator(
|
||||||
|
"rigpicker.remove_picker_source",
|
||||||
|
icon="PANEL_CLOSE",
|
||||||
|
text="",
|
||||||
|
emboss=False,
|
||||||
|
).index = i
|
||||||
|
|
||||||
if collection:
|
if collection:
|
||||||
#layout.separator()
|
# layout.separator()
|
||||||
layout.prop(collection.rig_picker, 'destination', text='Filepath')
|
layout.prop(collection.rig_picker, "destination", text="Filepath")
|
||||||
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
layout.operator(
|
||||||
|
"rigpicker.save_picker", icon="PASTEDOWN", text="Save Picker"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RP_PT_shape(bpy.types.Panel):
|
class RP_PT_shape(bpy.types.Panel):
|
||||||
bl_label = 'Shape'
|
bl_label = "Shape"
|
||||||
bl_category = 'Rigging'
|
bl_category = "Rigging"
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = "VIEW_3D"
|
||||||
bl_region_type = 'UI'
|
bl_region_type = "UI"
|
||||||
bl_parent_id = "RP_PT_picker_maker_panel"
|
bl_parent_id = "RP_PT_picker_maker_panel"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@@ -86,63 +107,92 @@ class RP_PT_shape(bpy.types.Panel):
|
|||||||
col = layout.column(align=False)
|
col = layout.column(align=False)
|
||||||
|
|
||||||
if collection:
|
if collection:
|
||||||
#if context.collection and context.collection.rig_picker.enabled:
|
# if context.collection and context.collection.rig_picker.enabled:
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
|
row.operator(
|
||||||
row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror Shape')
|
"rigpicker.create_shape", icon="MESH_DATA", text="Create Shape"
|
||||||
col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto Bone Assign')
|
)
|
||||||
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name From Bones')
|
row.operator(
|
||||||
|
"rigpicker.mirror_shape", icon="MOD_MIRROR", text="Mirror Shape"
|
||||||
|
)
|
||||||
|
col.prop(
|
||||||
|
scn.rig_picker,
|
||||||
|
"use_pick_bone",
|
||||||
|
icon="EYEDROPPER",
|
||||||
|
text="Auto Bone Assign",
|
||||||
|
)
|
||||||
|
col.operator(
|
||||||
|
"rigpicker.name_from_bone", icon="SORTALPHA", text="Name From Bones"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
|
row.operator(
|
||||||
|
"rigpicker.create_shape", icon="MESH_DATA", text="Create Shape"
|
||||||
|
)
|
||||||
|
|
||||||
if ob.type != 'ARMATURE':
|
if ob.type != "ARMATURE":
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
col = box.column(align=False)
|
col = box.column(align=False)
|
||||||
|
|
||||||
material_row = col.row(align=True)
|
material_row = col.row(align=True)
|
||||||
material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='')
|
material_row.operator("rigpicker.remove_mat", icon="REMOVE", text="")
|
||||||
material_row.operator('rigpicker.add_mat', icon='ADD', text='')
|
material_row.operator("rigpicker.add_mat", icon="ADD", text="")
|
||||||
mat = False
|
mat = False
|
||||||
if ob.type in ('MESH', 'CURVE', 'FONT') and ob.data.materials:
|
if ob.type in ("MESH", "CURVE", "FONT") and ob.data.materials:
|
||||||
mat = get_mat(ob)
|
mat = get_mat(ob)
|
||||||
if mat and mat.node_tree:
|
if mat and mat.node_tree:
|
||||||
emission_nodes = [n for n in mat.node_tree.nodes if n.type =='EMISSION']
|
emission_nodes = [
|
||||||
|
n for n in mat.node_tree.nodes if n.type == "EMISSION"
|
||||||
|
]
|
||||||
if emission_nodes:
|
if emission_nodes:
|
||||||
material_row.prop(emission_nodes[0].inputs[0], 'default_value', text='')
|
material_row.prop(
|
||||||
|
emission_nodes[0].inputs[0], "default_value", text=""
|
||||||
|
)
|
||||||
mat = True
|
mat = True
|
||||||
if not mat:
|
if not mat:
|
||||||
material_row.label(text='No Material')
|
material_row.label(text="No Material")
|
||||||
material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='')
|
material_row.operator(
|
||||||
|
"rigpicker.eyedropper_mat", icon="EYEDROPPER", text=""
|
||||||
|
)
|
||||||
|
|
||||||
shape_type_row = col.row(align=True)
|
shape_type_row = col.row(align=True)
|
||||||
shape_type_row.prop(ob.rig_picker,'shape_type',expand = True)
|
shape_type_row.prop(ob.rig_picker, "shape_type", expand=True)
|
||||||
shape_type_row.operator('rigpicker.select_shape_type', text='', icon='RESTRICT_SELECT_OFF')
|
shape_type_row.operator(
|
||||||
|
"rigpicker.select_shape_type", text="", icon="RESTRICT_SELECT_OFF"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ob.rig_picker.shape_type == "OPERATOR":
|
||||||
|
|
||||||
if ob.rig_picker.shape_type == 'OPERATOR':
|
|
||||||
|
|
||||||
op_row = col.row(align=True)
|
op_row = col.row(align=True)
|
||||||
op_row.prop(ob.rig_picker, 'operator', text='')
|
op_row.prop(ob.rig_picker, "operator", text="")
|
||||||
op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU')
|
op_row.operator(
|
||||||
|
"rigpicker.operator_selector", text="", icon="COLLAPSEMENU"
|
||||||
|
)
|
||||||
|
|
||||||
if ob.rig_picker.operator:
|
if ob.rig_picker.operator:
|
||||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
col.prop(ob.rig_picker, "name", text="Tooltip")
|
||||||
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
col.prop(ob.rig_picker, "shortcut", text="Shortcut")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
col.prop(ob.rig_picker, "name", text="Tooltip")
|
||||||
|
|
||||||
elif ob.rig_picker.shape_type == 'BONE':
|
elif ob.rig_picker.shape_type == "BONE":
|
||||||
if collection and collection.rig_picker.rig:
|
if collection and collection.rig_picker.rig:
|
||||||
col.prop_search(ob.rig_picker, 'name', collection.rig_picker.rig.pose, 'bones', text='Bone')
|
col.prop_search(
|
||||||
|
ob.rig_picker,
|
||||||
|
"name",
|
||||||
|
collection.rig_picker.rig.pose,
|
||||||
|
"bones",
|
||||||
|
text="Bone",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
#RP_UL_picker_source,
|
# RP_UL_picker_source,
|
||||||
RP_PT_picker_maker_panel,
|
RP_PT_picker_maker_panel,
|
||||||
RP_PT_shape
|
RP_PT_shape,
|
||||||
)
|
)
|
||||||
|
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "black"
|
||||||
|
version = "25.11.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "mypy-extensions" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pathspec" },
|
||||||
|
{ name = "platformdirs" },
|
||||||
|
{ name = "pytokens" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/8c/ad/33adf4708633d047950ff2dfdea2e215d84ac50ef95aff14a614e4b6e9b2/black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08", size = 655669, upload-time = "2025-11-10T01:53:50.558Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/12/5c35e600b515f35ffd737da7febdb2ab66bb8c24d88560d5e3ef3d28c3fd/black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac", size = 1772831, upload-time = "2025-11-10T02:03:47Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/75/b3896bec5a2bb9ed2f989a970ea40e7062f8936f95425879bbe162746fe5/black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96", size = 1608520, upload-time = "2025-11-10T01:58:46.895Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f3/b5/2bfc18330eddbcfb5aab8d2d720663cd410f51b2ed01375f5be3751595b0/black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd", size = 1682719, upload-time = "2025-11-10T01:56:55.24Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/fb/f7dc2793a22cdf74a72114b5ed77fe3349a2e09ef34565857a2f917abdf2/black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409", size = 1362684, upload-time = "2025-11-10T01:57:07.639Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/47/3378d6a2ddefe18553d1115e36aea98f4a90de53b6a3017ed861ba1bd3bc/black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b", size = 1772446, upload-time = "2025-11-10T02:02:16.181Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/4b/0f00bfb3d1f7e05e25bfc7c363f54dc523bb6ba502f98f4ad3acf01ab2e4/black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd", size = 1607983, upload-time = "2025-11-10T02:02:52.502Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/fe/49b0768f8c9ae57eb74cc10a1f87b4c70453551d8ad498959721cc345cb7/black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993", size = 1682481, upload-time = "2025-11-10T01:57:12.35Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/55/17/7e10ff1267bfa950cc16f0a411d457cdff79678fbb77a6c73b73a5317904/black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c", size = 1363869, upload-time = "2025-11-10T01:58:24.608Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/c0/cc865ce594d09e4cd4dfca5e11994ebb51604328489f3ca3ae7bb38a7db5/black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170", size = 1771358, upload-time = "2025-11-10T02:03:33.331Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/77/4297114d9e2fd2fc8ab0ab87192643cd49409eb059e2940391e7d2340e57/black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545", size = 1612902, upload-time = "2025-11-10T01:59:33.382Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/63/d45ef97ada84111e330b2b2d45e1dd163e90bd116f00ac55927fb6bf8adb/black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda", size = 1680571, upload-time = "2025-11-10T01:57:04.239Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/4b/5604710d61cdff613584028b4cb4607e56e148801ed9b38ee7970799dab6/black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664", size = 1382599, upload-time = "2025-11-10T01:57:57.427Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click"
|
||||||
|
version = "8.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fake-bpy-module"
|
||||||
|
version = "20251003"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3a/d7/8e34a795715d18497a257929ace8776a94d6c2c744e36b61e75567cbda64/fake_bpy_module-20251003.tar.gz", hash = "sha256:43d7fd082efc34497e238de3ad4d31fb850f2d1a8bb07418ae36eae56c1c7434", size = 973644, upload-time = "2025-10-03T06:19:57.638Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c6/24/d318f51ae1ebe953038984ed26fa0f9cd5466ef83da30c317282645695ec/fake_bpy_module-20251003-py3-none-any.whl", hash = "sha256:424f75fee6f300fc6d8e0d39abb28dbccd8777f611afa47ecdb9a03c00f2f43f", size = 1103011, upload-time = "2025-10-03T06:19:55.524Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy-extensions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "25.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathspec"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "4.5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytokens"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rig-picker"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
|
||||||
|
[package.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "black" },
|
||||||
|
{ name = "fake-bpy-module" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
|
||||||
|
[package.metadata.requires-dev]
|
||||||
|
dev = [
|
||||||
|
{ name = "black", specifier = ">=25.11.0" },
|
||||||
|
{ name = "fake-bpy-module", specifier = ">=20251003" },
|
||||||
|
]
|
||||||