前言
最近翻 RJ 的博客,找到了一个可以K帧轴心的绑定设置 链接
恰巧动画组有人也有相关的轴心K帧的需求,于是我就在 RJ 的基础上进行了进一步研究。
这篇文章是 2019 年末的研究成果了,因为各种原因拖到了现在才开始写博客做记录。
最近临近春节,年会等事情比较多,暴饮暴食,结果也胖了不少(:з」∠)
说会正题,这次尝试了 Script Job 来实现自动执行代码效果,也算是花了不少心思了。
问题分析
几个星期前动画人员做动画,遇到了个挪动轴心点K帧的问题。
通过挪动轴心点来实现不同轴心的旋转定义,如此旋转效果才比较合理。
比如说角色用剑砸到地上,这个时候剑的轴心在手上
砸到地上之后,希望剑来反向控制角色,这个时候轴心应该是剑接触的地面上。
解决方案 - 轴心点控制
最简单的方法就是控制 transform 节点的 rotatePivot 属性
RJ大佬就是比较经典的绑定制作方案,其实也可以更加简化操作。
这样连接 Locator 就可以控制到物体的轴心点位置了。
但是这种搭建方法会有一些问题
没错,这种方案的轴心点一旦移动旋转了之后,再次移动会给模型带来位置的偏移。
于是下一步的解决方案就是重点解决这个问题。
解决方案 - 事件钩子调用
首先可以看到,如果是正常的 pivot 模式下是可以任意移动而不会造成模型偏移的。
因此要实现这个方案需要研究这种 pivot 模式下是如何让模型不产生偏移的。
最初我打算使用 绑定的方式 来实现这种效果,但是始终解决不了问题。
后来我发现 transform 节点下有 rotatePivotTranslate 属性
当我移动轴心点的时候,模型偏移的数值会自动计算赋值到这个属性上。
因此只要算出这个数值然后赋值到这个属性上就可以了。
然而我折腾了好久,发现如果不通过节点来计算的话,就必须要移动过程中更新这个属性,需要用到 scirptJob
但是这样其实和直接移动轴心操作是没有区别的(:з」∠)
于是最初我是采用了 scirptJob 的生成方案
但是 scirptJob 有一个缺点就是不能保存下来,还需要使用 scriptNode 实现打开加载功能,同时还要避免生成过多的垃圾 scriptNode 节点。
因此整一个实现方法还是有些许复杂,不比开发一个节点简单多少
OpenMaya 事件钩子调用
scriptJob 解决方案有一些问题,因此代码上面注释掉了。
scirptJob 是有 attributeChange 事件可以捕获物体的移动然后执行相应的脚本
但是这个更新是当物体移动停止之后才开始执行的,没有办法实现类似于节点的操作,一边移动一边更新
因此后面我将代码升级成了 OpenMaya MMessage 的方案。
调用 OpenMaya.MNodeMessage.addAttributeChangedCallback 函数可以实现节点动态改变动态更新效果。
具体的实现方法参考了autodesk论坛的回答 链接
1 | def pivotEvent(sel,loc,msg, m_plug, otherMplug, clientData): |
代码层面上 sel 和 loc 都是我额外传入的参数
msg 后面的参数是这个函数回调传入的参数。
msg 是类型数字 这里回调涉及到所有的情况,因此需要通过 msg 来过滤属性变化的情况。
后续就是判断传入的参数是否存在,如果存在的话更新 sel 变量的 旋转轴心
相当于一个物体的移动,无时无刻都在同步另一个物体轴心点位置。
scirptJob 删除钩子
传入的回调事件如果不删除的话会一直循环执行的,如果不及时删除可能会严重影响Maya的性能,甚至导致Maya崩溃。
因此这里通过 scriptJob runOnce 的方案来解决掉 OpenMaya 当中的事件
1 | # 此处省略代码 ... |
因此两个 scirptJob 会分别在 节点删除以及打开新场景的时候删除事件。
ScriptNode 重置效果
无论是 scriptJob 还是 MMessage 事件都是无法保存到文件里面的
因此需要在打开文件的时候重新创建事件,确保脚本运行正常。
而这里可以通过 ScriptNode 来实现。
scriptNode 命令提供了几种监听状态来实现
- 0 Execute on demand.
- 1 Execute on file load or on node deletion.
- 2 Execute on file load or on node deletion when not in batch mode.
- 3 Internal
- 4 Execute on software render
- 5 Execute on software frame render
- 6 Execute on scene configuration
- 7 Execute on time changed
文档里面有说明,而且命令运行完之后会生成一个 scriptNode 节点
为了保证文件的干净,这里使用 UUID 来生成全局唯一标识符
通过这个标识符命名 scriptNode 就可以让这个 ScriptNode 几乎在所有的 Maya 场景中是唯一的,
也就可以光明正大删除 ScriptNode 而不会造成误删的情况。
(注:最初我是想用序号的方式的,想想就难办)
1 | from textwrap import dedent |
虽然这样设置可以完美实现移动轴心的效果
但是却会丢失物体原先的位置,K帧可能会导致物体无法归位,还是有很严重的隐患(:з」∠)
这是鱼和熊掌不可兼得,两个方案都各有各自的问题。
相比于隐藏在暗处的方案二,我更加推崇简单的方案一。
毕竟方案二很可能会导致前面的动画毁于一旦的,方案一的问题至少还是肉眼可见的。
总结
这次又进一步尝试了 Maya 的事件系统,感觉用起来还是有点危险的。
测试 OpenMaya 的时候就忘记了过滤 msg 在输出,结果造成了永动机,被迫强制关闭 Maya 。
回想起最初接触 scriptJob 还是在 2年前,那个时候看教程,通过 scriptjob 实现 IKFK 自动切换的黑科技
简直是印象深刻,如今发现 Maya API 里面的事件系统更加全面,能实现的 黑科技 可谓是数不胜数呀。