RIGGING - Arms, hinge, spring, path constraints (willing to pay for help!)

RIGGING - Arms, hinge, spring, path constraints (willing to pay for help!)

thefighting24th
Explorer Explorer
3,920 Views
20 Replies
Message 1 of 21

RIGGING - Arms, hinge, spring, path constraints (willing to pay for help!)

thefighting24th
Explorer
Explorer

In the attached video you will see the basic idea.  I would like to automate this animation, so that when the yellow arm rotates, the actions you see will be automated.  I plan on making multiple arms, and do not want to animate the "retraction near the bottom" for every arm.

There is a spring as well, which might be easier to keyframe, but open to suggestions.

I am more than willing to pay $100 for a "walkthrough" via a call or zoom to anyone who is able to 100% able to teach me this process.  I have an expertise in modeling with 3DS Max, but my animation (beyond keyframes, simple path constraints, etc.) is limited.

 

Many Thanks!

Michael

0 Likes
3,921 Views
20 Replies
Replies (20)
Message 2 of 21

leeminardi
Mentor
Mentor

This can be done with a transform script.  Cam you post the Max file and I will take a stab at it.

 

lee.minardi
0 Likes
Message 3 of 21

A娘
Advocate
Advocate

A_1-1749197465680.png

 

the inter path was unnecessary , you can get position of P0 P1 P2 , you can calculate angle γ ,and you know length of A B , then using trigonometric functions to get the angle β,then you can get the matrix of B , set it
set the operate above as script expression , it will set B automatic 
the spring was just rigged with a scalable bone

0 Likes
Message 4 of 21

leeminardi
Mentor
Mentor

Here's a first pass at one approach using a combination of LookAt contraints and a transform script.

 

The yellow link has a LookAT constraint to point towards the point on the path (pointB).  Given the following:

leeminardi_1-1749233681019.png

we need to find the location of point3 that is at the intersection of a circle of radius D centered at the conection point of the yellow and white links, and a circle of radius L centered at point B.  Since there are two intersection points there is the possibility of getting the wrong one.  I used a numerical technique to find the intersection with an initial guess towards the bottom (i.e., -[0,D,0] or minus y). Ater the initial guess 10 more improved guesses are made giving fairly accurate results.   As you will notice, the solution flips when the yellow link is vertical.

Point3 has the following transform script:

S = 142.54 -- length of yellow link
whitePivot = yellowLink.pos + yellowLink.row1 * S 
D = 67.434 -- length of white link to connection point from red link
L = 58.2513 -- length of red link
g = whitePivot - [0,D,0]
for i = 1 to 10 do
( 
g = normalize( g - pointB) * L + pointB
g = whitePivot + normalize(g - whitePivot) * D
)
matrix3 [1,0,0] [0,1,0] [0,0, 1] g

The red and whit links have LookAt constriants pointing toward Point3.  

 

If you feel this approach has potential for you then post your geometry and I can try to address the flipping issue. 

lee.minardi
0 Likes
Message 5 of 21

leeminardi
Mentor
Mentor

Here's an improved solution that doesn't flip  Point 4 is linked to the yellow link and is used as the initial guess for the intersection of the two circles. To get its world coordinates it is mutiplied by the transorm for its parent, the yellow link.  I've addded a spring which works well.

leeminardi_0-1749260122500.png

S = 142.54 -- length of yellow link
whitePivot = yellowLink.pos + yellowLink.row1 * S 
D = 67.434 -- length of white link to connection point from red link
L = 58.2513 -- length of red link
g = pt4 * yellowLink
for i = 1 to 5 do
( 
g = normalize( g - pointB) * L + pointB
g = whitePivot + normalize(g - whitePivot) * D
)

matrix3 [1,0,0] [0,1,0] [0,0, 1] g

[EDIT 6/7/2025]  The local x axis for each of the three links is in the long direction of the link.  The red and white links have LooKAt constraints pointing their x axis towards point3.

lee.minardi
0 Likes
Message 6 of 21

thefighting24th
Explorer
Explorer

This is incredible information and work - Thank you!

We have a few plug-ins that are dependent on using 3DS Max 2023 - can you please save the .max file for that version?  I am very excited to give it a look.  If all goes well, I would also like to pay you (vie Paypal or Venmo) for your time!

Cheers!

M

0 Likes
Message 7 of 21

leeminardi
Mentor
Mentor

Here's a 2023 version.

Let me know if you have any questions.

Lee

lee.minardi
0 Likes
Message 8 of 21

thefighting24th
Explorer
Explorer

