Switching Between Pivot Points

Started by paulscode, February 22, 2008, 05:29:16 AM

Previous topic - Next topic

paulscode

Each frame, I am trying to change the rotation pivot of an object from (0,-135,0) to (0,0,0) before doing a rotateAxis() on it, and then change the rotation pivot back to (0,-135,0) afterwards.  It seems like the following code should work:


        myObject.setRotationPivot( new SimpleVector( 0, 0, 0 ) );
        myObject.rotateAxis( myObject.getZAxis(), radZ );
        myObject.setRotationPivot( new SimpleVector( 0, -135.0f, 0 ) );


Instead, the object just rotates around the original pivot (0,-135,0), the same as if I don't put any setRotationPivot()'s in there at all.  I am sure there is a simple solution to this, but I can't seem figure it out.

EgonOlsen

The rotation is applied to the rotation matrix, not to the object when calling rotateXXX(). It can't take the pivot into account at that stage. Only renderScene() takes the pivot and the matrix and applies that to the object. In your case, the pivot has been set back to ...,-135,... at that time, which is why it works with that value instead of the 0,0,0.

paulscode

Ahh, I see.  That makes sense.  So, how would I accomplish rotations around different pivots within the same frame (short of rendering the scene twice per call to paint)?  I suppose this could maybe be done with a rotation followed by a translation, to create the appearance of using a different pivot.
Has anyone out there tackeled this problem before? (planets revolving around a sun while also rotating around their axis, gear assemblies, etc.)

EgonOlsen

