View Full Version : Wheel rig script!
ivanisavich 10-16-2007, 05:30 AM K...here we go! This is a script I made while trying to get a wheel rig working (where the wheel can be pointed in any direction and roll properly).
To get this script working, all you need to do is create a cylinder (or any kind of wheel) and a point helper, align both of their pivots to the world, and then link the cylinder to the point. Finally, click on the cylinder and then run the script!
Things to note:
-to get the wheel rolling, animate the point helper's position and rotation. No keys need to be set on the wheel itself
-the script works on past positional data, which means your wheel won't start rolling until there are some keys set on the point helper (the point helper can also be linked to another object that's animated....so linking the point to an animated car chassis would also work)
-the float script on the wheel's rotation channel works by incrementing a positional data value at each frame....which means the rotation might not look correct if you're dragging the timeslider backwards, or if you're playing the viewport back in realtime....but of course, in a playblast and in a render the rotation will look correct
-in the script below, you'll see a line that says "radius=45"....that's where you'd replace "45" with the radius of your wheel to get the rotations to go at the right speed :)
-the rig is also scalable
Here's the script:
distCA = attributes "dist"
(
parameters main
(
dist1 type:#float
)
)
custAttributes.add $ distCA
$.rotation.controller[2].controller = float_script()
$.rotation.controller[2].controller.addNode "self" $
$.rotation.controller[2].controller.addNode "p0" $.parent
$.rotation.controller[2].controller.script =
"radius=45*p0.scale[1]
val = 360/(3.14*radius*2)
at time (f-1)
(
p1 = p0.transform
oldPos = p0.pos
)
newPos = p0.pos
dist = distance oldPos newPos
mult = (normalize (p1 * inverse p0.transform).pos).x
if mult <= 0 then mult = -1 else mult = 1
with animate off(
self.dist1 += dist*mult)
-(degtorad self.dist1*val)
"
And here's an animation showing it in action :D
http://www.tysonibele.com/wheelRoll2.gif
|
|
cool!
Thanks for sharing.
I hopt the time will come even I can use "inverse something" goal-oriented.
One note:
The script might perform better with a comment in line "print mult".
Georg
ivanisavich
10-16-2007, 07:26 AM
Oh oops...that line is redundant...thanks for pointing it out...I'll remove it :D
The not looking correct part is because in the viewport playback sometimes multiple times per frame the controller gets evaluated right, wich increments the controller. Wouldn't it be possible to get the currenttime as integer and do a if statement on it to only update on a new integer frame value... it's that feedback in the viewport I would like a lot!
Don't know if you want others to mess with your script, but this could be made into a nice user friendly script to quickly setup some wheels :)
-Johan
ivanisavich
10-16-2007, 02:20 PM
The not looking correct part is because in the viewport playback sometimes multiple times per frame the controller gets evaluated right, wich increments the controller. Wouldn't it be possible to get the currenttime as integer and do a if statement on it to only update on a new integer frame value... it's that feedback in the viewport I would like a lot!
Don't know if you want others to mess with your script, but this could be made into a nice user friendly script to quickly setup some wheels :)
-Johan
Hehe yea...I've actually tried that in the past, but never been able to get it to work. It is especially a problem when playback is choppy/slow, because then frames get skipped (ie...since technically in "Real-time" mode it's attempting to play through all ticks...if choppy, it'll sometimes skip the exact integers)...
If anybody has any suggestions about that feel free to post them up! But it's not really a bug that affects the scripts real function, so it's not a big deal if it doesn't get solved.
You can take "with animate off" too, because it hasnt been defined as animatable in the paramblock. ( you could turn off its keyability too), im thinking you could define a point3 param and a matrix3 in the custom attribute for the old pos and old transform, plus a float variable for the radius.
I like how your using a transform space to check if the x is +/-, very nice. Im thinking you could encapsulate it all into just one object.
I get the opposite problem happening with my dynamics stuff, in the viewport its correct but when i render or playblast its slow - only way is to bake the keys down i think.
ivanisavich
10-16-2007, 03:01 PM
Hey eek,
Cheers mate! Yea there's definitely some some clean-up that could be done...I'm not yet to the point where I fully understand matrix3's though....so it might be a little un-optimized.
As for, "with animate off"....well I added that in there, because when my auto-key button was enabled the script was creating millions of keys (ie...at each tick it executes)...
Hey eek,
Cheers mate! Yea there's definitely some some clean-up that could be done...I'm not yet to the point where I fully understand matrix3's though....so it might be a little un-optimized.
As for, "with animate off"....well I added that in there, because when my auto-key button was enabled the script was creating millions of keys (ie...at each tick it executes)...
good point!
Masquerade
01-27-2008, 09:19 PM
I used to work on the same thing some time ago. Here is what I have achieved.
--Wheel rig script v1.1--
/* To use the script
- create a cylinder object and a dummy
- link the cylinder to the dummy
- assign a rotation script to cylinders rotation channel and copy/paste this script
This script actually belongs to Paul Neale (Pen Productions Inc.) and Cuneyt Ozdas. I simply tried to
use their ideas together to achieve the desired effect
obj = $cylinder01 -- change this with the wheel object
dum1 = $dummy01 -- change this with dummy object
timeres = 1f -- time resolution
-----------------------------------------------------
fn getrot t =
(
if t<=0f then return quat 0 0 0 1 -- t=0 => no rotation
t0 = t-timeres -- previous frame time
t1 = t -- current time
rot0 = getrot(t0) -- previous rotation:
p0 = at time t0 obj.position -- previous position
p1 = at time t1 obj.position -- current position
if(p0==p1) then return rot0 -- no distance is traveled
r0 = at time t0 obj.radius -- previous radius
r1 = at time t1 obj.radius -- current radius
dif = p1-p0 -- difference in positions
len = Length(dif) -- distance that's traveled
vec = dif / len -- normalized movement vector.
angle = 360*len/((r0+r1)*pi) -- angle difference for distance that' traveled
z2=(at time (t1) Dum1.pos)
z1=(at Time (t0) Dum1.pos)
--Get the direction vector
dir=normalize(z1-z2)
--Build the matrix parts
Z=dum1.transform[3] --Set the Z axis to that of the dummy
X=dir --Set the X axis to the direction vector
Y=normalize (cross Z X) --Build the Y axis using X and Z
--Build the matrix
m=(matrix3 X Y Z [0,0,0])
--Using the rotation of the both helpers Z axis get the angle between them
a=(m.rotation as eulerAngles).z - (dum1.transform.rotation as eulerAngles).z
an=((eulerAngles a a a) as angleAxis).angle
--If the angle is less then 90 then it is going forward
if (an < 90) then (
rotdif = quat angle y
rot1 = rot0 - rotdif
)
else(
rotdif = quat angle y
rot1 = rot0 + rotdif
)
)
getrot(currentTime)
You may saw this script in this thread:
http://forums.cgsociety.org/showthread.php?f=6&t=435526
I have done a minor adjustment. Maybe it may give you some ideas.
Here is my implimentation of the same thing.
This is the def that was added to the script controller. Might not be the best place for it as it can make the trackview freek out when the script controller is expanded.
def=attributes wheel
(
local dist=0
local lastTime=0
)
custAttributes.add $.rotation.controller[2].x_rotation.controller def
And here is the body of the script controller that is on $.rotation.controller[2].x_rotation.controller
There are two variables that have been added to the script controller.
parent == to the the parent node for the wheel. This is the object that is moved.
radius == this is a controller parameter that stores the controller for the radius of the wheel.
--Reset the stored values on the wheel at frame 0 just to make sure
--we have a set starting rotation. If the user skips frames the rotation
--at frame 0 would not be the same each time.
if F==0 then
(
this.wheel.dist=0
this.wheel.lastTime=0
)
--Get the old position and the current position.
p0=at time (this.wheel.lastTime) parent.pos
p1=parent.pos
--Get the distance traveled.
newDist=distance p0 p1
--Calculate the direction to wheel was traveling in so that it can
--rotate backwards. +Y is moving forward in local space so use
--the y vector of the transform compared to the normalized
--direction vector
multi=dot parent.transform.row2 (normalize (p1-p0))
--Calculate the amout to rotate the wheel and add it to the
--distance variable stored in the ca def.
this.wheel.dist+=newDist*(360/(2*pi*radius.value))*multi
--Set the last frame to the current frame.
this.wheel.lastTime=F
--Convert the value to radians and apply the rotation to the wheel.
-degToRad this.wheel.dist
This equasion can be simplified as we are deviding the circumference into 360 degrees. If we use radians instead we will not need the degToRad for the last line. So... this.wheel.dist-=newDist*(6.28319/(2*pi*radius.value))*multi.
6.28319==2*pi
ddustin
01-28-2008, 05:43 PM
I have tried the script and have the following questions and observations.
You can not render to mov or Max crashes (another I know had the same thing happen).
How will this custom controller work with Net rendering?
Can it be set up to create key fames?
Thanks,
David
David, as you know in my car rig I have had to program all this in. Rendering to MOV shouldn't be an issue unless you are on Max 64 bit as I don't think that 64bit is supported yet by quicktime.
ddustin
01-28-2008, 06:14 PM
Paul,
It was on 2- 32 bit systems that the .mov errors occured.
We have taken to key framing wheels and motions (long, long story).
This looked like it had promise, but as soon as things start crashing I run for the hills........
David
Send me a file David as I can render mov's just fine.
bhupendra
02-17-2008, 06:20 PM
Hi Tyson,
I used your script and its working g8. I got 2 issues that i need help on:
1) The script does not work on a "group" of objects, how does the script be modified to work with a "group" also?
2) Lets say u have setup the wheel and the helper and attached the script. now if u delete the helper first, the max throws up script error. Can anything be done to handle this automatically?
Thanks,
Bhupendra
Mr-BlueSummers
02-17-2008, 06:59 PM
Ahah! How clever.
Nice work. :D
DeVangiel
04-04-2008, 08:02 PM
Hi Tyson,
Excellent script, and I thank you for sharing with us.
For ease of use, I thought the 45 part could be replaced with a $.Radius, assuming the wheel object has a Radius attribute. This way the script could be applied quickly and easily to multiple objects with different radii. But obviously, as soon as you deselect the wheel object, the code tries to run and throws out an error because $ is undefined. Not sure if there is a way to apply this to a selected object and then deselect it without the problem, but I will look into it more.
Just a small issue I ran into, if the wheel object is rotating funny (like a coin flipping rather than a wheel turning), change the orientation of the wheel to match the way it is spinning, reapply the script, then rotate the helper to the direction you want.
Thanx all,
Chris
P.S. This is my first post, and I look forward to speaking to you all on future threads.:applause:
Hi Tyson,
Excellent script, and I thank you for sharing with us.
For ease of use, I thought the 45 part could be replaced with a $.Radius, assuming the wheel object has a Radius attribute. This way the script could be applied quickly and easily to multiple objects with different radii. But obviously, as soon as you deselect the wheel object, the code tries to run and throws out an error because $ is undefined. Not sure if there is a way to apply this to a selected object and then deselect it without the problem, but I will look into it more.
Just a small issue I ran into, if the wheel object is rotating funny (like a coin flipping rather than a wheel turning), change the orientation of the wheel to match the way it is spinning, reapply the script, then rotate the helper to the direction you want.
Thanx all,
Chris
P.S. This is my first post, and I look forward to speaking to you all on future threads.:applause:
Build a custom attribute with a maxObject weak reference to the wheel. using $ isnt a good idea.
DeVangiel
04-05-2008, 03:44 PM
Build a custom attribute with a maxObject weak reference to the wheel. using $ isnt a good idea.
Thanks eek, I'm fairly new to MAXscript and haven't fully tried to understand weak referencing, but I'll definately look more into it.
-Chris
IkerCLoN
04-11-2008, 10:43 AM
I used to work on the same thing some time ago. Here is what I have achieved.
--Wheel rig script v1.1--
/* To use the script
- create a cylinder object and a dummy
- link the cylinder to the dummy
- assign a rotation script to cylinders rotation channel and copy/paste this script
This script actually belongs to Paul Neale (Pen Productions Inc.) and Cuneyt Ozdas. I simply tried to
use their ideas together to achieve the desired effect
obj = $cylinder01 -- change this with the wheel object
dum1 = $dummy01 -- change this with dummy object
timeres = 1f -- time resolution
-----------------------------------------------------
fn getrot t =
(
if t<=0f then return quat 0 0 0 1 -- t=0 => no rotation
t0 = t-timeres -- previous frame time
t1 = t -- current time
rot0 = getrot(t0) -- previous rotation:
p0 = at time t0 obj.position -- previous position
p1 = at time t1 obj.position -- current position
if(p0==p1) then return rot0 -- no distance is traveled
r0 = at time t0 obj.radius -- previous radius
r1 = at time t1 obj.radius -- current radius
dif = p1-p0 -- difference in positions
len = Length(dif) -- distance that's traveled
vec = dif / len -- normalized movement vector.
angle = 360*len/((r0+r1)*pi) -- angle difference for distance that' traveled
z2=(at time (t1) Dum1.pos)
z1=(at Time (t0) Dum1.pos)
--Get the direction vector
dir=normalize(z1-z2)
--Build the matrix parts
Z=dum1.transform[3] --Set the Z axis to that of the dummy
X=dir --Set the X axis to the direction vector
Y=normalize (cross Z X) --Build the Y axis using X and Z
--Build the matrix
m=(matrix3 X Y Z [0,0,0])
--Using the rotation of the both helpers Z axis get the angle between them
a=(m.rotation as eulerAngles).z - (dum1.transform.rotation as eulerAngles).z
an=((eulerAngles a a a) as angleAxis).angle
--If the angle is less then 90 then it is going forward
if (an < 90) then (
rotdif = quat angle y
rot1 = rot0 - rotdif
)
else(
rotdif = quat angle y
rot1 = rot0 + rotdif
)
)
getrot(currentTime)
You may saw this script in this thread:
http://forums.cgsociety.org/showthread.php?f=6&t=435526
I have done a minor adjustment. Maybe it may give you some ideas.
I gave it a try, and do you have any idea why when switching my time line from 0-100 to 2000 - 2100 MAX automatically closes?
Masquerade
04-11-2008, 01:03 PM
??
I dont have any slightest idea. Though I never tried to change the timeline after adding the script controller. On the other hand, it is not much surprising cause that piece of code contains handful of bugs. I almost give up the hope making that script properly working.
Thats because I didnt try to write it down from scratch just stitched some pre-written codes together.
DeVangiel
04-15-2008, 05:48 PM
Don't know if anyone else has run into this, but when using this script, rendering an animation with the Default Scanline will cause Max9 to crash after rendering the first frame.:surprised
DeVangiel
04-15-2008, 07:38 PM
Okay, so when using any script involving frame to frame distance evaluation, with only the wheel rotation on the wheels, and using a raycasting script to snap the wheels to the ground, I have run into a problem with rendering.
If I create an animation, and render using mental ray (lighting and material purposes), then pass this through multiple render nodes on backburner, the wheels rotate......odd. In fact they seem to more shake around the pivot point, snapping forwards and backwards about 10 or 15 degrees on each frame. Now rendering this scene on a single node yields the correct results, so I theorize that somehow the distance information is being lost by having multiple machines render different frames, or is it something else?
If it is this, any ideas on how to correct the problem?
Any ideas on why this script would work differently when network rendering? If I render locally everything works perfect. When I render through the network I get a jumpy look like it is trying to rotate but it can’t. Does it have something to do with each computer re-evaluating the rotation and therefore each frame rendered has the wheel in a different rotation?
Correct, you will need to bake the data to be able to net work render.
Pen,
Thanks. We actually used your script instead and network rendering seems to be fine now. Any ideas on adapting that script to a path constrait? We are trying to get a dummy to rotate around a path based on the distance traveled. Its basically for tracked vehicles.
CGTalk Moderation
04-16-2008, 04:45 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.