Search code examples
javaanimationtransformationjava-3d

Java3D Sphere Orbit


I've looked everywhere and I can't find anything on how to make a sphere orbit a central point in Java 3D. Specifically I want to make a sphere do a circular motion around the origin at a constant speed and for it to loop forever. I guess that these equations are to do with it:

X = originX + sin(angle)*Size;

Y = originY + cos(angle)*Size;

I tried using a PositionInterpolator but that only covers 1 axis so doesn't form a circular orbit. Also I noticed only 1 transformation can be done at a time, the spinning on the axis OR the PositionInterpolator, How do I have both transformations applied to the object at once ?

      Planet planet = new Planet(new Color3f(0.2f,0.2f,0.2f),new Vector3f(1.0f,0.0f,-10.0f), 0.2f,1.0f,1.0f, 1.0f);
      Sphere planet = new Sphere(planet.radius,planet.pl);
    
    Transform3D tfgPlanet = new Transform3D();
    
  //  tfg.setTranslation(planet.position);
    tfgplanet.setTranslation(planet.position);
    TransformGroup tgm = new TransformGroup(tfgPlanet);
    tgm.addChild(planet);
    theScene.addChild(tgm);


    Transform3D planetRotate = new Transform3D();
    
    int timerotation = 1500;//A slow rotation takes 1.5 seconds.
    
    //The Alpha for rotation
    Alpha planetRotationStart = new Alpha(1000,
            Alpha.INCREASING_ENABLE,0,0,timerotation,0,0,0,0,0);

    //rotate around axis
    RotationInterpolator planetrotation = new RotationInterpolator(
    planetRotationStart,tgm,
    planetRotate,planet.orbitAngle,(float) Math.PI*2);

    BoundingSphere bind = new BoundingSphere(new Point3d(0.0,0.0,0.0),Double.MAX_VALUE);
    planetrotation.setSchedulingBounds(bind);

    tgm.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    tgm.addChild(planetrotation);
    

    Alpha planetOrbit = new Alpha(1000,
            Alpha.INCREASING_ENABLE,0,0,timerotation,0,0,0,0,0);
    
    Transform3D axis = new Transform3D();
    PositionInterpolator pi = new PositionInterpolator(planetOrbit,tgm,axis,1.0f, 10.0f);
    pi.setSchedulingBounds(bind);
    tgm.addChild(pi);

    //compiles scene
     theScene.compile();

    //Add everything to the universe.
    su.addBranchGraph(theScene);
    }

