/* * @(#)SplineAnim.java 1.5 00/03/20 14:30:47 * * Copyright (c) 1996-2000 Sun Microsystems, Inc. All Rights Reserved. * * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) Licensee does not utilize the software in a manner * which is disparaging to Sun. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or * redistribute the Software for such purposes. */ import java.io.*; import java.applet.Applet; import java.awt.FlowLayout; import java.awt.*; import java.awt.event.*; import java.util.Hashtable; import javax.media.j3d.*; import javax.vecmath.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.*; import com.sun.j3d.utils.universe.*; import com.sun.j3d.loaders.Scene; import com.sun.j3d.loaders.objectfile.ObjectFile; import com.sun.j3d.loaders.ParsingErrorException; import com.sun.j3d.loaders.IncorrectFormatException; import com.sun.j3d.utils.behaviors.mouse.*; import com.sun.j3d.utils.behaviors.interpolators.*; /* * This program demonstrates the use of KBRotPosScaleSplinePathInterpolator * in order to to do spline animation paths using Kochanek-Bartels (also * known as TCB or Tension-Continuity-Bias ) splines. A cone red cone * is animated along a spline path specified by 5 knot points, which * are showns as cyan spheres. * * Use the left mouse button to changes orientation of scene. * Use the middle mouse button to zoom in/out * Use the right mouse button to pan the scene */ public class SplineAnim extends Applet implements ActionListener, AdjustmentListener, ItemListener { // 3D Canvas Canvas3D canvas; // UI Components Panel controlPanel; Panel canvasPanel; Button animateButton; Choice interpChoice; Scrollbar speedSlider; Label speedLabel; Label interpLabel; // Scene Graph BoundingSphere bounds; BranchGroup root; BranchGroup behaviorBranch; Transform3D sceneTransform; TransformGroup sceneTransformGroup; Transform3D objTransform; TransformGroup objTransformGroup; Transform3D lightTransform1; Transform3D lightTransform2; TransformGroup light1TransformGroup; TransformGroup light2TransformGroup; // Key Frames & Interpolator int duration = 5000; Alpha animAlpha; Transform3D yAxis; KBKeyFrame[] linearKeyFrames = new KBKeyFrame[6]; KBKeyFrame[] splineKeyFrames = new KBKeyFrame[6]; KBRotPosScaleSplinePathInterpolator splineInterpolator; KBRotPosScaleSplinePathInterpolator linearInterpolator; // Data: Knot positions & transform groups Vector3f pos0 = new Vector3f(-5.0f, -5.0f, 0.0f); Vector3f pos1 = new Vector3f(-5.0f, 5.0f, 0.0f); Vector3f pos2 = new Vector3f( 0.0f, 5.0f, 0.0f); Vector3f pos3 = new Vector3f( 0.0f, -5.0f, 0.0f); Vector3f pos4 = new Vector3f( 5.0f, -5.0f, 0.0f); Vector3f pos5 = new Vector3f( 5.0f, 5.0f, 0.0f); TransformGroup k0TransformGroup; TransformGroup k1TransformGroup; TransformGroup k2TransformGroup; TransformGroup k3TransformGroup; TransformGroup k4TransformGroup; TransformGroup k5TransformGroup; // Flags boolean animationOn = true; boolean linear = false; public SplineAnim() { this.setLayout(new FlowLayout()); // Create the canvas and the UI canvasPanel = new Panel(); controlPanel = new Panel(); createCanvasPanel(canvasPanel); this.add(canvasPanel); createControlPanel(controlPanel); this.add(controlPanel); // Create the scene. BranchGroup scene = createSceneGraph(); // Setup keyframe data for our animation setupSplineKeyFrames (); setupLinearKeyFrames (); // Setup alpha, create the interpolators and start them. We // create both a linear and a spline interpolator and turn on // one depending on user selection. The default is spline. setupAnimationData (); createInterpolators(); startInterpolator(); // Add viewing platform SimpleUniverse u = new SimpleUniverse(canvas); u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } /* * This creates the control panel which contains a choice menu to * toggle between spline and linear interpolation, a slider to * adjust the speed of the animation and a animation start/stop * button. */ private void createControlPanel(Panel p) { GridBagLayout gl = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); p.setLayout (gl); gbc.weightx = 100; gbc.weighty = 100; gbc.fill = GridBagConstraints.BOTH; gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; interpLabel = new Label("Interpolation Type", Label.LEFT); p.add(interpLabel, gbc); gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 1; gbc.gridheight = 1; interpChoice = new Choice(); interpChoice.add("Spline"); interpChoice.add("Linear"); p.add(interpChoice, gbc); interpChoice.addItemListener (this); gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; gbc.gridheight = 1; speedSlider = new Scrollbar(Scrollbar.HORIZONTAL, 2, 1, 0, 11); speedSlider.setUnitIncrement (1); p.add(speedSlider, gbc); speedSlider.addAdjustmentListener(this); gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2; gbc.gridheight = 1; speedLabel = new Label(" - Animation Speed +", Label.CENTER); p.add(speedLabel, gbc); gbc.gridx = 0; gbc.gridy = 5; gbc.gridwidth = 2; gbc.gridheight = 1; animateButton = new Button("Stop Animation"); p.add(animateButton, gbc); animateButton.addActionListener (this); } /* * This creates the Java3D canvas */ private void createCanvasPanel(Panel p) { GridBagLayout gl = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); p.setLayout(gl); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 5; gbc.gridheight = 5; GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); canvas = new Canvas3D(config); canvas.setSize(490,490); p.add(canvas,gbc); } /* * This creates the scene with 5 knot points represented by cyan * spheres, a cone obejct that will be transformed, and two directional * lights + and ambient light. */ public BranchGroup createSceneGraph() { // Colors for lights and objects Color3f aColor = new Color3f(0.2f, 0.2f, 0.2f); Color3f eColor = new Color3f(0.0f, 0.0f, 0.0f); Color3f sColor = new Color3f(1.0f, 1.0f, 1.0f); Color3f coneColor = new Color3f(0.9f, 0.1f, 0.1f); Color3f sphereColor= new Color3f(0.1f, 0.7f, 0.9f); Color3f bgColor = new Color3f(0.0f, 0.0f, 0.0f); Color3f lightColor = new Color3f(1.0f, 1.0f, 1.0f); // Root of the branch grsph BranchGroup root = new BranchGroup(); // Create transforms such that all objects appears in the scene sceneTransform = new Transform3D(); sceneTransform.setScale(0.14f); Transform3D yrot = new Transform3D(); yrot.rotY(-Math.PI/5.0d); sceneTransform.mul(yrot); sceneTransformGroup = new TransformGroup(sceneTransform); sceneTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); sceneTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); root.addChild(sceneTransformGroup); // Create bounds for the background and lights bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0f); // Add mouse behaviors // Create the rotate behavior node MouseRotate rotBehavior = new MouseRotate(); rotBehavior.setTransformGroup(sceneTransformGroup); sceneTransformGroup.addChild(rotBehavior); rotBehavior.setSchedulingBounds(bounds); // Create the zoom behavior node MouseZoom zoomBehavior = new MouseZoom(); zoomBehavior.setTransformGroup(sceneTransformGroup); sceneTransformGroup.addChild(zoomBehavior); zoomBehavior.setSchedulingBounds(bounds); // Create the translate behavior node MouseTranslate transBehavior = new MouseTranslate(); transBehavior.setTransformGroup(sceneTransformGroup); sceneTransformGroup.addChild(transBehavior); transBehavior.setSchedulingBounds(bounds); // Set up the background Background bg = new Background(bgColor); bg.setApplicationBounds(bounds); sceneTransformGroup.addChild(bg); // Create the transform group node for the lights lightTransform1 = new Transform3D(); lightTransform2 = new Transform3D(); Vector3d lightPos1 = new Vector3d(0.0, 0.0, 2.0); Vector3d lightPos2 = new Vector3d(1.0, 0.0, -2.0); lightTransform1.set(lightPos1); lightTransform2.set(lightPos2); light1TransformGroup = new TransformGroup(lightTransform1); light2TransformGroup = new TransformGroup(lightTransform2); sceneTransformGroup.addChild(light1TransformGroup); sceneTransformGroup.addChild(light2TransformGroup); // Create lights AmbientLight ambLight = new AmbientLight(aColor); Light dirLight1; Light dirLight2; Vector3f lightDir1 = new Vector3f(lightPos1); Vector3f lightDir2 = new Vector3f(lightPos2); lightDir1.negate(); lightDir2.negate(); dirLight1 = new DirectionalLight(lightColor, lightDir1); dirLight2 = new DirectionalLight(lightColor, lightDir2); // Set the influencing bounds ambLight.setInfluencingBounds(bounds); dirLight1.setInfluencingBounds(bounds); dirLight2.setInfluencingBounds(bounds); // Add the lights into the scene graph sceneTransformGroup.addChild(ambLight); sceneTransformGroup.addChild(dirLight1); sceneTransformGroup.addChild(dirLight2); // Create a cone and add it to the scene graph. objTransform = new Transform3D(); objTransformGroup = new TransformGroup(objTransform); objTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); sceneTransformGroup.addChild(objTransformGroup); Material m = new Material(coneColor, eColor, coneColor, sColor, 100.0f); Appearance a = new Appearance(); m.setLightingEnable(true); a.setMaterial(m); Cone cone = new Cone(0.4f, 1.0f); cone.setAppearance(a); objTransformGroup.addChild(cone); // Create transform groups for each knot point // knot point 0 Transform3D t3dKnot = new Transform3D(); t3dKnot.set (pos0); TransformGroup k0TransformGroup = new TransformGroup(t3dKnot); sceneTransformGroup.addChild(k0TransformGroup); // knot point 1 t3dKnot = new Transform3D(); t3dKnot.set (pos1); TransformGroup k1TransformGroup = new TransformGroup(t3dKnot); sceneTransformGroup.addChild(k1TransformGroup); // knot point 2 t3dKnot = new Transform3D(); t3dKnot.set (pos2); TransformGroup k2TransformGroup = new TransformGroup(t3dKnot); sceneTransformGroup.addChild(k2TransformGroup); // knot point 3 t3dKnot = new Transform3D(); t3dKnot.set (pos3); TransformGroup k3TransformGroup = new TransformGroup(t3dKnot); sceneTransformGroup.addChild(k3TransformGroup); // knot point 4 t3dKnot = new Transform3D(); t3dKnot.set (pos4); TransformGroup k4TransformGroup = new TransformGroup(t3dKnot); sceneTransformGroup.addChild(k4TransformGroup); // knot point 5 t3dKnot = new Transform3D(); t3dKnot.set (pos5); TransformGroup k5TransformGroup = new TransformGroup(t3dKnot); sceneTransformGroup.addChild(k5TransformGroup); // Create spheres for each knot point's transform group ColoringAttributes sphereColorAttr = new ColoringAttributes(); sphereColorAttr.setColor(sphereColor); Appearance sphereAppearance = new Appearance(); sphereAppearance.setColoringAttributes(sphereColorAttr); k0TransformGroup.addChild(new Sphere(0.10f, sphereAppearance)); k1TransformGroup.addChild(new Sphere(0.10f, sphereAppearance)); k2TransformGroup.addChild(new Sphere(0.10f, sphereAppearance)); k3TransformGroup.addChild(new Sphere(0.10f, sphereAppearance)); k4TransformGroup.addChild(new Sphere(0.10f, sphereAppearance)); k5TransformGroup.addChild(new Sphere(0.10f, sphereAppearance)); return root; } /* * This sets up the key frame data for the spline interpolator. Each knot * point has a scale and rotation component specified. The second argument * to KBKeyFrame (in this case 0) tells the interpolator that this is * to be interpolated using splines. The last three arguments to * KBKeyFrame are Tension, Continuity, and Bias components for each * key frame. */ private void setupSplineKeyFrames () { // Prepare spline keyframe data Point3f p = new Point3f (pos0); // position float head = (float)Math.PI/2.0f; // heading float pitch = 0.0f; // pitch float bank = 0.0f; // bank Point3f s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale splineKeyFrames[0] = new KBKeyFrame(0.0f, 0, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos1); head = 0.0f; // heading pitch = 0.0f; // pitch bank = (float)-Math.PI/2.0f; // bank s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale splineKeyFrames[1] = new KBKeyFrame(0.2f, 0, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos2); head = 0.0f; // heading pitch = 0.0f; // pitch bank = 0.0f; // bank s = new Point3f(0.7f, 0.7f, 0.7f); // uniform scale splineKeyFrames[2] = new KBKeyFrame(0.4f, 0, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos3); head = (float)Math.PI/2.0f; // heading pitch = 0.0f; // pitch bank = (float)Math.PI/2.0f; // bank s = new Point3f(0.5f, 0.5f, 0.5f); // uniform scale splineKeyFrames[3] = new KBKeyFrame(0.6f, 0, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos4); head = (float)-Math.PI/2.0f; // heading pitch = (float)-Math.PI/2.0f; // pitch bank = (float)Math.PI/2.0f; // bank s = new Point3f(0.4f, 0.4f, 0.4f); // uniform scale splineKeyFrames[4] = new KBKeyFrame(0.8f, 0, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos5); head = 0.0f; // heading pitch = 0.0f; // pitch bank = 0.0f; // bank s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale splineKeyFrames[5] = new KBKeyFrame(1.0f, 0, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); } /* * This sets up the key frame data for the linear interpolator. Each knot * point has a scale and rotation component specified. The second argument * to KBKeyFrame (in this case 1) tells the interpolator that this is * to be interpolated linearly. The last three arguments to TCBKeyFrame * are Tension, Continuity, and Bias components for each key frame. */ private void setupLinearKeyFrames () { // Prepare linear keyframe data Point3f p = new Point3f (pos0); float head = 0.0f; // heading float pitch = 0.0f; // pitch float bank = 0.0f; // bank Point3f s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale linearKeyFrames[0] = new KBKeyFrame(0.0f, 1, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos1); linearKeyFrames[1] = new KBKeyFrame(0.2f, 1, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos2); linearKeyFrames[2] = new KBKeyFrame(0.4f, 1, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos3); linearKeyFrames[3] = new KBKeyFrame(0.6f, 1, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos4); linearKeyFrames[4] = new KBKeyFrame(0.8f, 1, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); p = new Point3f (pos5); linearKeyFrames[5] = new KBKeyFrame(1.0f, 1, p, head, pitch, bank, s, 0.0f, 0.0f, 0.0f); } /* * This sets up alpha for the interpolator */ private void setupAnimationData () { yAxis = new Transform3D(); animAlpha = new Alpha (-1,Alpha.INCREASING_ENABLE,0,0,duration,0,0,0,0,0); } /* * create a spline and a linear interpolator, but we will activate only * one in startInterpolator() */ private void createInterpolators () { behaviorBranch = new BranchGroup(); // create spline interpolator splineInterpolator = new KBRotPosScaleSplinePathInterpolator(animAlpha, objTransformGroup, yAxis, splineKeyFrames); splineInterpolator.setSchedulingBounds(bounds); behaviorBranch.addChild(splineInterpolator); // create linear interpolator linearInterpolator = new KBRotPosScaleSplinePathInterpolator(animAlpha, objTransformGroup, yAxis, linearKeyFrames); linearInterpolator.setSchedulingBounds(bounds); behaviorBranch.addChild(linearInterpolator); objTransformGroup.addChild(behaviorBranch); } /* * This activates one of the interpolators depending on the state of the * linear boolean flag which may be toggled by the user using the choice * menu. */ public void startInterpolator () { if (animationOn) { if (linear) { splineInterpolator.setEnable(false); linearInterpolator.setEnable(true); } else { linearInterpolator.setEnable(false); splineInterpolator.setEnable(true); } } } /* * Toggle animation */ public void actionPerformed (ActionEvent event) { Object source = event.getSource(); if (source == animateButton) { try { // toggle animation if (animationOn) { animationOn = false; splineInterpolator.setEnable(false); linearInterpolator.setEnable(false); animateButton.setLabel("Start Animation"); } else { animationOn = true; startInterpolator(); animateButton.setLabel("Stop Animation"); } } catch (Exception e) { System.err.println ("Exception " + e); } } } /* * Toggle the interpolators */ public void itemStateChanged (ItemEvent event) { Object source = event.getSource(); ItemSelectable ie = event.getItemSelectable(); if (source == interpChoice) { try { if (ie.getSelectedObjects()[0] == "Spline") { linear = false; } if (ie.getSelectedObjects()[0] == "Linear") { linear = true; } startInterpolator(); } catch (Exception e) { System.err.println ("Exception " + e); } } } /* * Adjust the speed of the animations */ public void adjustmentValueChanged (AdjustmentEvent e) { int value = e.getValue(); duration = 6000 - (500 * value); animAlpha.setIncreasingAlphaDuration(duration); } public static void main(String[] args) { Frame frame = new MainFrame(new SplineAnim(), 500, 600); } }