/**
|
* Make a donation http://sourceforge.net/donate/index.php?group_id=98797
|
*
|
* Microcrowd.com
|
*
|
* This library is free software; you can redistribute it and/or
|
* modify it under the terms of the GNU Lesser General Public
|
* License as published by the Free Software Foundation; either
|
* version 2.1 of the License, or (at your option) any later version.
|
*
|
* This library is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* Lesser General Public License for more details.
|
*
|
* You should have received a copy of the GNU Lesser General Public
|
* License along with this library; if not, write to the Free Software
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
*
|
* Contact Josh DeFord jdeford@microcrowd.com
|
*/
|
package com.microcrowd.loader.java3d.max3ds.data;
|
|
import java.util.Enumeration;
|
import java.util.HashMap;
|
import java.util.List;
|
import javax.media.j3d.Alpha;
|
import javax.media.j3d.Behavior;
|
import javax.media.j3d.Group;
|
import javax.media.j3d.Node;
|
import javax.media.j3d.RotPosPathInterpolator;
|
import javax.media.j3d.Transform3D;
|
import javax.media.j3d.TransformGroup;
|
import javax.media.j3d.TransformInterpolator;
|
import javax.vecmath.Matrix4f;
|
import javax.vecmath.Point3f;
|
import javax.vecmath.Quat4f;
|
import javax.vecmath.Vector3d;
|
import javax.vecmath.Vector3f;
|
|
/**
|
* @author Josh DeFord
|
*/
|
public class KeyFramer
|
{
|
private HashMap lastGroupMap = new HashMap();
|
private HashMap fatherMap = new HashMap();
|
|
private Quat4f rotation;
|
private Point3f position;
|
private Point3f pivotCenter;
|
private Vector3f pivot;
|
private Vector3f scale;
|
private HashMap namedObjectCoordinateSystems = new HashMap();
|
|
private List positionKeys;
|
private List orientationKeys;
|
private List scaleKeys;
|
|
private Integer id;
|
private Group father;
|
private Group dummyObject;
|
|
|
/**
|
* Retrieves the named object for the current key framer
|
* inserts the rotation, position and pivot transformations for frame 0
|
* and assigns the coordinate system to it.
|
*
|
* The inverse of the local coordinate system converts from 3ds
|
* semi-absolute coordinates (what is in the file) to local coordinates.
|
*
|
* Then these local coordinates are converted with matrix
|
* that will instantiate them to absolute coordinates:
|
* Xabs = sx a1 (Xl-Px) + sy a2 (Yl-Py) + sz a3 (Zl-Pz) + Tx
|
* Yabs = sx b1 (Xl-Px) + sy b2 (Yl-Py) + sz b3 (Zl-Pz) + Ty
|
* Zabs = sx c1 (Xl-Px) + sy c2 (Yl-Py) + sz c3 (Zl-Pz) + Tz
|
* Where:
|
* (Xabs,Yabs,Zabs) = absolute coordinate
|
* (Px,Py,Pz) = mesh pivot (constant)
|
* (X1,Y1,Z1) = local coordinates
|
*
|
*/
|
public Behavior createBehavior(String meshName, Group transformGroup, Object testObject)
|
{
|
Group objectGroup = getObjectByName(meshName, transformGroup, testObject);
|
//System.out.println("mesh " + meshName + " scale " + scale);
|
if(objectGroup == null)
|
return null;
|
|
insertFather(objectGroup, meshName);
|
|
TransformInterpolator behavior = null;
|
Transform3D coordinateSystem = (Transform3D)namedObjectCoordinateSystems.get(meshName);
|
|
//Gonna put these children back later.
|
Enumeration children = removeChildren(objectGroup);
|
|
Transform3D coordinateTransform = coordinateSystem == null ? new Transform3D() : new Transform3D(coordinateSystem);
|
|
Transform3D targetTransform = new Transform3D();
|
TransformGroup targetGroup = new TransformGroup(targetTransform);
|
|
TransformGroup localCoordinates = hasKeys() ? buildLocalCoordinates(coordinateSystem) : new TransformGroup();
|
TransformGroup lastGroup = (TransformGroup)addGroups(objectGroup, new Group[]
|
{
|
localCoordinates,
|
targetGroup,
|
buildPivotGroup(coordinateTransform, pivot),
|
buildKeysGroup(),
|
});
|
|
addChildren(children, lastGroup);
|
lastGroupMap.put(objectGroup, lastGroup);
|
|
|
behavior = buildInterpolator(targetGroup, coordinateSystem);
|
if(behavior != null)
|
{
|
behavior.setEnable(false);
|
targetGroup.addChild(behavior);
|
|
behavior.computeTransform(0f, targetTransform);
|
targetGroup.setTransform(targetTransform);
|
}
|
return behavior;
|
}
|
|
private Enumeration removeChildren(Group group)
|
{
|
Enumeration children = group.getAllChildren();
|
group.removeAllChildren();
|
return children;
|
}
|
|
private void addChildren(Enumeration children, Group group)
|
{
|
if(group == null)
|
return;
|
while(children.hasMoreElements())
|
{
|
Node node = (Node)(children.nextElement());
|
group.addChild(node);
|
}
|
}
|
|
/**
|
* Looks up the current object.
|
* objectGroup is returned if it is the right one to return
|
* otherwise a new dummy object may be returned.
|
* If it isn't there it gets the dummy object
|
* from the frames description chunk.
|
*/
|
private Group getObjectByName(String objectName, Group objectGroup, Object testObject)
|
{
|
|
//This means its a dummy object. It needs to be created.
|
if(objectGroup == null && testObject == null)
|
{
|
namedObjectCoordinateSystems.put(objectName, new Transform3D());
|
objectGroup = dummyObject;
|
}
|
|
return objectGroup;
|
}
|
|
/**
|
* Locates the father for the object named objectName and inserts
|
* its transform cluster between the parent and all
|
* the parent's children. This only occurs in a hierarchical
|
* model. like bones and stuff. The fatherGroup, if found,
|
* is removed from whatever parent belonged to on before insertion.
|
*/
|
private void insertFather(Group parentGroup, String objectName)
|
{
|
if(father == null)
|
return;
|
Group topGroup = new TransformGroup();
|
topGroup.addChild(father);
|
Group bottomGroup = (Group)lastGroupMap.get(father);
|
|
if(topGroup == null)
|
return;
|
|
Group fatherParent = (Group)topGroup.getParent();
|
if(fatherParent != null)
|
fatherParent.removeChild(topGroup);
|
|
Enumeration originalChildren = removeChildren(parentGroup);
|
parentGroup.addChild(topGroup);
|
addChildren(originalChildren, bottomGroup);
|
}
|
|
/**
|
* Builds a transform group from the zeroth key of the
|
* position and rotation tracks.
|
* @return transform group with position and rotation information
|
*/
|
private TransformGroup buildKeysGroup()
|
{
|
Transform3D positionTransform = new Transform3D();
|
positionTransform.set(new Vector3f(position));
|
|
Transform3D rotationTransform = new Transform3D();
|
rotationTransform.set(rotation);
|
|
Transform3D scaleTransform = new Transform3D();
|
scaleTransform.setScale(new Vector3d(scale));
|
TransformGroup scaleGroup = new TransformGroup(scaleTransform);
|
|
Transform3D keyTransform = new Transform3D(positionTransform);
|
keyTransform.mul(scaleTransform);
|
keyTransform.mul(rotationTransform);
|
return new TransformGroup(keyTransform);
|
}
|
|
/**
|
* Builds a pivot group that will allow the objects
|
* to be positioned properly according to their rotations
|
* and positions.
|
* @param coordinateTransform the coordinate system defining the
|
* location and orientation of the local axis. This is not modified.
|
* @param pivot the pivot defined in the 3ds file loaded by pivot chunk.
|
* This is not changed.
|
*/
|
private TransformGroup buildPivotGroup(Transform3D coordinateTransform, Vector3f pivot)
|
{
|
Transform3D pivotTransform = new Transform3D();
|
pivotTransform.mulInverse(coordinateTransform);
|
pivot = new Vector3f(pivot);
|
pivot.negate();
|
translatePivot(pivotTransform, pivot, pivotCenter);
|
return new TransformGroup(pivotTransform);
|
}
|
|
/**
|
* Builds a coordinate group that will allow the objects
|
* to be positioned properly according to their rotations
|
* and positions.
|
* @param coordinateSystem the coordinate system defining the
|
* location and orientation of the local axis. This is modified
|
* so it will be useful during the construction
|
* of the animations.
|
*/
|
private TransformGroup buildLocalCoordinates(Transform3D coordinateSystem)
|
{
|
Matrix4f coordMatrix = new Matrix4f();
|
Vector3f translation = new Vector3f();
|
|
coordinateSystem.get(translation);
|
coordinateSystem.invert();
|
|
coordinateSystem.get(coordMatrix);
|
coordMatrix.m03 = translation.x;
|
coordMatrix.m13 = translation.y;
|
coordMatrix.m23 = translation.z;
|
coordinateSystem.set(coordMatrix);
|
coordinateSystem.invert();
|
TransformGroup systemGroup = new TransformGroup(coordinateSystem);
|
coordinateSystem.invert();
|
return systemGroup;
|
}
|
|
/**
|
* Hierarchically adds the provided groups in order to parentGroup.
|
* groups[0] is added to parentGroup, groups[1] is added to groups[0] etc.
|
* @return the last group added (groups[groups.length - 1]).
|
*/
|
private Group addGroups(Group parentGroup, Group[] groups)
|
{
|
Group nextGroup = parentGroup;
|
for(int i=0; i < groups.length; i++)
|
{
|
nextGroup.addChild(groups[i]);
|
nextGroup = groups[i];
|
}
|
return groups[groups.length - 1];
|
}
|
|
/**
|
* Does a pre rotational translation of the pivot.
|
* @param transform the matrix that will have a translation concatenated to it.
|
* @param vector the vector which will be used to translate the matrix.
|
* @param offset the offset used to offset the pivot.
|
*/
|
private void translatePivot(Transform3D transform, Vector3f vector, Point3f offset)
|
{
|
if(offset != null)
|
{
|
pivot.sub(offset);
|
}
|
Matrix4f matrix = new Matrix4f();
|
transform.get(matrix);
|
|
matrix.m03 += (matrix.m00*vector.x + matrix.m01*vector.y + matrix.m02*vector.z);
|
matrix.m13 += (matrix.m10*vector.x + matrix.m11*vector.y + matrix.m12*vector.z);
|
matrix.m23 += (matrix.m20*vector.x + matrix.m21*vector.y + matrix.m22*vector.z);
|
|
transform.set(matrix);
|
}
|
|
|
/**
|
* Builds a rotation position interpolator for use on this mesh using position and rotation information
|
* adds it to targetGroup.
|
* This does not set the capability bits that need to be set for the animation
|
* to be used. The capability bits of the targetGroup must be set by the client application.
|
* The alpha object on the Interpolator must also be enabled.
|
* The Interpolator must also have its scheduling bounds set.
|
* @param pivotGroup transform group which will be operated on by the interpolator.
|
* @param interpolatorAxis the axis that about which rotations will be centered.
|
*/
|
//TODO... This needs to use both a rotation interpolator and a position interpolator
|
//in case there are keys with no position information but position information and
|
//vice versa right now its using RotPosPathInterpolator
|
private TransformInterpolator buildInterpolator(TransformGroup targetGroup, Transform3D axisOfTransform)
|
{
|
makeTwoListsTheSameSize(positionKeys, orientationKeys);
|
int numKeys = positionKeys.size();
|
|
Point3f currentPoint = position;
|
Quat4f currentQuat = rotation;
|
RotPosPathInterpolator rotator = null;
|
if(numKeys > 1)
|
{
|
float[] knots = new float[numKeys];
|
Point3f[] points = new Point3f[numKeys];
|
Quat4f[] quats = new Quat4f[numKeys];
|
|
for(int i=0; i < numKeys; i++)
|
{
|
//Knots need to be between 0(beginning) and 1(end)
|
knots[i]= (i==0?0:((float)i/((float)(numKeys-1))));
|
if(positionKeys.size() > i)
|
{
|
Point3f newPoint = (Point3f)positionKeys.get(i);
|
if(newPoint != null)
|
{
|
currentPoint = newPoint;
|
}
|
|
Quat4f newQuat = (Quat4f)orientationKeys.get(i);
|
if(newQuat != null)
|
{
|
currentQuat = newQuat;
|
}
|
}
|
|
points[i] = currentPoint;
|
quats[i] = currentQuat;
|
quats[i].inverse();
|
}
|
|
//This gives a continuous loop at a rate of 30 fps
|
Alpha alpha = new Alpha(-1, (long)(numKeys/.03));
|
alpha.setStartTime(System.currentTimeMillis());
|
alpha.setDuplicateOnCloneTree(true);
|
|
rotator = new RotPosPathInterpolator(alpha, targetGroup,
|
axisOfTransform, knots,
|
quats, points);
|
}
|
return rotator;
|
}
|
|
public void makeTwoListsTheSameSize(List list1, List list2)
|
{
|
growList(list2.size() - 1, list1);
|
growList(list1.size() - 1, list2);
|
}
|
|
/**
|
* Make sure the list is at least able to
|
* hold a value at index.
|
* @param index an int specifying the initial size
|
* @parame the list that may need to grow
|
*/
|
public void growList(int index, List list)
|
{
|
int numNeeded = (index + 1) - list.size();
|
while(numNeeded-- > 0)
|
{
|
list.add(null);
|
}
|
}
|
|
/**
|
* Sets the center of the bounding box that the pivot
|
* should offset.
|
*/
|
public void setPivotCenter(Point3f center)
|
{
|
this.pivotCenter = center;
|
}
|
|
/**
|
* Called to set the coordinate system transform for an object named
|
* objectName.
|
* This is the first t
|
*/
|
public void setCoordinateSystem(String objectName, Transform3D coordinateSystem)
|
{
|
namedObjectCoordinateSystems.put(objectName, coordinateSystem);
|
}
|
|
/**
|
* Sets the group that will be used to center rotations.
|
* This is applied to the mesh after all other transforms
|
* have been applied.
|
* @param group the group that will act as the rotation transform.
|
*/
|
public void setRotation(Quat4f rotation)
|
{
|
this.rotation = rotation;
|
}
|
|
/**
|
* Sets the pivot that will be used to as a pivot for
|
* these transfomations.
|
* @param group the group that will act as the pivot.
|
*/
|
public void setPivot(Vector3f pivot)
|
{
|
this.pivot = pivot;
|
}
|
|
/**
|
* Sets the scale for x y and z axis for objects.
|
* This is applied to the mesh before the rotation transform
|
* has been applied.
|
* @param group the group that will act as the scale
|
*/
|
public void setScale(Vector3f scale)
|
{
|
this.scale = scale;
|
}
|
|
/**
|
* Sets the scale information necessary for animation.s
|
* @param scaleKeys a list of Vector3f, which may contain null elements,
|
* containing a position for some keys.
|
*/
|
public void setScaleKeys(List scaleKeys)
|
{
|
this.scaleKeys = scaleKeys;
|
}
|
|
/**
|
* Sets the group that will be used to translate the mesh..
|
* This is applied to the mesh just before the rotation transform
|
* has been applied.
|
* @param group the group that will act as the position transform.
|
*/
|
public void setPosition(Point3f position)
|
{
|
this.position = position;
|
}
|
|
/**
|
* Sets the position information necessary for animation.s
|
* @param positions a list of Point3f, which may contain null elements,
|
* containing a position for some keys.
|
*/
|
public void setPositionKeys(List positions)
|
{
|
positionKeys = positions;
|
}
|
|
/**
|
* Sets the orientation information necessary for animation.s
|
* @param positions a list of Quat4f, which may contain null elements,
|
* containing a orientation for some keys.
|
*/
|
public void setOrientationKeys(List orientations)
|
{
|
orientationKeys = orientations;
|
}
|
|
/**
|
*
|
*/
|
public void setDummyObject(Group object)
|
{
|
dummyObject = object;
|
}
|
|
/**
|
* returns true if position keys and orientation
|
* keys are longer than one element each.
|
*/
|
public boolean hasKeys()
|
{
|
return (positionKeys.size() > 1 || orientationKeys.size() > 1);
|
}
|
|
/**
|
*/
|
public void addFather(int fatherID, TransformGroup newFather)
|
{
|
if(fatherID < 0)
|
{
|
father = null;
|
}
|
else
|
{
|
father = (TransformGroup)(fatherMap.get(new Integer(fatherID)));
|
//Remove the father's father because the father will
|
//be inserted somewhere later.
|
Group grandFather = (Group)father.getParent();
|
if(grandFather != null)
|
{
|
grandFather.removeChild(father);
|
}
|
}
|
fatherMap.put(id, newFather);
|
}
|
|
/**
|
* Sets the id for these frames.
|
* @param id the id for these frames.
|
*/
|
public void setID(int id)
|
{
|
this.id = new Integer(id);
|
}
|
}
|