6
votes

Not sure what I'm missing here. Trying to make a planet (i.e. sphere) rotate by having the user click on a 'Rotate' button, but can't seem to figure it out. I do have the following segment which rotates the sphere by way of user interaction with the mouse:

    document.onmousemove = function() {
        if (!mouseDown) {
            return;
        }
        var newX = event.clientX;
        var newY = event.clientY;

        var deltaX = newX - lastMouseX
        var newRotationMatrix = mat4.create();
        mat4.identity(newRotationMatrix);
        mat4.rotate(newRotationMatrix, degToRad(deltaX / 10), [0, 1, 0]);

        var deltaY = newY - lastMouseY;
        mat4.rotate(newRotationMatrix, degToRad(deltaY / 10), [1, 0, 0]);

        mat4.multiply(newRotationMatrix, planetRotationMatrix, planetRotationMatrix);

        lastMouseX = newX
        lastMouseY = newY;
}

This one works fine, but I also want to make the planet rotate automatically after the user clicks the button. Here is my onLoad function:

    window.onload = function onLoad() {
        canvas = document.getElementById("gl-canvas");
        initGL(canvas);
        initShaders();
        initBuffers();
        initTexture();
        initEvtHandlers();

        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);

        var newRotationMatrix = mat4.create();
        mat4.identity(newRotationMatrix);
        document.getElementById("rotate").onclick = function () {
            // rotation code goes here?
        }
        render(); 
}

My question is: How can I 'toggle' the rotation after the user clicks the button? So far what I've tried is modify the onmousemove function in an attempt to make the sphere rotate without user interaction, but that's not working.

As an update, here is a new doRotate() function I added:

var myRotationMatrix = mat4.create();
mat4.identity(myRotationMatrix);

doRotate = function () {
    var dx = 1;
    var newMatrix = mat4.create();
    mat4.identity(newMatrix);
    mat4.rotate(newMatrix, degToRad(dx / 10), [0, 1, 0]);

    mat4.multiply(newMatrix, myRotationMatrix);
    requestAnimFrame(doRotate);
}

Which is currently not working for me. This function is supposed to be called when the button is clicked, toggling the rotation.

Update:

For a similar demo, please see this page. What I'm trying to do is make the sphere rotate automatically after the user clicks the relevant button. This is obviously not in the demo linked--that's only for clarification purposes.

Update 2:

I've since figured it out. Please see my answer below for details.

2
I'm not really sure what you're asking. If you want to keep it rotating you need to have some kind of animation loop using requestAnimationFrame and every time in the loop rotate the planet a little. Example.gman
I am trying to make the sphere rotate by itself after the user clicks the button, without having to interact with it using the mouse. I realize this requires requestAnimationFrame, however I have not been successful so far.Fiery Phoenix
Interesting question. It would help if you could put together an example e.g. using codepen or jsfiddle. You can then demo your problem better and most likely get an answer quicker.Ben Smith
Or much better than codepen / jsfiddle you could just post it inline in your question. Just click the little document icon with the <> in the toolbar above the area where you edit your question.gman
@gman nice. I need to start using that too!Ben Smith

2 Answers

3
votes

I think the problem with your doRotate is that it doesnt re-draw the scene. Sure, you calculated a new rotation matrix but you have not pass the information to the gpu nor tell it to draw the scene again. But without knowing how your program is written it is hard to say if that is actually the problem.

It is usually a good idea to structure the program around a mainLoop function that runs every frame and handles all the necessary updates and redraw the scene. Like so:

var lastTimeStamp = 0;

function mainLoop(timeStamp){
    var dt = timeStamp - lastTimeStamp;
    lastTimeStamp = timeStamp;

    everythingThatNeedsUpdate.doUpdate(dt); // this should calculate the new rotationMatrix for your sphere and send it to GPU

    gl.drawEverything();

    requestAnimationFrame(mainLoop);
}

Then, for example, *.doUpdate may be implemented like such:

*.doUpdate = function(dt){
    if (clickedOnSphere){
        rotationMatrix = doCalculations(dt);
        // send or flag rotationMatrix to be sent to GPU; 
    }
}
3
votes

I figured it out on my own after countless amounts of trial and error. Basically, as WaclawJapser alluded to, I was not redrawing the scene. I was also ignoring the uniforms altogether. I won't post the whole code, but here is the relevant function after the fix:

requestAnimationFrame(rotatePlanet);

    function rotatePlanet(now) {
        if (flag == false) {
            return; // do nothing if unchecked
        } else {
            // Start by clearing the current buffers to avoid an overlap.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            // Subtract the previous time from the current time.
            now *= 0.001;
            var deltaTime = now - then;
            // Cache the current time to remember it for the next frame.
            then = now;

            // Every frame increases the rotation a little.
            rotation[0] += rotationSpeed * deltaTime;

            // Perform the necessary transformations. 
            mat4.identity(mvMatrix);
            mat4.translate(mvMatrix, [0, 0, -6]);
            mat4.rotate(mvMatrix, rotation[0], [0, 1, 0]); // rotates around y
            mat4.rotate(mvMatrix, rotation[1], [1, 0, 0]); // rotates around x

            // Set the matrix.
            setMatrixUniforms();

            // Draw the geometry.
            gl.drawElements(gl.TRIANGLES, planetVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

            requestAnimationFrame(rotatePlanet);
        }
    }

This now works exactly as I had originally intended. I don't know if it will help anyone since it seems like quite a narrow issue, but I figure I'd post a status update.

Thanks to everyone who has tried to help!