You should be able to use dummy objects for that and create an object hierarchy (the dummies don't have to belong to the world, they are just lending their transformation to other objects) among them, i.e. place a dummy object in the center of the sun and make the planet its child. That way, the planet executes the rotation of the dummy as well as its own.

paulscode

Yep, that is definitely the trick.  I had actually solved the problem with a rotation + translation, but this way is a lot cleaner.

I did notice something odd, though.

When I use Object3D.createDummyObj() to make the dummy object, the world looks kind of funky, as though a number of polygons are not being drawn.

Using "new Object3D(0)" instead of "createDummyObj()" also causes the same behavior.

However, using "new Object3D(1)" works perfectly.  There is probably a perfectly reasonable explanation for this behavior, but I thought I would mention it since it did look a bit odd to me.

Here is my source code for reference:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.io.File;

import javax.swing.JApplet;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Lights;
import com.threed.jpct.Loader;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

import java.lang.System;

public class jPCTGears extends JApplet implements MouseListener, MouseMotionListener
{
    private Object3D redGear, blueGear, assemblyPivot;
    private FrameBuffer buffer = null;
    private World world = null;
    private Camera camera = null;
    private int width = 640;
    private int height = 480;
    private float gear_rotation = 0.02f;
    private int prevMouseX, prevMouseY;
   
    // Initialize all components of the applet
    @Override
    public void init()
    {
        // sign the applet up to receive mouse messages:
        addMouseListener( this );
        addMouseMotionListener( this );
       
        world = new World();  // create a new world

        // create a new buffer to render the world on:
        buffer = new FrameBuffer( width, height, FrameBuffer.SAMPLINGMODE_NORMAL );
        buffer.enableRenderer( IRenderer.RENDERER_SOFTWARE );               

        // load some 3D objects and make sure they have the correct orientation:
        redGear = loadMeshFile( "RedGear.3ds" );
        redGear.rotateY( (float)Math.PI / 2.0f );
        redGear.rotateMesh();
        redGear.setRotationMatrix( new Matrix() );
        redGear.setOrigin( new SimpleVector( 0, 0, 0 ) );
        redGear.build();
        blueGear = loadMeshFile( "BlueGear.3ds" );
        blueGear.rotateY( (float)Math.PI / 2.0f );
        blueGear.rotateZ( 0.35f );
        blueGear.rotateMesh();
        blueGear.setRotationMatrix( new Matrix() );
        blueGear.setOrigin( new SimpleVector( 0, 135.0f, 0 ) );
        blueGear.build();
       
        // Set up a pivot point for the entire gear assembly:
        assemblyPivot = new Object3D(1);
        assemblyPivot.setOrigin( new SimpleVector( 0, 0, 0 ) );
        // Make the gears children to assemblyPivot.
        // Translations and rotations to assemblyPivot
        // will affect the entire gear assembly:
        assemblyPivot.addChild( redGear );
        assemblyPivot.addChild( blueGear );
               
        // add the objects our world:
        world.addObject( redGear );
        world.addObject( blueGear );
        world.buildAllObjects();
       
        lookAt( redGear );  // make sure the camera is facing towards the gear assembly
        letThereBeLight();  // create light sources for the scene
    }

    // Draw the scene
    @Override
    public void paint( Graphics g )
    {
        // rotate the gears:
        redGear.rotateAxis( redGear.getZAxis(), -gear_rotation );
        blueGear.rotateAxis( blueGear.getZAxis(), 2.0f * gear_rotation );
       
        buffer.clear();   // erase the previous frame

        // render the world onto the buffer:
        world.renderScene( buffer );
        world.draw( buffer );
        buffer.update();

        buffer.display(g, 0, 0);  // draw the buffer onto the applet frame
       
        repaint( 200, 0, 0, width, height );  // keep the graphics auto-refreshing
    }

    // Load a 3Ds file, and return its Object3D handle
    private Object3D loadMeshFile( String filename )
    {
        Object3D newObject;
        Object3D[] objs = Loader.load3DS( this.getDocumentBase(), "models" + File.separatorChar + filename, 1.0f );

        if( objs.length==1 )
        {
            // The object loaded fine, just need to initialize it
            newObject=objs[0];
            newObject.setCulling( Object3D.CULLING_DISABLED );
            newObject.build();
        }
        else
        {
            // Didn't load anything, or loaded
            //     more than 1 object (not supposed to happen)
            System.out.println( "Unknown file format: " + filename );
            newObject = null;
        }

        return newObject;
    }

    // point the camera toward the given object
    private void lookAt( Object3D obj )
    {
        camera = world.getCamera();  // grab a handle to the camera
        camera.setPosition( 0, 0, 500 );  // set its *relative* position
        camera.lookAt( obj.getTransformedCenter() );  // look toward the object
    }

    // create light sources for the scene
    private void letThereBeLight()
    {
        world.getLights().setOverbrightLighting (
            Lights.OVERBRIGHT_LIGHTING_DISABLED );
        world.getLights().setRGBScale( Lights.RGB_SCALE_2X );

        // Set the overall brightness of the world:
        world.setAmbientLight( 50, 50, 50 );

        // Create a main light-source:
        world.addLight( new SimpleVector( 50, -50, 300 ), 20, 20, 20 );
    }
   
    // Dragging the mouse should rotate the entire gear assembly
    public void mouseDragged( MouseEvent e )
    {
        // get the mouse's coordinates:
        int x = e.getX();
        int y = e.getY();
        Dimension size = e.getComponent().getSize();

        // Calculate the angles to rotate the assembly:
        float thetaY = (float)Math.PI * ( (float)(x-prevMouseX)/(float)size.width );
        float thetaX = (float)Math.PI * ( (float)(prevMouseY-y)/(float)size.height );
       
        // Apply the rotations to the gear assembly:
        assemblyPivot.rotateAxis( assemblyPivot.getXAxis(), thetaX );
        assemblyPivot.rotateAxis( assemblyPivot.getYAxis(), thetaY );

        // Keep track of the mouse location:
        prevMouseX = x;
        prevMouseY = y;
    }
    public void mousePressed( MouseEvent e )
    {
        // Start keeping track of the mouse location now.
        // This prevent the gear assembly from jerking to the new angle
        // whenever mouseDragged first gets called:
        prevMouseX = e.getX();
        prevMouseY = e.getY();
    }
    public void mouseReleased( MouseEvent e ){}
    public void mouseEntered( MouseEvent e ) {}
    public void mouseExited( MouseEvent e ) {}
    public void mouseClicked( MouseEvent e ) {}
    public void mouseMoved( MouseEvent e ) {}
}


At any rate, my question is solved, and I learned a little bit along the way.  Time for me to try something more difficult  ;D  Thanks a lot!

EgonOlsen

Quote from: paulscode on February 23, 2008, 03:29:21 AM
When I use Object3D.createDummyObj() to make the dummy object, the world looks kind of funky, as though a number of polygons are not being drawn.
I can't find a reason for this neither in your code nor in mine... ??? Can you upload the 3ds-files you are using so that i'm able to run the applet and see for myself. That should definitely not happen and if it does, it's a bug.

EgonOlsen

#6
Nevermind, i've found the models on your webpage. I'll now see if i can reproduce the problem.

Edit: No, i can't reproduce it. Your applet looks fine no matter if i'm using createDummyObj() or new Object3D(0) or new Object3D(1). If you are still having the problem, you may try the rc1 of 1.16 (http://www.jpct.net/download/beta/jpctapi116rc1.zip), because that is what i was using, albeit i don't think that i've done anything what may cause/fix this between that versions.

paulscode

The upgrade did the trick.  Whatever the issue was with the other version, it seems to be fixed now  ;)

Thanks again!

EgonOlsen

Strange...well, at least it works now, so everything is fine.  ;D