李晓东
李晓东

Reputation: 83

How to rotate a path in threejs

I'm new to three.js I want let an object moving along the ellipse curve, but after I rotated the ellipse with ellipse.rotation.y, the object will no longer moving along the path, the object will still move with the original path, so how can I fix this problem? I was using the code in this site to simulate my problem

http://jsfiddle.net/w9914420/krw8nwLn/14/

// GLOBALS - ALLOCATE THESE OUTSIDE OF THE RENDER LOOP - CHANGED
var cubes = [], marker, spline;
var matrix = new THREE.Matrix4();
var up = new THREE.Vector3( 0, 1, 0 );
var axis = new THREE.Vector3( );
var pt, radians, axis, tangent, path;

// the getPoint starting variable - !important - You get me ;)
var t = 0;

//This function generates the cube and chooses a random color for it 
//on intial load.

function getCube(){
    // cube mats and cube
    var mats = [];
    for (var i = 0; i < 6; i ++) {
        mats.push(new THREE.MeshBasicMaterial({color:Math.random()*0xffffff}));
    }

    var cube = new THREE.Mesh(
        new THREE.CubeGeometry(2, 2, 2),
        new THREE.MeshFaceMaterial( mats )
    );

    return cube
}

// Ellipse class, which extends the virtual base class Curve
function Ellipse( xRadius, yRadius ) {

        THREE.Curve.call( this );

        // add radius as a property
        this.xRadius = xRadius;
        this.yRadius = yRadius;

}

Ellipse.prototype = Object.create( THREE.Curve.prototype );
Ellipse.prototype.constructor = Ellipse;

// define the getPoint function for the subClass
Ellipse.prototype.getPoint = function ( t ) {

    var radians = 2 * Math.PI * t;

    return new THREE.Vector3( this.xRadius * Math.cos( radians ),
                              this.yRadius * Math.sin( radians ),
                              0 );

};

//

var mesh, renderer, scene, camera, controls;


function init() {

    // renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    // scene
    scene = new THREE.Scene();

    // camera
    camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
    camera.position.set( 20, 20, 20 );

    // controls
    controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.addEventListener( 'change', render ); // use if there is no animation loop
    controls.minDistance = 10;
    controls.maxDistance = 50;

    // light
    var light = new THREE.PointLight( 0xffffff, 0.7 );
    camera.add( light );
    scene.add( camera ); // add to scene only because the camera  has a child

    // axes
    scene.add( new THREE.AxisHelper( 20 ) );


    ////////////////////////////////////////
    //      Create the cube               //
    ////////////////////////////////////////

    marker = getCube();
    marker.position.set(0,0,0);
    scene.add(marker);


    ////////////////////////////////////////
    //      Create an Extruded shape      //
    ////////////////////////////////////////

    // path
    path = new Ellipse( 5, 10 );

    // params
    var pathSegments = 64;
    var tubeRadius = 0.5;
    var radiusSegments = 16;
    var closed = true;

    var geometry = new THREE.TubeBufferGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );

    // material
    var material = new THREE.MeshPhongMaterial( {
        color: 0x0080ff, 
    } );

    // mesh
    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    //////////////////////////////////////////////////////////////////////////
    //       Create the path which is based on our shape above              //
    //////////////////////////////////////////////////////////////////////////

    //Please note that this red ellipse was only created has a guide so that I could  be certain that the square is true to the tangent and positioning.

    // Ellipse class, which extends the virtual base class Curve
        var curve = new THREE.EllipseCurve(
            0,  0,            // ax, aY
            6, 11,           // xRadius, yRadius
            0,  2 * Math.PI,  // aStartAngle, aEndAngle
            false,            // aClockwise
            0                 // aRotation
        );

        //defines the amount of points the path will have
        var path2 = new THREE.Path( curve.getPoints( 100 ) );
         geometrycirc = path2.createPointsGeometry( 100 );
        var materialcirc = new THREE.LineBasicMaterial( {
            color : 0xff0000
        } );

        // Create the final object to add to the scene
        var ellipse = new THREE.Line( geometrycirc, materialcirc );
        ellipse.position.set(0,0,0);
        scene.add( ellipse );

}

function animate() {
    requestAnimationFrame(animate);

    render();
}


function render() {

        // set the marker position
        pt = path.getPoint( t );

        // set the marker position
        marker.position.set( pt.x, pt.y, pt.z );

        // get the tangent to the curve
        tangent = path.getTangent( t ).normalize();

        // calculate the axis to rotate around
        axis.crossVectors( up, tangent ).normalize();

        // calcluate the angle between the up vector and the tangent
        radians = Math.acos( up.dot( tangent ) );

        // set the quaternion
        marker.quaternion.setFromAxisAngle( axis, radians );

        t = (t >= 1) ? 0 : t += 0.002;

        renderer.render( scene, camera );

}

init();
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 2

Views: 901

Answers (1)

Rabbid76
Rabbid76

Reputation: 210968

The easiest way to achieve what you want, is not to add the tube (mesh) and the path tracking object (marker) to the scene, but to add it to the ellipse.
So the marker and mesh are children of the ellipse and transformations which are applied to ellipse, are "inherited" to marker and mesh:

