import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Vector;
import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BackgroundSound;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.MediaContainer;
import javax.media.j3d.Node;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.PointSound;
import javax.media.j3d.PositionInterpolator;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Sound;
import javax.media.j3d.Texture;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupCondition;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnCollisionEntry;
import javax.media.j3d.WakeupOnCollisionExit;
import javax.media.j3d.WakeupOr;
import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;
import com.sun.j3d.audioengines.javasound.JavaSoundMixer;
import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator;
import com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewerAvatar;
/**
* Displays a simple driving type game scene, using texture mapped cubes. It
* assigns an Avatar to the viewer and incorporates simple sounds and collision
* detection/notification/
* <p>
* 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;
}
}
|
You can share your information about this topic using the form below!
Please do not post your questions with this form! Thanks.