Instances changing rotation based on curve's direction?

I am confused on how this user from this post was able to get the green circles to rotate based on the direction the curve was pointing. So far all of the instances of my mesh are pointing in the same direction. I don’t want to just copy and paste the code, I wish to understand step-by-step what was done to make this possible. If you could please point me in the right direction on how to comprehend what is going on in this piece of code, I would greatly appreciate it.

Here is the user’s solution,
As well as a few other examples mentioned in the post that also show the same concept:
https://hofk.de/main/discourse.threejs/2021/MovementOnCurve/MovementOnCurve.html
https://hofk.de/main/discourse.threejs/2021/CirclesOnCurve/CirclesOnCurve%20-%20TEST.html

Thank you so much for your help!

The key elements in the code you shared can be seen in the following images which i’ll try to break down…

u: find a spaced interval from 0 - 1 along the curve…
centerOfCircle: get a point along the curve at “u”
tangent: get the tangent of the curve at “u”
axis: cross of up and tangent vectors…

Set the position and quaternion of an object along the curve based on the above calculated center position, tangential axis and radians…

2 Likes

I understand it a little bit better, but I cannot figure out why the plank instances are not following along the curve that the circled extruded geometry is following.

Here is what my code looks like:

export function Plank({index, curve, position} : props) { 
  const plankRef = useRef<THREE.Group>(null!)

  const instances = useContext(context)

  useEffect(() => {  
    const up = new THREE.Vector3(0, 1, 0);
    const axis = new THREE.Vector3();

    let u = (index * 100)/(300 - 1)/100;
    let tangent = curve.getTangent(u).normalize();
    axis.crossVectors(up, tangent).normalize();
    var radians = Math.acos(up.dot(tangent));
    plankRef.current.quaternion.setFromAxisAngle(axis, radians);
    plankRef.current.rotateX(Math.PI / 2);
  }, [])
    
  return (
    <group dispose={null} ref={plankRef} position={position}>
      <instances.Plank />
    </group>
  )
}

I should have realized this before, but I did not set the origin of the plank to the center of the mesh, and so now it is arranged correctly. Thank you for your helpful explanation, @Lawrence3DPK !

Unfortunately, the rotation is still a little bit off. I was hoping they would be more like this, with the wider faces facing the direction the curve is going while staying flat along the curve:

Instead it looks like this:

If you know what might be causing this weird direction, please let me know.

Thank you!

Here is a post I watched myself at the beginning.
15-462 Graphics Project 2: Simulating a Roller Coaster via Splines - Camera Control Method
Instead of the camera, you can also place any other element in this way.

With this method you get continuous arrangements.

Once you have the individual bases, you can determine the quaternation from them.

Because this function is not available in three.js docs, I have created it myself, see Quaternion - method .setFromBasis( e1, e2, e3 )

Among others used there:
CarRacingQuaternion
FlightRouteQuaternion

2 Likes

Somehow, the rotation is still not working, I am unsure of what I did wrong:

            // t tangent, n normal, b binormal
            const n = new THREE.Vector3().normalize()
            const b = new THREE.Vector3().normalize()
            // index is the index of plank instance
            let f = index;
            let t = curve.getTangent( f );
            b.crossVectors( t, n );
            n.crossVectors( t, b.negate( ) );
            plankRef.current.quaternion.setFromBasis( t.negate(), b, n )
const n = new THREE.Vector3().normalize()
const b = new THREE.Vector3().normalize()

are not orthogonal and obviously very arbitrary.

The best thing to do is to make a codepen or jsfiddle with the code that is relevant to the problem.

I apologize for my lack of knowledge with this feature. Here is my codesandbox for this issue, though I keep getting an error in there saying that children are not a function and I don’t know how to solve that either.

Sorry, but I don’t use react.

1 Like

No worries, what else can I do to try and find a solution? I think I might just use Blender to create this effect.

This might help other people in case if they get weird rotation like this, I found out that you can have the 3d object look at something. Now it appears to be rotated a little less awkwardly.

Is it something like this what you want to achieve?

https://codepen.io/boytchev/full/VwGKQzp

image

4 Likes

Yes it is, thank you for this example. If you don’t mind, I have a few questions to understand the code further. Why does using a matrix work better in this scenario instead of setFromAxisAngle? Why do you put %1 in some places? Also why is subVectors used instead of crossVectors?

Whenever I want to give a specific absolute orientation of an object, I prefer to use matrices. I just feel more comfortable with them. The data in a rotational matrix are the coordinates of the local axes. So if I can get the directions of the three axes, this is enough to make the rotation.

The path spans from 0 to 1. To make object loop along the path, I use time%1 – this is the fractional part of the time. Thus an object will be at the same place at times 0.2, 1.2, 2.2, 3.2 … because %1 will give just 0.2 for each of them.

You might be confused by the meaning of ‘vector’ – it could mean a vector (as a direction), but it could also mean a specific point in space (sometimes this is called radius-vector). In my code I use subVectors to subtract two vectors, this is the vector between the two points. However, I simplified the code, so now subVectors is not used at all.

I’d use crossVectors when I set the axes manually and I need to make them orthogonal. In the code I used Matrix4.lookAt which does this for me.

Here is an example of transfering from one coordinate system to another, where I construct the matrix manually and I use cross:
https://codepen.io/boytchev/pen/JjVmGQM

5 Likes