Struggling with paths

So i have my pen here with no error: https://codepen.io/uiunicorn/pen/xxqgjeg
its a mesh following a path but for some reason nothing shows up?

here is my reference that works:

I’ve been up all night what am i missing? :sleeping:

im very new to three and also coding in general so i am sorry if this is a silly question i’m just trying to learn

EDIT: so i got it working but it doesn’t loop / go back to the start is there anyway of doing that like in the reference link
Updated pen:

2 Likes

Hi!
In this part:

  renderer = new THREE.WebGLRenderer({antialias: true});
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(devicePixelRatio);
  document.body.appendChild(renderer.domElement); // add this line

PS Have more sleep :slight_smile:

1 Like

Omg I can’t believe I missed that :sweat_smile: the shame.
I’ve updated it and got it to work: https://codepen.io/uiunicorn/pen/qBrroLG
however it doesn’t seem to loop like in the reference link

https://codepen.io/uiunicorn/pen/abJJKqw fully working

1 Like

Another short option based on three.js examples .

In the Collection of examples from discourse.threejs.org 2021 MotionAlongCurve


<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/struggling-with-paths/26486/4 -->
<!-- see also https://threejs.org/examples/?q=modi#webgl_modifier_curve -->
	<head>
		<title> MotionAlongCurve </title>
		<meta charset="utf-8" />	 
	</head>
	<body> 	</body>

<script type="module">

// @author hofk

import * as THREE from "../jsm/three.module.128.js";
import { OrbitControls } from "../jsm/OrbitControls.128.js";
import { Flow } from "../jsm/CurveModifier.128.js";

const scene = new THREE.Scene( );
const camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 1, 3 );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
new  OrbitControls( camera, renderer.domElement );

const somePoints = [

	new THREE.Vector3(  1,   0, -1 ),
	new THREE.Vector3(  1, 0.6,  1 ),
	new THREE.Vector3( -1,   0,  1 ),
	new THREE.Vector3( -1, 0.2, -1 ),
	
];

const curve = new THREE.CatmullRomCurve3( somePoints );	
curve.closed = true;

const points = curve.getPoints( 60 );
const line = new THREE.LineLoop( new THREE.BufferGeometry( ).setFromPoints( points ), new THREE.LineBasicMaterial( { color: 0xffffaa } ) );
scene.add( line );

const light = new THREE.DirectionalLight( 0xc0c0c0 );
light.position.set( - 8, 12, 10 );
light.intensity = 1.0;
scene.add( light );

const geometry = new THREE.BoxGeometry( 0.2, 0.08, 0.05 );
const material = new THREE.MeshPhongMaterial( { color: 0x99ffff, wireframe: false } );
const objectToCurve = new THREE.Mesh( geometry, material );

const flow = new Flow( objectToCurve ); 
flow.updateCurve( 0, curve );
scene.add( flow.object3D );

animate( );

function animate( ) {

	requestAnimationFrame( animate );
	flow.moveAlongCurve( 0.0006 );
	renderer.render( scene, camera );

}

</script>
</html>
1 Like

Does this work with models?
i keep getting this in the console:

Uncaught TypeError: Cannot read property ‘clone’ of undefined

I think no.

see CurveModifier.js


  A helper class for making meshes bend aroudn curves
 
