Thanks again Cairyn. It sounds like 'op' is similar to 'this' in c#.
BTW - I was able to join your Patreon.
This posting is now closed.
Your suggested changed fixed my issue! How do I access the user data for parameters from the tag?
My old method was to first find the null object, then the user data within it:
# Find this controller
contlr = doc.SearchObject("Re-Target_Controller")
if not contlr:
c4d.gui.MessageDialog("ERROR Motion Retarget Controller not found!")
baseSource = contlr[c4d.ID_USERDATA, 2]
if not baseSource:
c4d.gui.MessageDialog("ERROR Source object joint root not provided!")
Thanks for the VERY detailed reply and sample project! I'll try re-doing my project.
BTW - I'm trying to join your Patreon (if I get the web site to send me an email to reset my password). I have another Python joint animation re-targeting project I'm having challenges with matrixes and changing joint rotation orientation.
Thanks for the reply. Much of your insight is correct, here is the breakdown.
I created a null and added user data for parameters, object links and a button. This is the only way I know how to create an interface.
I attached a Python tag to the null. The Python tag has a AddEventNotification and a message function to capture when the button is pressed, which in turn calls the Python code that does the re-targeting.
When the re-targeting code starts, it first obtains all the user data (interface) attached to the null, then performs the re-targeting.
I'm guessing the AddEventNotification is stealing Cinema 4D performance because it's being called anytime anything happens in Cinema 4D.
Reading between the lines of your last reply, I should be calling a Python script? Does this mean a standalone Python file? If so, do you know of any examples of that demonstrate how to create a user interface that is displayed that the Python code can obtain the parameters and run specific code when buttons is pressed?
I already tried doc.ExecutePasses(None,True,True,True, c4d.BUILDFLAGS_NONE) after the SetTime() function, but it doesn't make a difference. Are there different parameters I should be using?
I've created two Python scripts (tag based) that transfer animation from one character to another. The first one simply copies the source character joint rotation keyframes to the target character joints. This script works fine.
The second script is a bit different. Here the source character is setup with IK chains and goals. I first perform my animation using the goals plus some xPresso that also moves the joints. In this case, the joints don't have any keyframes to copy because the joints are being rotates by external means. I wrote my script to read the source joints rotation values, then copy them to the target character joints creating keyframe. I'm using the doc.SetTime() function to move to the next frame in the timeline. The problem is, it looks like doc.SetTime() doesn't actually change the current frame while the script is running. My target character does get new keyframes for each frame, but it's only of the first frame. It's like time line (and source character) isn't getting updated every time doc.SetTime() is called. Is there a Python function that tells Cinema 4D to update everything when the current frame is changed by the script?
I'm using Cinema 4D R21.
Is the version of Python used within Cinema 4D R21 "built-in" to Cinema 4D, or can it be updated to a new version?
If it can, how is it done, and is there backwards compatibility risks with exiting plugins, scripts, etc?
Sorry for not making my last post clear, I wasn't requesting you to test my code as is it seems to work from my tests. I just wanted to see if I was combining the matrixes together correctly. Thanks for the help, consider this issue "Solved".
It looks like the code does what I need, though I'm still working to understand why it works. Below is a code snip of how I combined all of the joints three rotation values from the source joint and transferred it to the target joint as well as obtaining the new target rotation vector to set animation keyframes. Does this look correct?
# Rotation values from source joint (for code testing only).
sourceYValue = 90
sourceXValue = -90
sourceZValue = 0
# Define a matrix rotation around the axis of the source joint rotation values
mX = c4d.utils.MatrixRotX(c4d.utils.DegToRad(sourceXValue))
mY = c4d.utils.MatrixRotY(c4d.utils.DegToRad(sourceYValue))
mZ = c4d.utils.MatrixRotZ(c4d.utils.DegToRad(sourceZValue))
# Combine all source joint rotation maxtrixs into one.
mTotalTargetRelitiveTransforms = mX * mY * mZ
# We just want the rotation so we can reset the offset
mTotalTargetRelitiveTransforms.off = c4d.Vector(0, 0, 0)
# Get the target joint orginal matrix rotations.
mTargetOrginalTransforms = targetJoint.GetMg()
# Combine target joint orginal matrix with new relitive rotation matrix.
newTargetMatrix = mTotalTargetRelitiveTransforms * mTargetOrginalTransforms
# As we just want to keep the rotation, we "reset" the position with the
# previous one.
newTargetMatrix.off = mTargetOrginalTransforms.off
# To be sure the scale remain to 1 we normalize the matrix
# Set the target joint new rotations.
# Use this vector to set animation keyframes.
vNewAnimationValues = targetJoint.GetRelRot()
Thanks for the same code, I try it this weekend.
I've spent the last couple of days studying Matrixes and playing with code. During my tests, I noticed the GetMg() function returned the rotation created by the parent shoulder joint being changed to 45 deg, where I can see why the order of operation is important. I found a bunch of sample code rotating objects with points, but noting on a joint that has no points.
Thanks again. Bruce.