First Commit
parent
5efad05dcb
commit
2d16d82c99
|
@ -0,0 +1,2 @@
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
|
@ -0,0 +1,2 @@
|
||||||
|
.pyc
|
||||||
|
*.pyc
|
91
README.md
91
README.md
|
@ -1,92 +1,7 @@
|
||||||
# rig_picker
|
# rig_picker
|
||||||
|
|
||||||
|
An OpenGl tool for having a 2d interface for the 3d animators
|
||||||
|
|
||||||
|
|
||||||
## Getting started
|
Previous Version tutorial:
|
||||||
|
https://vimeo.com/241970235
|
||||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
|
||||||
|
|
||||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
|
||||||
|
|
||||||
## Add your files
|
|
||||||
|
|
||||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
|
||||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd existing_repo
|
|
||||||
git remote add origin https://gitlab.com/autour-de-minuit/blender/rig_picker.git
|
|
||||||
git branch -M main
|
|
||||||
git push -uf origin main
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integrate with your tools
|
|
||||||
|
|
||||||
- [ ] [Set up project integrations](https://gitlab.com/autour-de-minuit/blender/rig_picker/-/settings/integrations)
|
|
||||||
|
|
||||||
## Collaborate with your team
|
|
||||||
|
|
||||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
|
||||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
|
||||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
|
||||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
|
||||||
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
|
||||||
|
|
||||||
## Test and Deploy
|
|
||||||
|
|
||||||
Use the built-in continuous integration in GitLab.
|
|
||||||
|
|
||||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
|
||||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
|
||||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
|
||||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
|
||||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
# Editing this README
|
|
||||||
|
|
||||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
|
||||||
|
|
||||||
## Suggestions for a good README
|
|
||||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
|
||||||
|
|
||||||
## Name
|
|
||||||
Choose a self-explaining name for your project.
|
|
||||||
|
|
||||||
## Description
|
|
||||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
|
||||||
|
|
||||||
## Badges
|
|
||||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
|
||||||
|
|
||||||
## Visuals
|
|
||||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
|
||||||
|
|
||||||
## Support
|
|
||||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
State if you are open to contributions and what your requirements are for accepting them.
|
|
||||||
|
|
||||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
|
||||||
|
|
||||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
|
||||||
|
|
||||||
## Authors and acknowledgment
|
|
||||||
Show your appreciation to those who have contributed to the project.
|
|
||||||
|
|
||||||
## License
|
|
||||||
For open source projects, say how it is licensed.
|
|
||||||
|
|
||||||
## Project status
|
|
||||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
bl_info = {
|
||||||
|
"name": "Rig Picker",
|
||||||
|
"author": "Christophe Seux",
|
||||||
|
"version": (0, 1),
|
||||||
|
"blender": (3, 0, 1),
|
||||||
|
"location": "",
|
||||||
|
"description": "",
|
||||||
|
"warning": "",
|
||||||
|
"wiki_url": "",
|
||||||
|
"tracker_url": "",
|
||||||
|
"category": "Rigging"}
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
'.op_material',
|
||||||
|
'.op_picker',
|
||||||
|
'.op_shape',
|
||||||
|
'.properties',
|
||||||
|
'.panels',
|
||||||
|
'.area',
|
||||||
|
'.gizmo',
|
||||||
|
'.picker'
|
||||||
|
]
|
||||||
|
|
||||||
|
functions = [
|
||||||
|
'.func_picker',
|
||||||
|
'.func_shape',
|
||||||
|
'.snapping_utils',
|
||||||
|
'.utils'
|
||||||
|
]
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
if "bpy" in locals():
|
||||||
|
for name in modules + functions:
|
||||||
|
module = importlib.import_module(name, __name__)
|
||||||
|
importlib.reload(module)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for name in modules:
|
||||||
|
module = importlib.import_module(name, __name__)
|
||||||
|
module.register()
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
for name in reversed(modules):
|
||||||
|
module = importlib.import_module(name, __name__)
|
||||||
|
module.unregister()
|
|
@ -0,0 +1,289 @@
|
||||||
|
import bpy
|
||||||
|
import bgl
|
||||||
|
import blf
|
||||||
|
|
||||||
|
from mathutils import Vector
|
||||||
|
from .func_picker import *
|
||||||
|
from .utils import intersect_rectangles, point_inside_rectangle,\
|
||||||
|
point_over_shape, border_over_shape, canvas_space
|
||||||
|
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
from .constants import PICKERS
|
||||||
|
from .picker import Picker
|
||||||
|
|
||||||
|
"""
|
||||||
|
def draw_polygon_2d(verts, indices, loops, color, contour, width=None):
|
||||||
|
dpi = int(bpy.context.user_preferences.system.pixel_size)
|
||||||
|
|
||||||
|
bgl.glColor4f(*color)
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
#bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||||
|
|
||||||
|
bgl.glColor4f(*color)
|
||||||
|
for face in faces:
|
||||||
|
bgl.glBegin(bgl.GL_POLYGON)
|
||||||
|
for v_index in face:
|
||||||
|
coord = verts[v_index]
|
||||||
|
bgl.glVertex2f(coord[0],coord[1])
|
||||||
|
bgl.glEnd()
|
||||||
|
|
||||||
|
if width:
|
||||||
|
bgl.glLineWidth(width*dpi)
|
||||||
|
bgl.glColor4f(*contour)
|
||||||
|
|
||||||
|
for loop in loops:
|
||||||
|
if faces:
|
||||||
|
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||||
|
else:
|
||||||
|
bgl.glBegin(bgl.GL_LINE_STRIP)
|
||||||
|
for v_index in loop:
|
||||||
|
coord = verts[v_index]
|
||||||
|
bgl.glVertex2f(coord[0],coord[1])
|
||||||
|
bgl.glEnd()
|
||||||
|
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
#bgl.glDisable(bgl.GL_LINE_SMOOTH)
|
||||||
|
bgl.glEnd()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def draw_border(border):
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
bgl.glColor4f(1,1,1,0.2)
|
||||||
|
bgl.glBegin(bgl.GL_POLYGON)
|
||||||
|
for v in border:
|
||||||
|
bgl.glVertex2f(v[0],v[1])
|
||||||
|
|
||||||
|
bgl.glEnd()
|
||||||
|
|
||||||
|
bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
|
||||||
|
bgl.glLineWidth(2)
|
||||||
|
bgl.glLineStipple(3, 0xAAAA)
|
||||||
|
bgl.glEnable(bgl.GL_LINE_STIPPLE)
|
||||||
|
|
||||||
|
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||||
|
|
||||||
|
for v in border:
|
||||||
|
bgl.glVertex2f(v[0],v[1])
|
||||||
|
|
||||||
|
bgl.glEnd()
|
||||||
|
|
||||||
|
bgl.glColor4f(1.0, 1.0, 1.0, 1)
|
||||||
|
bgl.glLineWidth(1)
|
||||||
|
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||||
|
for v in border:
|
||||||
|
bgl.glVertex2f(v[0],v[1])
|
||||||
|
|
||||||
|
bgl.glEnd()
|
||||||
|
bgl.glDisable(bgl.GL_LINE_STIPPLE)
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
def draw_text(mouse,text,color):
|
||||||
|
dpi = int(bpy.context.user_preferences.system.pixel_size)
|
||||||
|
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
font_id =0 # XXX, need to find out how best to get this.
|
||||||
|
# draw some text
|
||||||
|
bgl.glColor4f(0,0,0,0.75)
|
||||||
|
blf.blur(font_id,5)
|
||||||
|
blf.position(font_id, mouse[0]+10*dpi, mouse[1]-20*dpi, 0)
|
||||||
|
blf.size(font_id, 9*dpi, 96)
|
||||||
|
blf.draw(font_id, text)
|
||||||
|
|
||||||
|
bgl.glEnd()
|
||||||
|
bgl.glColor4f(*color)
|
||||||
|
blf.blur(font_id,0)
|
||||||
|
blf.draw(font_id, text)
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
|
||||||
|
def select_bone(self,context,event):
|
||||||
|
ob = context.object
|
||||||
|
if ob and ob.type =='ARMATURE' and ob.data.rig_picker:
|
||||||
|
shape_data = ob.data.rig_picker
|
||||||
|
selected_bones = [b for b in ob.pose.bones if b.bone.select]
|
||||||
|
|
||||||
|
if not event.shift and not event.alt:
|
||||||
|
for b in ob.pose.bones:
|
||||||
|
b.bone.select= False
|
||||||
|
|
||||||
|
for shape in [s for s in shape_data['shapes'] if not s['shape_type']==["DISPLAY"]]:
|
||||||
|
points = [canvas_space(p,self.scale,self.offset) for p in shape['points']]
|
||||||
|
bound = [canvas_space(p,self.scale,self.offset) for p in shape['bound']]
|
||||||
|
loops = shape['loops']
|
||||||
|
|
||||||
|
## Colision check
|
||||||
|
over = False
|
||||||
|
if self.is_border:
|
||||||
|
if intersect_rectangles(self.border,bound): #start check on over bound_box
|
||||||
|
over = border_over_shape(self.border,points,loops)
|
||||||
|
else:
|
||||||
|
if point_inside_rectangle(self.end,bound):
|
||||||
|
over = point_over_shape(self.end,points,loops)
|
||||||
|
|
||||||
|
if over:
|
||||||
|
if shape['shape_type'] == 'BONE':
|
||||||
|
bone = context.object.pose.bones.get(shape['bone'])
|
||||||
|
if bone:
|
||||||
|
if event.alt:
|
||||||
|
bone.bone.select = False
|
||||||
|
else:
|
||||||
|
bone.bone.select = True
|
||||||
|
context.object.data.bones.active = bone.bone
|
||||||
|
|
||||||
|
if shape['shape_type'] == 'FUNCTION' and event.value== 'RELEASE' and not self.is_border:
|
||||||
|
# restore selection
|
||||||
|
for b in selected_bones:
|
||||||
|
b.bone.select = True
|
||||||
|
|
||||||
|
function = shape['function']
|
||||||
|
if shape.get('variables'):
|
||||||
|
variables=shape['variables'].to_dict()
|
||||||
|
|
||||||
|
else:
|
||||||
|
variables={}
|
||||||
|
|
||||||
|
variables['event']=event
|
||||||
|
|
||||||
|
print(variables)
|
||||||
|
|
||||||
|
globals()[function](variables)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_callback_view():
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
if not ob or ob.type !='ARMATURE' or 'shapes' not in ob.data.rig_picker.keys():
|
||||||
|
return
|
||||||
|
|
||||||
|
if ob not in PICKERS:
|
||||||
|
shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']]
|
||||||
|
PICKERS[ob] = Picker(ob, shapes=shapes)
|
||||||
|
|
||||||
|
picker = PICKERS.get(ob)
|
||||||
|
picker.draw()
|
||||||
|
|
||||||
|
handle_view = None
|
||||||
|
handle_pixel = None
|
||||||
|
|
||||||
|
def register():
|
||||||
|
global handle_view, handle_pixel
|
||||||
|
|
||||||
|
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
|
||||||
|
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
global handle_view, handle_pixel
|
||||||
|
|
||||||
|
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW')
|
||||||
|
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW')
|
||||||
|
|
||||||
|
handle_view = None
|
||||||
|
handle_pixel = None
|
||||||
|
|
||||||
|
'''
|
||||||
|
return
|
||||||
|
|
||||||
|
shape_data = ob.data.rig_picker
|
||||||
|
rig_layers = [i for i,l in enumerate(ob.data.layers) if l]
|
||||||
|
|
||||||
|
r = context.region
|
||||||
|
#self.scale = region.height
|
||||||
|
|
||||||
|
#draw BG
|
||||||
|
canvas = shape_data['shapes'][0]
|
||||||
|
#bg_point = [(0, r.height), (r.width, r.height), (r.width, 0),(0, 0)]
|
||||||
|
bg_color = [*canvas['color'], 1]
|
||||||
|
draw_polygon_2d(bg_point,[[0,1,2,3]],[[0,1,2,3]], bg_color,(0,0,0,1),0)
|
||||||
|
|
||||||
|
show_tooltip = False
|
||||||
|
for shape in shape_data['shapes']:
|
||||||
|
|
||||||
|
color = shape['color']
|
||||||
|
points = [canvas_space(p,self.scale,self.offset) for p in shape['points']]
|
||||||
|
bound = [canvas_space(p,self.scale,self.offset) for p in shape['bound']]
|
||||||
|
loops = shape['loops']
|
||||||
|
faces = shape['faces']
|
||||||
|
|
||||||
|
select=None
|
||||||
|
contour_color = [0,0,0]
|
||||||
|
contour_alpha = 1
|
||||||
|
width = 0
|
||||||
|
shape_color = [c for c in color]
|
||||||
|
shape_alpha = 1
|
||||||
|
|
||||||
|
|
||||||
|
if shape['shape_type'] == 'DISPLAY' and not faces:
|
||||||
|
width = 1
|
||||||
|
|
||||||
|
if shape['shape_type'] != 'DISPLAY':
|
||||||
|
if shape['shape_type'] == 'BONE':
|
||||||
|
bone = ob.pose.bones.get(shape['bone'])
|
||||||
|
if bone:
|
||||||
|
b_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||||
|
|
||||||
|
if bone.bone_group:
|
||||||
|
group_color = list(bone.bone_group.colors.normal)
|
||||||
|
contour_color = [c*0.85 for c in group_color]
|
||||||
|
width = 1
|
||||||
|
|
||||||
|
if bone.bone.select :
|
||||||
|
shape_color = [c*1.2+0.1 for c in color]
|
||||||
|
if bone.bone_group:
|
||||||
|
contour_color = [0.05,0.95,0.95]
|
||||||
|
|
||||||
|
if ob.data.bones.active and shape['bone'] == ob.data.bones.active.name:
|
||||||
|
if bone.bone_group:
|
||||||
|
if bone.bone.select :
|
||||||
|
shape_color = [c*1.2+0.2 for c in color]
|
||||||
|
contour_color = [1,1,1]
|
||||||
|
width = 1.5
|
||||||
|
else:
|
||||||
|
shape_color = [c*1.2+0.15 for c in color]
|
||||||
|
contour_color = [0.9,0.9,0.9]
|
||||||
|
width = 1
|
||||||
|
|
||||||
|
|
||||||
|
if bone.bone.hide or not len(set(b_layers).intersection(rig_layers)):
|
||||||
|
shape_alpha = 0.33
|
||||||
|
contour_alpha = 0.33
|
||||||
|
|
||||||
|
elif shape['shape_type'] == 'FUNCTION':
|
||||||
|
if shape['function'] == 'boolean':
|
||||||
|
path = shape['variables']['data_path']
|
||||||
|
|
||||||
|
if ob.path_resolve(path):
|
||||||
|
shape_color = [c*1.4+0.08 for c in color]
|
||||||
|
else:
|
||||||
|
shape_color = [color[0],color[1],color[2]]
|
||||||
|
|
||||||
|
|
||||||
|
## On mouse over checking
|
||||||
|
over = False
|
||||||
|
if self.is_border:
|
||||||
|
if intersect_rectangles(self.border,bound): #start check on over bound_box
|
||||||
|
over = border_over_shape(self.border,points,loops)
|
||||||
|
else:
|
||||||
|
if point_inside_rectangle(self.end,bound):
|
||||||
|
over = point_over_shape(self.end,points,loops)
|
||||||
|
|
||||||
|
if over:
|
||||||
|
show_tooltip = True
|
||||||
|
tooltip = shape['tooltip']
|
||||||
|
if not self.press:
|
||||||
|
shape_color = [c*1.02+0.05 for c in shape_color]
|
||||||
|
contour_color = [c*1.03+0.05 for c in contour_color]
|
||||||
|
|
||||||
|
shape_color.append(shape_alpha)
|
||||||
|
contour_color.append(contour_alpha)
|
||||||
|
draw_polygon_2d(points,faces,loops,shape_color,contour_color,width)
|
||||||
|
|
||||||
|
if show_tooltip:
|
||||||
|
draw_text(self.end,tooltip,(1,1,1,1))
|
||||||
|
|
||||||
|
if self.is_border:
|
||||||
|
draw_border(self.border)
|
||||||
|
'''
|
|
@ -0,0 +1,95 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header
|
||||||
|
|
||||||
|
|
||||||
|
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
|
||||||
|
class RigPickerTree(NodeTree):
|
||||||
|
# Description string
|
||||||
|
'''A custom node tree type that will show up in the editor type list'''
|
||||||
|
# Optional identifier string. If not explicitly defined, the python class name is used.
|
||||||
|
# Label for nice name display
|
||||||
|
bl_label = "Rig Picker"
|
||||||
|
# Icon identifier
|
||||||
|
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_header(self, context):
|
||||||
|
if 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.separator_spacer()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._draw(context)
|
||||||
|
|
||||||
|
def tools_from_context(context, mode=None):
|
||||||
|
sp = context.space_data
|
||||||
|
if sp and sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree':
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return NODE_PT_tools_active._tools_from_context(context, mode)
|
||||||
|
|
||||||
|
def tool_set_by_id(self, context):
|
||||||
|
sd = context.space_data
|
||||||
|
if sd and sd.type == 'NODE_EDITOR' and sd.tree_type == 'RigPickerTree':
|
||||||
|
return {"FINISHED"}
|
||||||
|
else:
|
||||||
|
return self._execute(context)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
sp = context.space_data
|
||||||
|
if sp and sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree':
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return self._poll(context)
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
RigPickerTree,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
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
|
||||||
|
|
||||||
|
NODE_PT_tools_active._tools_from_context = NODE_PT_tools_active.tools_from_context
|
||||||
|
NODE_PT_tools_active.tools_from_context = tools_from_context
|
||||||
|
|
||||||
|
NODE_HT_header._draw = NODE_HT_header.draw
|
||||||
|
NODE_HT_header.draw = draw_header
|
||||||
|
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
|
bpy.types.WM_OT_tool_set_by_id.execute = bpy.types.WM_OT_tool_set_by_id._execute
|
||||||
|
del bpy.types.WM_OT_tool_set_by_id._execute
|
||||||
|
|
||||||
|
NODE_PT_tools_active.tools_from_context = NODE_PT_tools_active._tools_from_context
|
||||||
|
del NODE_PT_tools_active._tools_from_context
|
||||||
|
|
||||||
|
NODE_HT_header.draw = NODE_HT_header._draw
|
||||||
|
del NODE_HT_header._draw
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
PICKERS = {}
|
|
@ -0,0 +1,248 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
#from. insert_keyframe import insert_keyframe
|
||||||
|
from. snapping_utils import *
|
||||||
|
from .utils import get_IK_bones
|
||||||
|
|
||||||
|
try:
|
||||||
|
from rigutils.driver_utils import split_path
|
||||||
|
from rigutils.insert_keyframe import insert_keyframe
|
||||||
|
from rigutils.snap_ik_fk import snap_ik_fk
|
||||||
|
from rigutils.utils import find_mirror
|
||||||
|
except:
|
||||||
|
print('You need to install the rigutils module in your blender modules path')
|
||||||
|
|
||||||
|
def hide_layers(args):
|
||||||
|
""" """
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
layers=[]
|
||||||
|
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||||
|
for i,l in enumerate(bone.bone.layers):
|
||||||
|
if l and i not in layers:
|
||||||
|
layers.append(i)
|
||||||
|
|
||||||
|
for i in layers:
|
||||||
|
ob.data.layers[i] = not ob.data.layers[i]
|
||||||
|
|
||||||
|
|
||||||
|
def boolean(args):
|
||||||
|
""" data_path, keyable """
|
||||||
|
ob = bpy.context.object
|
||||||
|
data_path = args['data_path']
|
||||||
|
keyable = args['keyable']
|
||||||
|
|
||||||
|
#bone,prop = split_path(data_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
value = ob.path_resolve(data_path)
|
||||||
|
#setattr(ob.pose.bones.get(bone),'["%s"]'%prop,not value)
|
||||||
|
try:
|
||||||
|
exec("ob.%s = %s"%(data_path,not value))
|
||||||
|
except:
|
||||||
|
exec("ob%s= %s"%(data_path,not value))
|
||||||
|
|
||||||
|
if keyable and bpy.context.scene.tool_settings.use_keyframe_insert_auto:
|
||||||
|
|
||||||
|
if not ob.animation_data:
|
||||||
|
ob.animation_data_create()
|
||||||
|
|
||||||
|
ob.keyframe_insert(data_path = data_path ,group = bone)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
print("Property don't exist")
|
||||||
|
|
||||||
|
|
||||||
|
def select_layer(args):
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
layers =[]
|
||||||
|
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||||
|
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||||
|
|
||||||
|
for l in bone_layers:
|
||||||
|
if l not in layers:
|
||||||
|
layers.append(l)
|
||||||
|
|
||||||
|
for bone in ob.pose.bones:
|
||||||
|
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||||
|
|
||||||
|
if len(set(bone_layers).intersection(layers)):
|
||||||
|
bone.bone.select = True
|
||||||
|
|
||||||
|
def hide_bones(args):
|
||||||
|
ob = bpy.context.object
|
||||||
|
selected_bone = [b for b in ob.pose.bones if b.bone.select]
|
||||||
|
|
||||||
|
hide = [b.bone.hide for b in selected_bone if not b.bone.hide]
|
||||||
|
|
||||||
|
visibility = True if len(hide) else False
|
||||||
|
|
||||||
|
for bone in selected_bone:
|
||||||
|
bone.bone.hide = visibility
|
||||||
|
|
||||||
|
|
||||||
|
def select_all(args):
|
||||||
|
ob = bpy.context.object
|
||||||
|
shapes = ob.data.rig_picker['shapes']
|
||||||
|
bones = [s['bone'] for s in shapes if s['shape_type']=='BONE']
|
||||||
|
|
||||||
|
for bone_name in bones:
|
||||||
|
bone = ob.pose.bones.get(bone_name)
|
||||||
|
|
||||||
|
if bone:
|
||||||
|
bone.bone.select = True
|
||||||
|
|
||||||
|
def select_bones(args):
|
||||||
|
"""bones (name list)"""
|
||||||
|
ob = bpy.context.object
|
||||||
|
pBones = ob.pose.bones
|
||||||
|
bones_name =args['bones']
|
||||||
|
event = args['event']
|
||||||
|
if not event.shift:
|
||||||
|
for bone in bpy.context.object.pose.bones:
|
||||||
|
bone.bone.select = False
|
||||||
|
|
||||||
|
bones = [pBones.get(b) for b in bones_name]
|
||||||
|
|
||||||
|
select = False
|
||||||
|
for bone in bones:
|
||||||
|
if bone.bone.select == False:
|
||||||
|
select =True
|
||||||
|
break
|
||||||
|
|
||||||
|
for bone in bones:
|
||||||
|
bone.bone.select = select
|
||||||
|
ob.data.bones.active = bones[-1].bone
|
||||||
|
|
||||||
|
def keyframe_bones(args):
|
||||||
|
print(args)
|
||||||
|
event=args['event']
|
||||||
|
bones=[]
|
||||||
|
|
||||||
|
for bone in bpy.context.object.pose.bones:
|
||||||
|
if not bone.name.startswith(('DEF','ORG','MCH')) and not bone.get('_unkeyable_') ==1:
|
||||||
|
if event.shift:
|
||||||
|
bones.append(bone)
|
||||||
|
elif not event.shift and bone.bone.select :
|
||||||
|
bones.append(bone)
|
||||||
|
|
||||||
|
|
||||||
|
for bone in bones:
|
||||||
|
insert_keyframe(bone)
|
||||||
|
|
||||||
|
def reset_bones(args):
|
||||||
|
event=args['event']
|
||||||
|
avoid_value =args['avoid_value']
|
||||||
|
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
bones=[]
|
||||||
|
for bone in bpy.context.object.pose.bones:
|
||||||
|
if not bone.name.startswith(('DEF','ORG','MCH')) and not bone.get('_unkeyable_') ==1:
|
||||||
|
if event.shift:
|
||||||
|
bones.append(bone)
|
||||||
|
elif not event.shift and bone.bone.select :
|
||||||
|
bones.append(bone)
|
||||||
|
|
||||||
|
|
||||||
|
for bone in bones:
|
||||||
|
if bone.rotation_mode =='QUATERNION':
|
||||||
|
bone.rotation_quaternion = 1, 0, 0, 0
|
||||||
|
|
||||||
|
if bone.rotation_mode == 'AXIS_ANGLE':
|
||||||
|
bone.rotation_axis_angle = 0, 0, 1, 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
bone.rotation_euler = 0, 0, 0
|
||||||
|
|
||||||
|
bone.location = 0, 0, 0
|
||||||
|
bone.scale = 1, 1, 1
|
||||||
|
|
||||||
|
for key,value in bone.items():
|
||||||
|
if key not in avoid_value and type(value) in (int,float):
|
||||||
|
if ob.data.get("DefaultValues") and ob.data.DefaultValues['bones'].get(bone.name):
|
||||||
|
|
||||||
|
if key in ob.data.DefaultValues['bones'][bone.name]:
|
||||||
|
bone[key] = ob.data.DefaultValues['bones'][bone.name][key]
|
||||||
|
|
||||||
|
else:
|
||||||
|
if type(value)== int:
|
||||||
|
bone[key]=0
|
||||||
|
else:
|
||||||
|
bone[key]=0.0
|
||||||
|
else:
|
||||||
|
if type(value)== int:
|
||||||
|
bone[key]=0
|
||||||
|
else:
|
||||||
|
bone[key]=0.0
|
||||||
|
|
||||||
|
if bpy.context.scene.tool_settings.use_keyframe_insert_auto:
|
||||||
|
insert_keyframe(bone)
|
||||||
|
|
||||||
|
|
||||||
|
def flip_bones(args):
|
||||||
|
event=args['event']
|
||||||
|
|
||||||
|
ob = bpy.context.object
|
||||||
|
arm = bpy.context.object.pose.bones
|
||||||
|
|
||||||
|
selected_bones = [bone for bone in ob.pose.bones if bone.bone.select==True ]
|
||||||
|
mirrorActive = None
|
||||||
|
|
||||||
|
for bone in selected_bones:
|
||||||
|
boneName = bone.name
|
||||||
|
mirrorBoneName= find_mirror(boneName)
|
||||||
|
|
||||||
|
mirrorBone = ob.pose.bones.get(mirrorBoneName) if mirrorBoneName else None
|
||||||
|
|
||||||
|
if bpy.context.active_pose_bone == bone:
|
||||||
|
mirrorActive = mirrorBone
|
||||||
|
|
||||||
|
#print(mirrorBone)
|
||||||
|
if not event.shift and mirrorBone:
|
||||||
|
bone.bone.select = False
|
||||||
|
|
||||||
|
if mirrorBone:
|
||||||
|
mirrorBone.bone.select = True
|
||||||
|
if mirrorActive:
|
||||||
|
ob.data.bones.active = mirrorActive.bone
|
||||||
|
|
||||||
|
def snap_ikfk(args):
|
||||||
|
""" way, chain_index """
|
||||||
|
|
||||||
|
way =args['way']
|
||||||
|
chain_index = args['chain_index']
|
||||||
|
#auto_switch = self.auto_switch
|
||||||
|
|
||||||
|
ob = bpy.context.object
|
||||||
|
armature = ob.data
|
||||||
|
|
||||||
|
SnappingChain = armature.get('SnappingChain')
|
||||||
|
|
||||||
|
poseBone = ob.pose.bones
|
||||||
|
dataBone = ob.data.bones
|
||||||
|
|
||||||
|
IKFK_chain = SnappingChain['IKFK_bones'][chain_index]
|
||||||
|
switch_prop = IKFK_chain['switch_prop']
|
||||||
|
|
||||||
|
FK_root = poseBone.get(IKFK_chain['FK_root'])
|
||||||
|
FK_mid = [poseBone.get(b['name']) for b in IKFK_chain['FK_mid']]
|
||||||
|
FK_tip = poseBone.get(IKFK_chain['FK_tip'])
|
||||||
|
|
||||||
|
IK_last = poseBone.get(IKFK_chain['IK_last'])
|
||||||
|
IK_tip = poseBone.get(IKFK_chain['IK_tip'])
|
||||||
|
IK_pole = poseBone.get(IKFK_chain['IK_pole'])
|
||||||
|
|
||||||
|
invert = IKFK_chain['invert_switch']
|
||||||
|
|
||||||
|
ik_fk_layer = (IKFK_chain['FK_layer'],IKFK_chain['IK_layer'])
|
||||||
|
|
||||||
|
|
||||||
|
for lock in ('lock_ik_x','lock_ik_y','lock_ik_z'):
|
||||||
|
if getattr(IK_last,lock):
|
||||||
|
full_snapping = False
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
snap_ik_fk(ob,way,switch_prop,FK_root,FK_tip,IK_last,IK_tip,IK_pole,FK_mid,full_snapping,invert,ik_fk_layer,auto_switch=True)
|
|
@ -0,0 +1,102 @@
|
||||||
|
import bpy
|
||||||
|
import bmesh
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
from bpy_extras import mesh_utils
|
||||||
|
from .utils import bound_box_center, contour_loops, get_object_color
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_datas(objects, canvas, rig):
|
||||||
|
picker_datas = []
|
||||||
|
gamma = 1 / 2.2
|
||||||
|
|
||||||
|
if canvas.type =='CURVE':
|
||||||
|
canvas_points = canvas.data.splines[0].points
|
||||||
|
else:
|
||||||
|
canvas_points = canvas.data.vertices
|
||||||
|
|
||||||
|
canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]
|
||||||
|
|
||||||
|
canvas_x = [p[0] for p in canvas_coords]
|
||||||
|
canvas_y = [p[1] for p in canvas_coords]
|
||||||
|
|
||||||
|
canvas_center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
||||||
|
canvas_scale_fac = 1024 / (max(canvas_y) - min(canvas_y))# Reference height for the canvas
|
||||||
|
|
||||||
|
objects.append(canvas)
|
||||||
|
|
||||||
|
dg = bpy.context.evaluated_depsgraph_get()
|
||||||
|
|
||||||
|
#sorted by their z axes
|
||||||
|
for ob in sorted(objects, key=lambda x: bound_box_center(x)[2]):
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
color = [0.5, 0.5, 0.5]
|
||||||
|
|
||||||
|
shape = {
|
||||||
|
'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)
|
||||||
|
|
||||||
|
|
||||||
|
print(picker_datas)
|
||||||
|
|
||||||
|
return picker_datas
|
||||||
|
#rig.data.rig_picker['shapes'] = picker_datas
|
|
@ -0,0 +1,124 @@
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from bpy.props import (IntProperty, EnumProperty, BoolProperty)
|
||||||
|
from bpy.types import (AddonPreferences, GizmoGroup, Operator, Gizmo)
|
||||||
|
|
||||||
|
from mathutils import Vector, Matrix, Euler
|
||||||
|
from .constants import PICKERS
|
||||||
|
from .picker import Picker
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
class RP_OT_simple_operator(bpy.types.Operator):
|
||||||
|
"""Tooltip"""
|
||||||
|
bl_idname = "object.simple_operator"
|
||||||
|
bl_label = "Simple Object Operator"
|
||||||
|
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
|
||||||
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
|
print('Invoke RP_OT_simple_operator')
|
||||||
|
bpy.ops.node.rp_box_select('INVOKE_DEFAULT', tweak=True)
|
||||||
|
|
||||||
|
#bpy.ops.node.select_box('INVOKE_DEFAULT', wait_for_input=True, tweak=True)
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
print('modal op', event.type, event.value)
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
print('Select Shape')
|
||||||
|
return {'FINISHED'}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RP_GT_gizmo(Gizmo):
|
||||||
|
|
||||||
|
def test_select(self, context, location):
|
||||||
|
ob = context.object
|
||||||
|
|
||||||
|
picker = PICKERS.get(ob)
|
||||||
|
if not picker:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
picker.move_event(location)
|
||||||
|
|
||||||
|
#if bpy.app.timers.is_registered(tooltip):
|
||||||
|
# bpy.app.timers.unregister(tooltip)
|
||||||
|
#context.region.tag_redraw()
|
||||||
|
|
||||||
|
#picker.tooltip_event(event='START')
|
||||||
|
#bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
|
||||||
|
|
||||||
|
#print(location)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
context.region.tag_redraw()
|
||||||
|
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
return
|
||||||
|
'''
|
||||||
|
def invoke(self, context, event):
|
||||||
|
print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def modal(self, context, event, tweak):
|
||||||
|
print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def exit(self, context, cancel):
|
||||||
|
print('EXIT')
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RP_GT_gizmogroup(GizmoGroup):
|
||||||
|
""" test gizmo button 2d """
|
||||||
|
bl_idname = "view3d.gizmo_button_2d"
|
||||||
|
bl_label = "Test button 2d"
|
||||||
|
bl_space_type = 'NODE_EDITOR'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_options = {'PERSISTENT'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.space_data.tree_type == 'RigPickerTree'
|
||||||
|
|
||||||
|
def setup(self, context):
|
||||||
|
self.gizmo = self.gizmos.new("RP_GT_gizmo")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
RP_GT_gizmo,
|
||||||
|
RP_GT_gizmogroup
|
||||||
|
)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,146 @@
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from bpy_extras import view3d_utils
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_add_material(bpy.types.Operator):
|
||||||
|
bl_label = "Add Ui Material"
|
||||||
|
bl_idname = "rigpicker.add_mat"
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
scene = context.scene
|
||||||
|
|
||||||
|
mat = bpy.data.materials.new('UI')
|
||||||
|
mat.use_nodes = True
|
||||||
|
|
||||||
|
for node in mat.node_tree.nodes:
|
||||||
|
if node.type == 'OUTPUT_MATERIAL':
|
||||||
|
mat_output = node
|
||||||
|
else:
|
||||||
|
mat.node_tree.nodes.remove(node)
|
||||||
|
|
||||||
|
emission = mat.node_tree.nodes.new('ShaderNodeEmission')
|
||||||
|
mat.node_tree.links.new(emission.outputs[0],mat_output.inputs[0])
|
||||||
|
|
||||||
|
|
||||||
|
if not context.object.data.materials:
|
||||||
|
context.object.data.materials.append(mat)
|
||||||
|
else:
|
||||||
|
context.object.material_slots[0].material = mat
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_remove_material(bpy.types.Operator):
|
||||||
|
bl_label = "Remove Ui Material"
|
||||||
|
bl_idname = "rigpicker.remove_mat"
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
scene = context.scene
|
||||||
|
#print(self.shape_type)
|
||||||
|
for mat in context.object.data.materials:
|
||||||
|
bpy.data.materials.remove(mat)
|
||||||
|
context.area.tag_redraw()
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_eyedropper_material(bpy.types.Operator):
|
||||||
|
"""Tooltip"""
|
||||||
|
bl_idname = "rigpicker.eyedropper_mat"
|
||||||
|
bl_label = "Eye Dropper mat"
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
#first_mouse_x = IntProperty()
|
||||||
|
#first_value = FloatProperty()
|
||||||
|
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
context.area.tag_redraw()
|
||||||
|
|
||||||
|
context.window.cursor_modal_set("EYEDROPPER")
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
region = context.region
|
||||||
|
rv3d = context.region_data
|
||||||
|
|
||||||
|
if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
|
||||||
|
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
|
|
||||||
|
dg = context.evaluated_depsgraph_get()
|
||||||
|
|
||||||
|
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, self.mouse)
|
||||||
|
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, self.mouse)
|
||||||
|
|
||||||
|
raycast = scene.ray_cast(dg, ray_origin, view_vector)
|
||||||
|
|
||||||
|
if raycast[0]==True:
|
||||||
|
ob = raycast[4]
|
||||||
|
|
||||||
|
if ob.data.materials:
|
||||||
|
mat = ob.data.materials[0]
|
||||||
|
for shape in [o for o in context.selected_objects if o.type in ('MESH','CURVE','FONT')]:
|
||||||
|
if not shape.data.materials:
|
||||||
|
shape.data.materials.append(mat)
|
||||||
|
else:
|
||||||
|
shape.material_slots[0].material = mat
|
||||||
|
|
||||||
|
|
||||||
|
#context.space_data.draw_handler_remove(self._handle, 'WINDOW')
|
||||||
|
|
||||||
|
context.window.cursor_modal_restore()
|
||||||
|
|
||||||
|
for ob in self.temp_ob:
|
||||||
|
bpy.data.objects.remove(ob)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
#return {'FINISHED'}
|
||||||
|
|
||||||
|
elif event.type in {'RIGHTMOUSE', 'ESC'}:
|
||||||
|
#context.object.location.x = self.first_value
|
||||||
|
context.window.cursor_modal_restore()
|
||||||
|
|
||||||
|
for ob in self.temp_ob:
|
||||||
|
bpy.data.objects.remove(ob)
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
scene = context.scene
|
||||||
|
#self.local_cursor = tuple(context.space_data.cursor.location)
|
||||||
|
#self.cursor = tuple(context.scene.cursor.location)
|
||||||
|
|
||||||
|
curves =[o for o in context.visible_objects if o.type in ('CURVE', 'FONT')]
|
||||||
|
|
||||||
|
self.temp_ob = []
|
||||||
|
|
||||||
|
dg = bpy.context.evaluated_depsgraph_get()
|
||||||
|
|
||||||
|
for c in curves:
|
||||||
|
mesh = bpy.data.meshes.new_from_object(c.evaluated_get(dg))
|
||||||
|
copy = bpy.data.objects.new(c.name+'_tmp', mesh)
|
||||||
|
copy.matrix_world = c.matrix_world
|
||||||
|
for mat in c.data.materials:
|
||||||
|
copy.data.materials.append(mat)
|
||||||
|
scene.collection.objects.link(copy)
|
||||||
|
self.temp_ob.append(copy)
|
||||||
|
#args = (self,context)
|
||||||
|
#self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
|
||||||
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
RP_OT_add_material,
|
||||||
|
RP_OT_remove_material,
|
||||||
|
RP_OT_eyedropper_material,
|
||||||
|
)
|
||||||
|
|
||||||
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
|
@ -0,0 +1,532 @@
|
||||||
|
import bpy
|
||||||
|
from .constants import PICKERS
|
||||||
|
#from .func_bgl import draw_callback_px
|
||||||
|
#from .func_bgl import select_bone
|
||||||
|
#from .func_picker import *
|
||||||
|
#from .utils import is_over_region
|
||||||
|
import gpu
|
||||||
|
import bgl
|
||||||
|
from mathutils import Vector
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vertex_shader = '''
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
fragment_shader = '''
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_callback(self):
|
||||||
|
#print('draw callback border')
|
||||||
|
if not self.draw_border:
|
||||||
|
return
|
||||||
|
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
#print('DRAW BORDER')
|
||||||
|
|
||||||
|
self.color_shader.bind()
|
||||||
|
self.color_shader.uniform_float("color", self.bg_color)
|
||||||
|
self.bg_batch.draw(self.color_shader)
|
||||||
|
|
||||||
|
self.dash_shader.bind()
|
||||||
|
matrix = gpu.matrix.get_projection_matrix()
|
||||||
|
|
||||||
|
self.dash_shader.uniform_float("color", self.border_color)
|
||||||
|
self.dash_shader.uniform_float("viewMatrix", matrix)
|
||||||
|
self.dash_shader.uniform_float("dashSize", 5)
|
||||||
|
self.dash_shader.uniform_float("gapSize", 4)
|
||||||
|
|
||||||
|
self.contour_batch.draw(self.dash_shader)
|
||||||
|
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_box_select(bpy.types.Operator):
|
||||||
|
"""Tooltip"""
|
||||||
|
bl_idname = "node.rp_box_select"
|
||||||
|
bl_label = "Picker Box Select"
|
||||||
|
|
||||||
|
mode: bpy.props.EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
|
||||||
|
|
||||||
|
color_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||||
|
dash_shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
sp = context.space_data
|
||||||
|
|
||||||
|
if not sp.tree_type == 'RigPickerTree':
|
||||||
|
return
|
||||||
|
|
||||||
|
ob = context.object
|
||||||
|
return ob and ob in PICKERS
|
||||||
|
|
||||||
|
'''
|
||||||
|
def mode_from_event(self, event):
|
||||||
|
if event.alt:
|
||||||
|
return 'SUBSTRACT'
|
||||||
|
elif event.ctrl or event.shift:
|
||||||
|
return 'EXTEND'
|
||||||
|
else:
|
||||||
|
return 'SET'
|
||||||
|
'''
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
#print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
|
if context.object.mode != 'POSE':
|
||||||
|
bpy.ops.object.posemode_toggle()
|
||||||
|
|
||||||
|
self.timer = None
|
||||||
|
#self.mode = self.mode_from_event(event)
|
||||||
|
#self.invoke_event = event.copy()
|
||||||
|
self.region = context.region
|
||||||
|
self.draw_border = False
|
||||||
|
|
||||||
|
self.picker = PICKERS[context.object]
|
||||||
|
|
||||||
|
self.start_mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
|
#self.shader = line_strip_shader
|
||||||
|
self.border_color = [1, 1, 1, 1]
|
||||||
|
self.bg_color = [1, 1, 1, 0.05]
|
||||||
|
|
||||||
|
#args = (self, context)
|
||||||
|
self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback, (self,), 'WINDOW', 'POST_PIXEL')
|
||||||
|
|
||||||
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
|
self.picker.press_event(self.mode)
|
||||||
|
self.region.tag_redraw()
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
|
||||||
|
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
|
points_x = [v[0] for v in (self.start_mouse, self.mouse)]
|
||||||
|
points_y = [v[1] for v in (self.start_mouse, self.mouse)]
|
||||||
|
|
||||||
|
self.border = [
|
||||||
|
(min(points_x), max(points_y)),
|
||||||
|
(max(points_x), max(points_y)),
|
||||||
|
(max(points_x), min(points_y)),
|
||||||
|
(min(points_x), min(points_y))
|
||||||
|
]
|
||||||
|
|
||||||
|
self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border})
|
||||||
|
self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border})
|
||||||
|
|
||||||
|
self.draw_border = True
|
||||||
|
|
||||||
|
self.region.tag_redraw()
|
||||||
|
|
||||||
|
if event.value == 'RELEASE':
|
||||||
|
return self.release_event()
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def release_event(self):
|
||||||
|
if (self.start_mouse[0] != self.mouse[0] and self.start_mouse[1] != self.mouse[1]):
|
||||||
|
self.picker.border_select(self.border, self.mode)
|
||||||
|
else:
|
||||||
|
self.picker.move_event(self.mouse)
|
||||||
|
self.picker.release_event(self.mode)
|
||||||
|
|
||||||
|
return self.exit()
|
||||||
|
|
||||||
|
def exit(self):
|
||||||
|
#print('Border Select Finished')
|
||||||
|
|
||||||
|
bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
|
||||||
|
self.region.tag_redraw()
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_move_bone(bpy.types.Operator):
|
||||||
|
"""Tooltip"""
|
||||||
|
bl_idname = "node.move_bone"
|
||||||
|
bl_label = "Move Bone in Picker View"
|
||||||
|
|
||||||
|
'''
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
sp = context.space_data
|
||||||
|
|
||||||
|
if not sp.tree_type == 'RigPickerTree':
|
||||||
|
return
|
||||||
|
|
||||||
|
ob = context.object
|
||||||
|
return ob and ob in PICKERS
|
||||||
|
'''
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||||
|
|
||||||
|
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
|
||||||
|
|
||||||
|
context.window_manager.modal_handler_add(self)
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
|
||||||
|
delta_x = (event.mouse_region_x - self.mouse[0]) / 1000
|
||||||
|
delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for bone, matrix in self.bone_data.items():
|
||||||
|
bone.matrix.translation = matrix.translation + Vector((delta_x, 0, delta_y))
|
||||||
|
|
||||||
|
|
||||||
|
#print(delta_x, delta_y)
|
||||||
|
|
||||||
|
if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
if event.type=="RIGHTMOUSE":
|
||||||
|
for bone, matrix in self.bone_data.items():
|
||||||
|
bone.matrix = matrix
|
||||||
|
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_function_execute(bpy.types.Operator):
|
||||||
|
bl_idname = "rigpicker.function_execute"
|
||||||
|
bl_label = 'Function Execute'
|
||||||
|
|
||||||
|
shape_index = bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
event = self.event
|
||||||
|
ob = context.object
|
||||||
|
shape = ob.data.rig_picker['shapes'][self.shape_index]
|
||||||
|
|
||||||
|
function = shape['function']
|
||||||
|
if shape.get('variables'):
|
||||||
|
variables=shape['variables'].to_dict()
|
||||||
|
else:
|
||||||
|
variables={}
|
||||||
|
|
||||||
|
variables['event']=event
|
||||||
|
globals()[function](variables)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self,context,event):
|
||||||
|
self.event = event
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_reload_picker(bpy.types.Operator):
|
||||||
|
bl_idname = "rigpicker.reload_picker"
|
||||||
|
bl_label = 'Reload Picker'
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
PICKERS.clear()
|
||||||
|
context.region.tag_redraw()
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_toogle_bone_layer(bpy.types.Operator):
|
||||||
|
bl_idname = "rigpicker.toogle_bone_layer"
|
||||||
|
bl_label = 'Toogle Bone Layer'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if not context.space_data.tree_type == 'RigPickerTree':
|
||||||
|
return
|
||||||
|
|
||||||
|
ob = context.object
|
||||||
|
picker = PICKERS.get(ob)
|
||||||
|
|
||||||
|
if picker.hover_shape and picker.hover_shape.type=='bone':
|
||||||
|
return True
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ob = context.object
|
||||||
|
picker = PICKERS.get(ob)
|
||||||
|
|
||||||
|
bone = picker.hover_shape.bone
|
||||||
|
hide = picker.hover_shape.hide
|
||||||
|
|
||||||
|
|
||||||
|
if bone:
|
||||||
|
for i, l in enumerate(bone.bone.layers):
|
||||||
|
if l:
|
||||||
|
ob.data.layers[i] = hide
|
||||||
|
|
||||||
|
print('Bone Layer toogle')
|
||||||
|
|
||||||
|
#if picker.hover_bone:
|
||||||
|
context.region.tag_redraw()
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_call_operator(bpy.types.Operator):
|
||||||
|
bl_idname = "rigpicker.call_operator"
|
||||||
|
bl_label = 'Toogle Bone Layer'
|
||||||
|
|
||||||
|
operator: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
sp = context.space_data
|
||||||
|
return (sp.type=='NODE_EDITOR' and sp.tree_type != 'RigPickerTree')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
|
||||||
|
print('CALL OPERATOR', self.operator)
|
||||||
|
|
||||||
|
exec(self.operator)
|
||||||
|
|
||||||
|
context.region.tag_redraw()
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_MT_context_menu(bpy.types.Menu):
|
||||||
|
bl_label = "Context Menu"
|
||||||
|
bl_idname = "rigpicker.context_menu"
|
||||||
|
|
||||||
|
# Set the menu operators and draw functions
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
|
||||||
|
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
|
||||||
|
|
||||||
|
'''
|
||||||
|
class RP_OT_ui_draw(bpy.types.Operator):
|
||||||
|
bl_idname = "rigpicker.ui_draw"
|
||||||
|
bl_label = "Rig UI Draw"
|
||||||
|
|
||||||
|
_handle = None
|
||||||
|
tmp_ob = None
|
||||||
|
tmp_bones = []
|
||||||
|
start = (0,0)
|
||||||
|
end = (0,0)
|
||||||
|
border = ((0,0),(0,0),(0,0),(0,0))
|
||||||
|
is_border = False
|
||||||
|
press = False
|
||||||
|
scale = 1
|
||||||
|
offset = 0
|
||||||
|
outside_point = (-1,-1)
|
||||||
|
addon_keymaps = []
|
||||||
|
|
||||||
|
def set_shorcut(self,context):
|
||||||
|
ob = context.object
|
||||||
|
addon = bpy.context.window_manager.keyconfigs.addon
|
||||||
|
|
||||||
|
if ob and ob.type =='ARMATURE' and ob.data.rig_picker and addon:
|
||||||
|
for i,shape in [(i,s) for i,s in enumerate(ob.data.rig_picker['shapes']) if s.get('function') and s.get('shortcut')]:
|
||||||
|
km = addon.keymaps.new(name = 'Image Generic', space_type = 'IMAGE_EDITOR',region_type = 'WINDOW')
|
||||||
|
|
||||||
|
split = shape["shortcut"].split(' ')
|
||||||
|
if len(split)==1:
|
||||||
|
shortcut = shape["shortcut"].upper()
|
||||||
|
modifier = None
|
||||||
|
else:
|
||||||
|
shortcut = split[1].upper()
|
||||||
|
modifier = split[0].lower()
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("rigpicker.function_execute", type = shortcut, value = "CLICK")
|
||||||
|
kmi.properties.shape_index = i
|
||||||
|
|
||||||
|
if modifier:
|
||||||
|
setattr(kmi,modifier,True)
|
||||||
|
|
||||||
|
self.addon_keymaps.append(km)
|
||||||
|
|
||||||
|
def remove_shorcut(self,context):
|
||||||
|
# Remove Shortcut
|
||||||
|
wm = bpy.context.window_manager
|
||||||
|
for km in self.addon_keymaps:
|
||||||
|
for kmi in km.keymap_items:
|
||||||
|
km.keymap_items.remove(kmi)
|
||||||
|
|
||||||
|
self.addon_keymaps.clear()
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
inside = is_over_region(self,context,event)
|
||||||
|
|
||||||
|
if context.object and context.object.type == 'ARMATURE' and context.area:
|
||||||
|
if not context.screen.is_animation_playing:
|
||||||
|
if self.tmp_ob != context.object:
|
||||||
|
context.area.tag_redraw()
|
||||||
|
self.remove_shorcut(context)
|
||||||
|
self.set_shorcut(context)
|
||||||
|
self.tmp_ob = context.object
|
||||||
|
|
||||||
|
if self.tmp_bones != context.selected_pose_bones:
|
||||||
|
context.area.tag_redraw()
|
||||||
|
self.tmp_bones = context.selected_pose_bones
|
||||||
|
|
||||||
|
if inside:
|
||||||
|
context.area.tag_redraw()
|
||||||
|
|
||||||
|
if event.type == 'LEFTMOUSE':
|
||||||
|
if event.value == 'PRESS': # start selection
|
||||||
|
if inside:
|
||||||
|
self.start = (event.mouse_region_x,event.mouse_region_y)
|
||||||
|
self.press = True
|
||||||
|
|
||||||
|
elif event.value == 'RELEASE' and self.press:
|
||||||
|
self.end = (event.mouse_region_x, event.mouse_region_y)
|
||||||
|
|
||||||
|
select_bone(self, context, event)
|
||||||
|
bpy.ops.ed.undo_push()
|
||||||
|
|
||||||
|
self.is_border= False
|
||||||
|
self.press = False
|
||||||
|
|
||||||
|
if event.type == 'MOUSEMOVE':
|
||||||
|
self.end = (event.mouse_region_x, event.mouse_region_y)
|
||||||
|
|
||||||
|
if self.press:
|
||||||
|
b_x = (min(self.start[0], self.end[0]), max(self.start[0], self.end[0]))
|
||||||
|
b_y = (min(self.start[1], self.end[1]), max(self.start[1], self.end[1]))
|
||||||
|
self.border = ((b_x[0], b_y[1]), (b_x[1], b_y[1]), (b_x[1], b_y[0]), (b_x[0], b_y[0]))
|
||||||
|
self.is_border = True if (b_x[1]-b_x[0])+(b_y[1]-b_y[0]) > 4 else False
|
||||||
|
|
||||||
|
if self.is_border:
|
||||||
|
select_bone(self, context, event)
|
||||||
|
|
||||||
|
elif event.type in {'ESC',} and inside:
|
||||||
|
bpy.types.SpaceImageEditor.draw_handler_remove(self._handle, 'WINDOW')
|
||||||
|
self.remove_shorcut(context)
|
||||||
|
|
||||||
|
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
return {'PASS_THROUGH'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
#shortcut Creation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
context.space_data.image = None
|
||||||
|
self.adress = context.area.as_pointer()
|
||||||
|
args = (self, context)
|
||||||
|
|
||||||
|
self._handle = bpy.types.SpaceImageEditor.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
|
||||||
|
|
||||||
|
|
||||||
|
context.window_manager.modal_handler_add(self)
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
'''
|
||||||
|
|
||||||
|
keymaps = []
|
||||||
|
def register_keymaps():
|
||||||
|
wm = bpy.context.window_manager
|
||||||
|
|
||||||
|
km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
|
||||||
|
kmi.properties.operator = "bpy.ops.animtoolbox.reset_bone()"
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS")
|
||||||
|
kmi.properties.operator = "bpy.ops.pose.select_all(action='SELECT')"
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS", alt=True)
|
||||||
|
kmi.properties.operator = "bpy.ops.pose.select_all(action='DESELECT')"
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("node.move_bone", type="G", value="PRESS")
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("wm.call_menu", type="RIGHTMOUSE", value="PRESS")
|
||||||
|
kmi.properties.name = "rigpicker.context_menu"
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
|
||||||
|
kmi.properties.mode = 'SET'
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True)
|
||||||
|
kmi.properties.mode = 'EXTEND'
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
#kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True)
|
||||||
|
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True)
|
||||||
|
kmi.properties.mode = 'SUBSTRACT'
|
||||||
|
|
||||||
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
def unregister_keymaps():
|
||||||
|
for km, kmi in keymaps:
|
||||||
|
km.keymap_items.remove(kmi)
|
||||||
|
keymaps.clear()
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
RP_OT_box_select,
|
||||||
|
RP_OT_function_execute,
|
||||||
|
RP_OT_reload_picker,
|
||||||
|
RP_OT_toogle_bone_layer,
|
||||||
|
RP_OT_call_operator,
|
||||||
|
RP_MT_context_menu,
|
||||||
|
RP_OT_move_bone
|
||||||
|
#RP_OT_ui_draw
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
register_keymaps()
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
unregister_keymaps()
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,217 @@
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from .func_shape import get_picker_datas
|
||||||
|
from .utils import is_shape, find_mirror, link_mat_to_object
|
||||||
|
#import os
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_create_shape(bpy.types.Operator):
|
||||||
|
bl_label = 'Create UI shape'
|
||||||
|
bl_idname = 'rigpicker.create_shape'
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.object and context.object.mode == 'POSE')
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
scn = context.scene
|
||||||
|
vl = context.view_layer
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
for bone in context.selected_pose_bones:
|
||||||
|
name = bone.name
|
||||||
|
|
||||||
|
mesh = bpy.data.meshes.new(name)
|
||||||
|
ob = bpy.data.objects.new(name, mesh)
|
||||||
|
|
||||||
|
ob.rig_picker.name = name
|
||||||
|
|
||||||
|
verts=[(0,0,0)]
|
||||||
|
edges = []
|
||||||
|
faces =[]
|
||||||
|
|
||||||
|
mesh.from_pydata(verts, edges, faces)
|
||||||
|
|
||||||
|
for c in scn.rig_picker.canvas.users_collection:
|
||||||
|
c.objects.link(ob)
|
||||||
|
|
||||||
|
ob.location.z = 0.05
|
||||||
|
ob.location.x = offset
|
||||||
|
|
||||||
|
for obj in scn.objects:
|
||||||
|
obj.select_set(False)
|
||||||
|
|
||||||
|
vl.objects.active = ob
|
||||||
|
ob.select_set(True)
|
||||||
|
|
||||||
|
offset += 0.05
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_name_from_bone(bpy.types.Operator):
|
||||||
|
bl_label = 'Name Shape from selected bones'
|
||||||
|
bl_idname = 'rigpicker.name_from_bone'
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
scene = context.scene
|
||||||
|
rig = scene.rig_picker.rig
|
||||||
|
bone = rig.data.bones.active
|
||||||
|
|
||||||
|
if bone:
|
||||||
|
context.object.name = bone.name
|
||||||
|
context.object.rig_picker.name = bone.name
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_mirror_shape(bpy.types.Operator):
|
||||||
|
bl_label = 'Mirror UI shape'
|
||||||
|
bl_idname = 'rigpicker.mirror_shape'
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
scn = context.scene
|
||||||
|
|
||||||
|
for ob in bpy.context.selected_objects:
|
||||||
|
|
||||||
|
name = find_mirror(ob.name)
|
||||||
|
link_mat_to_object(ob)
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
name = ob.name + '_flip'
|
||||||
|
|
||||||
|
old_shape = bpy.data.objects.get(name)
|
||||||
|
old_mat = None
|
||||||
|
if old_shape:
|
||||||
|
old_mat = next((sl.material for sl in old_shape.material_slots if sl.material), None)
|
||||||
|
bpy.data.objects.remove(old_shape)
|
||||||
|
|
||||||
|
mirror_ob = ob.copy()
|
||||||
|
mirror_ob.data = ob.data
|
||||||
|
mirror_ob.name = name
|
||||||
|
|
||||||
|
if old_mat:
|
||||||
|
#mirror_ob.data.materials.clear()
|
||||||
|
#mirror_ob.data.materials.append(None)
|
||||||
|
#mirror_ob.material_slots[0].link = 'OBJECT'
|
||||||
|
|
||||||
|
mirror_ob.material_slots[0].material = old_mat
|
||||||
|
#mirror_ob = bpy.data.objects.new(name,ob.data.copy())
|
||||||
|
|
||||||
|
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':
|
||||||
|
mirror_ob.rig_picker.name = find_mirror(ob.rig_picker.name)
|
||||||
|
|
||||||
|
elif ob.rig_picker.shape_type == 'FUNCTION':
|
||||||
|
args = {}
|
||||||
|
for key,value in eval(ob.rig_picker.arguments).items():
|
||||||
|
if type(value) == list:
|
||||||
|
mirrored_value = []
|
||||||
|
for item in value:
|
||||||
|
mirrored_value.append(find_mirror(item))
|
||||||
|
|
||||||
|
elif type(value) == str:
|
||||||
|
mirrored_value = find_mirror(value)
|
||||||
|
args[key] = mirrored_value
|
||||||
|
|
||||||
|
mirror_ob.rig_picker.arguments = str(args)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_select_shape_type(bpy.types.Operator):
|
||||||
|
bl_label = 'Select Shape by Type'
|
||||||
|
bl_idname = 'rigpicker.select_shape_type'
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
shape_type = bpy.props.EnumProperty(items =[(i.upper(), i, '') for i in ('Bone', 'Display', 'Function')])
|
||||||
|
|
||||||
|
def draw(self,context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
col.prop(self,'shape_type', expand=True)
|
||||||
|
|
||||||
|
def execute(self,context):
|
||||||
|
scene = context.scene
|
||||||
|
#print(self.shape_type)
|
||||||
|
canvas = context.scene.rig_picker.canvas
|
||||||
|
if canvas:
|
||||||
|
for ob in [o for o in bpy.data.objects if o.layers==canvas.layers]:
|
||||||
|
if ob.type in ['MESH', 'CURVE', 'FONT'] and ob.rig_picker.shape_type == self.shape_type:
|
||||||
|
ob.select = True
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self, width=150)
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_save_picker(bpy.types.Operator):
|
||||||
|
bl_label = 'Store UI Data'
|
||||||
|
bl_idname = 'rigpicker.save_picker'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
scn = context.scene
|
||||||
|
canvas= scn.rig_picker.canvas
|
||||||
|
rig = scn.rig_picker.rig
|
||||||
|
shapes = [o for o in scn.objects if o != canvas and is_shape(o)]
|
||||||
|
|
||||||
|
if not rig:
|
||||||
|
self.report({'ERROR'}, 'Choose a Rig')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
if not canvas:
|
||||||
|
self.report({'ERROR'}, 'Choose a Canvas')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
|
data = get_picker_datas(shapes, canvas, rig)
|
||||||
|
|
||||||
|
picker_path = Path(bpy.path.abspath(scn.rig_picker.destination))
|
||||||
|
picker_path.write_text(json.dumps(data))
|
||||||
|
|
||||||
|
bpy.ops.rigpicker.reload_picker()
|
||||||
|
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
RP_OT_create_shape,
|
||||||
|
RP_OT_name_from_bone,
|
||||||
|
RP_OT_mirror_shape,
|
||||||
|
RP_OT_select_shape_type,
|
||||||
|
RP_OT_save_picker
|
||||||
|
)
|
||||||
|
|
||||||
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
|
@ -0,0 +1,110 @@
|
||||||
|
import bpy
|
||||||
|
#import collections
|
||||||
|
#import inspect
|
||||||
|
from .utils import get_operator_from_id, get_mat
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PT_picker_maker_panel(bpy.types.Panel):
|
||||||
|
bl_label = 'Rig UI'
|
||||||
|
bl_category = 'RIG Tools'
|
||||||
|
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':
|
||||||
|
layout.prop(ob.data.rig_picker, 'source', text='Picker')
|
||||||
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
|
@ -0,0 +1,589 @@
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import gpu
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
import bgl
|
||||||
|
import blf
|
||||||
|
from mathutils import bvhtree, Vector
|
||||||
|
from mathutils.geometry import intersect_point_quad_2d, intersect_point_tri_2d, intersect_tri_tri_2d
|
||||||
|
from .constants import PICKERS
|
||||||
|
from .utils import get_operator_from_id
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Shape:
|
||||||
|
def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None):
|
||||||
|
|
||||||
|
self.type = 'display'
|
||||||
|
self.picker = picker
|
||||||
|
self.rig = picker.rig
|
||||||
|
|
||||||
|
self.hover = False
|
||||||
|
self.press = False
|
||||||
|
|
||||||
|
|
||||||
|
self.shader = gpu.shader.from_builtin('2D_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_color = [1, 1, 1, 0.1]
|
||||||
|
|
||||||
|
self.tooltip = tooltip
|
||||||
|
self.color = color
|
||||||
|
self.points = points
|
||||||
|
self.polygons = polygons 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.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges)
|
||||||
|
|
||||||
|
#if polygons:
|
||||||
|
# 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
|
||||||
|
def color(self):
|
||||||
|
return self._color
|
||||||
|
|
||||||
|
@color.setter
|
||||||
|
def color(self, color=None):
|
||||||
|
if not color:
|
||||||
|
color = [0.5, 0.5, 0.5, 1]
|
||||||
|
elif isinstance(color, (tuple, list)) and len(color) in (3, 4):
|
||||||
|
if len(color) == 3:
|
||||||
|
color = [*color, 1]
|
||||||
|
elif len(color) == 4:
|
||||||
|
color = list(color)
|
||||||
|
else:
|
||||||
|
raise Exception('color must have a len of 3 or 4')
|
||||||
|
else:
|
||||||
|
raise Exception(f'color is {type(color)} must be None or (tuple, list)')
|
||||||
|
|
||||||
|
#self.shader.uniform_float("color", color)
|
||||||
|
self._color = color
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
|
||||||
|
self.shader.bind()
|
||||||
|
self.shader.uniform_float("color", self.color)
|
||||||
|
|
||||||
|
if self.polygons:
|
||||||
|
self.p_batch.draw(self.shader)
|
||||||
|
if self.edges:
|
||||||
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
|
def move_event(self, location):
|
||||||
|
if not intersect_point_quad_2d(location, *self.bound):
|
||||||
|
self.hover = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
for p in self.polygons:
|
||||||
|
if intersect_point_tri_2d(location, *[self.points[i] for i in p]):
|
||||||
|
self.hover = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.hover = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def press_event(self, mode='SET'):
|
||||||
|
self.press = True
|
||||||
|
|
||||||
|
def release_event(self, mode='SET'):
|
||||||
|
self.press = False
|
||||||
|
|
||||||
|
|
||||||
|
class BoneShape(Shape):
|
||||||
|
def __init__(self, picker, points, polygons, edges, bone, tooltip='', color=None):
|
||||||
|
super().__init__(picker, points=points, polygons=polygons, edges=edges,
|
||||||
|
tooltip=tooltip, color=color)
|
||||||
|
|
||||||
|
self.type = 'bone'
|
||||||
|
self.bone = bone
|
||||||
|
self.active_color = [1, 1, 1, 0.1]
|
||||||
|
#self.contour_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||||
|
|
||||||
|
#self.contour_batches = []
|
||||||
|
|
||||||
|
#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.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]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def select(self):
|
||||||
|
return self.bone in (bpy.context.selected_pose_bones or []) #self.bone.bone.select
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active(self):
|
||||||
|
return self.bone == bpy.context.active_pose_bone #self.rig.data.bones.active == self.bone.bone
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hide(self):
|
||||||
|
#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 len(set(bl).intersection(rl))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bone_color(self):
|
||||||
|
bone = self.bone.bone
|
||||||
|
|
||||||
|
bl = bone.layers
|
||||||
|
rl = self.rig.data.layers
|
||||||
|
|
||||||
|
if self.select and self.active:
|
||||||
|
return self.bone_colors['active']
|
||||||
|
elif self.select:
|
||||||
|
return self.bone_colors['select']
|
||||||
|
elif self.hide:
|
||||||
|
return self.bone_colors['hide']
|
||||||
|
else:
|
||||||
|
return self.bone_colors['normal']
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
if self.hide:
|
||||||
|
self.shader.uniform_float("color", (*self.color[:3], 0.4))
|
||||||
|
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()
|
||||||
|
|
||||||
|
if self.select:
|
||||||
|
color = self.hover_color
|
||||||
|
|
||||||
|
if self.select or self.hover:
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
#self.contour_shader.bind()
|
||||||
|
|
||||||
|
#print(self.bone_color)
|
||||||
|
if self.select or self.active:
|
||||||
|
bgl.glLineWidth(2)
|
||||||
|
#if not self.hide:
|
||||||
|
self.shader.uniform_float("color", self.bone_color)
|
||||||
|
#for b in self.contour_batches:
|
||||||
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
|
bgl.glLineWidth(1)
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
def release_event(self, mode='SET'):
|
||||||
|
super().release_event(mode)
|
||||||
|
|
||||||
|
if self.hide:
|
||||||
|
return
|
||||||
|
|
||||||
|
select = True
|
||||||
|
if mode == 'SUBSTRACT':
|
||||||
|
select = False
|
||||||
|
|
||||||
|
if self.hover:
|
||||||
|
self.bone.bone.select = select
|
||||||
|
|
||||||
|
if mode != 'SUBSTRACT':
|
||||||
|
self.rig.data.bones.active = self.bone.bone
|
||||||
|
else:
|
||||||
|
self.bone.bone.select = select
|
||||||
|
|
||||||
|
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 self.hide:
|
||||||
|
self.bone.bone.select = False
|
||||||
|
return
|
||||||
|
|
||||||
|
bound_tri1 = self.bound[0], self.bound[1], self.bound[2]
|
||||||
|
bound_tri2 = self.bound[2], self.bound[3], self.bound[0]
|
||||||
|
|
||||||
|
border_tri1 = border[0], border[1], border[2]
|
||||||
|
border_tri2 = border[2], border[3], border[0]
|
||||||
|
|
||||||
|
if (not intersect_tri_tri_2d(*border_tri1, *bound_tri1) and
|
||||||
|
not intersect_tri_tri_2d(*border_tri1, *border_tri2) and
|
||||||
|
not intersect_tri_tri_2d(*border_tri2, *border_tri2) and
|
||||||
|
not intersect_tri_tri_2d(*border_tri2, *border_tri2)):
|
||||||
|
return
|
||||||
|
|
||||||
|
select = True
|
||||||
|
if mode == 'SUBSTRACT':
|
||||||
|
select = False
|
||||||
|
|
||||||
|
for polygon in self.polygons:
|
||||||
|
points = [self.points[i] for i in polygon]
|
||||||
|
|
||||||
|
if intersect_tri_tri_2d(*border_tri1, *points):
|
||||||
|
self.bone.bone.select = select
|
||||||
|
return
|
||||||
|
|
||||||
|
if intersect_tri_tri_2d(*border_tri2, *points):
|
||||||
|
self.bone.bone.select = select
|
||||||
|
return
|
||||||
|
|
||||||
|
'''
|
||||||
|
for b in border:
|
||||||
|
if intersect_point_tri_2d(b, *points):
|
||||||
|
self.bone.bone.select = select
|
||||||
|
return
|
||||||
|
|
||||||
|
for p in points:
|
||||||
|
if intersect_point_quad_2d(p, *border):
|
||||||
|
self.bone.bone.select = select
|
||||||
|
return
|
||||||
|
'''
|
||||||
|
|
||||||
|
class OperatorShape(Shape):
|
||||||
|
def __init__(self, picker, points, polygons, operator, tooltip='', color=None):
|
||||||
|
super().__init__(picker, points=points, polygons=polygons, tooltip=tooltip, color=color)
|
||||||
|
|
||||||
|
self.type = 'operator'
|
||||||
|
self.active_color = [1, 1, 1, 0.15]
|
||||||
|
self.press_color = [0, 0, 0, 0.25]
|
||||||
|
self.operator = operator
|
||||||
|
#self.arguments = arguments#{k:eval(v)}
|
||||||
|
#self.operator = get_operator_from_id(self.operator)
|
||||||
|
|
||||||
|
#self.reg_args = re.compile(r'(\w+)=')
|
||||||
|
'''
|
||||||
|
def parse_args(self):
|
||||||
|
|
||||||
|
args = self.reg_args.split(self.arguments)[1:]
|
||||||
|
#print(args, zip(args[::2], args[1::2]))
|
||||||
|
return {k: eval(v) for k, v in zip(args[::2], args[1::2])}
|
||||||
|
#return {k:eval(v) for k, v in self.reg_args.split(self.arguments)}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def release_event(self, mode='SET'):
|
||||||
|
super().release_event(mode)
|
||||||
|
|
||||||
|
#args = self.parse_args()
|
||||||
|
if not self.operator:
|
||||||
|
return
|
||||||
|
|
||||||
|
exec(self.operator)
|
||||||
|
#f'bpy.ops;{idname}'
|
||||||
|
|
||||||
|
#print(self.idname)
|
||||||
|
#print(self.arguments)
|
||||||
|
#else:
|
||||||
|
# self.bone.bone.select = False
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
super().draw()
|
||||||
|
|
||||||
|
if self.press:
|
||||||
|
color = self.press_color
|
||||||
|
elif self.hover:
|
||||||
|
color = self.hover_color
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
self.shader.uniform_float("color", color)
|
||||||
|
self.p_batch.draw(self.shader)
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Picker:
|
||||||
|
def __init__(self, rig, shapes):
|
||||||
|
|
||||||
|
self.region = bpy.context.region
|
||||||
|
self.rig = rig
|
||||||
|
|
||||||
|
self.shapes = []
|
||||||
|
self.box_select = None
|
||||||
|
self.hover_shape = None
|
||||||
|
self.shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||||
|
|
||||||
|
self.tooltip_shape = None
|
||||||
|
self.tooltip_mouse = None
|
||||||
|
self.tooltip = ''
|
||||||
|
|
||||||
|
self.timer = None
|
||||||
|
self.mouse = None
|
||||||
|
|
||||||
|
for s in shapes:
|
||||||
|
if not s['points']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if s['type'] in ('CANVAS', 'DISPLAY'):
|
||||||
|
shape = Shape(
|
||||||
|
self,
|
||||||
|
points=s['points'],
|
||||||
|
polygons=s['polygons'],
|
||||||
|
edges=s['edges'],
|
||||||
|
color=s['color']
|
||||||
|
)
|
||||||
|
|
||||||
|
elif s['type'] == 'BONE':
|
||||||
|
shape = BoneShape(
|
||||||
|
self,
|
||||||
|
points=s['points'],
|
||||||
|
polygons=s['polygons'],
|
||||||
|
edges=s['edges'],
|
||||||
|
bone=rig.pose.bones.get(s['bone']),
|
||||||
|
color=s['color']
|
||||||
|
)
|
||||||
|
|
||||||
|
elif s['type'] == 'OPERATOR':
|
||||||
|
shape = OperatorShape(
|
||||||
|
self,
|
||||||
|
points=s['points'],
|
||||||
|
polygons=s['polygons'],
|
||||||
|
operator=s['operator'],
|
||||||
|
color=s['color']
|
||||||
|
)
|
||||||
|
|
||||||
|
self.shapes.append(shape)
|
||||||
|
|
||||||
|
def press_event(self, mode='SET'):
|
||||||
|
for s in self.shapes:
|
||||||
|
if s.hover:
|
||||||
|
s.press_event(mode)
|
||||||
|
else:
|
||||||
|
s.press = False
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
#print('PICKER release event', mode)
|
||||||
|
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
|
for s in self.shapes:
|
||||||
|
if s.hover:
|
||||||
|
s.release_event(mode)
|
||||||
|
|
||||||
|
s.press = False
|
||||||
|
|
||||||
|
|
||||||
|
def tooltip_event(self):
|
||||||
|
print('Tooltip Event', self)
|
||||||
|
|
||||||
|
if self.hover_shape:
|
||||||
|
if self.hover_shape.type == 'bone':
|
||||||
|
self.tooltip = self.hover_shape.bone.name
|
||||||
|
self.tooltip_shape = self.hover_shape
|
||||||
|
else:
|
||||||
|
self.tooltip = ''
|
||||||
|
self.tooltip_shape = None
|
||||||
|
|
||||||
|
self.tooltip_mouse = self.mouse
|
||||||
|
|
||||||
|
self.timer.cancel()
|
||||||
|
|
||||||
|
self.region.tag_redraw()
|
||||||
|
|
||||||
|
'''
|
||||||
|
picker.tooltip_event(event='SHOW')
|
||||||
|
region.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#context.region.tag_redraw()
|
||||||
|
|
||||||
|
picker.tooltip_event(event='HIDE')
|
||||||
|
bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
def tooltip_event(self, event):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.tooltip = ''
|
||||||
|
|
||||||
|
if event == 'SHOW':
|
||||||
|
if self.hover_shape.type == 'bone':
|
||||||
|
self.tooltip = self.hover_shape.bone.name
|
||||||
|
|
||||||
|
#bpy.context.region.tag_redraw()
|
||||||
|
'''
|
||||||
|
|
||||||
|
def border_select(self, border, mode):
|
||||||
|
border = [bpy.context.region.view2d.region_to_view(*b) for b in border]
|
||||||
|
|
||||||
|
if mode == 'SET':
|
||||||
|
for b in self.rig.pose.bones:
|
||||||
|
b.bone.select = False
|
||||||
|
|
||||||
|
for s in (s for s in self.shapes if s.type=='bone'):
|
||||||
|
s.border_select(border, mode)
|
||||||
|
|
||||||
|
def move_event(self, location):
|
||||||
|
self.mouse = location
|
||||||
|
location = self.region.view2d.region_to_view(*location)
|
||||||
|
|
||||||
|
for s in self.shapes:
|
||||||
|
if s.move_event(location):
|
||||||
|
self.hover_shape = s
|
||||||
|
|
||||||
|
#if point_inside_rectangle(self.end, bound):
|
||||||
|
# over = point_over_shape(self.end,points, edges)
|
||||||
|
|
||||||
|
#if bpy.app.timers.is_registered(self.tooltip_event):
|
||||||
|
#try:
|
||||||
|
# bpy.app.timers.unregister(self.tooltip_event)
|
||||||
|
#except:
|
||||||
|
# pass
|
||||||
|
if self.tooltip_shape is not self.hover_shape:
|
||||||
|
self.tooltip = ''
|
||||||
|
|
||||||
|
if self.timer:
|
||||||
|
self.timer.cancel()
|
||||||
|
|
||||||
|
self.timer = threading.Timer(0.5, self.tooltip_event)
|
||||||
|
self.timer.start()
|
||||||
|
|
||||||
|
|
||||||
|
#bpy.app.timers.register(self.tooltip_event, first_interval=1)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
for s in self.shapes:
|
||||||
|
s.draw()
|
||||||
|
'''
|
||||||
|
if self.box_select:
|
||||||
|
|
||||||
|
self.box_shader.uniform_float("color", self.box_select_color)
|
||||||
|
batch = batch_for_shader(self.shader, 'LINE_LOOP', {"pos": []})
|
||||||
|
|
||||||
|
|
||||||
|
for b in self.contour_batches:
|
||||||
|
b.draw(self.contour_shader)
|
||||||
|
|
||||||
|
self.batch.draw(self.shader)
|
||||||
|
|
||||||
|
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def draw_callback_view():
|
||||||
|
sp = bpy.context.space_data
|
||||||
|
|
||||||
|
if not sp.tree_type == 'RigPickerTree':
|
||||||
|
return
|
||||||
|
|
||||||
|
ob = bpy.context.object
|
||||||
|
if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.source:
|
||||||
|
return
|
||||||
|
|
||||||
|
if ob not in PICKERS:
|
||||||
|
picker_path = Path(bpy.path.abspath(ob.data.rig_picker.source))
|
||||||
|
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():
|
||||||
|
sp = bpy.context.space_data
|
||||||
|
if not sp.tree_type == 'RigPickerTree':
|
||||||
|
return
|
||||||
|
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
picker = PICKERS.get(ob)
|
||||||
|
if not picker or not picker.tooltip:
|
||||||
|
return
|
||||||
|
|
||||||
|
text = picker.tooltip
|
||||||
|
|
||||||
|
font_id = 0
|
||||||
|
#blf.dimensions(font_id, text)
|
||||||
|
blf.enable(font_id, blf.SHADOW)
|
||||||
|
#bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
|
||||||
|
# BLF drawing routine
|
||||||
|
blf.position(font_id, picker.tooltip_mouse[0]-5, picker.tooltip_mouse[1]+5, 0)
|
||||||
|
blf.size(font_id, 14, 0)
|
||||||
|
blf.color(font_id, 1, 1, 1, 1)
|
||||||
|
|
||||||
|
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
||||||
|
blf.shadow_offset(font_id, 1, -1)
|
||||||
|
|
||||||
|
blf.draw(font_id, text)
|
||||||
|
|
||||||
|
blf.disable(font_id, blf.SHADOW)
|
||||||
|
#bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
handle_view = None
|
||||||
|
handle_pixel = None
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
global handle_view
|
||||||
|
global handle_pixel
|
||||||
|
|
||||||
|
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
|
||||||
|
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
global handle_view
|
||||||
|
global handle_pixel
|
||||||
|
|
||||||
|
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW')
|
||||||
|
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW')
|
||||||
|
|
||||||
|
handle_view = None
|
||||||
|
handle_pixel = None
|
|
@ -0,0 +1,90 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.props import EnumProperty, StringProperty, PointerProperty
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
'''
|
||||||
|
def get_operator_items(self,context):
|
||||||
|
items = []
|
||||||
|
from . import func_picker as mod
|
||||||
|
|
||||||
|
for name, func in inspect.getmembers(mod, inspect.isfunction):
|
||||||
|
if inspect.getmodule(func) == mod:
|
||||||
|
items.append((name,name,""))
|
||||||
|
|
||||||
|
return items
|
||||||
|
'''
|
||||||
|
|
||||||
|
def bones_item(self,context):
|
||||||
|
items = []
|
||||||
|
|
||||||
|
if context.scene.rig_picker.rig:
|
||||||
|
for bone in context.scene.rig_picker.rig.pose.bones:
|
||||||
|
items.append((bone.name,bone.name,''))
|
||||||
|
return items
|
||||||
|
|
||||||
|
class RP_PG_armature_ui_settings(bpy.types.PropertyGroup):
|
||||||
|
name: StringProperty()
|
||||||
|
source: StringProperty(subtype='FILE_PATH')
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
||||||
|
shape_type: EnumProperty(items=[(i.upper(), i, "") for i in ('Bone', 'Display', 'Operator')])
|
||||||
|
#idname: StringProperty()
|
||||||
|
#arguments: StringProperty()
|
||||||
|
operator: StringProperty()
|
||||||
|
shortcut: StringProperty()
|
||||||
|
name: StringProperty()
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
|
||||||
|
rig: PointerProperty(type=bpy.types.Object)
|
||||||
|
canvas: PointerProperty(type=bpy.types.Object)
|
||||||
|
symmetry: PointerProperty(type=bpy.types.Object)
|
||||||
|
#idname: EnumProperty(items=[])
|
||||||
|
destination: StringProperty(subtype='FILE_PATH')
|
||||||
|
#bone_list: bpy.props.EnumProperty(items = bones_item)
|
||||||
|
|
||||||
|
|
||||||
|
class RP_OT_operator_selector(bpy.types.Operator):
|
||||||
|
bl_label = "Select function"
|
||||||
|
bl_idname = "rigpicker.operator_selector"
|
||||||
|
bl_property = "idname"
|
||||||
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
idname: EnumProperty(items=[])
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ob = context.object
|
||||||
|
ob.rig_picker.idname = self.idname
|
||||||
|
context.area.tag_redraw()
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
wm.invoke_search_popup(self)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
RP_PG_object_ui_settings,
|
||||||
|
RP_PG_scene_ui_settings,
|
||||||
|
RP_PG_armature_ui_settings,
|
||||||
|
RP_OT_operator_selector,
|
||||||
|
)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
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.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
del bpy.types.Scene.rig_picker
|
||||||
|
del bpy.types.Object.rig_picker
|
||||||
|
del bpy.types.Armature.rig_picker
|
||||||
|
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
import bpy
|
||||||
|
from mathutils import Vector,Matrix
|
||||||
|
from math import acos, pi
|
||||||
|
#from .insert_keyframe import insert_keyframe
|
||||||
|
|
||||||
|
############################
|
||||||
|
## Math utility functions ##
|
||||||
|
############################
|
||||||
|
|
||||||
|
def perpendicular_vector(v):
|
||||||
|
""" Returns a vector that is perpendicular to the one given.
|
||||||
|
The returned vector is _not_ guaranteed to be normalized.
|
||||||
|
"""
|
||||||
|
# Create a vector that is not aligned with v.
|
||||||
|
# It doesn't matter what vector. Just any vector
|
||||||
|
# that's guaranteed to not be pointing in the same
|
||||||
|
# direction.
|
||||||
|
if abs(v[0]) < abs(v[1]):
|
||||||
|
tv = Vector((1,0,0))
|
||||||
|
else:
|
||||||
|
tv = Vector((0,1,0))
|
||||||
|
|
||||||
|
# Use cross prouct to generate a vector perpendicular to
|
||||||
|
# both tv and (more importantly) v.
|
||||||
|
return v.cross(tv)
|
||||||
|
|
||||||
|
|
||||||
|
def rotation_difference(mat1, mat2):
|
||||||
|
""" Returns the shortest-path rotational difference between two
|
||||||
|
matrices.
|
||||||
|
"""
|
||||||
|
q1 = mat1.to_quaternion()
|
||||||
|
q2 = mat2.to_quaternion()
|
||||||
|
angle = acos(min(1,max(-1,q1.dot(q2)))) * 2
|
||||||
|
if angle > pi:
|
||||||
|
angle = -angle + (2*pi)
|
||||||
|
return angle
|
||||||
|
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
## "Visual Transform" helper functions ##
|
||||||
|
#########################################
|
||||||
|
|
||||||
|
def get_pose_matrix_in_other_space(mat, pose_bone):
|
||||||
|
""" Returns the transform matrix relative to pose_bone's current
|
||||||
|
transform space. In other words, presuming that mat is in
|
||||||
|
armature space, slapping the returned matrix onto pose_bone
|
||||||
|
should give it the armature-space transforms of mat.
|
||||||
|
TODO: try to handle cases with axis-scaled parents better.
|
||||||
|
"""
|
||||||
|
rest = pose_bone.bone.matrix_local.copy()
|
||||||
|
rest_inv = rest.inverted()
|
||||||
|
if pose_bone.parent:
|
||||||
|
par_mat = pose_bone.parent.matrix.copy()
|
||||||
|
par_inv = par_mat.inverted()
|
||||||
|
par_rest = pose_bone.parent.bone.matrix_local.copy()
|
||||||
|
else:
|
||||||
|
par_mat = Matrix()
|
||||||
|
par_inv = Matrix()
|
||||||
|
par_rest = Matrix()
|
||||||
|
|
||||||
|
# Get matrix in bone's current transform space
|
||||||
|
smat = rest_inv * (par_rest * (par_inv * mat))
|
||||||
|
|
||||||
|
# Compensate for non-local location
|
||||||
|
#if not pose_bone.bone.use_local_location:
|
||||||
|
# loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion()
|
||||||
|
# smat.translation = loc
|
||||||
|
|
||||||
|
return smat
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_pose_matrix(pose_bone):
|
||||||
|
""" Returns the local transform matrix of the given pose bone.
|
||||||
|
"""
|
||||||
|
return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone)
|
||||||
|
|
||||||
|
|
||||||
|
def set_pose_translation(pose_bone, mat):
|
||||||
|
""" Sets the pose bone's translation to the same translation as the given matrix.
|
||||||
|
Matrix should be given in bone's local space.
|
||||||
|
"""
|
||||||
|
if pose_bone.bone.use_local_location is True:
|
||||||
|
pose_bone.location = mat.to_translation()
|
||||||
|
else:
|
||||||
|
loc = mat.to_translation()
|
||||||
|
|
||||||
|
rest = pose_bone.bone.matrix_local.copy()
|
||||||
|
if pose_bone.bone.parent:
|
||||||
|
par_rest = pose_bone.bone.parent.matrix_local.copy()
|
||||||
|
else:
|
||||||
|
par_rest = Matrix()
|
||||||
|
|
||||||
|
q = (par_rest.inverted() * rest).to_quaternion()
|
||||||
|
pose_bone.location = q * loc
|
||||||
|
|
||||||
|
|
||||||
|
def set_pose_rotation(pose_bone, mat):
|
||||||
|
""" Sets the pose bone's rotation to the same rotation as the given matrix.
|
||||||
|
Matrix should be given in bone's local space.
|
||||||
|
"""
|
||||||
|
q = mat.to_quaternion()
|
||||||
|
|
||||||
|
if pose_bone.rotation_mode == 'QUATERNION':
|
||||||
|
pose_bone.rotation_quaternion = q
|
||||||
|
elif pose_bone.rotation_mode == 'AXIS_ANGLE':
|
||||||
|
pose_bone.rotation_axis_angle[0] = q.angle
|
||||||
|
pose_bone.rotation_axis_angle[1] = q.axis[0]
|
||||||
|
pose_bone.rotation_axis_angle[2] = q.axis[1]
|
||||||
|
pose_bone.rotation_axis_angle[3] = q.axis[2]
|
||||||
|
else:
|
||||||
|
pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode)
|
||||||
|
|
||||||
|
|
||||||
|
def set_pose_scale(pose_bone, mat):
|
||||||
|
""" Sets the pose bone's scale to the same scale as the given matrix.
|
||||||
|
Matrix should be given in bone's local space.
|
||||||
|
"""
|
||||||
|
pose_bone.scale = mat.to_scale()
|
||||||
|
|
||||||
|
|
||||||
|
def match_pose_translation(pose_bone, target_bone):
|
||||||
|
""" Matches pose_bone's visual translation to target_bone's visual
|
||||||
|
translation.
|
||||||
|
This function assumes you are in pose mode on the relevant armature.
|
||||||
|
"""
|
||||||
|
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||||
|
set_pose_translation(pose_bone, mat)
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
bpy.ops.object.mode_set(mode='POSE')
|
||||||
|
|
||||||
|
|
||||||
|
def match_pose_rotation(pose_bone, target_bone):
|
||||||
|
""" Matches pose_bone's visual rotation to target_bone's visual
|
||||||
|
rotation.
|
||||||
|
This function assumes you are in pose mode on the relevant armature.
|
||||||
|
"""
|
||||||
|
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||||
|
set_pose_rotation(pose_bone, mat)
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
bpy.ops.object.mode_set(mode='POSE')
|
||||||
|
|
||||||
|
|
||||||
|
def match_pose_scale(pose_bone, target_bone):
|
||||||
|
""" Matches pose_bone's visual scale to target_bone's visual
|
||||||
|
scale.
|
||||||
|
This function assumes you are in pose mode on the relevant armature.
|
||||||
|
"""
|
||||||
|
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||||
|
set_pose_scale(pose_bone, mat)
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
bpy.ops.object.mode_set(mode='POSE')
|
||||||
|
|
||||||
|
|
||||||
|
##############################
|
||||||
|
## IK/FK snapping functions ##
|
||||||
|
##############################
|
||||||
|
|
||||||
|
def match_pole_target(ik_first, ik_last, pole, match_bone, length):
|
||||||
|
""" Places an IK chain's pole target to match ik_first's
|
||||||
|
transforms to match_bone. All bones should be given as pose bones.
|
||||||
|
You need to be in pose mode on the relevant armature object.
|
||||||
|
ik_first: first bone in the IK chain
|
||||||
|
ik_last: last bone in the IK chain
|
||||||
|
pole: pole target bone for the IK chain
|
||||||
|
match_bone: bone to match ik_first to (probably first bone in a matching FK chain)
|
||||||
|
length: distance pole target should be placed from the chain center
|
||||||
|
"""
|
||||||
|
a = ik_first.matrix.to_translation()
|
||||||
|
b = ik_last.matrix.to_translation() + ik_last.vector
|
||||||
|
|
||||||
|
# Vector from the head of ik_first to the
|
||||||
|
# tip of ik_last
|
||||||
|
ikv = b - a
|
||||||
|
|
||||||
|
# Get a vector perpendicular to ikv
|
||||||
|
pv = perpendicular_vector(ikv).normalized() * length
|
||||||
|
|
||||||
|
def set_pole(pvi):
|
||||||
|
""" Set pole target's position based on a vector
|
||||||
|
from the arm center line.
|
||||||
|
"""
|
||||||
|
# Translate pvi into armature space
|
||||||
|
ploc = a + (ikv/2) + pvi
|
||||||
|
|
||||||
|
# Set pole target to location
|
||||||
|
mat = get_pose_matrix_in_other_space(Matrix.Translation(ploc), pole)
|
||||||
|
set_pose_translation(pole, mat)
|
||||||
|
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
bpy.ops.object.mode_set(mode='POSE')
|
||||||
|
|
||||||
|
set_pole(pv)
|
||||||
|
|
||||||
|
# Get the rotation difference between ik_first and match_bone
|
||||||
|
angle = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||||
|
|
||||||
|
# Try compensating for the rotation difference in both directions
|
||||||
|
pv1 = Matrix.Rotation(angle, 4, ikv) * pv
|
||||||
|
set_pole(pv1)
|
||||||
|
ang1 = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||||
|
|
||||||
|
pv2 = Matrix.Rotation(-angle, 4, ikv) * pv
|
||||||
|
set_pole(pv2)
|
||||||
|
ang2 = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||||
|
|
||||||
|
# Do the one with the smaller angle
|
||||||
|
if ang1 < ang2:
|
||||||
|
set_pole(pv1)
|
|
@ -0,0 +1,217 @@
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from mathutils import Vector
|
||||||
|
from mathutils.geometry import intersect_line_line_2d
|
||||||
|
|
||||||
|
|
||||||
|
def get_mat(ob):
|
||||||
|
for sl in ob.material_slots:
|
||||||
|
if sl.material:
|
||||||
|
return sl.material
|
||||||
|
|
||||||
|
def link_mat_to_object(ob):
|
||||||
|
for sl in ob.material_slots:
|
||||||
|
m = sl.material
|
||||||
|
sl.link = 'OBJECT'
|
||||||
|
sl.material = m
|
||||||
|
|
||||||
|
def get_operator_from_id(idname):
|
||||||
|
if not '.' in idname:
|
||||||
|
return
|
||||||
|
|
||||||
|
m, o = idname.split(".")
|
||||||
|
try:
|
||||||
|
op = getattr(getattr(bpy.ops, m), o)
|
||||||
|
op.get_rna_type()
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
|
||||||
|
return op
|
||||||
|
'''
|
||||||
|
def canvas_space(point,scale,offset):
|
||||||
|
return scale*Vector(point)+Vector(offset)
|
||||||
|
'''
|
||||||
|
|
||||||
|
def get_object_color(ob):
|
||||||
|
if not ob.data.materials:
|
||||||
|
return
|
||||||
|
|
||||||
|
mat = get_mat(ob)
|
||||||
|
if not mat or not mat.node_tree or not mat.node_tree.nodes:
|
||||||
|
return
|
||||||
|
|
||||||
|
emit_node = mat.node_tree.nodes.get('Emission')
|
||||||
|
if not emit_node:
|
||||||
|
return
|
||||||
|
|
||||||
|
return emit_node.inputs['Color'].default_value
|
||||||
|
|
||||||
|
def intersect_rectangles(bound, border): # returns None if rectangles don't intersect
|
||||||
|
dx = min(border[1][0],bound[1][0]) - max(border[0][0],bound[0][0])
|
||||||
|
dy = min(border[0][1],bound[0][1]) - max(border[2][1],bound[2][1])
|
||||||
|
|
||||||
|
if (dx>=0) and (dy>=0):
|
||||||
|
return dx*dy
|
||||||
|
|
||||||
|
def point_inside_rectangle(point, rect):
|
||||||
|
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)):
|
||||||
|
out = Vector(outside_point)
|
||||||
|
pt = Vector(point)
|
||||||
|
|
||||||
|
intersections = 0
|
||||||
|
for loop in loops:
|
||||||
|
for i,p in enumerate(loop):
|
||||||
|
a = Vector(verts[loop[i-1]])
|
||||||
|
b = Vector(verts[p])
|
||||||
|
|
||||||
|
if intersect_line_line_2d(pt,out,a,b):
|
||||||
|
intersections += 1
|
||||||
|
|
||||||
|
if intersections%2 == 1: #chek if the nb of intersection is odd
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def border_over_shape(border,verts,loops):
|
||||||
|
for loop in loops:
|
||||||
|
for i,p in enumerate(loop):
|
||||||
|
a = Vector(verts[loop[i-1]])
|
||||||
|
b = Vector(verts[p])
|
||||||
|
|
||||||
|
for j in range(0,4):
|
||||||
|
c = border[j-1]
|
||||||
|
d = border[j]
|
||||||
|
if intersect_line_line_2d(a,b,c,d):
|
||||||
|
return True
|
||||||
|
|
||||||
|
for point in verts:
|
||||||
|
if point_inside_rectangle(point,border):
|
||||||
|
return True
|
||||||
|
|
||||||
|
for point in border:
|
||||||
|
if point_over_shape(point,verts,loops):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def border_loop(vert, loop):
|
||||||
|
border_edge =[e for e in vert.link_edges if e.is_boundary]
|
||||||
|
|
||||||
|
if border_edge:
|
||||||
|
for edge in border_edge:
|
||||||
|
other_vert = edge.other_vert(vert)
|
||||||
|
|
||||||
|
if not other_vert in loop:
|
||||||
|
loop.append(other_vert)
|
||||||
|
border_loop(other_vert, loop)
|
||||||
|
|
||||||
|
return loop
|
||||||
|
else:
|
||||||
|
return [vert]
|
||||||
|
|
||||||
|
|
||||||
|
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
||||||
|
loops = loops or []
|
||||||
|
vert_indices = vert_indices or [v.index for v in bm.verts]
|
||||||
|
|
||||||
|
bm.verts.ensure_lookup_table()
|
||||||
|
|
||||||
|
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
|
||||||
|
if len(loop) >1:
|
||||||
|
loops.append(loop)
|
||||||
|
|
||||||
|
for v in loop:
|
||||||
|
vert_indices.remove(v.index)
|
||||||
|
|
||||||
|
if len(vert_indices):
|
||||||
|
contour_loops(bm, vert_indices[0], loops, vert_indices)
|
||||||
|
|
||||||
|
return loops
|
||||||
|
|
||||||
|
|
||||||
|
def get_IK_bones(IK_last):
|
||||||
|
ik_chain = IK_last.parent_recursive
|
||||||
|
ik_len = 0
|
||||||
|
|
||||||
|
#Get IK len:
|
||||||
|
for c in IK_last.constraints:
|
||||||
|
if c.type == 'IK':
|
||||||
|
ik_len = c.chain_count -2
|
||||||
|
break
|
||||||
|
|
||||||
|
IK_root = ik_chain[ik_len]
|
||||||
|
|
||||||
|
IK_mid= ik_chain[:ik_len]
|
||||||
|
|
||||||
|
IK_mid.reverse()
|
||||||
|
IK_mid.append(IK_last)
|
||||||
|
|
||||||
|
return IK_root,IK_mid
|
||||||
|
|
||||||
|
def find_mirror(name):
|
||||||
|
mirror = None
|
||||||
|
prop= False
|
||||||
|
|
||||||
|
if name:
|
||||||
|
|
||||||
|
if name.startswith('[')and name.endswith(']'):
|
||||||
|
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
|
||||||
|
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_shape(ob):
|
||||||
|
shape = False
|
||||||
|
|
||||||
|
if ob.hide_render:
|
||||||
|
return shape
|
||||||
|
|
||||||
|
if ob.type in ('MESH', 'CURVE', 'FONT'):
|
||||||
|
if ob.rig_picker.shape_type == 'BONE':
|
||||||
|
if ob.rig_picker.name:
|
||||||
|
shape = True
|
||||||
|
else:
|
||||||
|
shape = True
|
||||||
|
return shape
|
||||||
|
|
||||||
|
def is_over_region(self,context,event):
|
||||||
|
inside = 2 < event.mouse_region_x < context.region.width -2 and \
|
||||||
|
2 < event.mouse_region_y < context.region.height -2 and \
|
||||||
|
[a for a in context.screen.areas if a.as_pointer()==self.adress] and \
|
||||||
|
not context.screen.show_fullscreen
|
||||||
|
|
||||||
|
return inside
|
||||||
|
|
||||||
|
def bound_box_center(ob):
|
||||||
|
points = [ob.matrix_world@Vector(p) for p in ob.bound_box]
|
||||||
|
|
||||||
|
x = [v[0] for v in points]
|
||||||
|
y = [v[1] for v in points]
|
||||||
|
z = [v[2] for v in points]
|
||||||
|
|
||||||
|
return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points))
|
Loading…
Reference in New Issue