* This example does not use the Java3dApplet base class but is based on a * SimpleUniverse construction instead. that way we can illustrate the * setPlatformGeometry call. */ public class AvatarTest extends Applet { public BranchGroup createSceneGraph() { BranchGroup bg = new BranchGroup(); TransformGroup tgRoot = addBehaviors(bg); createBuildings(tgRoot); createRoad(tgRoot); createLand(tgRoot); createCars(tgRoot); createBackground(bg); return bg; } public void createBackground(Group bg) { // add the sky backdrop Background back = new Background(); back.setApplicationBounds(getBoundingSphere()); bg.addChild(back); BranchGroup bgGeometry = new BranchGroup(); // create an appearance and assign the texture image Appearance app = new Appearance(); Texture tex = new TextureLoader("back.jpg", this).getTexture(); app.setTexture(tex); Sphere sphere = new Sphere(1.0f, Primitive.GENERATE_TEXTURE_COORDS | Primitive.GENERATE_NORMALS_INWARD, app); bgGeometry.addChild(sphere); back.setGeometry(bgGeometry); } public Group createLand(Group g) { Land land = new Land(this, g, ComplexObject.GEOMETRY | ComplexObject.TEXTURE); return land.createObject(new Appearance(), new Vector3d(0, 0, 0), new Vector3d(1, 1, 1), "land.jpg", null, null); } public Group createRoad(Group g) { Road road = new Road(this, g, ComplexObject.GEOMETRY | ComplexObject.TEXTURE); return road.createObject(new Appearance(), new Vector3d(0, 0, 0), new Vector3d(1, 1, 1), "road.jpg", null, null); } private float getRandomNumber(float basis, float random) { return basis + ((float) Math.random() * random * 2) - (random); } public Group createBuildings(Group g) { BranchGroup bg = new BranchGroup(); for (int n = (int) Road.ROAD_LENGTH; n < 0; n = n + 10) { Building building = new Building(this, bg, ComplexObject.GEOMETRY | ComplexObject.TEXTURE | ComplexObject.COLLISION); building.createObject(new Appearance(), new Vector3d( getRandomNumber(-4.0f, 0.25f), getRandomNumber(1.0f, 0.5f), getRandomNumber(n, 0.5f)), new Vector3d(1, 1, 1), "house.jpg", null, null); building = new Building(this, bg, ComplexObject.GEOMETRY | ComplexObject.TEXTURE | ComplexObject.COLLISION); building.createObject(new Appearance(), new Vector3d( getRandomNumber(4.0f, 0.25f), getRandomNumber(1.0f, 0.5f), getRandomNumber(n, 0.5f)), new Vector3d(1, 1, 1), "house.jpg", null, null); } g.addChild(bg); return bg; } public Group createCars(Group g) { BranchGroup bg = new BranchGroup(); for (int n = (int) Road.ROAD_LENGTH; n < 0; n = n + 10) { Car car = new Car(this, bg, ComplexObject.GEOMETRY | ComplexObject.TEXTURE | ComplexObject.SOUND); car.createObject(new Appearance(), new Vector3d(getRandomNumber( 0.0f, 2.0f), Car.CAR_HEIGHT / 2.0f, getRandomNumber(n, 5.0f)), new Vector3d(1, 1, 1), "car0.jpg", "car.wav", "collide.wav"); } g.addChild(bg); return bg; } public TransformGroup addBehaviors(Group bgRoot) { // Create the transform group node and initialize it to the // identity. Enable the TRANSFORM_WRITE capability so that // our behavior code can modify it at runtime. Add it to the // root of the subgraph. TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); Transform3D zAxis = new Transform3D(); zAxis.rotY(Math.toRadians(90.0)); Alpha zoomAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 20000, 0, 0, 0, 0, 0); PositionInterpolator posInt = new PositionInterpolator(zoomAlpha, objTrans, zAxis, 0, -160); posInt.setSchedulingBounds(getBoundingSphere()); objTrans.addChild(posInt); bgRoot.addChild(objTrans); return objTrans; } BoundingSphere getBoundingSphere() { return new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 400.0); } ViewerAvatar createAvatar() { ViewerAvatar va = new ViewerAvatar(); TransformGroup tg = new TransformGroup(); Car car = new Car(this, tg, ComplexObject.GEOMETRY | ComplexObject.TEXTURE | ComplexObject.COLLISION | ComplexObject.COLLISION_SOUND); car.createObject(new Appearance(), new Vector3d(0, -0.3, -0.3), new Vector3d(0.3, 0.3, 1), "platform.jpg", null, "collide.wav"); tg.addChild(car); va.addChild(tg); return va; } public static void main(String[] args) { AvatarTest avatarTest = new AvatarTest(); // Create a simple scene and attach it to the virtual universe SimpleUniverse u = new SimpleUniverse(); PhysicalEnvironment physicalEnv = u.getViewer() .getPhysicalEnvironment(); TransformGroup tg = u.getViewer().getViewingPlatform() .getViewPlatformTransform(); Transform3D t3d = new Transform3D(); t3d.set(new Vector3f(0, 0.5f, 0)); tg.setTransform(t3d); CarSteering keys = new CarSteering(tg); keys.setSchedulingBounds(avatarTest.getBoundingSphere()); u.getViewer().setAvatar(avatarTest.createAvatar()); if (physicalEnv != null) { JavaSoundMixer javaSoundMixer = new JavaSoundMixer(physicalEnv); if (javaSoundMixer == null) System.out.println("Unable to create AudioDevice."); javaSoundMixer.initialize(); } // Add everthing to the scene graph - it will now be displayed. BranchGroup scene = avatarTest.createSceneGraph(); scene.addChild(keys); // Java3dTree j3dTree = new Java3dTree(); // j3dTree.recursiveApplyCapability(scene); u.addBranchGraph(scene); //j3dTree.updateNodes(u); u.getViewingPlatform().getViewPlatform().setActivationRadius(2); } } /** * This class is a simple behavior that invokes the KeyNavigator to modify the * view platform transform. */ class Building extends ComplexObject { private final float BUILDING_WIDTH = 1.0f; private final float BUILDING_LENGTH = 1.0f; public Building(Component comp, Group g, int nFlags) { super(comp, g, nFlags); } private float getRandomNumber(float basis, float random) { return basis + ((float) Math.random() * random * 2) - (random); } protected Group createGeometryGroup(Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile) { int nPrimFlags = 0; if ((m_nFlags & ComplexObject.TEXTURE) == ComplexObject.TEXTURE) { nPrimFlags |= Primitive.GENERATE_TEXTURE_COORDS; setTexture(app, szTextureFile); } return new Box(getRandomNumber(BUILDING_WIDTH, 0.25f), (float) position.y, getRandomNumber(BUILDING_LENGTH, 0.15f), nPrimFlags, app); } } /** * This class is a simple behavior that invokes the KeyNavigator to modify the * view platform transform. */ class Land extends ComplexObject { private final float LAND_WIDTH = 100.0f; private final float LAND_HEIGHT = 0.0f; private final float LAND_LENGTH = -200.0f; public Land(Component comp, Group g, int nFlags) { super(comp, g, nFlags); } protected Group createGeometryGroup(Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile) { QuadArray quadArray = new QuadArray(4, GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2); float[] coordArray = { -LAND_WIDTH, LAND_HEIGHT, 0, LAND_WIDTH, LAND_HEIGHT, 0, LAND_WIDTH, LAND_HEIGHT, LAND_LENGTH, -LAND_WIDTH, LAND_HEIGHT, LAND_LENGTH }; float[] texArray = { 0, 0, 1, 0, 1, 1, 0, 1 }; quadArray.setCoordinates(0, coordArray, 0, 4); if ((m_nFlags & TEXTURE) == TEXTURE) { quadArray.setTextureCoordinates(0, 0, texArray, 0, 4); setTexture(app, szTextureFile); } Shape3D sh = new Shape3D(quadArray, app); BranchGroup bg = new BranchGroup(); bg.addChild(sh); return bg; } } /** * This class is a simple behavior that invokes the KeyNavigator to modify the * view platform transform. */ class CollisionBehavior extends Behavior { private WakeupOnCollisionEntry wakeupOne = null; private WakeupOnCollisionExit wakeupTwo = null; private WakeupCriterion[] wakeupArray = new WakeupCriterion[2]; private WakeupCondition wakeupCondition = null; private ComplexObject m_Owner = null; public CollisionBehavior(Node node, ComplexObject owner) { wakeupOne = new WakeupOnCollisionEntry(node, WakeupOnCollisionEntry.USE_BOUNDS); wakeupTwo = new WakeupOnCollisionExit(node, WakeupOnCollisionExit.USE_BOUNDS); wakeupArray[0] = wakeupOne; wakeupArray[1] = wakeupTwo; wakeupCondition = new WakeupOr(wakeupArray); m_Owner = owner; } /** * Override Behavior's initialize method to setup wakeup criteria. */ public void initialize() { // Establish initial wakeup criteria wakeupOn(wakeupCondition); } /** * Override Behavior's stimulus method to handle the event. */ public void processStimulus(Enumeration criteria) { WakeupCriterion genericEvt; while (criteria.hasMoreElements()) { genericEvt = (WakeupCriterion) criteria.nextElement(); if (genericEvt instanceof WakeupOnCollisionEntry) { m_Owner.onCollide(true); } else if (genericEvt instanceof WakeupOnCollisionExit) { m_Owner.onCollide(false); } } // Set wakeup criteria for next time wakeupOn(wakeupCondition); } } /** * This class is a simple behavior that invokes the KeyNavigator to modify the * view platform transform. */ class CarSteering extends Behavior { private WakeupOnAWTEvent wakeupOne = null; private WakeupCriterion[] wakeupArray = new WakeupCriterion[1]; private WakeupCondition wakeupCondition = null; private final float TRANSLATE_LEFT = -0.05f; private final float TRANSLATE_RIGHT = 0.05f; TransformGroup m_TransformGroup = null; public CarSteering(TransformGroup tg) { m_TransformGroup = tg; try { m_TransformGroup .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); } catch (Exception e) { } wakeupOne = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED); wakeupArray[0] = wakeupOne; wakeupCondition = new WakeupOr(wakeupArray); } /** * Override Behavior's initialize method to setup wakeup criteria. */ public void initialize() { // Establish initial wakeup criteria wakeupOn(wakeupCondition); } /** * Override Behavior's stimulus method to handle the event. */ public void processStimulus(Enumeration criteria) { WakeupOnAWTEvent ev; WakeupCriterion genericEvt; AWTEvent[] events; while (criteria.hasMoreElements()) { genericEvt = (WakeupCriterion) criteria.nextElement(); if (genericEvt instanceof WakeupOnAWTEvent) { ev = (WakeupOnAWTEvent) genericEvt; events = ev.getAWTEvent(); processAWTEvent(events); } } // Set wakeup criteria for next time wakeupOn(wakeupCondition); } /** * Process a keyboard event */ private void processAWTEvent(AWTEvent[] events) { for (int n = 0; n < events.length; n++) { if (events[n] instanceof KeyEvent) { KeyEvent eventKey = (KeyEvent) events[n]; if (eventKey.getID() == KeyEvent.KEY_PRESSED) { int keyCode = eventKey.getKeyCode(); int keyChar = eventKey.getKeyChar(); Vector3f translate = new Vector3f(); Transform3D t3d = new Transform3D(); m_TransformGroup.getTransform(t3d); t3d.get(translate); switch (keyCode) { case KeyEvent.VK_LEFT: translate.x += TRANSLATE_LEFT; break; case KeyEvent.VK_RIGHT: translate.x += TRANSLATE_RIGHT; break; } // System.out.println( "Steering: " + translate.x ); translate.y = 0.5f; t3d.setTranslation(translate); m_TransformGroup.setTransform(t3d); } } } } } /** * This class is a simple behavior that invokes the KeyNavigator to modify the * view platform transform. */ class Car extends ComplexObject { public static final float CAR_WIDTH = 0.2f; public static final float CAR_HEIGHT = 0.2f; public static final float CAR_LENGTH = 0.6f; public Car(Component comp, Group g, int nFlags) { super(comp, g, nFlags); } private float getRandomNumber(float basis, float random) { return basis + ((float) Math.random() * random * 2) - (random); } public Bounds getGeometryBounds() { return new BoundingSphere(new Point3d(0, 0, 0), 0.2); } protected Group createGeometryGroup(Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile) { int nPrimFlags = 0; if ((m_nFlags & ComplexObject.TEXTURE) == ComplexObject.TEXTURE) { nPrimFlags |= Primitive.GENERATE_TEXTURE_COORDS; setTexture(app, szTextureFile); } return new Box(CAR_WIDTH, (float) position.y, getRandomNumber( CAR_LENGTH, 0.01f), nPrimFlags, app); } } /** * This class is a simple behavior that invokes the KeyNavigator to modify the * view platform transform. */ class Road extends ComplexObject { public static final float ROAD_WIDTH = 3.0f; public static final float ROAD_HEIGHT = 0.01f; public static final float ROAD_LENGTH = -200.0f; public Road(Component comp, Group g, int nFlags) { super(comp, g, nFlags); } protected Group createGeometryGroup(Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile) { // creates a segment of road 200 x 2 QuadArray quadArray = new QuadArray(4, GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2); float[] coordArray = { -ROAD_WIDTH, ROAD_HEIGHT, 0, ROAD_WIDTH, ROAD_HEIGHT, 0, ROAD_WIDTH, ROAD_HEIGHT, ROAD_LENGTH, -ROAD_WIDTH, ROAD_HEIGHT, ROAD_LENGTH }; float[] texArray = { 0, 0, 1, 0, 1, 1, 0, 1 }; quadArray.setCoordinates(0, coordArray, 0, 4); if ((m_nFlags & TEXTURE) == TEXTURE) { quadArray.setTextureCoordinates(0, 0, texArray, 0, 4); setTexture(app, szTextureFile); } Shape3D sh = new Shape3D(quadArray, app); BranchGroup bg = new BranchGroup(); bg.addChild(sh); return bg; } } abstract class ComplexObject extends BranchGroup { protected Group m_ParentGroup = null; protected int m_nFlags = 0; protected BackgroundSound m_CollideSound = null; protected Component m_Component = null; protected TransformGroup m_TransformGroup = null; protected TransformGroup m_BehaviorTransformGroup = null; public static final int SOUND = 0x001; public static final int GEOMETRY = 0x002; public static final int TEXTURE = 0x004; public static final int COLLISION = 0x008; public static final int COLLISION_SOUND = 0x010; public ComplexObject(Component comp, Group group, int nFlags) { m_ParentGroup = group; m_nFlags = nFlags; m_Component = comp; } public Bounds getGeometryBounds() { return new BoundingSphere(new Point3d(0, 0, 0), 100); } private MediaContainer loadSoundFile(String szFile) { try { File file = new File(System.getProperty("user.dir")); URL url = file.toURL(); URL soundUrl = new URL(url, szFile); return new MediaContainer(soundUrl); } catch (Exception e) { System.err.println("Error could not load sound file: " + e); System.exit(-1); } return null; } protected void setTexture(Appearance app, String szFile) { Texture tex = new TextureLoader(szFile, m_Component).getTexture(); app.setTexture(tex); } abstract protected Group createGeometryGroup(Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile); protected Group loadGeometryGroup(String szModel, Appearance app) throws java.io.FileNotFoundException { // load the object file Scene scene = null; Shape3D shape = null; // read in the geometry information from the data file ObjectFile objFileloader = new ObjectFile(ObjectFile.RESIZE); scene = objFileloader.load(szModel); // retrieve the Shape3D object from the scene BranchGroup branchGroup = scene.getSceneGroup(); shape = (Shape3D) branchGroup.getChild(0); shape.setAppearance(app); return branchGroup; } protected int getSoundLoop(boolean bCollide) { return 1; } protected float getSoundPriority(boolean bCollide) { return 1.0f; } protected float getSoundInitialGain(boolean bCollide) { return 1.0f; } protected boolean getSoundInitialEnable(boolean bCollide) { return true; } protected boolean getSoundContinuousEnable(boolean bCollide) { return false; } protected Bounds getSoundSchedulingBounds(boolean bCollide) { return new BoundingSphere(new Point3d(0, 0, 0), 1.0); } protected boolean getSoundReleaseEnable(boolean bCollide) { return true; } protected Point2f[] getSoundDistanceGain(boolean bCollide) { return null; } protected void setSoundAttributes(Sound sound, boolean bCollide) { sound.setCapability(Sound.ALLOW_ENABLE_WRITE); sound.setCapability(Sound.ALLOW_ENABLE_READ); sound.setSchedulingBounds(getSoundSchedulingBounds(bCollide)); sound.setEnable(getSoundInitialEnable(bCollide)); sound.setLoop(getSoundLoop(bCollide)); sound.setPriority(getSoundPriority(bCollide)); sound.setInitialGain(getSoundInitialGain(bCollide)); sound.setContinuousEnable(getSoundContinuousEnable(bCollide)); sound.setReleaseEnable(bCollide); if (sound instanceof PointSound) { PointSound pointSound = (PointSound) sound; pointSound.setInitialGain(getSoundInitialGain(bCollide)); Point2f[] gainArray = getSoundDistanceGain(bCollide); if (gainArray != null) pointSound.setDistanceGain(gainArray); } } public Group createObject(Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile, String szCollisionSound) { m_TransformGroup = new TransformGroup(); Transform3D t3d = new Transform3D(); t3d.setScale(scale); t3d.setTranslation(position); m_TransformGroup.setTransform(t3d); m_BehaviorTransformGroup = new TransformGroup(); if ((m_nFlags & GEOMETRY) == GEOMETRY) m_BehaviorTransformGroup.addChild(createGeometryGroup(app, position, scale, szTextureFile, szSoundFile)); if ((m_nFlags & SOUND) == SOUND) { MediaContainer media = loadSoundFile(szSoundFile); PointSound pointSound = new PointSound(media, getSoundInitialGain(false), 0, 0, 0); setSoundAttributes(pointSound, false); m_BehaviorTransformGroup.addChild(pointSound); } if ((m_nFlags & COLLISION) == COLLISION) { m_BehaviorTransformGroup .setCapability(Node.ENABLE_COLLISION_REPORTING); m_BehaviorTransformGroup.setCollidable(true); m_BehaviorTransformGroup.setCollisionBounds(getGeometryBounds()); if ((m_nFlags & COLLISION_SOUND) == COLLISION_SOUND) { MediaContainer collideMedia = loadSoundFile(szCollisionSound); m_CollideSound = new BackgroundSound(collideMedia, 1); setSoundAttributes(m_CollideSound, true); m_TransformGroup.addChild(m_CollideSound); } CollisionBehavior collision = new CollisionBehavior( m_BehaviorTransformGroup, this); collision.setSchedulingBounds(getGeometryBounds()); m_BehaviorTransformGroup.addChild(collision); } m_TransformGroup.addChild(m_BehaviorTransformGroup); m_ParentGroup.addChild(m_TransformGroup); return m_BehaviorTransformGroup; } public void onCollide(boolean bCollide) { System.out.println("Collide: " + bCollide); if (m_CollideSound != null && bCollide == true) m_CollideSound.setEnable(true); } public void attachBehavior(Behavior beh) { m_BehaviorTransformGroup .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); beh.setSchedulingBounds(getGeometryBounds()); m_BehaviorTransformGroup.addChild(beh); } public TransformGroup getBehaviorTransformGroup() { return m_BehaviorTransformGroup; } public void attachSplinePathInterpolator(Alpha alpha, Transform3D axis, URL urlKeyframes) { // read a spline path definition file and // add a Spline Path Interpolator to the TransformGroup for the object. m_BehaviorTransformGroup .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); RotPosScaleTCBSplinePathInterpolator splineInterpolator = Utils .createSplinePathInterpolator(alpha, m_BehaviorTransformGroup, axis, urlKeyframes); if (splineInterpolator != null) { splineInterpolator.setSchedulingBounds(getGeometryBounds()); m_BehaviorTransformGroup.addChild(splineInterpolator); } else { System.out.println("attachSplinePathInterpolator failed for: " + urlKeyframes); } } } //***************************************************************************** /** * Utils * * @author Daniel Selman * @version 1.0 */ //***************************************************************************** class Utils { // convert an angular rotation about an axis to a Quaternion static Quat4f createQuaternionFromAxisAndAngle(Vector3d axis, double angle) { double sin_a = Math.sin(angle / 2); double cos_a = Math.cos(angle / 2); // use a vector so we can call normalize Vector4f q = new Vector4f(); q.x = (float) (axis.x * sin_a); q.y = (float) (axis.y * sin_a); q.z = (float) (axis.z * sin_a); q.w = (float) cos_a; // It is necessary to normalise the quaternion // in case any values are very close to zero. q.normalize(); // convert to a Quat4f and return return new Quat4f(q); } // convert three rotations about the Euler axes to a Quaternion static Quat4f createQuaternionFromEuler(double angleX, double angleY, double angleZ) { // simply call createQuaternionFromAxisAndAngle // for each axis and multiply the results Quat4f qx = createQuaternionFromAxisAndAngle(new Vector3d(1, 0, 0), angleX); Quat4f qy = createQuaternionFromAxisAndAngle(new Vector3d(0, 1, 0), angleY); Quat4f qz = createQuaternionFromAxisAndAngle(new Vector3d(0, 0, 1), angleZ); // qx = qx * qy qx.mul(qy); // qx = qx * qz qx.mul(qz); return qx; } static public double getRandomNumber(double basis, double random) { return basis + ((float) Math.random() * random * 2f) - (random); } static public double getRandomNumber(double basis, double random, double scale) { double value = basis + ((float) Math.random() * random * 2f) - (random); return value * scale; } static public StringBuffer readFile(URL urlFile) { // allocate a temporary buffer to store the input file StringBuffer szBufferData = new StringBuffer(); Vector keyFramesVector = new Vector(); try { InputStream inputStream = urlFile.openStream(); int nChar = 0; // read the entire file into the StringBuffer while (true) { nChar = inputStream.read(); // if we have not hit the end of file // add the character to the StringBuffer if (nChar != -1) szBufferData.append((char) nChar); else // EOF break; } inputStream.close(); } catch (Exception e) { System.err.println(e.toString()); return null; } return szBufferData; } static public RotPosScaleTCBSplinePathInterpolator createSplinePathInterpolator( Alpha alpha, TransformGroup tg, Transform3D axis, URL urlKeyframes) { TCBKeyFrame[] keyFrames = readKeyFrames(urlKeyframes); if (keyFrames != null) return new RotPosScaleTCBSplinePathInterpolator(alpha, tg, axis, keyFrames); return null; } static public TCBKeyFrame[] readKeyFrames(URL urlKeyframes) { StringBuffer szBufferData = readFile(urlKeyframes); if (szBufferData == null) return null; Vector keyFramesVector = new Vector(); // create a tokenizer to tokenize the input file at whitespace java.util.StringTokenizer tokenizer = new java.util.StringTokenizer( szBufferData.toString()); // each keyframe is defined as follows // - knot (0 >= k <= 1) // - position (x,y,z) // - rotation (rx,ry,rz) // - scale (x,y,z) // - tension (-1 >= t <= 1) // - continuity (-1 >= c <= 1) // - bias (-1 >= b <= 1) // - linear (int - 0 or 1) while (true) { try { float knot = Float.parseFloat(tokenizer.nextToken()); float posX = Float.parseFloat(tokenizer.nextToken()); float posY = Float.parseFloat(tokenizer.nextToken()); float posZ = Float.parseFloat(tokenizer.nextToken()); float rotX = Float.parseFloat(tokenizer.nextToken()); float rotY = Float.parseFloat(tokenizer.nextToken()); float rotZ = Float.parseFloat(tokenizer.nextToken()); float scaleX = Float.parseFloat(tokenizer.nextToken()); float scaleY = Float.parseFloat(tokenizer.nextToken()); float scaleZ = Float.parseFloat(tokenizer.nextToken()); float tension = Float.parseFloat(tokenizer.nextToken()); float continuity = Float.parseFloat(tokenizer.nextToken()); float bias = Float.parseFloat(tokenizer.nextToken()); int linear = Integer.parseInt(tokenizer.nextToken()); TCBKeyFrame keyframe = new TCBKeyFrame(knot, linear, new Point3f(posX, posY, posZ), createQuaternionFromEuler(rotX, rotY, rotZ), new Point3f(scaleX, scaleY, scaleZ), tension, continuity, bias); keyFramesVector.add(keyframe); } catch (Exception e) { break; } } // create the return structure and populate TCBKeyFrame[] keysReturn = new TCBKeyFrame[keyFramesVector.size()]; for (int n = 0; n < keysReturn.length; n++) keysReturn[n] = (TCBKeyFrame) keyFramesVector.get(n); // return the array return keysReturn; } }