Thank you, Lee - This is amazing and opening my eyes to these mechanics that I have not been aware of.

I am still in the process of unpacking the script - it is making some sense as I reverse-engineer it, but am currently working (well, struggling) on making it fit my specific needs.

If you are game:
1) I have uploaded the 3DS Max File
2) My plan is to have multiple arms on a singe rotating center (e.g. like a watch, but the hour, minute, and second hand are rotating at the same speed / fixed to the same pivot point), all clearing the line/plane that is indicated in the 3DS Max file i have attached here.
3) I believe I found you on YouTube (www.youtube.com/@LeeMinardi).  You are certainly a wealth of knowledge and I would be more than open to a tutorial from you.  I would pay $100 / hour.  We could facilitate it via Zoom, perhaps.

My work can be found here (www.TF24th.com - based in Seattle, WA.  and just started this site www.MWWentz.com (an incomplete site as of this date) to showcase "me" more than my company (The Fighting 24th)).  I have been in the 3D Rendering and Animation business for 20 years, but am looking to add to my skill set, and learning some of these advanced 3DS Mechanics you are working with here would be wildly helpful to my professional career.

Many Thanks for your time thus far!

Michael

0 Likes
Message 9 of 21

leeminardi
Mentor
Mentor

@thefighting24th  Michael,

 

I deleted the keyframing from your file and rigged it with the method I outlined above.  I made a few changes to my script as your links used the local Z axis and I had assumed x. I changed the units to decimal inches from feet and inches as they were inconsistet with system units.  Note that the white pivot in world coordinates is:

whitePivot = yellowCylinder.pos + yellowCylinder.row3 * s

Row 3 of the yellowcylider transform  is used for the z axis.

leeminardi_0-1749517988700.png

 

You can see in the script the values I used for S, D, and L. 

I replaced the helix with a dynamic spring.

 

I appreciate the offer of compensation but I do this for the fun of it.

 

Rather than a zoom meeting, please outline any questions you may have.

 

Lee

lee.minardi
0 Likes
Message 10 of 21

thefighting24th
Explorer
Explorer

Understood and Many Thanks, Lee!

I have attached two images illustrating the final goal - A) needing 3 to 4 additional arms per "unit",  being driven by a linked rotating pivot point / object in the center, and B) an assembly with about 4 units on the right and 3-4 units on the left, rotating into each other.

Any advice as to how i can complete these is greatly appreciated.  i.e. I am not sure what copying would do to produce the multiple arms and how to relocate those arms to different positions while maintaining the general constraints keeping them from "passing the lower line".

I am spending time right now looking into your work to see if i can figure it out - any guidance would be healpful.

Again - Thank you.  You are helping me out immensely!

Michael

0 Likes
Message 11 of 21

thefighting24th
Explorer
Explorer

(added note) - It seems that the speed is driven solely by PointB.  If that is the case, I imagine that animating a rotating axle in the center must govern the PointB's percentage along the path's constraint for each arm.

The idea is to animate this axle, driving these automated, animated components that is being worked on here.

?

M

0 Likes
Message 12 of 21

thefighting24th
Explorer
Explorer

I have duplicated a second arm and offset PointB (attached image).  It functions / animates well, but the speed along the inner "kidney bean" path, (since not circular in shape) produces the arms moving at varied speeds during their journey.  If these two arms (or, in the final product, three or four arms) are attached to the same, spinning axel, then the angles shown in red would be a constant.

So it seems we are close - but need to find a solution whereas the speed is driven by a center axel spinning, dictating every arm's movement and ensuring these angles stay a constant.

0 Likes
Message 13 of 21

thefighting24th
Explorer
Explorer

As an illustration:  The "PercentAlongPath_Disconnect" video shows a constant rotation of the arm, and the point illustrates the speed our current arm is rotating along the %Path constraint.

The "PercentAlongPath_CheatedFix" (which i keyframed the % Along Path numeric to match the constant rotation of the arm) shows the basic idea needed.

I have played with using wire parameters and (as i know you probably know, given your expertise) was a trip down the wrong rabbit hole!

Thanks again for your time, Lee.  I'm learning a lot here, that would otherwise be a mightily painful journey!

M

0 Likes
Message 14 of 21

leeminardi
Mentor
Mentor

@thefighting24th 

