Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d09823321 | ||
|
|
eb5843063e | ||
|
|
c7df358fe8 | ||
|
|
21c4fe2117 | ||
|
|
f35fbb95b8 | ||
|
|
30064faf3d | ||
|
|
f4f8ee1613 | ||
|
|
e6c9dde63c | ||
|
|
e1abb88b7e | ||
|
|
747cfa0f7a | ||
|
|
fbdd994d67 | ||
|
|
7d2216bc3b | ||
|
|
b4defc1296 | ||
|
|
6798053389 | ||
|
|
de3b78e741 | ||
|
|
76ea717df8 | ||
|
|
7d2d69ba0f | ||
|
|
75bc414e26 | ||
|
|
150bae5920 | ||
|
|
917531f59a | ||
|
|
c31a73f80f | ||
|
|
b855e8ac37 | ||
|
|
7627b76d64 |
@@ -2,6 +2,8 @@
|
|||||||
> Blender addon for picking rig contollers
|
> Blender addon for picking rig contollers
|
||||||
|
|
||||||
Rig_picker is an OpenGl tool for having a 2d interface for the 3d animators allowing them to pick a controller easily.
|
Rig_picker is an OpenGl tool for having a 2d interface for the 3d animators allowing them to pick a controller easily.
|
||||||
|
The addon is drawing 2d shapes inside a dedicated Node Editor Area using the gpu module.
|
||||||
|
You can use multiple pickers for one rig, each picker shapes are in there own collection.
|
||||||
|
|
||||||
Video of the previous version : https://vimeo.com/241970235
|
Video of the previous version : https://vimeo.com/241970235
|
||||||
|
|
||||||
@@ -13,17 +15,26 @@ 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:
|
||||||
|
```sh
|
||||||
|
git clone https://git.autourdeminuit.com/autour_de_minuit/rig_picker.git
|
||||||
|
```
|
||||||
|
|
||||||
|
For Internal user:
|
||||||
1. Create your own local directory in
|
1. Create your own local directory in
|
||||||
```sh
|
```sh
|
||||||
/home/<USER>/dev
|
/home/<USER>/dev
|
||||||
@@ -32,10 +43,192 @@ Video of the previous version : https://vimeo.com/241970235
|
|||||||
```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
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import importlib
|
|||||||
modules = (
|
modules = (
|
||||||
'.operators',
|
'.operators',
|
||||||
'.properties',
|
'.properties',
|
||||||
'.panels',
|
'.ui',
|
||||||
'.area',
|
'.area',
|
||||||
'.gizmo',
|
'.gizmo',
|
||||||
'.draw_handlers'
|
'.draw_handlers'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header
|
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header, Menu
|
||||||
|
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.
|
||||||
@@ -13,36 +14,116 @@ class RigPickerTree(NodeTree):
|
|||||||
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
||||||
|
|
||||||
|
|
||||||
|
class RP_MT_picker(Menu):
|
||||||
|
"""Picker"""
|
||||||
|
bl_label = "Picker"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
scn = context.scene
|
||||||
|
|
||||||
|
|
||||||
|
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH')
|
||||||
|
row = layout.row(align=True)
|
||||||
|
|
||||||
|
# Has at least one picker collection in the scene
|
||||||
|
if not [c.rig_picker.enabled for c in scn.collection.children_recursive]:
|
||||||
|
row.enabled = False
|
||||||
|
row.prop(scn.rig_picker, 'use_pick_bone', text='Auto Bone Assign')
|
||||||
|
|
||||||
|
|
||||||
|
class RP_MT_animation(Menu):
|
||||||
|
"""Picker"""
|
||||||
|
bl_label = "Animation"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.operator("rigpicker.toogle_bone_layer", icon="MOUSE_LMB_DRAG")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
op = row.operator("node.context_menu_picker", icon="MOUSE_RMB")
|
||||||
|
if not context.active_pose_bone:
|
||||||
|
row.enabled = False
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Select All')
|
||||||
|
op.operator = "pose.select_all"
|
||||||
|
op.arguments = '{"action": "SELECT"}'
|
||||||
|
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Select All')
|
||||||
|
op.operator = "pose.select_all"
|
||||||
|
op.arguments = '{"action": "DESELECT"}'
|
||||||
|
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Frame Selected')
|
||||||
|
op.operator = "view3d.view_selected"
|
||||||
|
op.view_3d = True
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.operator("rigpicker.call_operator", text='Insert Keyframe').operator="animtoolbox.insert_keyframe"
|
||||||
|
layout.operator("anim.keyframe_delete_v3d", text='Delete Keyframe')
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Move')
|
||||||
|
op.operator="transform.translate"
|
||||||
|
op.invoke = True
|
||||||
|
op.view_3d = True
|
||||||
|
|
||||||
|
layout.operator("node.picker_transform", text='Rotate').mode='ROTATE'
|
||||||
|
layout.operator("node.picker_transform", text='Scale').mode='SCALE'
|
||||||
|
layout.separator()
|
||||||
|
layout.operator("rigpicker.call_operator", text='Reset Bone').operator="animtoolbox.reset_bone"
|
||||||
|
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 context.space_data.tree_type == 'RigPickerTree':
|
if not context.space_data.tree_type == 'RigPickerTree':
|
||||||
layout = self.layout
|
|
||||||
layout.template_header()
|
|
||||||
#layout.separator_spacer()
|
|
||||||
|
|
||||||
if not context.space_data.node_tree:
|
|
||||||
ntree = bpy.data.node_groups.get('.rig_picker')
|
|
||||||
if not ntree:
|
|
||||||
ntree = bpy.data.node_groups.new('.rig_picker', 'RigPickerTree')
|
|
||||||
|
|
||||||
context.space_data.node_tree = ntree
|
|
||||||
|
|
||||||
#layout.template_ID(context.space_data, "node_tree", new="node.new_node_tree")
|
|
||||||
#layout.separator_spacer()
|
|
||||||
|
|
||||||
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH', text='')
|
|
||||||
|
|
||||||
#layout.prop('rigpicker.reload_picker', icon='FILE_REFRESH', text='')
|
|
||||||
|
|
||||||
layout.separator_spacer()
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._draw(context)
|
self._draw(context)
|
||||||
|
return
|
||||||
|
|
||||||
|
scn = context.scene
|
||||||
|
|
||||||
|
layout = self.layout
|
||||||
|
layout.template_header()
|
||||||
|
|
||||||
|
if not context.space_data.node_tree:
|
||||||
|
ntree = bpy.data.node_groups.get('.rig_picker')
|
||||||
|
if not ntree:
|
||||||
|
ntree = bpy.data.node_groups.new('.rig_picker', 'RigPickerTree')
|
||||||
|
|
||||||
|
context.space_data.node_tree = ntree
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.menu("RP_MT_picker")
|
||||||
|
row.menu("RP_MT_animation")
|
||||||
|
|
||||||
|
ob = context.object
|
||||||
|
if not ob or not ob.type == 'ARMATURE':
|
||||||
|
return
|
||||||
|
|
||||||
|
picker_group = PICKERS.get(ob)
|
||||||
|
if not picker_group:
|
||||||
|
return
|
||||||
|
|
||||||
|
if scn.rig_picker.use_pick_bone:
|
||||||
|
layout.alert = True
|
||||||
|
layout.label(text='Auto Bone Assign')
|
||||||
|
layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False)
|
||||||
|
|
||||||
|
layout.separator_spacer()
|
||||||
|
row = layout.row()
|
||||||
|
row.enabled = False
|
||||||
|
row.label(text=ob.name)
|
||||||
|
|
||||||
|
layout.separator_spacer()
|
||||||
|
row = layout.row(align=True)
|
||||||
|
|
||||||
|
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='', 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
|
||||||
@@ -68,10 +149,13 @@ def poll(cls, context):
|
|||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
RigPickerTree,
|
RigPickerTree,
|
||||||
|
RP_MT_picker,
|
||||||
|
RP_MT_animation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|||||||
|
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,2 +1,29 @@
|
|||||||
|
import gpu
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
PICKERS = {}
|
class LazyDict(dict):
|
||||||
|
def __getitem__(self, k):
|
||||||
|
v = super().__getitem__(k)
|
||||||
|
if callable(v):
|
||||||
|
v = v()
|
||||||
|
super().__setitem__(k, v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
def get(self, k, default=None):
|
||||||
|
if k in self:
|
||||||
|
return self.__getitem__(k)
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
PICKERS = {}
|
||||||
|
|
||||||
|
|
||||||
|
MODULE_DIR = Path(__file__).parent
|
||||||
|
SHADER_DIR = MODULE_DIR / 'shaders'
|
||||||
|
|
||||||
|
SHADERS = LazyDict()
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
SHADERS['dashed_line'] = lambda : gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||||
|
|||||||
@@ -1,6 +1,21 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from .bl_utils import get_mat
|
from .bl_utils import get_mat, get_collection_parents
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_collection(ob=None):
|
||||||
|
"""Return the picker collection of an object"""
|
||||||
|
|
||||||
|
if not ob:
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
for col in ob.users_collection:
|
||||||
|
if col.rig_picker.enabled:
|
||||||
|
return col
|
||||||
|
|
||||||
|
if picker_col := next((c for c in get_collection_parents(col) if c.rig_picker.enabled), None):
|
||||||
|
return picker_col
|
||||||
|
|
||||||
|
|
||||||
def is_shape(ob):
|
def is_shape(ob):
|
||||||
scn = bpy.context.scene
|
scn = bpy.context.scene
|
||||||
@@ -15,6 +30,7 @@ def is_shape(ob):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_object_color(ob):
|
def get_object_color(ob):
|
||||||
if not ob.data.materials:
|
if not ob.data.materials:
|
||||||
return
|
return
|
||||||
@@ -29,6 +45,7 @@ def get_object_color(ob):
|
|||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -2,6 +2,34 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
|
def get_collection_parents(col, root=None, cols=None):
|
||||||
|
"""Return all direct collection parents
|
||||||
|
|
||||||
|
Args:
|
||||||
|
col (bpy.types.Collection): collection to get parents from
|
||||||
|
root (bpy.types.Collection, optional): collection to search in (recursive) instead of the scene collection.
|
||||||
|
Defaults to None.
|
||||||
|
cols (_type_, optional): for recursivity, store the parent collections.
|
||||||
|
Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[bpy.types.Collection]: a list of direct parents
|
||||||
|
"""
|
||||||
|
|
||||||
|
if cols is None:
|
||||||
|
cols = []
|
||||||
|
|
||||||
|
if root is None:
|
||||||
|
root = bpy.context.scene.collection
|
||||||
|
|
||||||
|
for sub in root.children:
|
||||||
|
if sub == col:
|
||||||
|
cols.append(root)
|
||||||
|
|
||||||
|
if len(sub.children):
|
||||||
|
cols = get_collection_parents(col, root=sub, cols=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']
|
||||||
@@ -31,40 +59,34 @@ def link_mat_to_object(ob):
|
|||||||
sl.link = 'OBJECT'
|
sl.link = 'OBJECT'
|
||||||
sl.material = m
|
sl.material = m
|
||||||
|
|
||||||
def find_mirror(name):
|
def eval_attr(ob, name):
|
||||||
mirror = None
|
resolved = ob
|
||||||
prop= False
|
for o in name.split("."):
|
||||||
|
resolved = getattr(resolved, o)
|
||||||
|
return resolved
|
||||||
|
|
||||||
if name:
|
def flip_name(name):
|
||||||
|
if not name:
|
||||||
if name.startswith('[')and name.endswith(']'):
|
return
|
||||||
prop = True
|
|
||||||
name= name[:-2][2:]
|
|
||||||
|
|
||||||
match={
|
|
||||||
'R': 'L',
|
|
||||||
'r': 'l',
|
|
||||||
'L': 'R',
|
|
||||||
'l': 'r',
|
|
||||||
}
|
|
||||||
|
|
||||||
separator=['.','_']
|
|
||||||
|
|
||||||
if name.startswith(tuple(match.keys())):
|
|
||||||
if name[1] in separator:
|
|
||||||
mirror = match[name[0]]+name[1:]
|
|
||||||
|
|
||||||
if name.endswith(tuple(match.keys())):
|
|
||||||
if name[-2] in separator:
|
|
||||||
mirror = name[:-1]+match[name[-1]]
|
|
||||||
|
|
||||||
if mirror and prop == True:
|
|
||||||
mirror='["%s"]'%mirror
|
|
||||||
|
|
||||||
return mirror
|
|
||||||
|
|
||||||
|
if name.startswith('[') and name.endswith(']'): #It's a custom property
|
||||||
|
flipped_name = bpy.utils.flip_name(name[:-2][2:])
|
||||||
|
return f'["{flipped_name}"]'
|
||||||
else:
|
else:
|
||||||
return None
|
return bpy.utils.flip_name(name)
|
||||||
|
|
||||||
|
def split_path(path) :
|
||||||
|
try :
|
||||||
|
bone_name = path.split('["')[1].split('"]')[0]
|
||||||
|
except Exception:
|
||||||
|
bone_name = None
|
||||||
|
|
||||||
|
try :
|
||||||
|
prop_name = path.split('["')[2].split('"]')[0]
|
||||||
|
except Exception:
|
||||||
|
prop_name = None
|
||||||
|
|
||||||
|
return bone_name, prop_name
|
||||||
|
|
||||||
def hide_layers(args):
|
def hide_layers(args):
|
||||||
""" """
|
""" """
|
||||||
|
|||||||
@@ -21,15 +21,25 @@ def bound_box_center(ob):
|
|||||||
|
|
||||||
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):
|
||||||
|
x_points = sorted(p[0] for p in points)
|
||||||
|
y_points = sorted(p[1] for p in points)
|
||||||
|
|
||||||
def intersect_rectangles(bound, border): # returns None if rectangles don't intersect
|
return [
|
||||||
dx = min(border[1][0],bound[1][0]) - max(border[0][0],bound[0][0])
|
Vector((x_points[0], y_points[-1])),
|
||||||
dy = min(border[0][1],bound[0][1]) - max(border[2][1],bound[2][1])
|
Vector((x_points[-1], y_points[-1])),
|
||||||
|
Vector((x_points[-1], 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):
|
if (dx>=0) and (dy>=0):
|
||||||
return dx*dy
|
return dx*dy
|
||||||
|
|
||||||
def point_inside_rectangle(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)):
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ 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
|
from mathutils import bvhtree, Vector, Color
|
||||||
from mathutils.geometry import intersect_point_quad_2d, intersect_point_tri_2d, intersect_tri_tri_2d
|
from mathutils.geometry import (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 pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
@@ -16,7 +18,6 @@ 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, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''):
|
||||||
|
|
||||||
@@ -28,47 +29,37 @@ class Shape:
|
|||||||
self.hover = False
|
self.hover = False
|
||||||
self.press = False
|
self.press = False
|
||||||
|
|
||||||
|
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
self.shader.bind()
|
||||||
|
|
||||||
self.shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
#self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
|
||||||
#self.hover_shader = gpu.shader.from_builtin('2D_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.hover_color = [1, 1, 1, 0.1]
|
|
||||||
|
self.color = color
|
||||||
|
self.hover_color = self.brighten_color(self.color)
|
||||||
|
|
||||||
self.tooltip = tooltip
|
self.tooltip = tooltip
|
||||||
self.color = color
|
|
||||||
self.points = points
|
self.points = points
|
||||||
self.polygons = polygons or []
|
self.polygons = polygons or []
|
||||||
self.edges = edges or []
|
self.edges = edges or []
|
||||||
|
|
||||||
#print(points, self.polygons)
|
|
||||||
|
|
||||||
self.p_batch = batch_for_shader(self.shader, 'TRIS', {"pos": self.points}, indices=self.polygons)
|
self.p_batch = batch_for_shader(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.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges)
|
||||||
|
|
||||||
#if polygons:
|
self.rect = bounding_rect(points)
|
||||||
# self.batch = batch_for_shader(self.shader, 'TRIS', {"pos": points}, indices=polygons)
|
|
||||||
#else:
|
|
||||||
#pts = []
|
|
||||||
#for loop in self.edges:
|
|
||||||
# pts += [self.points[i] for i in loop]
|
|
||||||
|
|
||||||
# self.batch = batch_for_shader(self.shader, 'LINES', {"pos": points}, indices=indices)
|
|
||||||
|
|
||||||
points_x = [v[0] for v in points]
|
|
||||||
points_y = [v[1] for v in points]
|
|
||||||
|
|
||||||
self.bound = [
|
|
||||||
(min(points_x), max(points_y)),
|
|
||||||
(max(points_x), max(points_y)),
|
|
||||||
(max(points_x), min(points_y)),
|
|
||||||
(min(points_x), min(points_y))
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self):
|
def color(self):
|
||||||
return self._color
|
return self._color
|
||||||
|
|
||||||
|
def brighten_color(self, color):
|
||||||
|
brighten_color = Color(color[:3])
|
||||||
|
brighten_color.v += 0.05
|
||||||
|
brighten_color.v *= 1.1
|
||||||
|
brighten_color.v = max(brighten_color.v, 0.15)
|
||||||
|
|
||||||
|
return [*brighten_color, color[3]]
|
||||||
|
|
||||||
@color.setter
|
@color.setter
|
||||||
def color(self, color=None):
|
def color(self, color=None):
|
||||||
if not color:
|
if not color:
|
||||||
@@ -88,16 +79,17 @@ class Shape:
|
|||||||
|
|
||||||
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:
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
if self.edges:
|
if self.edges:
|
||||||
self.e_batch.draw(self.shader)
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
def move_event(self, location):
|
def move_event(self, location):
|
||||||
if not intersect_point_quad_2d(location, *self.bound):
|
if not intersect_point_quad_2d(location, *self.rect):
|
||||||
self.hover = False
|
self.hover = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -124,33 +116,9 @@ class BoneShape(Shape):
|
|||||||
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.contour_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
self.select_color = self.brighten_color(self.hover_color)
|
||||||
|
|
||||||
#self.contour_batches = []
|
self.bone_colors = self.get_bone_colors()
|
||||||
|
|
||||||
#self.line_batch =
|
|
||||||
#for loop in edges:
|
|
||||||
# loop_points = [points[i] for i in loop]
|
|
||||||
# batch = batch_for_shader(self.shader, 'LINE_LOOP', {"pos": loop_points})
|
|
||||||
# #self.contour_batches.append(batch)
|
|
||||||
|
|
||||||
theme = bpy.context.preferences.themes['Default']
|
|
||||||
self.bone_colors = {
|
|
||||||
'select': [*theme.view_3d.bone_pose, 1],
|
|
||||||
'normal': [0.05, 0.05, 0.05, 1],
|
|
||||||
'active': [*theme.view_3d.bone_pose_active, 1],
|
|
||||||
'hide': [0.85, 0.85, 0.85, 0.2],
|
|
||||||
}
|
|
||||||
|
|
||||||
if bone and bone.bone_group:
|
|
||||||
normal_color = bone.bone_group.colors.normal.copy()
|
|
||||||
normal_color.s *= 0.75
|
|
||||||
self.bone_colors['normal'] = [*normal_color, 1]
|
|
||||||
self.bone_colors['select'] = [*bone.bone_group.colors.select, 1]
|
|
||||||
self.bone_colors['active'] = [*bone.bone_group.colors.active, 1]
|
|
||||||
self.bone_colors['hide'] = [*normal_color, 0.1]
|
|
||||||
|
|
||||||
#self.color = [i for i in self.color]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def select(self):
|
def select(self):
|
||||||
@@ -172,20 +140,14 @@ class BoneShape(Shape):
|
|||||||
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 [])
|
||||||
bl = [i for i, l in enumerate(self.bone.bone.layers) if l]
|
|
||||||
rl = [i for i, l in enumerate(self.rig.data.layers) if l]
|
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 len(set(bl).intersection(rl))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bone_color(self):
|
def bone_color(self):
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return [0, 0, 0, 1]
|
return [0, 0, 0, 1]
|
||||||
|
|
||||||
bone = self.bone.bone
|
|
||||||
|
|
||||||
bl = bone.layers
|
|
||||||
rl = self.rig.data.layers
|
|
||||||
|
|
||||||
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:
|
||||||
@@ -195,58 +157,110 @@ class BoneShape(Shape):
|
|||||||
else:
|
else:
|
||||||
return self.bone_colors['normal']
|
return self.bone_colors['normal']
|
||||||
|
|
||||||
|
def get_bone_colors(self):
|
||||||
|
|
||||||
|
theme = bpy.context.preferences.themes['Default']
|
||||||
|
bone_colors = {
|
||||||
|
'select': [*theme.view_3d.bone_pose, 1],
|
||||||
|
'normal': [0.05, 0.05, 0.05, 1],
|
||||||
|
'active': [*theme.view_3d.bone_pose_active, 1],
|
||||||
|
'hide': [0.85, 0.85, 0.85, 0.2],
|
||||||
|
}
|
||||||
|
|
||||||
|
if not self.bone:
|
||||||
|
return bone_colors
|
||||||
|
|
||||||
|
if self.bone.color.palette == 'CUSTOM':
|
||||||
|
bone_color = self.bone
|
||||||
|
|
||||||
|
elif self.bone.color.palette == 'DEFAULT':
|
||||||
|
bone_color = self.bone.bone
|
||||||
|
|
||||||
|
normal_color = bone_color.color.custom.normal.copy()
|
||||||
|
normal_color.s *= 0.75
|
||||||
|
|
||||||
|
bone_colors['normal'] = [*normal_color, 1]
|
||||||
|
bone_colors['select'] = [*bone_color.color.custom.select, 1]
|
||||||
|
bone_colors['active'] = [*bone_color.color.custom.active, 1]
|
||||||
|
bone_colors['hide'] = [*normal_color, 0.1]
|
||||||
|
|
||||||
|
return bone_colors
|
||||||
|
|
||||||
|
def draw_hided(self):
|
||||||
|
gpu.state.blend_set('ALPHA')
|
||||||
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
|
line_color = (1, 1, 1, 0.1)
|
||||||
|
color = Color(self.color[:3])
|
||||||
|
if self.hover:
|
||||||
|
color.v += 0.05
|
||||||
|
color.v *= 1.1
|
||||||
|
|
||||||
|
line_color = (1, 1, 1, 0.3)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", (*color[:3], 0.5))
|
||||||
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", line_color)
|
||||||
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
|
gpu.state.blend_set('NONE')
|
||||||
|
|
||||||
|
def draw_zero_scaled(self):
|
||||||
|
gpu.state.blend_set('ALPHA')
|
||||||
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
|
line_color = (*self.bone_colors['normal'][:3], 0.2)
|
||||||
|
color = Color(self.color[:3])
|
||||||
|
if self.hover or self.select:
|
||||||
|
color.v += 0.05
|
||||||
|
color.v *= 1.1
|
||||||
|
line_color = (*self.bone_color[:3], 0.66)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", (*color[:3], 0.5))
|
||||||
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", line_color)
|
||||||
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
if self.hide:
|
if self.hide:
|
||||||
self.shader.uniform_float("color", (*self.color[:3], 0.4))
|
return self.draw_hided()
|
||||||
self.p_batch.draw(self.shader)
|
|
||||||
#elif self.select:
|
|
||||||
# self.shader.uniform_float("color", self.bone_color)
|
|
||||||
# self.p_batch.draw(self.shader)
|
|
||||||
else:
|
|
||||||
super().draw()
|
|
||||||
|
|
||||||
# Overlay the fill slightly with the bone color
|
elif self.bone and self.bone.custom_shape_scale_xyz.length < 0.00001:
|
||||||
#self.shader.uniform_float("color", (*self.bone_color[:3], 0.1))
|
return self.draw_zero_scaled()
|
||||||
#self.p_batch.draw(self.shader)
|
|
||||||
|
|
||||||
|
# Draw Fill
|
||||||
|
color = self.color
|
||||||
if self.select:
|
if self.select:
|
||||||
|
color = self.select_color
|
||||||
|
elif self.hover:
|
||||||
color = self.hover_color
|
color = self.hover_color
|
||||||
|
|
||||||
if self.select or self.hover:
|
self.shader.uniform_float("color", color)
|
||||||
color = self.hover_color
|
|
||||||
|
|
||||||
if self.select and self.hover:
|
|
||||||
color = self.active_color
|
|
||||||
|
|
||||||
self.shader.uniform_float("color", color)
|
|
||||||
self.p_batch.draw(self.shader)
|
|
||||||
|
|
||||||
#Overlay the fill slightly with the bone color
|
|
||||||
self.shader.uniform_float("color", (*self.bone_colors['normal'][:3], 0.1))
|
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
|
# Draw Outline
|
||||||
#self.contour_shader.bind()
|
gpu.state.blend_set('NONE')
|
||||||
|
|
||||||
#print(self.bone_color)
|
|
||||||
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)
|
||||||
|
|
||||||
#if not self.hide:
|
|
||||||
self.shader.uniform_float("color", self.bone_color)
|
self.shader.uniform_float("color", self.bone_color)
|
||||||
#for b in self.contour_batches:
|
|
||||||
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)
|
||||||
gpu.state.blend_set('NONE')
|
|
||||||
|
|
||||||
def assign_bone_event(self):
|
def assign_bone_event(self):
|
||||||
|
|
||||||
#print('assign_bone_event', self)
|
#print('assign_bone_event', self)
|
||||||
|
|
||||||
scn = bpy.context.scene
|
scn = bpy.context.scene
|
||||||
rig = scn.rig_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')
|
||||||
@@ -257,6 +271,9 @@ class BoneShape(Shape):
|
|||||||
print('You need to have an active bone')
|
print('You need to have an active bone')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#print(active_bone, source_object)
|
||||||
|
#print(rig)
|
||||||
|
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'):
|
||||||
@@ -277,11 +294,6 @@ class BoneShape(Shape):
|
|||||||
|
|
||||||
|
|
||||||
def border_select(self, border, mode='SET'):
|
def border_select(self, border, mode='SET'):
|
||||||
'''
|
|
||||||
if ( not any(intersect_point_quad_2d(b, *self.bound) for b in border) and
|
|
||||||
not any(intersect_point_quad_2d(b, *border) for b in self.bound) ):
|
|
||||||
return
|
|
||||||
'''
|
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -289,16 +301,16 @@ class BoneShape(Shape):
|
|||||||
self.bone.bone.select = False
|
self.bone.bone.select = False
|
||||||
return
|
return
|
||||||
|
|
||||||
bound_tri1 = self.bound[0], self.bound[1], self.bound[2]
|
rect_tri1 = self.rect[0], self.rect[1], self.rect[2]
|
||||||
bound_tri2 = self.bound[2], self.bound[3], self.bound[0]
|
rect_tri2 = self.rect[2], self.rect[3], self.rect[0]
|
||||||
|
|
||||||
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, *bound_tri1) and
|
if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and
|
||||||
not intersect_tri_tri_2d(*border_tri1, *bound_tri2) and
|
not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and
|
||||||
not intersect_tri_tri_2d(*border_tri2, *bound_tri1) and
|
not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and
|
||||||
not intersect_tri_tri_2d(*border_tri2, *bound_tri2)):
|
not intersect_tri_tri_2d(*border_tri2, *rect_tri2)):
|
||||||
return
|
return
|
||||||
|
|
||||||
select = True
|
select = True
|
||||||
@@ -385,121 +397,94 @@ class OperatorShape(Shape):
|
|||||||
|
|
||||||
|
|
||||||
class Picker:
|
class Picker:
|
||||||
def __init__(self, rig, shapes):
|
def __init__(self, parent, rig, shapes):
|
||||||
|
self.parent = parent
|
||||||
self.region = bpy.context.region
|
self.region = bpy.context.region
|
||||||
self.rig = rig
|
self.rig = rig
|
||||||
|
|
||||||
|
self.translation = Vector((0, 0))
|
||||||
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('2D_UNIFORM_COLOR')
|
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
|
||||||
self.tooltip_shape = None
|
|
||||||
self.tooltip_mouse = None
|
|
||||||
self.tooltip = ''
|
|
||||||
|
|
||||||
self.timer = None
|
|
||||||
self.mouse = None
|
self.mouse = None
|
||||||
|
|
||||||
for s in shapes:
|
for shape_data in shapes:
|
||||||
if not s['points']:
|
if not shape_data['points']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if s['type'] in ('CANVAS', 'DISPLAY'):
|
if shape_data['type'] in ('CANVAS', 'DISPLAY'):
|
||||||
shape = Shape(
|
shape = Shape(
|
||||||
self,
|
self,
|
||||||
points=s['points'],
|
points=shape_data['points'],
|
||||||
polygons=s['polygons'],
|
polygons=shape_data['polygons'],
|
||||||
edges=s['edges'],
|
edges=shape_data['edges'],
|
||||||
color=s['color']
|
color=shape_data['color']
|
||||||
)
|
)
|
||||||
|
|
||||||
elif s['type'] == 'BONE':
|
elif shape_data['type'] == 'BONE':
|
||||||
bone = rig.pose.bones.get(s['bone'])
|
bone = rig.pose.bones.get(shape_data['bone'])
|
||||||
#if not bone:
|
#if not bone:
|
||||||
# print(f'Bone {s["bone"]} not exist')
|
# print(f'Bone {shape_data["bone"]} not exist')
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
shape = BoneShape(
|
shape = BoneShape(
|
||||||
self,
|
self,
|
||||||
source_name=s['source_name'],
|
source_name=shape_data['source_name'],
|
||||||
points=s['points'],
|
points=shape_data['points'],
|
||||||
polygons=s['polygons'],
|
polygons=shape_data['polygons'],
|
||||||
edges=s['edges'],
|
edges=shape_data['edges'],
|
||||||
bone=bone,
|
bone=bone,
|
||||||
color=s['color']
|
color=shape_data['color']
|
||||||
)
|
)
|
||||||
|
|
||||||
elif s['type'] == 'OPERATOR':
|
elif shape_data['type'] == 'OPERATOR':
|
||||||
shape = OperatorShape(
|
shape = OperatorShape(
|
||||||
self,
|
self,
|
||||||
source_name=s['source_name'],
|
source_name=shape_data['source_name'],
|
||||||
points=s['points'],
|
points=shape_data['points'],
|
||||||
polygons=s['polygons'],
|
polygons=shape_data['polygons'],
|
||||||
operator=s['operator'],
|
operator=shape_data['operator'],
|
||||||
color=s['color'],
|
color=shape_data['color'],
|
||||||
tooltip=s['tooltip'],
|
tooltip=shape_data['tooltip'],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.shapes.append(shape)
|
self.shapes.append(shape)
|
||||||
|
|
||||||
|
self.rect = bounding_rect([p for s in self.shapes for p in s.points])
|
||||||
|
|
||||||
def assign_bone_event(self):
|
def assign_bone_event(self):
|
||||||
for s in self.shapes:
|
for shape in self.shapes:
|
||||||
if s.type=='bone' and s.hover:
|
if shape.type == 'bone' and shape.hover:
|
||||||
s.assign_bone_event()
|
shape.assign_bone_event()
|
||||||
|
|
||||||
bpy.ops.rigpicker.save_picker()
|
bpy.ops.rigpicker.save_picker(index=self.index)
|
||||||
|
|
||||||
def press_event(self, mode='SET'):
|
def press_event(self, mode='SET'):
|
||||||
for s in self.shapes:
|
for shape in self.shapes:
|
||||||
if s.hover:
|
#print(s)
|
||||||
s.press_event(mode)
|
if shape.hover:
|
||||||
|
shape.press_event(mode)
|
||||||
else:
|
else:
|
||||||
s.press = False
|
shape.press = False
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode='SET'):
|
||||||
if mode == 'SET':
|
|
||||||
for b in self.rig.pose.bones:
|
|
||||||
b.bone.select = False
|
|
||||||
|
|
||||||
#bpy.ops.pose.select_all(action='DESELECT')
|
#bpy.ops.pose.select_all(action='DESELECT')
|
||||||
|
|
||||||
#print('PICKER release event', mode)
|
#print('PICKER release event', mode)
|
||||||
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
for s in self.shapes:
|
for shape in self.shapes:
|
||||||
if s.hover:
|
if shape.hover:
|
||||||
s.release_event(mode)
|
shape.release_event(mode)
|
||||||
|
|
||||||
s.press = False
|
shape.press = False
|
||||||
|
|
||||||
#bpy.context.area.tag_redraw()
|
#bpy.context.area.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
def tooltip_event(self):
|
|
||||||
#print('Tooltip Event', self)
|
|
||||||
|
|
||||||
#print(self.hover_shape, self.hover_shape.type)
|
|
||||||
|
|
||||||
if self.hover_shape and self.hover_shape.type != 'display':
|
|
||||||
if self.hover_shape.type == 'bone':
|
|
||||||
self.tooltip = self.hover_shape.bone.name
|
|
||||||
else:
|
|
||||||
self.tooltip = self.hover_shape.tooltip
|
|
||||||
self.tooltip_shape = self.hover_shape
|
|
||||||
else:
|
|
||||||
self.tooltip = ''
|
|
||||||
self.tooltip_shape = None
|
|
||||||
|
|
||||||
self.tooltip_mouse = self.mouse
|
|
||||||
|
|
||||||
self.timer.cancel()
|
|
||||||
|
|
||||||
#print(self.tooltip)
|
|
||||||
|
|
||||||
self.region.tag_redraw()
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
picker.tooltip_event(event='SHOW')
|
picker.tooltip_event(event='SHOW')
|
||||||
region.tag_redraw()
|
region.tag_redraw()
|
||||||
@@ -526,27 +511,27 @@ class Picker:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def border_select(self, border, mode):
|
def border_select(self, border, mode):
|
||||||
border = [bpy.context.region.view2d.region_to_view(*b) for b in border]
|
border = [Vector(b) - Vector(self.translation) for b in border]
|
||||||
|
|
||||||
if mode == 'SET':
|
for shape in self.shapes:
|
||||||
for b in self.rig.pose.bones:
|
shape.press = False
|
||||||
b.bone.select = False
|
shape.hover = False
|
||||||
|
if shape.type != 'bone':
|
||||||
|
continue
|
||||||
|
|
||||||
for s in (s for s in self.shapes if s.type=='bone'):
|
shape.border_select(border, mode)
|
||||||
s.border_select(border, mode)
|
|
||||||
|
|
||||||
def move_event(self, location):
|
def move_event(self, location):
|
||||||
self.mouse = location
|
self.mouse = Vector(location) - Vector(self.translation)
|
||||||
location = self.region.view2d.region_to_view(*location)
|
|
||||||
|
|
||||||
self.hover_shape = None
|
self.hover_shape = None
|
||||||
for shape in reversed(self.shapes):
|
for shape in reversed(self.shapes):
|
||||||
if self.hover_shape:
|
if self.hover_shape:
|
||||||
shape.hover = False
|
shape.hover = False
|
||||||
elif shape.move_event(location):
|
elif shape.move_event(self.mouse):
|
||||||
self.hover_shape = shape
|
self.hover_shape = shape
|
||||||
|
|
||||||
#if point_inside_rectangle(self.end, bound):
|
#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):
|
||||||
@@ -554,57 +539,221 @@ class Picker:
|
|||||||
# 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)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
with gpu.matrix.push_pop():
|
||||||
|
gpu.matrix.translate(self.translation)
|
||||||
|
for shape in self.shapes:
|
||||||
|
shape.draw()
|
||||||
|
|
||||||
|
def under_mouse(self, location):
|
||||||
|
location = Vector(location) - Vector(self.translation)
|
||||||
|
return intersect_point_quad_2d(location, *self.rect)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def center(self):
|
||||||
|
relative_center = (self.rect[1] + self.rect[3]) * 0.5
|
||||||
|
return relative_center + self.translation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def index(self):
|
||||||
|
return self.parent.pickers.index(self)
|
||||||
|
|
||||||
|
|
||||||
|
class PickerGroup:
|
||||||
|
def __init__(self, rig, pickers):
|
||||||
|
|
||||||
|
#self.view_location = Vector((0, 0))
|
||||||
|
self.region = bpy.context.region
|
||||||
|
self.rig = rig
|
||||||
|
self.pickers = []
|
||||||
|
|
||||||
|
self.mouse = Vector((0, 0))
|
||||||
|
self.location = Vector((0, 0))
|
||||||
|
|
||||||
|
self.hover_shape = None
|
||||||
|
|
||||||
|
self.tooltip_shape = None
|
||||||
|
self.tooltip_mouse = Vector((0, 0))
|
||||||
|
self.tooltip = ''
|
||||||
|
|
||||||
|
self.timer = None
|
||||||
|
|
||||||
|
for picker in pickers:
|
||||||
|
self.add_picker(picker)
|
||||||
|
|
||||||
|
def add_picker(self, picker):
|
||||||
|
self.pickers.append(Picker(self, self.rig, picker))
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
y = 0
|
||||||
|
for picker in self.pickers:
|
||||||
|
height = picker.rect[1][1] - picker.rect[-1][1]
|
||||||
|
|
||||||
|
picker.translation = Vector((0, -y-height*0.5))
|
||||||
|
picker.draw()
|
||||||
|
|
||||||
|
y += height + 50
|
||||||
|
|
||||||
|
#break #TODO for now only draw first picker
|
||||||
|
|
||||||
|
def move_event(self, mouse):
|
||||||
|
self.mouse = mouse
|
||||||
|
self.location = self.region.view2d.region_to_view(*mouse)
|
||||||
|
|
||||||
|
# Try to detect view pan to remove tooltip
|
||||||
|
# view_location = self.region.view2d.region_to_view(0, 0)
|
||||||
|
# if view_location != self.view_location:
|
||||||
|
# self.view_location = view_location
|
||||||
|
# self.tooltip = ''
|
||||||
|
# if self.timer:
|
||||||
|
# self.timer.cancel()
|
||||||
|
# return
|
||||||
|
|
||||||
|
hover_shape = None
|
||||||
|
for picker in self.pickers:
|
||||||
|
if not picker.under_mouse(self.location):
|
||||||
|
continue
|
||||||
|
|
||||||
|
picker.move_event(self.location)
|
||||||
|
|
||||||
|
if picker.hover_shape:
|
||||||
|
hover_shape = picker.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()
|
||||||
|
|
||||||
self.timer = threading.Timer(0.5, self.tooltip_event)
|
self.timer = threading.Timer(0.4, self.tooltip_event)
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
|
|
||||||
|
def press_event(self, mode='SET'):
|
||||||
|
#self.clear_tooltip()
|
||||||
|
|
||||||
#bpy.app.timers.register(self.tooltip_event, first_interval=1)
|
for picker in self.pickers:
|
||||||
|
if picker.under_mouse(self.location):
|
||||||
|
picker.press_event(mode)
|
||||||
|
else:
|
||||||
|
for shape in picker.shapes:
|
||||||
|
shape.press = False
|
||||||
|
|
||||||
def draw(self):
|
def release_event(self, mode='SET'):
|
||||||
for s in self.shapes:
|
if mode == 'SET':
|
||||||
s.draw()
|
for bone in self.rig.data.bones:
|
||||||
'''
|
bone.select = False
|
||||||
if self.box_select:
|
|
||||||
|
|
||||||
self.box_shader.uniform_float("color", self.box_select_color)
|
for picker in self.pickers:
|
||||||
batch = batch_for_shader(self.shader, 'LINE_LOOP', {"pos": []})
|
if picker.under_mouse(self.location):
|
||||||
|
picker.release_event(mode)
|
||||||
|
else:
|
||||||
|
for shape in picker.shapes:
|
||||||
|
shape.press = False
|
||||||
|
|
||||||
|
def assign_bone_event(self):
|
||||||
|
for picker in self.pickers:
|
||||||
|
if picker.under_mouse(self.location):
|
||||||
|
picker.assign_bone_event()
|
||||||
|
|
||||||
|
def border_select(self, border, mode):
|
||||||
|
border = [self.region.view2d.region_to_view(*b) for b in border]
|
||||||
|
|
||||||
|
if mode == 'SET':
|
||||||
|
for bone in self.rig.data.bones:
|
||||||
|
bone.select = False
|
||||||
|
|
||||||
|
for picker in self.pickers:
|
||||||
|
picker.border_select(border, mode)
|
||||||
|
|
||||||
|
def clear_tooltip(self):
|
||||||
|
self.tooltip = ''
|
||||||
|
self.tooltip_shape = None
|
||||||
|
self.timer.cancel()
|
||||||
|
self.region.tag_redraw()
|
||||||
|
|
||||||
|
def tooltip_event(self):
|
||||||
|
#print('Tooltip Event', self)
|
||||||
|
|
||||||
|
#print(self.hover_shape, self.hover_shape.type)
|
||||||
|
|
||||||
|
if self.hover_shape and self.hover_shape.type != 'display':
|
||||||
|
if self.hover_shape.type == 'bone' and self.hover_shape.bone:
|
||||||
|
self.tooltip = self.hover_shape.bone.name
|
||||||
|
else:
|
||||||
|
self.tooltip = self.hover_shape.tooltip
|
||||||
|
self.tooltip_shape = self.hover_shape
|
||||||
|
else:
|
||||||
|
return self.clear_tooltip()
|
||||||
|
|
||||||
for b in self.contour_batches:
|
|
||||||
b.draw(self.contour_shader)
|
|
||||||
|
|
||||||
self.batch.draw(self.shader)
|
self.tooltip_mouse = self.mouse
|
||||||
|
|
||||||
|
self.timer.cancel()
|
||||||
gpu.state.blend_set('NONE')
|
|
||||||
'''
|
|
||||||
|
|
||||||
def get_picker_path(rig, start=None):
|
#print(self.tooltip)
|
||||||
picker_path = rig.data.get('rig_picker', {}).get('source')
|
|
||||||
if not picker_path:
|
|
||||||
return
|
|
||||||
|
|
||||||
picker_path = bpy.path.abspath(picker_path, library=rig.data.library, start=start)
|
self.region.tag_redraw()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rect(self):
|
||||||
|
return bounding_rect([co - Vector(p.translation) for p in self.pickers for co in p.rect])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def center(self):
|
||||||
|
center = sum(self.rect, Vector((0, 0))) / len(self.rect)
|
||||||
|
center[1] = -center[1]
|
||||||
|
return center
|
||||||
|
|
||||||
|
|
||||||
|
def load_picker_data(rig):
|
||||||
|
if 'pickers' in rig.data.rig_picker:
|
||||||
|
picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']]
|
||||||
|
else:
|
||||||
|
picker_datas = []
|
||||||
|
|
||||||
|
for picker in rig.data.rig_picker.sources:
|
||||||
|
picker_path = Path(bpy.path.abspath(picker.source, library=rig.data.library))
|
||||||
|
|
||||||
|
if not picker_path.exists():
|
||||||
|
print(f'Picker path not exists: {picker_path.resolve()}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Load picker from', picker_path.resolve())
|
||||||
|
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||||
|
picker_datas.append(picker_data)
|
||||||
|
|
||||||
|
PICKERS[rig] = PickerGroup(rig, picker_datas)
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_path(rig, source, start=None):
|
||||||
|
picker_path = bpy.path.abspath(source, library=rig.data.library, start=start)
|
||||||
return Path(os.path.abspath(picker_path))
|
return Path(os.path.abspath(picker_path))
|
||||||
|
|
||||||
def pack_picker(rig, start=None):
|
|
||||||
picker_path = get_picker_path(rig, start=start)
|
|
||||||
if picker_path and picker_path.exists():
|
|
||||||
if 'rig_picker' not in rig.data.keys():
|
|
||||||
rig.data['rig_picker'] = {}
|
|
||||||
|
|
||||||
rig.data['rig_picker']['picker'] = json.loads(picker_path.read_text())
|
def pack_picker(rig, start=None):
|
||||||
|
if not 'rig_picker' in rig.data:
|
||||||
|
return
|
||||||
|
|
||||||
|
pickers = []
|
||||||
|
for picker_source in rig.data['rig_picker'].get('sources', []):
|
||||||
|
picker_path = get_picker_path(rig, picker_source['source'], start)
|
||||||
|
if not picker_path.exists():
|
||||||
|
print(f'{picker_path} not exists')
|
||||||
|
continue
|
||||||
|
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||||
|
pickers.append(picker_data)
|
||||||
|
|
||||||
|
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 'picker' in rig.data['rig_picker'].keys():
|
if 'pickers' in rig.data['rig_picker'].keys():
|
||||||
del rig.data['rig_picker']['picker']
|
del rig.data['rig_picker']['pickers']
|
||||||
@@ -20,7 +20,7 @@ def border_over_shape(border,verts,loops):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
for point in verts:
|
for point in verts:
|
||||||
if point_inside_rectangle(point,border):
|
if point_inside_rect(point,border):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for point in border:
|
for point in border:
|
||||||
@@ -29,7 +29,7 @@ def border_over_shape(border,verts,loops):
|
|||||||
|
|
||||||
|
|
||||||
def border_loop(vert, loop):
|
def border_loop(vert, loop):
|
||||||
border_edge =[e for e in vert.link_edges if e.is_boundary]
|
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:
|
||||||
@@ -61,99 +61,119 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
|||||||
|
|
||||||
return loops
|
return loops
|
||||||
|
|
||||||
def get_picker_datas(objects, canvas, rig):
|
|
||||||
picker_datas = []
|
|
||||||
gamma = 1 / 2.2
|
|
||||||
|
|
||||||
if canvas.type =='CURVE':
|
def get_shape_data(ob, matrix=None, depsgraph=None):
|
||||||
|
if not matrix:
|
||||||
|
matrix = Matrix()
|
||||||
|
if not depsgraph:
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
|
||||||
|
gamma = 1 / 2.2
|
||||||
|
eval_ob = ob.evaluated_get(depsgraph)
|
||||||
|
mesh = eval_ob.to_mesh()
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(mesh)
|
||||||
|
|
||||||
|
|
||||||
|
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.connect_verts_concave(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]
|
||||||
|
bm.to_mesh(mesh)
|
||||||
|
mesh.update()
|
||||||
|
bm.clear()
|
||||||
|
|
||||||
|
points = []
|
||||||
|
polygons = []
|
||||||
|
depths = []
|
||||||
|
|
||||||
|
for vert in mesh.vertices:
|
||||||
|
co = matrix @ (ob.matrix_world @ Vector(vert.co))
|
||||||
|
points.append([round(co[0], 1), round(co[1], 1)])
|
||||||
|
|
||||||
|
depths += [co[2]]
|
||||||
|
|
||||||
|
if depths:
|
||||||
|
depth = max(depths)
|
||||||
|
else:
|
||||||
|
print(f'{ob.name} has no vertices')
|
||||||
|
depth = 0
|
||||||
|
|
||||||
|
for face in mesh.polygons:
|
||||||
|
polygons.append([v for v in face.vertices])
|
||||||
|
|
||||||
|
color = get_object_color(ob)
|
||||||
|
if color:
|
||||||
|
color = [round(pow(c, gamma), 4) for c in color]
|
||||||
|
else:
|
||||||
|
color = [0.5, 0.5, 0.5]
|
||||||
|
|
||||||
|
shape = {
|
||||||
|
'source_name': ob.name,
|
||||||
|
'tooltip': ob.rig_picker.name,
|
||||||
|
'depth': depth,
|
||||||
|
'points': points,
|
||||||
|
'polygons': polygons,
|
||||||
|
'edges': edges,
|
||||||
|
'color': color,
|
||||||
|
'type': ob.rig_picker.shape_type
|
||||||
|
}
|
||||||
|
|
||||||
|
if shape['type'] =='OPERATOR':
|
||||||
|
shape['operator'] = ob.rig_picker.operator
|
||||||
|
shape['shortcut'] = ob.rig_picker.shortcut
|
||||||
|
|
||||||
|
elif shape['type'] =='BONE':
|
||||||
|
shape['bone'] = ob.rig_picker.name
|
||||||
|
|
||||||
|
eval_ob.to_mesh_clear()
|
||||||
|
|
||||||
|
return shape
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_data(collection):
|
||||||
|
picker_data = []
|
||||||
|
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
canvas = collection.rig_picker.canvas
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
canvas_x = [p[0] for p in canvas_coords]
|
height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords))
|
||||||
canvas_y = [p[1] for p in canvas_coords]
|
width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords))
|
||||||
|
|
||||||
canvas_center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
||||||
canvas_scale_fac = 4096 / (max(canvas_y) - min(canvas_y))# Reference height for the canvas
|
scale = 2048 / max(height, width)# Reference height for the canvas
|
||||||
|
|
||||||
objects.append(canvas)
|
matrix = Matrix.Translation(-center)
|
||||||
|
matrix = Matrix.Scale(scale, 4) @ matrix
|
||||||
dg = bpy.context.evaluated_depsgraph_get()
|
|
||||||
|
|
||||||
#sorted by their z axes
|
#sorted by their z axes
|
||||||
for ob in sorted(objects, key=lambda x: bound_box_center(x)[2]):
|
for ob in collection.all_objects:
|
||||||
|
if ob.instance_collection:
|
||||||
|
for shape in ob.instance_collection.all_objects:
|
||||||
|
picker_data.append(get_shape_data(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))
|
||||||
|
|
||||||
print('Storing shape', ob.name)
|
|
||||||
mesh = bpy.data.meshes.new_from_object(ob.evaluated_get(dg))
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.from_mesh(mesh)
|
|
||||||
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.connect_verts_concave(bm, faces=bm.faces)
|
|
||||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
|
||||||
|
|
||||||
#bm_loops = contour_loops(bm)
|
|
||||||
#loops = [[l.index for l in loop] for loop in bm_loops]
|
|
||||||
loops = []
|
|
||||||
|
|
||||||
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
|
|
||||||
|
|
||||||
bm.to_mesh(mesh)
|
|
||||||
mesh.update()
|
|
||||||
bm.clear()
|
|
||||||
|
|
||||||
points = []
|
|
||||||
#edges = []
|
|
||||||
polygons = []
|
|
||||||
|
|
||||||
for p in mesh.vertices:
|
|
||||||
point = ob.matrix_world@Vector((p.co))
|
|
||||||
point = (point - canvas_center) * canvas_scale_fac
|
|
||||||
points.append([round(point[0]), round(point[1])])
|
|
||||||
|
|
||||||
for f in mesh.polygons:
|
|
||||||
polygons.append([v for v in f.vertices])
|
|
||||||
|
|
||||||
#for e in mesh.edges:
|
|
||||||
#
|
|
||||||
# edges.append([v for v in e.vertices])
|
|
||||||
|
|
||||||
color = get_object_color(ob)
|
|
||||||
if color:
|
|
||||||
color = [round(pow(c, gamma), 4) for c in color]
|
|
||||||
else:
|
else:
|
||||||
color = [0.5, 0.5, 0.5]
|
#print(f'{ob.name} of type {ob.type} not supported')
|
||||||
|
continue
|
||||||
shape = {
|
|
||||||
'source_name': ob.name,
|
|
||||||
'tooltip': ob.rig_picker.name,
|
|
||||||
'points': points,
|
|
||||||
'polygons': polygons,
|
|
||||||
'edges': edges,
|
|
||||||
'loops': loops,
|
|
||||||
'color': color,
|
|
||||||
'type': 'CANVAS' if ob == canvas else ob.rig_picker.shape_type
|
|
||||||
}
|
|
||||||
|
|
||||||
if shape['type'] =='OPERATOR':
|
|
||||||
shape['operator'] = ob.rig_picker.operator
|
|
||||||
#if ob.rig_picker.arguments:
|
|
||||||
#shape['arguments'] = ob.rig_picker.arguments
|
|
||||||
#if ob.rig_picker.shortcut:
|
|
||||||
shape['shortcut'] = ob.rig_picker.shortcut
|
|
||||||
|
|
||||||
elif shape['type'] =='BONE':
|
|
||||||
shape['bone'] = ob.rig_picker.name
|
|
||||||
|
|
||||||
picker_datas.append(shape)
|
|
||||||
|
|
||||||
|
picker_data.sort(key=lambda x : x['depth'])
|
||||||
|
|
||||||
#print(picker_datas)
|
#print(picker_datas)
|
||||||
|
|
||||||
return picker_datas
|
return picker_data
|
||||||
#rig.data.rig_picker['shapes'] = picker_datas
|
#rig.data.rig_picker['shapes'] = picker_datas
|
||||||
|
|||||||
@@ -1,39 +1,63 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import gpu
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
import blf
|
import blf
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .constants import PICKERS
|
from .constants import PICKERS
|
||||||
from .core.picker import Picker
|
from .core.picker import Picker, PickerGroup, load_picker_data
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
def draw_callback_view():
|
def draw_rect_2d(position, width, height, color):
|
||||||
sp = bpy.context.space_data
|
"""
|
||||||
|
Draw a 2d rectangele.
|
||||||
|
|
||||||
if not sp or not sp.tree_type == 'RigPickerTree':
|
:arg position: Position of the lower left corner.
|
||||||
|
:type position: 2D Vector
|
||||||
|
:arg width: Width of the rect.
|
||||||
|
:type width: float
|
||||||
|
:arg height: Height of the rect.
|
||||||
|
:type height: float
|
||||||
|
"""
|
||||||
|
|
||||||
|
coords = ((0, 0), (1, 0), (1, 1), (0, 1))
|
||||||
|
|
||||||
|
shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
batch = batch_for_shader(
|
||||||
|
shader, 'TRI_FAN',
|
||||||
|
{"pos": coords},
|
||||||
|
)
|
||||||
|
|
||||||
|
with gpu.matrix.push_pop():
|
||||||
|
gpu.matrix.translate(position)
|
||||||
|
gpu.matrix.scale((width, height))
|
||||||
|
shader.uniform_float("color", color)
|
||||||
|
|
||||||
|
batch.draw(shader)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_callback_view():
|
||||||
|
space_data = bpy.context.space_data
|
||||||
|
|
||||||
|
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
|
||||||
|
if not space_data.pin:
|
||||||
|
space_data.pin = True
|
||||||
|
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.source:
|
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:
|
||||||
if 'picker' in ob.data.rig_picker:
|
load_picker_data(ob)
|
||||||
picker_datas = [s.to_dict() for s in ob.data.rig_picker['picker']]
|
|
||||||
else:
|
|
||||||
picker_path = Path(bpy.path.abspath(ob.data.rig_picker.source, library=ob.data.library))
|
|
||||||
|
|
||||||
if not picker_path.exists():
|
picker_group = PICKERS[ob]
|
||||||
print(f'Picker path not exists: {picker_path.resolve()}')
|
picker_group.draw()
|
||||||
return
|
|
||||||
|
|
||||||
print('Load picker from', picker_path.resolve())
|
|
||||||
picker_datas = json.loads(picker_path.read_text())
|
|
||||||
#shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']]
|
|
||||||
PICKERS[ob] = Picker(ob, shapes=picker_datas)
|
|
||||||
|
|
||||||
picker = PICKERS.get(ob)
|
|
||||||
picker.draw()
|
|
||||||
|
|
||||||
def draw_callback_px():
|
def draw_callback_px():
|
||||||
sp = bpy.context.space_data
|
sp = bpy.context.space_data
|
||||||
@@ -42,24 +66,37 @@ def draw_callback_px():
|
|||||||
|
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
|
|
||||||
picker = PICKERS.get(ob)
|
picker_group = PICKERS.get(ob)
|
||||||
if not picker or not picker.tooltip:
|
if not picker_group or not picker_group.tooltip:
|
||||||
return
|
return
|
||||||
|
|
||||||
text = picker.tooltip
|
region = bpy.context.region
|
||||||
|
text = picker_group.tooltip
|
||||||
|
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))
|
||||||
|
|
||||||
|
margins = [12, 5]
|
||||||
|
text_size = blf.dimensions(font_id, text)
|
||||||
|
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_size = (text_size[0] + 2*margins[0], text_size[1] + 2*margins[1])
|
||||||
|
|
||||||
|
gpu.state.blend_set('ALPHA')
|
||||||
|
draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.66))
|
||||||
|
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)
|
||||||
#gpu.state.blend_set('ALPHA')
|
|
||||||
|
|
||||||
|
|
||||||
# BLF drawing routine
|
# BLF drawing routine
|
||||||
blf.position(font_id, picker.tooltip_mouse[0]-5, picker.tooltip_mouse[1]+5, 0)
|
blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0)
|
||||||
blf.size(font_id, 14)
|
blf.color(font_id, 0.85, 0.85, 0.85, 1)
|
||||||
blf.color(font_id, 1, 1, 1, 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)
|
||||||
@@ -67,7 +104,6 @@ def draw_callback_px():
|
|||||||
blf.draw(font_id, text)
|
blf.draw(font_id, text)
|
||||||
|
|
||||||
blf.disable(font_id, blf.SHADOW)
|
blf.disable(font_id, blf.SHADOW)
|
||||||
#gpu.state.blend_set('NONE')
|
|
||||||
|
|
||||||
handle_view = None
|
handle_view = None
|
||||||
handle_pixel = None
|
handle_pixel = None
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ class RP_GT_gizmo(Gizmo):
|
|||||||
|
|
||||||
context.region.tag_redraw()
|
context.region.tag_redraw()
|
||||||
|
|
||||||
|
#print(location)
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
|
|
||||||
import bpy
|
|
||||||
from ..core.shape import get_picker_datas
|
|
||||||
from ..core.addon_utils import is_shape
|
|
||||||
from ..core.bl_utils import link_mat_to_object, find_mirror
|
|
||||||
from pathlib import Path
|
|
||||||
import json
|
import json
|
||||||
|
from os.path import abspath
|
||||||
|
from pathlib import Path
|
||||||
|
import bpy
|
||||||
|
from bpy.types import Operator
|
||||||
|
from bpy.props import IntProperty
|
||||||
|
from mathutils import Matrix
|
||||||
|
|
||||||
|
from ..core.shape import get_picker_data
|
||||||
|
from ..core.addon_utils import is_shape, get_picker_collection
|
||||||
|
from ..core.bl_utils import link_mat_to_object, flip_name
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_create_shape(bpy.types.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'}
|
||||||
@@ -15,6 +20,14 @@ class RP_OT_create_shape(bpy.types.Operator):
|
|||||||
@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):
|
||||||
|
return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
for col in bpy.data.collections:
|
||||||
|
if col.rig_picker.enabled:
|
||||||
|
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
|
||||||
@@ -35,8 +48,10 @@ class RP_OT_create_shape(bpy.types.Operator):
|
|||||||
|
|
||||||
mesh.from_pydata(verts, edges, faces)
|
mesh.from_pydata(verts, edges, faces)
|
||||||
|
|
||||||
for c in scn.rig_picker.canvas.users_collection:
|
picker_selected_cols = [col for col in bpy.data.collections if col.rig_picker.link_shape]
|
||||||
c.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
|
||||||
@@ -52,84 +67,82 @@ class RP_OT_create_shape(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_name_from_bone(bpy.types.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_options = {'REGISTER', 'UNDO'}
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self,context):
|
||||||
scene = context.scene
|
|
||||||
rig = scene.rig_picker.rig
|
|
||||||
bone = rig.data.bones.active
|
|
||||||
|
|
||||||
if bone:
|
col = get_picker_collection(context.object)
|
||||||
context.object.name = bone.name
|
|
||||||
context.object.rig_picker.name = bone.name
|
for ob in col.all_objects:
|
||||||
|
if ob.rig_picker.shape_type == 'BONE':
|
||||||
|
ob.name = ob.rig_picker.name
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_mirror_shape(bpy.types.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 = {'REGISTER', 'UNDO'}
|
#bl_options = g
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self,context):
|
||||||
scn = context.scene
|
collection = get_picker_collection(context.object)
|
||||||
|
objects = context.selected_objects
|
||||||
|
|
||||||
for ob in bpy.context.selected_objects:
|
# Remove mirror object:
|
||||||
|
for ob in list(collection.all_objects):
|
||||||
|
if ob in objects:
|
||||||
|
continue
|
||||||
|
|
||||||
name = find_mirror(ob.name)
|
for mod in ob.modifiers:
|
||||||
link_mat_to_object(ob)
|
if (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)
|
||||||
|
|
||||||
if not name:
|
if collection.rig_picker.symmetry:
|
||||||
name = ob.name + '_flip'
|
x_axis = collection.rig_picker.symmetry.matrix_world.to_translation()[0]
|
||||||
|
else:
|
||||||
|
x_axis = 0
|
||||||
|
|
||||||
old_shape = bpy.data.objects.get(name)
|
for ob in objects:
|
||||||
old_mat = None
|
flipped_name = flip_name(ob.name)
|
||||||
if old_shape:
|
if flipped_name == ob.name:
|
||||||
old_mat = next((sl.material for sl in old_shape.material_slots if sl.material), None)
|
suffix = '.L'
|
||||||
bpy.data.objects.remove(old_shape)
|
flipped_suffix = '.R'
|
||||||
|
# Determine side of the object
|
||||||
|
if ob.matrix_world.to_translation()[0] < x_axis:
|
||||||
|
suffix, flipped_suffix = flipped_suffix, suffix
|
||||||
|
|
||||||
|
flipped_name = f'{ob.name}{flipped_suffix}'
|
||||||
|
ob.name = f'{ob.name}{suffix}'
|
||||||
|
|
||||||
mirror_ob = ob.copy()
|
flipped_object = ob.copy()
|
||||||
mirror_ob.data = ob.data
|
flipped_object.name = flipped_name
|
||||||
mirror_ob.name = name
|
flipped_object.parent = None
|
||||||
|
flipped_object.matrix_world = Matrix()
|
||||||
|
for mod in list(flipped_object.modifiers):
|
||||||
|
flipped_object.modifiers.remove(mod)
|
||||||
|
|
||||||
if old_mat:
|
# Add symmetrize modifier TODO add it into a resource
|
||||||
#mirror_ob.data.materials.clear()
|
mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES')
|
||||||
#mirror_ob.data.materials.append(None)
|
mod.node_group = bpy.data.node_groups['Symmetrize']
|
||||||
#mirror_ob.material_slots[0].link = 'OBJECT'
|
mod['Socket_2'] = ob
|
||||||
|
mod['Socket_3'] = collection.rig_picker.symmetry
|
||||||
|
|
||||||
mirror_ob.material_slots[0].material = old_mat
|
for col in ob.users_collection:
|
||||||
#mirror_ob = bpy.data.objects.new(name,ob.data.copy())
|
col.objects.link(flipped_object)
|
||||||
|
|
||||||
for c in ob.users_collection:
|
|
||||||
c.objects.link(mirror_ob)
|
|
||||||
|
|
||||||
if scn.rig_picker.symmetry:
|
|
||||||
symmetry_loc = scn.rig_picker.symmetry.matrix_world.to_translation()[0]
|
|
||||||
else:
|
|
||||||
symmetry_loc = 0
|
|
||||||
|
|
||||||
#print(symmetry_loc)
|
|
||||||
mirror_ob.matrix_world = ob.matrix_world
|
|
||||||
|
|
||||||
#if mirror_ob.location[0] < symmetry_loc:
|
|
||||||
mirror_ob.location.x = symmetry_loc + (symmetry_loc - ob.location.x)
|
|
||||||
#else:
|
|
||||||
# mirror_ob.location[0]= symmetry_loc+ (symmetry_loc- ob.location[0])
|
|
||||||
|
|
||||||
mirror_ob.rotation_euler.y = -ob.rotation_euler.y
|
|
||||||
mirror_ob.rotation_euler.z = -ob.rotation_euler.z
|
|
||||||
|
|
||||||
mirror_ob.scale.x = -ob.scale.x
|
|
||||||
|
|
||||||
for key, value in ob.items():
|
|
||||||
if key not in ['_RNA_UI','cycles']:
|
|
||||||
mirror_ob[key] = value
|
|
||||||
|
|
||||||
if ob.rig_picker.shape_type == 'BONE':
|
if ob.rig_picker.shape_type == 'BONE':
|
||||||
mirror_ob.rig_picker.name = find_mirror(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 = {}
|
||||||
@@ -137,18 +150,18 @@ class RP_OT_mirror_shape(bpy.types.Operator):
|
|||||||
if type(value) == list:
|
if type(value) == list:
|
||||||
mirrored_value = []
|
mirrored_value = []
|
||||||
for item in value:
|
for item in value:
|
||||||
mirrored_value.append(find_mirror(item))
|
mirrored_value.append(flip_name(item))
|
||||||
|
|
||||||
elif type(value) == str:
|
elif type(value) == str:
|
||||||
mirrored_value = find_mirror(value)
|
mirrored_value = flip_name(value)
|
||||||
args[key] = mirrored_value
|
args[key] = mirrored_value
|
||||||
|
|
||||||
mirror_ob.rig_picker.arguments = str(args)
|
flipped_object.rig_picker.arguments = str(args)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_select_shape_type(bpy.types.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'}
|
||||||
@@ -177,33 +190,46 @@ class RP_OT_select_shape_type(bpy.types.Operator):
|
|||||||
return wm.invoke_props_dialog(self, width=150)
|
return wm.invoke_props_dialog(self, width=150)
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_save_picker(bpy.types.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)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
canvas = scn.rig_picker.canvas
|
ob = context.object
|
||||||
rig = scn.rig_picker.rig
|
|
||||||
shapes = [o for o in scn.objects if o != canvas and is_shape(o)]
|
|
||||||
|
|
||||||
if not rig:
|
if self.index == -1:
|
||||||
self.report({'ERROR'}, 'Choose a Rig')
|
collection = get_picker_collection(ob)
|
||||||
return {'CANCELLED'}
|
else:
|
||||||
|
source = context.active_object.data.rig_picker.sources[self.index].source
|
||||||
|
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
|
||||||
|
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
|
||||||
|
|
||||||
|
if not collection:
|
||||||
|
self.report({"ERROR"}, 'No Picker found')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
canvas = collection.rig_picker.canvas
|
||||||
|
#rig = collection.rig_picker.rig
|
||||||
|
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'}
|
||||||
|
|
||||||
|
|
||||||
data = get_picker_datas(shapes, canvas, rig)
|
picker_data = get_picker_data(collection)
|
||||||
|
|
||||||
picker_path = Path(bpy.path.abspath(scn.rig_picker.destination))
|
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))
|
||||||
picker_path.write_text(json.dumps(data))
|
|
||||||
|
print(f'Save Picker to {picker_path}')
|
||||||
|
picker_path.write_text(json.dumps(picker_data))
|
||||||
|
|
||||||
bpy.ops.rigpicker.reload_picker()
|
bpy.ops.rigpicker.reload_picker()
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
import bpy
|
|
||||||
#import collections
|
|
||||||
#import inspect
|
|
||||||
from .core.addon_utils import get_operator_from_id
|
|
||||||
from .core.bl_utils import get_mat
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RP_PT_picker_maker_panel(bpy.types.Panel):
|
|
||||||
bl_label = 'Rig Picker'
|
|
||||||
bl_category = 'Rigging'
|
|
||||||
bl_space_type = 'VIEW_3D'
|
|
||||||
bl_region_type = 'UI'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return context.object
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
ob = context.object
|
|
||||||
scn = context.scene
|
|
||||||
|
|
||||||
layout = self.layout
|
|
||||||
col = layout.column(align=False)
|
|
||||||
col.prop_search(scn.rig_picker, 'rig', scn, 'objects', text='Rig ')
|
|
||||||
col.prop_search(scn.rig_picker, 'canvas', scn, 'objects', text='Canvas ')
|
|
||||||
col.prop_search(scn.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ')
|
|
||||||
|
|
||||||
if ob.type == 'ARMATURE':
|
|
||||||
row = layout.row(align=True)
|
|
||||||
is_packed = ('picker' in ob.data.rig_picker.keys())
|
|
||||||
sub_row = row.row(align=True)
|
|
||||||
sub_row.prop(ob.data.rig_picker, 'source', text='Picker')
|
|
||||||
sub_row.enabled = not is_packed
|
|
||||||
row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='')
|
|
||||||
|
|
||||||
|
|
||||||
col = layout.column(align=True)
|
|
||||||
row = col.row(align=True)
|
|
||||||
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create shape')
|
|
||||||
row.operator('rigpicker.mirror_shape', icon='ARROW_LEFTRIGHT', text='Mirror shape')
|
|
||||||
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name from bones')
|
|
||||||
col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto bone assign')
|
|
||||||
|
|
||||||
|
|
||||||
if ob.type !='ARMATURE':
|
|
||||||
box = layout.box()
|
|
||||||
col = box.column(align=False)
|
|
||||||
|
|
||||||
material_row = col.row(align=True)
|
|
||||||
material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='')
|
|
||||||
material_row.operator('rigpicker.add_mat', icon='ADD', text='')
|
|
||||||
mat = False
|
|
||||||
if ob.type in ('MESH', 'CURVE', 'FONT') and ob.data.materials:
|
|
||||||
mat = get_mat(ob)
|
|
||||||
if mat and mat.node_tree:
|
|
||||||
emission_nodes = [n for n in mat.node_tree.nodes if n.type =='EMISSION']
|
|
||||||
if emission_nodes:
|
|
||||||
material_row.prop(emission_nodes[0].inputs[0], 'default_value', text='')
|
|
||||||
mat = True
|
|
||||||
if not mat:
|
|
||||||
material_row.label(text='No Material')
|
|
||||||
material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='')
|
|
||||||
|
|
||||||
shape_type_row = col.row(align=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')
|
|
||||||
|
|
||||||
|
|
||||||
if ob.rig_picker.shape_type == 'OPERATOR':
|
|
||||||
|
|
||||||
op_row = col.row(align=True)
|
|
||||||
op_row.prop(ob.rig_picker, 'operator', text='')
|
|
||||||
op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU')
|
|
||||||
|
|
||||||
if ob.rig_picker.operator:
|
|
||||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
|
||||||
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
|
||||||
|
|
||||||
else:
|
|
||||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
|
||||||
|
|
||||||
'''
|
|
||||||
if ob.rig_picker.operator:
|
|
||||||
op = get_operator_from_id(ob.rig_picker.idname)
|
|
||||||
if op:
|
|
||||||
doc = re.findall(r'\(([^\)]+)\)', op.__doc__)[0]
|
|
||||||
else:
|
|
||||||
doc = 'Operator not found'
|
|
||||||
|
|
||||||
col.prop(ob.rig_picker,'name', text='Tooltip')
|
|
||||||
if op:
|
|
||||||
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
|
||||||
col.prop(ob.rig_picker,'arguments', text='')
|
|
||||||
col.label(text=doc)
|
|
||||||
else:
|
|
||||||
col.label(text=doc)
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
if ob.rig_picker.shape_type == 'BONE':
|
|
||||||
if scn.rig_picker.rig:
|
|
||||||
col.prop_search(ob.rig_picker, 'name', scn.rig_picker.rig.pose, 'bones', text='Bone')
|
|
||||||
|
|
||||||
#layout.separator()
|
|
||||||
layout.prop(scn.rig_picker, 'destination', text='Filepath')
|
|
||||||
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
|
||||||
RP_PT_picker_maker_panel,
|
|
||||||
)
|
|
||||||
|
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.props import EnumProperty, StringProperty, PointerProperty, BoolProperty
|
from bpy.props import (EnumProperty, StringProperty, PointerProperty, BoolProperty,
|
||||||
|
CollectionProperty, IntProperty)
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -22,9 +24,15 @@ def bones_item(self,context):
|
|||||||
items.append((bone.name,bone.name,''))
|
items.append((bone.name,bone.name,''))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PG_picker_source(bpy.types.PropertyGroup):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
||||||
@@ -36,13 +44,17 @@ class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
|||||||
name: StringProperty()
|
name: StringProperty()
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_collection_ui_settings(bpy.types.PropertyGroup):
|
||||||
|
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)
|
||||||
#idname: EnumProperty(items=[])
|
|
||||||
destination: StringProperty(subtype='FILE_PATH')
|
destination: StringProperty(subtype='FILE_PATH')
|
||||||
#bone_list: bpy.props.EnumProperty(items = bones_item
|
use_pick_bone : BoolProperty(default=False)
|
||||||
|
link_shape : BoolProperty(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
|
||||||
use_pick_bone : BoolProperty(default=False)
|
use_pick_bone : BoolProperty(default=False)
|
||||||
|
|
||||||
|
|
||||||
@@ -67,7 +79,9 @@ class RP_OT_operator_selector(bpy.types.Operator):
|
|||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
RP_PG_picker_source,
|
||||||
RP_PG_object_ui_settings,
|
RP_PG_object_ui_settings,
|
||||||
|
RP_PG_collection_ui_settings,
|
||||||
RP_PG_scene_ui_settings,
|
RP_PG_scene_ui_settings,
|
||||||
RP_PG_armature_ui_settings,
|
RP_PG_armature_ui_settings,
|
||||||
RP_OT_operator_selector,
|
RP_OT_operator_selector,
|
||||||
@@ -79,10 +93,12 @@ def register():
|
|||||||
|
|
||||||
bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings)
|
bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings)
|
||||||
bpy.types.Object.rig_picker = bpy.props.PointerProperty(type=RP_PG_object_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.Object.rig_picker
|
del bpy.types.Object.rig_picker
|
||||||
del bpy.types.Armature.rig_picker
|
del bpy.types.Armature.rig_picker
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
flat in vec2 startPos;
|
||||||
|
in vec2 vertPos;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform vec4 color;
|
||||||
|
uniform float dashSize;
|
||||||
|
uniform float gapSize;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 dir = (vertPos.xy - startPos.xy);
|
||||||
|
float dist = length(dir);
|
||||||
|
|
||||||
|
if (fract(dist / (dashSize + gapSize)) > dashSize/(dashSize + gapSize))
|
||||||
|
discard;
|
||||||
|
fragColor = color;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
layout (location = 0) in vec2 pos;
|
||||||
|
|
||||||
|
flat out vec2 startPos;
|
||||||
|
out vec2 vertPos;
|
||||||
|
|
||||||
|
uniform mat4 viewMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 outPos = viewMatrix * vec4(pos.x, pos.y, 0.0, 1.0);
|
||||||
|
gl_Position = outPos;
|
||||||
|
vertPos = pos.xy / outPos.w;
|
||||||
|
startPos = vertPos;
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.types import UIList
|
||||||
|
#import collections
|
||||||
|
#import inspect
|
||||||
|
from .core.addon_utils import get_operator_from_id, get_picker_collection
|
||||||
|
from .core.bl_utils import get_mat
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
# class RP_UL_picker_source(UIList):
|
||||||
|
# def draw_item(self, context, layout, data, item, _icon, _active_data, _active_propname, _index):
|
||||||
|
# ob = context.object
|
||||||
|
# row = layout.row(align=True)
|
||||||
|
# is_packed = ('picker' in ob.data.rig_picker.keys())
|
||||||
|
# sub_row = row.row(align=True)
|
||||||
|
# sub_row.prop(item, 'source', text='')
|
||||||
|
# sub_row.enabled = not is_packed
|
||||||
|
# #row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='')
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PT_picker_maker_panel(bpy.types.Panel):
|
||||||
|
bl_label = 'Rig Picker'
|
||||||
|
bl_category = 'Rigging'
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object
|
||||||
|
|
||||||
|
def draw_header_preset(self, context):
|
||||||
|
self.layout.operator('rigpicker.add_picker_collection', text="", icon='ADD', emboss=False)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.object
|
||||||
|
scn = context.scene
|
||||||
|
collection = get_picker_collection(ob)
|
||||||
|
|
||||||
|
layout = self.layout
|
||||||
|
col = layout.column(align=False)
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
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(collection.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ')
|
||||||
|
|
||||||
|
if ob.type == 'ARMATURE':
|
||||||
|
box = col.box()
|
||||||
|
box.enabled = not ob.data.library
|
||||||
|
col = box.column(align=False)
|
||||||
|
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.separator(factor=0.5)
|
||||||
|
row.label(text="Sources")
|
||||||
|
|
||||||
|
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("rigpicker.add_picker_source", icon ='ADD', text="", emboss=False)
|
||||||
|
|
||||||
|
for i, item in enumerate(ob.data.rig_picker.sources):
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.enabled = not is_packed
|
||||||
|
row.prop(item, 'source', text='')
|
||||||
|
row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
#layout.separator()
|
||||||
|
layout.prop(collection.rig_picker, 'destination', text='Filepath')
|
||||||
|
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PT_shape(bpy.types.Panel):
|
||||||
|
bl_label = 'Shape'
|
||||||
|
bl_category = 'Rigging'
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_parent_id = "RP_PT_picker_maker_panel"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.object
|
||||||
|
scn = context.scene
|
||||||
|
collection = get_picker_collection(ob)
|
||||||
|
|
||||||
|
layout = self.layout
|
||||||
|
col = layout.column(align=False)
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
#if context.collection and context.collection.rig_picker.enabled:
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
|
||||||
|
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:
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
|
||||||
|
|
||||||
|
if ob.type != 'ARMATURE':
|
||||||
|
box = layout.box()
|
||||||
|
col = box.column(align=False)
|
||||||
|
|
||||||
|
material_row = col.row(align=True)
|
||||||
|
material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='')
|
||||||
|
material_row.operator('rigpicker.add_mat', icon='ADD', text='')
|
||||||
|
mat = False
|
||||||
|
if ob.type in ('MESH', 'CURVE', 'FONT') and ob.data.materials:
|
||||||
|
mat = get_mat(ob)
|
||||||
|
if mat and mat.node_tree:
|
||||||
|
emission_nodes = [n for n in mat.node_tree.nodes if n.type =='EMISSION']
|
||||||
|
if emission_nodes:
|
||||||
|
material_row.prop(emission_nodes[0].inputs[0], 'default_value', text='')
|
||||||
|
mat = True
|
||||||
|
if not mat:
|
||||||
|
material_row.label(text='No Material')
|
||||||
|
material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='')
|
||||||
|
|
||||||
|
shape_type_row = col.row(align=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')
|
||||||
|
|
||||||
|
|
||||||
|
if ob.rig_picker.shape_type == 'OPERATOR':
|
||||||
|
|
||||||
|
op_row = col.row(align=True)
|
||||||
|
op_row.prop(ob.rig_picker, 'operator', text='')
|
||||||
|
op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU')
|
||||||
|
|
||||||
|
if ob.rig_picker.operator:
|
||||||
|
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
||||||
|
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
||||||
|
|
||||||
|
else:
|
||||||
|
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
||||||
|
|
||||||
|
elif ob.rig_picker.shape_type == 'BONE':
|
||||||
|
if collection and collection.rig_picker.rig:
|
||||||
|
col.prop_search(ob.rig_picker, 'name', collection.rig_picker.rig.pose, 'bones', text='Bone')
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
#RP_UL_picker_source,
|
||||||
|
RP_PT_picker_maker_panel,
|
||||||
|
RP_PT_shape
|
||||||
|
)
|
||||||
|
|
||||||
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
||||||