javaanimationjava-3d

Making a correct animation with java3D and rotpospathInterpolator


I'm trying to make an object fly over my scene in a square (which it does correctly) but I want it to face towards where it's going as well...

Problem is currently the object flies, and while flying it rotates (which is not natural). I'd like it to fly to a point, then stop and rotate, then fly to the next one, then rotate again and so on.

I'm trying to achieve this with the Java3D function: RotPosPathInterpolator

And this is what I'm doing:

Alpha alphaNave = new Alpha( -1, Alpha.INCREASING_ENABLE, 0,0,6000,0,0,0,0,0 );
TransformGroup target = new TransformGroup();
Transform3D axisOfRotPos = new Transform3D();
float[] alphas = {0.0f, 0.25f, 0.50f, 0.75f, 1.0f};
Quat4f[] quats = new Quat4f[5];
Point3f[] positions = new Point3f[5];

target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    quats[0] = new Quat4f(0.0f, 1.0f, 0.0f, (float) Math.toRadians(0));
    quats[1] = new Quat4f(0.0f, 1.0f, 0.0f, (float) Math.toRadians(90));
    quats[2] = new Quat4f(0.0f, 1.0f, 0.0f, (float) Math.toRadians(180));
    quats[3] = new Quat4f(0.0f, 1.0f, 0.0f, (float) Math.toRadians(270));
    quats[4] = quats[0];

    positions[0]= new Point3f( -20.0f,  0.0f, 20.0f);
    positions[1]= new Point3f( -20.0f, 0.0f, -20.0f);
    positions[2]= new Point3f( 20.0f,  0.0f, -20.0f);
    positions[3]= new Point3f( 20.0f,  0.0f, 20.0f);
    positions[4]= positions[0];

RotPosPathInterpolator rotPosPath = new RotPosPathInterpolator(
            alphaNave, target, axisOfRotPos, alphas, quats, positions);

Solution

  • First a short side note: When creating a quaternion, the last component is not just the angle. In order to create a quaternion that describes the rotation around a certain axis, about a certain angle, the easiest is to go over the AxisAngle4f class.

    AxisAngle4f a = new AxisAngle4f(0.0f, 1.0f, 0.0f, angleInRadians);
    Quat4f q = new Quat4f();
    q.set(a);
    

    (unfortunately, there is no convenient constructor for this. I'd recommend wrapping these 3 lines in a utility method that returns the appropriate quaternion).


    Concerning the actual question: If I understood you correctly, the desired behavior is this:

    Animation

    (this was created from the MCVE that I added below).

    If you want to have only a movement (without rotation), then you'll have to insert two points into the interpolation path where only the position changes, but the rotation stays the same. Similarly, when you want only a rotation, then you have to create two points where only the rotation changes, but the position stays the same. Of course, you always have to adjust your alpha values accordingly.

    For this example, your path will consist of 9 points. Adding these points manually, and computing the required alpha values is a hassle. I'd recommend creating a small utility class for this. The following MCVE allows adding the points (each consisting of a position and an angle) to a InterpolatorData class, which afterwards provides the data for the interpolator.

    import java.awt.GraphicsConfiguration;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.media.j3d.Alpha;
    import javax.media.j3d.BoundingSphere;
    import javax.media.j3d.BranchGroup;
    import javax.media.j3d.Canvas3D;
    import javax.media.j3d.RotPosPathInterpolator;
    import javax.media.j3d.Transform3D;
    import javax.media.j3d.TransformGroup;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    import javax.vecmath.AxisAngle4f;
    import javax.vecmath.Point3d;
    import javax.vecmath.Point3f;
    import javax.vecmath.Quat4f;
    import javax.vecmath.Vector3d;
    
    import com.sun.j3d.utils.geometry.ColorCube;
    import com.sun.j3d.utils.universe.SimpleUniverse;
    
    public class RotPosPathInterpolatorTest
    {
        public static void main(String[] args) 
        {
            System.setProperty("sun.awt.noerasebackground", "true");
    
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {
                    createAndShowGUI();
                }
            });
        }
    
        private static void createAndShowGUI()
        {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);;
    
            GraphicsConfiguration config = 
                SimpleUniverse.getPreferredConfiguration();
            Canvas3D canvas = new Canvas3D(config);
            frame.getContentPane().add(canvas);
            SimpleUniverse simpleUniverse = new SimpleUniverse(canvas);
    
            BranchGroup rootBranchGroup = new BranchGroup();
            createContents(rootBranchGroup);
    
            simpleUniverse.addBranchGraph(rootBranchGroup);
    
            Transform3D t0 = new Transform3D();
            t0.rotX(Math.toRadians(-45));
            Transform3D t1 = new Transform3D();
            t1.setTranslation(new Vector3d(0,0,10));
            t0.mul(t1);
            simpleUniverse.
                getViewingPlatform().
                getViewPlatformTransform().
                setTransform(t0);
    
            frame.setSize(400, 400);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        private static class InterpolatorData
        {
            private final List<Point3f> positions = new ArrayList<Point3f>();
            private final List<Quat4f> orientations = new ArrayList<Quat4f>();
    
            void add(Point3f p, float angleDeg)
            {
                positions.add(p);
    
                AxisAngle4f a = new AxisAngle4f(
                    0.0f, 1.0f, 0.0f, (float) Math.toRadians(angleDeg));
                Quat4f q = new Quat4f();
                q.set(a);
                orientations.add(q);
            }
    
            Point3f[] getPositions()
            {
                return positions.toArray(new Point3f[0]);
            }
    
            Quat4f[] getOrientations()
            {
                return orientations.toArray(new Quat4f[0]);
            }
    
            float[] getAlphas()
            {
                float alphas[] = new float[positions.size()];
                float delta = 1.0f / (alphas.length - 1);
                for (int i=0; i<alphas.length; i++)
                {
                    alphas[i] = i * delta;
                }
                return alphas;
            }
        }
    
        private static void createContents(BranchGroup rootBranchGroup)
        {
            Alpha alpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 
                0, 0, 6000, 0, 0, 0, 0, 0);
            TransformGroup target = new TransformGroup();
            target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
            Transform3D axisOfTransform = new Transform3D();
    
            InterpolatorData i = new InterpolatorData();
            i.add(new Point3f(-2.0f, 0.0f,  2.0f),   0.0f);
            i.add(new Point3f(-2.0f, 0.0f, -2.0f),   0.0f);
            i.add(new Point3f(-2.0f, 0.0f, -2.0f),  90.0f);
            i.add(new Point3f( 2.0f, 0.0f, -2.0f),  90.0f);
            i.add(new Point3f( 2.0f, 0.0f, -2.0f), 180.0f);
            i.add(new Point3f( 2.0f, 0.0f,  2.0f), 180.0f);
            i.add(new Point3f( 2.0f, 0.0f,  2.0f), 270.0f);
            i.add(new Point3f(-2.0f, 0.0f,  2.0f), 270.0f);
            i.add(new Point3f(-2.0f, 0.0f,  2.0f),   0.0f);
    
            RotPosPathInterpolator interpolator = new RotPosPathInterpolator(
                alpha, target, axisOfTransform, 
                i.getAlphas(), i.getOrientations(), i.getPositions());        
            interpolator.setSchedulingBounds(
                new BoundingSphere(new Point3d(), 100.0));
    
            rootBranchGroup.addChild(target);
            target.addChild(interpolator);
            target.addChild(new ColorCube(0.4));
        }
    }
    

    Another comment: Using such a utility class, this solution may be OK, but for more complex paths and behaviors, one would probably create a dedicated infrastructure for the path handling.