Solution

  • The code is a mess (and it seems like parts of it have been duplicated while posting?). However regarding the actual question:

    It's true that one TransformGroup can only contain one specific Transform3D. Although it is possible to assemble multiple transformation (e.g. a rotation and a translation) in a single Transform3D, this does not work nicely with the predefined interpolators.

    The whole idea of Java3D, being a scene-graph-based API, is to assemble a "tree" of nodes, where each node serves a specific purpose.

    In this case, your tree will consist of several nodes:

     S     Sphere: The planet
     |
     |
    RTG    Rotation TransformGroup: Responsible for 
     |     rotating the planet about its y-asis
     |
     |
    TTG    Translation TransformGroup: Responsible for 
     |     translating the (rotating) planet away from
     |     the sun
     |
    OTG    Orbit TransformGroup: Responsible for 
     |     rotating the (translated and rotating) planet 
     |     about the center of the sun
     |
    Root   The root node of your universe
    

    You can make sure that the structure of the code resembles the structure of the graph by giving appropriate variable- and method names.

    There is a complete example of a rotating object that orbits around the center:

    import java.awt.GraphicsConfiguration;
    
    import javax.media.j3d.Alpha;
    import javax.media.j3d.BoundingSphere;
    import javax.media.j3d.BranchGroup;
    import javax.media.j3d.Canvas3D;
    import javax.media.j3d.Node;
    import javax.media.j3d.RotationInterpolator;
    import javax.media.j3d.Transform3D;
    import javax.media.j3d.TransformGroup;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    import javax.vecmath.Point3d;
    import javax.vecmath.Vector3d;
    
    import com.sun.j3d.utils.geometry.ColorCube;
    import com.sun.j3d.utils.geometry.Sphere;
    import com.sun.j3d.utils.universe.SimpleUniverse;
    
    public class SphereOrbit
    {
        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 viewPlatformTransform = new Transform3D();
            Transform3D t0 = new Transform3D();
            t0.setTranslation(new Vector3d(0,0,10));
            Transform3D t1 = new Transform3D();
            t1.rotX(Math.toRadians(-30));
            viewPlatformTransform.mul(t1, t0);
            simpleUniverse.getViewingPlatform().
                getViewPlatformTransform().setTransform(viewPlatformTransform);;
    
            frame.setSize(800,800);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
    
        }
    
        private static BoundingSphere boundingSphere = 
            new BoundingSphere(new Point3d(0.0,0.0,0.0), Double.MAX_VALUE);
    
    
        // Build the transform group that does the rotation
        // of the planet in its local coordinate system 
        // (This will cause the planet to spin about its own y-axis)
        private static TransformGroup createRotationTransformGroup(
            int rotationTimeMs)
        {
            TransformGroup rotationTransformGroup = new TransformGroup();
            rotationTransformGroup.setCapability(
                TransformGroup.ALLOW_TRANSFORM_WRITE);
            Alpha rotationAlpha = new Alpha(-1, rotationTimeMs);
            RotationInterpolator rotationInterpolator = 
                new RotationInterpolator(rotationAlpha, rotationTransformGroup);
            rotationInterpolator.setSchedulingBounds(boundingSphere);
            rotationTransformGroup.addChild(rotationInterpolator);
            return rotationTransformGroup;
        }
    
        // Build the transform group that moves the (rotating) planet 
        // about a certain (fixed) distance, away from the center
        private static TransformGroup createTranslatingTransformGroup(
            double distanceFromCenter)
        {
            TransformGroup translationTransformGroup = new TransformGroup();
            Transform3D translationTransform = new Transform3D();
            translationTransform.setTranslation(
                new Vector3d(distanceFromCenter, 0, 0));
            translationTransformGroup.setTransform(translationTransform);
            return translationTransformGroup;
        }
    
        // Build the transform group that orbits the planet. This
        // transform group will rotate the (translated and rotating)
        // planet around the center
        private static TransformGroup createOrbitTransformGroup(int orbitTimeMs)
        {
            TransformGroup orbitTransformGroup = new TransformGroup();
            orbitTransformGroup.setCapability(
                TransformGroup.ALLOW_TRANSFORM_WRITE);
            Alpha orbitAlpha = new Alpha(-1, orbitTimeMs);
            RotationInterpolator orbitInterpolator = 
                new RotationInterpolator(orbitAlpha, orbitTransformGroup);
            orbitInterpolator.setSchedulingBounds(boundingSphere);
            orbitTransformGroup.addChild(orbitInterpolator);
            return orbitTransformGroup;
        }
    
    
        private static void createContents(BranchGroup rootBranchGroup)
        {
            // The basic properties of the Planet
            int rotationTimeMs = 1500;
            double distanceFromCenter = 3;
            int orbitTimeMs = 4000;
    
            // The planet (using a color cube here, so that its 
            // own rotation is visible)
            //Node planet = new Sphere(0.2f);
            Node planet = new ColorCube(0.2);
    
            TransformGroup rotationTransformGroup = 
                createRotationTransformGroup(rotationTimeMs);
    
            // Attach the planet to the rotation transform group
            rotationTransformGroup.addChild(planet);
    
            TransformGroup translationTransformGroup =
                createTranslatingTransformGroup(distanceFromCenter);
    
            // Attach the rotating planet to the translation transform group
            translationTransformGroup.addChild(rotationTransformGroup);
    
            TransformGroup orbitTransformGroup = 
                createOrbitTransformGroup(orbitTimeMs);
    
            // Add the (translated and rotating) planet to the orbitTransformGroup
            orbitTransformGroup.addChild(translationTransformGroup);
    
            rootBranchGroup.addChild(orbitTransformGroup);
        }
    }
    

    (Note: When looking closely, you will notice that the createRotationTransformGroup method and the createOrbitTransformGroup method are actually doing the same! One of them refers to the planet, and one of them refers to the translated planet. So for a real application, they could be combined into one method. I hoped that in the current form, the idea of assembling several nodes might become clearer)


    Edit: Extended based on the comments

    In order to add another object (a moon) that rotates about an existing object (which iself is already rotating and orbiting the sun), you have to attach a new branch to the appropriate existing scene graph node. Based on the above ("ASCII-Art") image, you'll have to attach this node to the "TTG". The new node will itself contain an orbiting node, a translation node and a rotation node (which contains the actual moon object).

    Orbiting

    If your intention is to build a complete solar system like this, you should probably introduce appropriate utility methods - similar to the ones that I already sketched in this extended example:

    import java.awt.GraphicsConfiguration;
    
    import javax.media.j3d.Alpha;
    import javax.media.j3d.BoundingSphere;
    import javax.media.j3d.BranchGroup;
    import javax.media.j3d.Canvas3D;
    import javax.media.j3d.Node;
    import javax.media.j3d.RotationInterpolator;
    import javax.media.j3d.Transform3D;
    import javax.media.j3d.TransformGroup;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    import javax.vecmath.Point3d;
    import javax.vecmath.Vector3d;
    
    import com.sun.j3d.utils.geometry.ColorCube;
    import com.sun.j3d.utils.universe.SimpleUniverse;
    
    public class SphereOrbitExtended
    {
        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 viewPlatformTransform = new Transform3D();
            Transform3D t0 = new Transform3D();
            t0.setTranslation(new Vector3d(0,0,10));
            Transform3D t1 = new Transform3D();
            t1.rotX(Math.toRadians(-30));
            viewPlatformTransform.mul(t1, t0);
            simpleUniverse.getViewingPlatform().
                getViewPlatformTransform().setTransform(viewPlatformTransform);;
    
            frame.setSize(800,800);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
    
        }
    
        private static BoundingSphere boundingSphere = 
            new BoundingSphere(new Point3d(0.0,0.0,0.0), Double.MAX_VALUE);
    
    
        // Build the transform group that does a rotation about the
        // y-axis, rotating once in the given time
        private static TransformGroup createRotationTransformGroup(
            int rotationTimeMs, boolean forward)
        {
            TransformGroup rotationTransformGroup = new TransformGroup();
            rotationTransformGroup.setCapability(
                TransformGroup.ALLOW_TRANSFORM_WRITE);
            Alpha rotationAlpha = new Alpha(-1, rotationTimeMs);
            float angle = forward ? (float) (2 * Math.PI) : (float)(-2 * Math.PI);
            RotationInterpolator rotationInterpolator = 
                new RotationInterpolator(rotationAlpha, rotationTransformGroup, 
                    new Transform3D(), 0.0f, angle);
            rotationInterpolator.setSchedulingBounds(boundingSphere);
            rotationTransformGroup.addChild(rotationInterpolator);
            return rotationTransformGroup;
        }
    
        // Build the transform group that performs the specified translation
        private static TransformGroup createTranslatingTransformGroup(
            double dx, double dy, double dz)
        {
            TransformGroup translationTransformGroup = new TransformGroup();
            Transform3D translationTransform = new Transform3D();
            translationTransform.setTranslation(
                new Vector3d(dx, dy, dz));
            translationTransformGroup.setTransform(translationTransform);
            return translationTransformGroup;
        }
    
    
        private static void createContents(BranchGroup rootBranchGroup)
        {
            int orbitTimeMs = 4000;
            TransformGroup orbitTransformGroup = 
                createRotationTransformGroup(orbitTimeMs, true);
            rootBranchGroup.addChild(orbitTransformGroup);
    
            double distanceFromCenter = 3;
            TransformGroup translationTransformGroup =
                createTranslatingTransformGroup(distanceFromCenter, 0, 0);
            orbitTransformGroup.addChild(translationTransformGroup);
    
            int rotationTimeMs = 1500;
            Node planet = new ColorCube(0.2);
            TransformGroup rotationTransformGroup = 
                createRotationTransformGroup(rotationTimeMs, true);
            rotationTransformGroup.addChild(planet);
            translationTransformGroup.addChild(rotationTransformGroup);
    
            int moonOrbitTimeMs = 1000;
            TransformGroup moonOrbitTransformGroup = 
                createRotationTransformGroup(moonOrbitTimeMs, false);
            translationTransformGroup.addChild(moonOrbitTransformGroup);
    
            double moonDistanceFromPlanet = 0.8;
            TransformGroup moonTranslationTransformGroup =
                createTranslatingTransformGroup(moonDistanceFromPlanet, 0, 0);
            moonOrbitTransformGroup.addChild(moonTranslationTransformGroup);
    
            int moonRotationTimeMs = 500;
            Node moon = new ColorCube(0.1);
            TransformGroup moonRotationTransformGroup = 
                createRotationTransformGroup(moonRotationTimeMs, true);
            moonRotationTransformGroup.addChild(moon);
            moonTranslationTransformGroup.addChild(moonRotationTransformGroup);
        }
    
    
    }