scene.add(marker);
scene.add(mesh );

ellipse.add(mesh);
ellipse.add(marker);
scene.add(ellipse);

See the example, whee I applied the suggested changes to your original code:

// GLOBALS - ALLOCATE THESE OUTSIDE OF THE RENDER LOOP - CHANGED
var cubes = [], marker, spline;
var matrix = new THREE.Matrix4();
var up = new THREE.Vector3( 0, 1, 0 );
var axis = new THREE.Vector3( );
var pt, radians, axis, tangent, path;

var ellipse;

// the getPoint starting variable - !important - You get me ;)
var t = 0;

//This function generates the cube and chooses a random color for it 
//on intial load.

function getCube(){
    // cube mats and cube
    var mats = [];
    for (var i = 0; i < 6; i ++) {
        mats.push(new THREE.MeshBasicMaterial({color:Math.random()*0xffffff}));
    }

    var cube = new THREE.Mesh(
        new THREE.CubeGeometry(2, 2, 2),
        new THREE.MeshFaceMaterial( mats )
    );

    return cube
}

// Ellipse class, which extends the virtual base class Curve
function Ellipse( xRadius, yRadius ) {

        THREE.Curve.call( this );

        // add radius as a property
        this.xRadius = xRadius;
        this.yRadius = yRadius;
}

Ellipse.prototype = Object.create( THREE.Curve.prototype );
Ellipse.prototype.constructor = Ellipse;

// define the getPoint function for the subClass
Ellipse.prototype.getPoint = function ( t ) {

    var radians = 2 * Math.PI * t;

    return new THREE.Vector3( this.xRadius * Math.cos( radians ),
                              this.yRadius * Math.sin( radians ),
                              0 );
};

var mesh, renderer, scene, camera, controls;

function init() {

    // renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    // scene
    scene = new THREE.Scene();

    // camera
    camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
    camera.position.set( 20, 20, 20 );

    // controls
    controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.addEventListener( 'change', render ); // use if there is no animation loop
    controls.minDistance = 10;
    controls.maxDistance = 50;

    // light
    var light = new THREE.PointLight( 0xffffff, 0.7 );
    camera.add( light );
    scene.add( camera ); // add to scene only because the camera  has a child

    // axes
    scene.add( new THREE.AxisHelper( 20 ) );

    ////////////////////////////////////////
    //      Create the cube               //
    ////////////////////////////////////////

    marker = getCube();
    marker.position.set(0,0,0);
    //scene.add(marker);


    ////////////////////////////////////////
    //      Create an Extruded shape      //
    ////////////////////////////////////////

    // path
    path = new Ellipse( 5, 10 );

    // params
    var pathSegments = 64;
    var tubeRadius = 0.5;
    var radiusSegments = 16;
    var closed = true;

    var geometry = new THREE.TubeBufferGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );

    // material
    var material = new THREE.MeshPhongMaterial( {
        color: 0x0080ff, 
    } );

    // mesh
    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    //////////////////////////////////////////////////////////////////////////
    //       Create the path which is based on our shape above              //
    //////////////////////////////////////////////////////////////////////////

    //Please note that this red ellipse was only created has a guide so that I could  be certain that the square is true to the tangent and positioning.

    // Ellipse class, which extends the virtual base class Curve
    var curve = new THREE.EllipseCurve(
        0,  0,            // ax, aY
        6, 11,           // xRadius, yRadius
        0,  2 * Math.PI,  // aStartAngle, aEndAngle
        false,            // aClockwise
        0                 // aRotation
    );
  

    //defines the amount of points the path will have
    var path2 = new THREE.Path( curve.getPoints( 100 ) );
      geometrycirc = path2.createPointsGeometry( 100 );
    var materialcirc = new THREE.LineBasicMaterial( {
        color : 0xff0000
    } );
      

    // Create the final object to add to the scene
    ellipse = new THREE.Line( geometrycirc, materialcirc );
    ellipse.position.set(0,0,0);

    ellipse.add(mesh);
    ellipse.add(marker);
    scene.add( ellipse );
    window.onresize = resize;
}

function animate() {
    ellipse.rotation.y += Math.PI / 200
    requestAnimationFrame(animate);
    render();
}

function render() {

    // set the marker position
    pt = path.getPoint( t );

    // set the marker position
    marker.position.set( pt.x, pt.y, pt.z );

    // get the tangent to the curve
    tangent = path.getTangent( t ).normalize();

    // calculate the axis to rotate around
    axis.crossVectors( up, tangent ).normalize();

    // calcluate the angle between the up vector and the tangent
    radians = Math.acos( up.dot( tangent ) );

    // set the quaternion
    marker.quaternion.setFromAxisAngle( axis, radians );

    t = (t >= 1) ? 0 : t += 0.002;

    renderer.render( scene, camera );
}

function resize() {    
    var aspect = window.innerWidth / window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = aspect;
    camera.updateProjectionMatrix();
}

init();
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 1

Related Questions