The rigging works as follows:

  1. Point B has a position path constraint.
  2. Yellowcylinder (with a pivot at A) has a LookAt constraint pointing to B
    leeminardi_0-1749567507380.png

     

  3. Whitepivot (at the end of yellowcylinder) is a distance S from A in the direction of yellowcylinder.
       whitePivot = yellowCylinder.pos + yellowCylinder.row3 * s
    Note that  yellowCylinder.row3  is a unit vector pointing in the Z direction of yellowcylinder.
  4. We need to determine the location of a point (Point3) that is a distance L from point B and a distance D from whitepivot.  Point3 is therefore at the intersection of a circle of radius L centered at B and a circle of radius D centered at whitepivot. There are two possible solutions.  We could use trig to determine the exact solution but using angles can get messy.  I prefer using a numerical approach where a solution is guessed and then revised until a desired level of precision is achieved.
  5. The first guess (G1) for the location of point3 is at point4. This of course is way off so a new guess is made using G1 to locate guess 2 (G2) a distance L from B in the direction of B to G1.
    leeminardi_1-1749567507384.png

     

  6. G2 is then used to make a better guess G3,  a distance D from whitepivot in the direction from whitepivot  toward G2.
  7. The guessing continues until an acceptable result is achieved.  Rather than comparing recent guesses the program just does 10 guesses which from experience is good enough.  The following code does these guesses.
g = pt4 * yellowLink

for i = 1 to 10 do

(

g = normalize( g - pointB) * L + pointB

g = whitePivot + normalize(g - whitePivot) * D

)

 

Once the location of Point3 is determined we can add LookAt constraints to the white and yellow cylinders.  Using point4, which is linked to yellowcylinder, for the first guess ensures that we always converge to the desired intersection of the two circles.



 

lee.minardi
0 Likes
Message 15 of 21

leeminardi
Mentor
Mentor

@thefighting24th 

I now see that you want the yellow cylinder to have a constant angular velocity.  As you have seen, using point B's % along parameter does not yield that result.  As I see it you have two options.

1.  Keyframe using brute force the % along values to yield constant angular velocity.  I think ths is what you have done in your  "cheated fix" solution.

2. Use a numerical approach to determine the location of point B for a given rotation (angle theta).  This approach would use the Mascript  interpCurve3D function to determine the % along value for point B. I don't have time right now to explain but my  3ds Max Tutorial - Creating Your Own Solver for a Trolley Rig should give you a feel for the process.   If I have time I will try to write something up later today.  

Lee

lee.minardi
0 Likes
Message 16 of 21

thefighting24th
Explorer
Explorer

Thanks Lee - option 1. won't work due to the main drive, the axel, needing to have variable speeds (starts slowly, then ramps up, maintains for a while, and then ramps down again to a stop).  It is possible that spending the time to "brute force" some of this process is needed in 3DS MAX, but (albeit as a novice 3ds max animator) that seems surprising to me.

I am in no particular rush on this, as the process itself is of equal value to me as the end product - and with that, Thank You again!

M

0 Likes
Message 17 of 21

leeminardi
Mentor
Mentor

@thefighting24th 

 

Here's a version that I think yields constant angular velocity fot the yellow cylinder.

I replaced pointB with point Q (it was easier to debug using a new point) that has a path constraint to the curve.Line095.  sQ is the % along value for pointQ.  The vector w points in the direction of rotation for constant angular velocity. E.g., a frame 0 it is pointing straight up, at frame 50 it is point to the left.    Vector v points from the center of rotation (pointA) to pointQ.   The value of sQ starts at 0 and is incremented by delsQ up to a value of 1 UNLESS  the dot product of v and w is greater than 0.999 (i.e., they are pointing in almost the same direction). If so the looping stops and the value for sQ has been found. The dot product of u and v yields the cosine of the angel between the two unit vectors.  If u and v are pointing in exactly the same direction the angle is 0° and the cosine is 1.000. Steping through values of sQ starting at 0.0 unti a solution was found was easier to program then doing a bisectional search as I used in the trolley boom tutorial. 

 

Here's the script for pointQ

sQ global -- % along vlue for pointQ
sQfinal global  -- % along value for final position of pointQ
w global  -- vector of contant angular velocity
frameStart = 0
frameEnd = 200
totalFrames = frameEnd - FrameStart
delsQ = (1. / totalFrames)
flag = true
ang = -1 * F / totalFrames * 360
w = [sin(ang), 0, cos(ang)]
for sQ = 0 to 1.0 by delsQ while flag do
(
  ptQ =   (interpCurve3D curve 1 sQ)
  v = normalize(ptQ - ptA) -- vector A to Q
  dot_vw = (dot v w)
  if (dot_vw > 0.999) then 
  (
    flag = false
    sqFinal = sQ
  )
)
sQFinal 