export class Flow { ...

Since your code contains quite a lot of things that are unimportant for the thing under consideration, I shortened it to make it easier to understand.

I took a cuboid instead of the cylinder to see the orientation.
To this add a model. As you can see, the bee flies quite peculiar.

Something is missing there. But what?

Link MoveAlongCurve

<!DOCTYPE html>
 <!-- https://discourse.threejs.org/t/struggling-with-paths/26486/7 -->
<head>
  <title> MoveAlongCurve </title>
  <meta charset="utf-8" />
</head>

<body></body>
<script type="module">

import * as THREE from "../jsm/three.module.128.js";
import { OrbitControls } from "../jsm/OrbitControls.128.js";
import { GLTFLoader } from "../jsm/GLTFLoader.128.js";

const renderer = new THREE.WebGLRenderer( {antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( devicePixelRatio );
document.body.appendChild(renderer.domElement); 
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( -2.5, 2, 0 );
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x11aa11);

const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
directionalLight.position.set( 0, 20, 15 );
scene.add( directionalLight );

new  OrbitControls( camera, renderer.domElement );

let newPosition, tangent, radians; 
let fraction = 0;

const up = new THREE.Vector3( 0, 1, 0 );
let axis = new THREE.Vector3( );

const pointsPath = new THREE.CurvePath();

const bezierLine = new THREE.CubicBezierCurve3(

	new THREE.Vector3(  1, 0, -1 ),
	new THREE.Vector3(  1, 0,  1 ),
	new THREE.Vector3( -1, 0,  1 ),
	new THREE.Vector3( -1, 0, -1 ),
	
);

pointsPath.add( bezierLine );
const material = new THREE.MeshNormalMaterial( { wireframe: true } );
const box = new THREE.BoxGeometry( 0.05, 0.4, 0.2 );
 
const char = new THREE.Mesh( box, material );
scene.add( char );

const modelBee = new THREE.Object3D( );
const loader = new GLTFLoader( ).load( 'Kelli Ray_Bee/toi uu.gltf', processBee );
modelBee.scale.set( 0.005, 0.005, 0.005 ); // because gltf.scene is very big
modelBee.rotation.y = Math.PI / 2;
modelBee.rotation.z = -Math.PI / 2;
char.add( modelBee );

const material2 = new THREE.LineBasicMaterial({	color: 0x9132a8});
const points = pointsPath.curves.reduce( ( p, d ) => [...p, ...d.getPoints( 20 )], [] );

const geometry2 = new THREE.BufferGeometry( ).setFromPoints( points );
const liner = new THREE.Line( geometry2, material2 );
scene.add(liner);

animate();

function animate() {
	
	fraction += 0.0004;
	if ( fraction > 1 ) fraction = 0;
	newPosition = pointsPath.getPoint( fraction );
	tangent = pointsPath.getTangent( fraction );
	char.position.copy( newPosition );	
	axis.crossVectors( up, tangent ).normalize( );	
	radians = Math.acos( up.dot( tangent ) );
	char.quaternion.setFromAxisAngle( axis, radians );
	
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
	
};

function processBee( gltf ) { // Kelli Ray  (CC-BY) Poly by Googl
	
	const box = new THREE.Box3( ).setFromObject( gltf.scene );
	const c = box.getCenter( new THREE.Vector3( ) );
	const size = box.getSize( new THREE.Vector3( ) );
	gltf.scene.position.set( -c.x, size.y / 2 - c.y, -c.z ); // center the gltf scene
	modelBee.add( gltf.scene );
	
}

</script>
</html>
2 Likes

Yes sorry about that I was importing it into an existing project so there’s alot of var’s and things that aren’t really needed in the pen the bee rotating is interesting, good work!

A variation of the code results in the same problem. :question:

I have done something similar before, but without Quaternion.
Vector axis is the binormal.

See Addon to create special / extended geometries - #6 by hofk
or
RoadRace


MoveAlongCurve01

<!DOCTYPE html>
 <!-- https://discourse.threejs.org/t/struggling-with-paths/26486/9 -->
<head>
  <title> MoveAlongCurve01 </title>
  <meta charset="utf-8" />
</head>

<body></body>
<script type="module">

import * as THREE from "../jsm/three.module.128.js";
import { OrbitControls } from "../jsm/OrbitControls.128.js";
import { GLTFLoader } from "../jsm/GLTFLoader.128.js";

const renderer = new THREE.WebGLRenderer( {antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( devicePixelRatio );
document.body.appendChild(renderer.domElement); 
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( -2.5, 2, 0 );
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x11aa11);

const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
directionalLight.position.set( 0, 20, 15 );
scene.add( directionalLight );

new OrbitControls( camera, renderer.domElement );

let newPosition, tangent, radians; 
let fraction = 0;

let normal = new THREE.Vector3( 0, 1, 0 ); // up
let axis = new THREE.Vector3( );

const somePoints = [

	new THREE.Vector3(  1,   0, -1 ),
	new THREE.Vector3(  1, 0.6,  1 ),
	new THREE.Vector3( -1,   0,  1 ),
	new THREE.Vector3( -1, 0.2, -1 ),
	
];

const curve = new THREE.CatmullRomCurve3( somePoints );	
curve.closed = true;

const points = curve.getPoints( 60 );
const line = new THREE.LineLoop( new THREE.BufferGeometry( ).setFromPoints( points ), new THREE.LineBasicMaterial( { color: 0xffffaa } ) );
scene.add( line );

const material = new THREE.MeshNormalMaterial( { wireframe: true } );
const box = new THREE.BoxGeometry( 0.05, 0.4, 0.2 );
 
const char = new THREE.Mesh( box, material );
scene.add( char );

const modelBee = new THREE.Object3D( );
const loader = new GLTFLoader( ).load( 'Kelli Ray_Bee/toi uu.gltf', processBee );
modelBee.scale.set( 0.005, 0.005, 0.005 ); // because gltf.scene is very big
modelBee.rotation.y = Math.PI / 2;
modelBee.rotation.z = -Math.PI / 2;
char.add( modelBee );
 

animate();

function animate() {

	fraction += 0.001;
	
	if ( fraction > 1 ) {
	
		fraction = 0;
		normal.set( 0, 1, 0 );
		
	}
	
	char.position.copy( curve.getPoint( fraction ) );
	 	
	tangent = curve.getTangent( fraction );
	axis.crossVectors( normal, tangent ).normalize( );

	radians =  Math.acos( normal.dot( tangent ) );
	char.quaternion.setFromAxisAngle( axis, radians );

	normal.crossVectors( tangent, axis );
	
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
	
};

function processBee( gltf ) { // Kelli Ray  (CC-BY) Poly by Googl
	
	const box = new THREE.Box3( ).setFromObject( gltf.scene );
	const c = box.getCenter( new THREE.Vector3( ) );
	const size = box.getSize( new THREE.Vector3( ) );
	gltf.scene.position.set( -c.x, size.y / 2 - c.y, -c.z ); // center the gltf scene
	modelBee.add( gltf.scene );
	
}

</script>
</html>

UPDATE:

Obviously, the calculation of
radians = Math.acos( normal.dot( tangent ) ); does not matter.

see MoveAlongCurve02

function animate() {

	fraction += 0.001;
	
	if ( fraction > 1 ) {
	
		fraction = 0;
		//normal.set( 0, 1, 0 );
		
	}
	
	char.position.copy( curve.getPoint( fraction ) );
	 	
	tangent = curve.getTangent( fraction );
	axis.crossVectors( normal, tangent ).normalize( );

	//radians = Math.acos( normal.dot( tangent ) );	
	//char.quaternion.setFromAxisAngle( axis, radians );
	
	char.quaternion.setFromAxisAngle( axis, Math.PI / 2 );
	
	//normal.crossVectors( tangent, axis );
	
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
	
};
1 Like

I have a question about the const somePoints so lets say I add 4 more this doesn’t seem to do anything? it’ll only seem to work with like 4 of them

Try

const somePoints = [

    	new THREE.Vector3(  1,   0, -1 ),
    	new THREE.Vector3(  1, 0.6,  1 ),
    	
    	new THREE.Vector3(  0,   -2,  5 ), //new 
    	new THREE.Vector3( 0.7,  -3,  3 ), // new
    	
    	new THREE.Vector3( -1,   0,  1 ),
    	new THREE.Vector3( -1, 0.2, -1 ),
    	
    ];

Still only seems to do the first 4 and ignores the rest:

I used const curve = new THREE.CatmullRomCurve3( somePoints );
not bezierLine = new THREE.CubicBezierCurve3

see three.js docs

1 Like

The bee flies so something more natural. But sometimes upside down.

MoveAlongCurve03

function animate() {

	fraction += 0.001;	
	if ( fraction > 1 ) fraction = 0;
	
	char.position.copy( curve.getPoint( fraction ) );
	 	
	tangent = curve.getTangent( fraction );
	char.quaternion.setFromUnitVectors ( axisX, tangent );
	
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
	
};

And so there is then a backward flight in between.

const axisX = new THREE.Vector3( 1, 0 , 0 );
const axisXneg = new THREE.Vector3( -1, 0 , 0 );

.

function animate() {

	requestAnimationFrame( animate );
	
	fraction += 0.001;	
	if ( fraction > 1 ) fraction = 0;
	
	char.position.copy( curve.getPoint( fraction ) );
	 	
	tangent = curve.getTangent( fraction );
	
	if ( tangent.x < 0 ) {
		
		char.quaternion.setFromUnitVectors( axisXneg, tangent );
		
	} else {
	
		char.quaternion.setFromUnitVectors( axisX, tangent );
		
	}	

	renderer.render( scene, camera );
	
};

see MoveAlongCurve04


It looks more elegant that way.
see MoveAlongCurve05
I do not yet succeed in the complete forward flight. Quaternions are not so easy.

1 Like

Since quaternions are not so simple, I tried angle and rotation.

Thereby I get quite similar flight positions, which are partly not correct. In the console you can check better what happens with this method.

It shows that the angle is always positive and thus the relative position of the vectors is not taken into account. To see this better, I made the trajectory a bit more complicated.

See MoveAlongCurve06

Conclusion: One must make a case distinction.
Or is there a simpler solution using three.js methodes?


UPDATE:
No case distinction, just a better formula. I had already forgotten that I had this calculation 2019 in an example.

Math.atan2( )

See Addon to create special / extended geometries - #6 by hofk

RoadRace


A better solution see .setRotationFromMatrix( ); - what causes the mirroring and thus the flickering?

and

BeginnerExample
// … step 15: matrix, move along room curve
beeFlight( );


Related: [Solved] Help with wrong position meshes along a curve