【Maya C++开发】MPxIkSolverNode开发踩坑记录

前言

记录在测试IKSolver节点开发过程中,由于文档解释不全的原因造成的坑点。

后面打算如果能成功迭代到3.0版,直接甩到github上。现在就是一点一点在摸。
官方案例->非常简单只是实现xy平面的root的旋转

坑点1:MaxIterations 和Tolerance属性设置

官方案例将最大迭代数和阈值的设置方在构造函数中,然后又把这句话注释掉了。测试发现,放在构造函数里行不通,需要放在doSolve()或者其他重写计算方法。也可以通过节点外设置。如果你选择在方法里设置了,node面板的属性值是不能再修改了
在这里插入图片描述
在方法里如何获取这两个属性:使用MFnIKSolvermaxIterationstolerance

坑点2:如何获取IK chain的非末端关节的个数

这里想到实现两种方式,并且经过测试都是行的通的。一种是传统的开发一般node的思路,另一种就是用了IKSolver这种特殊节点的特性。

方法1

在自定义IKsolver节点中自定义一个显式接口,在初始化函数中,先定义其接口数据组织类型。然后在Maya面板中手动设置,然后通过MPlug类来查找到这个plug当前引用的数值。
在这里插入图片描述
如果你写法得当,你将会在node的Extra Attributes中找到他。并且输入的数值是可以被接收的

方法2

sao操作:MPxIkSolverNodegetJointAngles返回的数组,得到这个数组的长度,然后就能得到IKchain的关节个数。但这个方法似乎记录的是基于世界的旋转?而且存放方式有些诡异,是zyx的顺序进行存放。
下面是两种方式的示例

	MObject thisNode = thisMObject();
	nodeFn.setObject(thisNode);
	//========= get the num of joints in chain===========

	MDoubleArray jointAngles;
	int joint_num=0;
	if (getJointAngles(jointAngles)){
		joint_num = (int)jointAngles.length()/3;
	}
	else {// try to get num from the plug
		
		MPlug thisPlug(thisNode, num);
		joint_num = thisPlug.asInt();
	}

坑点3:虽然MPxIkSolverNode的基类是MPxNode,但。。。

如果以下这些方法在从MPxIkSolverNode派生的类中被重写,它们将被忽略。
compute
getInternalValue
setInternalValue
legalConnection
legalDisconnection
connectionMade
connectionBroken

坑点4:怎么得到chain中串联的其他关节对象,并得到相对位置和世界位置

关键点:首先得到handle实例,拜托MFnIKHandle工具找到起始关节

MIkHandleGroup* handle_group = handleGroup();
	if (NULL == handle_group) {
		return MS::kFailure;
	}
	MObject handle = handle_group->handle(0);
	// get handle path
	MDagPath handlePath;
	MDagPath::getAPathTo(handle, handlePath);
	MFnIkHandle fnHandle(handlePath, &stat);

	// get the start of the chain
	MDagPath root_joint_path;
	fnHandle.getStartJoint(root_joint_path);

然后事情就简单了。以root的子关节为例

	nodeFn.setObject(root_joint_path);
	MGlobal::displayInfo(nodeFn.name());
	MObject sec_joint=nodeFn.child(0);
	MFnTransform sec_tran(sec_joint);
	MVector sec_position = sec_tran.rotatePivot(MSpace::kTransform);

可以查询到相对坐标,但我试了试,世界坐标查询不到。见了鬼了。因为这是替身攻击。啃了n久的文档,orz。下面是可以访问到世界和相对坐标的正确姿势,必须得到子对象的DagPath

	MObject node_root = root_joint_path.node();
	// find a child's object
	nodeFn.setObject(root_joint_path);
	MGlobal::displayInfo(nodeFn.name());
	MObject sec_joint=nodeFn.child(0);
	// get child's dagpath
	MDagPath secPath;
	MDagPath::getAPathTo(sec_joint, secPath);
	MFnTransform start_transform(secPath);
	// get pos of the child
	MPoint sec_position = start_transform.rotatePivot(MSpace::kWorld);
	PrintVector(sec_position.x, sec_position.y, sec_position.z,"The 2nd Joint in world space:");
	MVector sec_vec = start_transform.getTranslation(MSpace::kTransform);
	PrintVector(sec_vec.x, sec_vec.y, sec_vec.z, "The 2nd Joint in relative space:");

在这里插入图片描述

遍历chain,得到世界位置

在这里插入图片描述

	// get the start of the chain
	MDagPath root_joint_path;
	fnHandle.getStartJoint(root_joint_path);
	MFnTransform start_transform(root_joint_path);
	MPoint start_position = start_transform.rotatePivot(MSpace::kWorld);
	// store the chain
	MPointArray joints_pos_list;
	joints_pos_list.setLength(joint_num+1);
	nodeFn.setObject(root_joint_path);
	MObject p = nodeFn.child(0);
	for (int i = 0; i < joint_num+1; ++i)// record the end joint
	{
		if (i == 0)
		{
			joints_pos_list[i] = start_position;
			PrintVector(joints_pos_list[i].x, joints_pos_list[i].y, joints_pos_list[i].z, nodeFn.name() + "in WS:");
			continue;
		}
		nodeFn.setObject(p);
		MDagPath path;
		MDagPath::getAPathTo(p, path);
		MFnTransform tran(path);
		joints_pos_list[i] = tran.rotatePivot(MSpace::kWorld);
		PrintVector(joints_pos_list[i].x, joints_pos_list[i].y, joints_pos_list[i].z, nodeFn.name() + "in WS:");
		p = nodeFn.child(0);
		
	}

坑点5:此End Effector非彼End Effector

仅针对个人的理解。本彩笔之前在其他平台上实操IK的时候。end effector的位置一直被认为是末端关节移动的期望位置。然而观察节点编辑器可以发现,effector和末端关节是牢牢锁死的。也就是说Handler的位置才是期望位置。effector的位置其实是当前末端关节的位置。
在这里插入图片描述

		MPoint ancesPos = joints_pos_list[i];
		MPoint curEndPos = joints_pos_list[joint_num];//末端关节
		MGlobal::displayInfo("done2");
		double s = VectorSquaredDistance(curEndPos, end_effector_pos);
		std::string str = std::to_string(s);
		const char* ch = str.c_str();
		MString sh(ch);
		MGlobal::displayInfo("Distance between effector and end joint is :" + sh);

坑点6 关于数据更新

Maya不像unity继承monobehavior那样拥有update,也没有可以随时访问的Transform组件掌握数据动向。如果涉及多级相互影响的实例,更加注意更新的问题。尽量就是初始化的时候就要保存相关的DagPath,方便更新和读取。

坑点7 IKSolverNode特有的计算模式

IKSolver这个节点会有两个原本存在的变量,最大迭代数和阈值。当effector和handler的距离小于阈值或者达到最大迭代数,IKSolver节点会停止计算。换句话说,当自定义的IK算法是优化法时,大循环是不写的,maya会自动完成。如果算法是几何分析或者数学建模之类的,最大迭代数限制为1。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值