leeminardi_0-1749603125915.png

 

I'm traveling for the next few days and will not have access to my CAD PC.

 

lee.minardi
0 Likes
Message 18 of 21

thefighting24th
Explorer
Explorer

Thank you, Lee - I am digging into it right now.

0 Likes
Message 19 of 21

leeminardi
Mentor
Mentor

@thefighting24th 

I just did more detailed testing of my last post and determined that the angular velocity s not that constant!  U think the problem is with using the dot product to determine the angle difference between the vectors v and w.  Since the cosine function is very flat near 0°, sigificant precision is required to get accurate angles.  I will modify the code to use angles directly instead of the dot product. Stay tuned!

Lee

lee.minardi
0 Likes
Message 20 of 21

leeminardi
Mentor
Mentor

@thefighting24th 

M.,

My original strategy for achieving constant angular velocity was to compare the vector direction of a rotating vector of contant velocity with a vector pointing to a point on the spline path and then adjusting the % along value to make t point in the same direction.  I originally used a dot product to determine if the two vectors were in fact pointing in the same direction.  Unfortunately, the dot product yields the cosine of an angle and for angle near 0° the cosine funtion is very flat which leads to precision problems. I decided to rewrite the code for point Q (the point on the path ath the correct angle) by comparing the angles directly.  The following code uses a numerical bisection method similar to the approach I used in   3ds Max Tutorial - Creating Your Own Solver for a Trolley Rig.  The seach for a solution will take up to 20 iterations to detemine the location of point Q that will yield an angle within 0.1° of the constant velocity angle.

 

When running the animation the angulat velocity looks constant altough the absolute angle difference from frame to frame has some minor variation.

 

 

 

== determine if two floatig point numbers are the same withi a fuzz factor
fn almostEqual f1 f2 fuzz =
(
  f1 == f2 OR abs (f1 - f2) <= fuzz
)
------------------
--  compute the angle from  horizontal and vertical vector components
fn vectorAngle horizontal vertical =
(
case of
(
  ((almostEqual horizontal 0.0 0.0001) and (vertical > 0 ))  : (ang2 = 0.0)
  ((almostEqual horizontal  0.0 0.0001) and (vertical  < 0 ))  : (ang2 = -180.0)
  ((horizontal  < 0) and (vertical  > 0)):(ang2 = atan(horizontal / vertical ))
  ((horizontal  < 0) and (vertical  < 0)) : (ang2 = atan(horizontal / vertical ) - 180)
  ((horizontal  > 0) and (vertical  < 0)) : (ang2 = atan(horizontal / vertical ) - 180)
  ((horizontal  > 0) and (vertical  > 0)) : (ang2 = atan(horizontal / vertical ) - 360)
) -- end case of
) -- end fn vectorAngle
---------------------

i global
betaB global  -- angle to upper interval location for point Q
sA global -- % along value for for for ptA, the upper interval  
sB global -- % along value for for for ptB, the upper interval  
frameStart = 0
frameEnd = 200
totalFrames = (frameEnd - FrameStart)
deltasQ = .1  == delta sq (the % along parameter)  for search interval
sA = 0.0  -- initial % along value for ptA 
ptA = interpCurve3D curve 1 sA
betaA = (vectorAngle (ptA - ptCen).x (ptA -  ptCen).z )
sB = deltasQ
ptB = (interpCurve3D curve 1 sB)
betaB = (vectorAngle (ptB - ptCen).x (ptB - ptCen).z )
alpha = -1 * F / totalFrames * 360  -- angle of constant velocity
more = true
for i = 1 to 20  while more do
(
  --format "\n\n i = %" i
  if (betaB < alpha) then
  (  deltasq = deltasq / 2.
    sB = sA + deltasQ  -- reduce search interval by 1/2
    ptB = (interpCurve3D curve 1 sB)
    betaB = (vectorAngle (ptB - ptCen).x (ptB - ptCen).z )
  )
  else
  (
    sA = sB
    ptA = ptB
    betaA = BetaB
    sB = sB + deltasq 
    ptB = (interpCurve3D curve 1 sB)
    betaB = (vectorAngle (ptB - ptCen).x (ptB - ptCen).z )
  )
error =  (abs(betaB - alpha))
if (error < 0.1) then (more = false -- difference between ideal angle and calculated angle
-- The following were used for debugging
--format "\n\n F = %, alpha = %, betaB = %," F alpha betaB  
--format "\n i = %, error =  %" i error
)
)
sB

 

lee.minardi
0 Likes