Files
dsrc-1.2/sku.0/sys.server/compiled/game/script/transform.java

625 lines
22 KiB
Java
Executable File

// transform.java
package script;
import java.io.Serializable;
// ======================================================================
/**
* The transform class represents a 4x4 matrix with only translation and orientation components.
*/
public final class transform implements Comparable, Serializable
{
// ----------------------------------------------------------------------
private final static long serialVersionUID = 2864167306712372792L;
/** a 3x4 matrix representing a 4x4 with row 4 == (0, 0, 0, 1) */
private final float[][] matrix;
/** The identity transform (identity orientation, position at origin) */
public final static transform identity = new transform(vector.unitX, vector.unitY, vector.unitZ, vector.zero);
// ----------------------------------------------------------------------
/**
* Construct a default transform.
*/
public transform()
{
matrix = identity.matrix;
}
// ----------------------------------------------------------------------
/**
* Construct a transform given all elements of its matrix.
*/
public transform(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23)
{
matrix = new float[3][4];
matrix[0][0] = m00;
matrix[0][1] = m01;
matrix[0][2] = m02;
matrix[0][3] = m03;
matrix[1][0] = m10;
matrix[1][1] = m11;
matrix[1][2] = m12;
matrix[1][3] = m13;
matrix[2][0] = m20;
matrix[2][1] = m21;
matrix[2][2] = m22;
matrix[2][3] = m23;
}
// ----------------------------------------------------------------------
/**
* Construct a matrix given its left handed orthonormal basis and translation vectors.
*
* @param i unit vector along the X axis
* @param j unit vector along the Y axis
* @param k unit vector along the Z axis
* @param p translation vector
*/
public transform(vector i, vector j, vector k, vector p)
{
matrix = new float[3][4];
matrix[0][0] = i.x;
matrix[0][1] = j.x;
matrix[0][2] = k.x;
matrix[0][3] = p.x;
matrix[1][0] = i.y;
matrix[1][1] = j.y;
matrix[1][2] = k.y;
matrix[1][3] = p.y;
matrix[2][0] = i.z;
matrix[2][1] = j.z;
matrix[2][2] = k.z;
matrix[2][3] = p.z;
validate();
}
// ----------------------------------------------------------------------
public transform(transform src)
{
matrix = src.matrix;
}
// ----------------------------------------------------------------------
public Object clone()
{
return new transform(this);
}
// ----------------------------------------------------------------------
public String toString()
{
return
"[ " + matrix[0][0] + " " + matrix[0][1] + " " + matrix[0][2] + " " + matrix[0][3] + " ]\n" +
"[ " + matrix[1][0] + " " + matrix[1][1] + " " + matrix[1][2] + " " + matrix[1][3] + " ]\n" +
"[ " + matrix[2][0] + " " + matrix[2][1] + " " + matrix[2][2] + " " + matrix[2][3] + " ]";
}
// ----------------------------------------------------------------------
public int compareTo(Object o) throws ClassCastException
{
return compareTo((transform)o);
}
// ----------------------------------------------------------------------
/**
* Compare this transform against another transform.
*
* @param tr the transform to compare this against
* @return 0 if all elements are equal, -1 if the first non-equal element of this encountered traversing the matricies is less than the corresponding element of tr, or else 1
*/
public int compareTo(transform tr)
{
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 4; ++j)
{
if (matrix[i][j] < tr.matrix[i][j])
return -1;
else if (matrix[i][j] > tr.matrix[i][j])
return 1;
}
}
return 0;
}
// ----------------------------------------------------------------------
public boolean equals(Object o)
{
if (o != null)
{
try
{
transform tr = (transform)o;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 4; ++j)
if (matrix[i][j] != tr.matrix[i][j])
return false;
return true;
}
catch (ClassCastException err)
{
}
}
return false;
}
// ----------------------------------------------------------------------
public void validate() throws java.lang.ArithmeticException
{
vector i = getLocalFrameI_p();
vector j = getLocalFrameJ_p();
vector k = getLocalFrameK_p();
if ( Math.abs(i.magnitudeSquared()-1.0f) > 0.0005f
|| Math.abs(j.magnitudeSquared()-1.0f) > 0.0005f
|| Math.abs(k.magnitudeSquared()-1.0f) > 0.0005f
|| Math.abs(i.dot(j)) > 0.0001f
|| Math.abs(j.dot(k)) > 0.0001f
|| Math.abs(i.dot(k)) > 0.0001f)
throw new ArithmeticException("transform basis is not orthonormal (i="+i+", j="+j+", k="+k+")");
}
// ----------------------------------------------------------------------
/**
* Reorthogonalize a transform.
*
* Repeated rotations will introduce numerical error into the transform,
* which will cause the upper 3x3 matrix to become non-orthonormal. If
* enough error is introduced, weird errors will begin to occur when using
* the transform. This routine attempts to reduce the numerical error by
* reorthonormalizing the upper 3x3 matrix.
*
* @return the reorthonormalized transform.
*/
public transform reorthonormalize()
{
vector k = getLocalFrameK_p().normalize();
vector i = getLocalFrameJ_p().normalize().cross(k);
vector j = k.cross(i);
return new transform(i, j, k, getPosition_p());
}
// ----------------------------------------------------------------------
/**
* Multiply two transforms together.
*
* The matrices and the multiply are defined as follows:
* a b c d m n o p am+bq+cu an+br+cv ao+bs+cw ap+bt+cx+d
* e f g h q r s t em+fq+gu en+fr+gv eo+fs+gw ep+ft+gx+h
* i j k l * u v w x = im+jq+ku in+jr+kv io+js+kw ip+jt+kx+l
* 0 0 0 1 0 0 0 1 0 0 0 1
* where the bottom row of the matrix is not stored.
*
* @param tr the transform to multiply this transform by
* @return the product of this and tr
*/
public transform multiply(transform tr)
{
return new transform(
matrix[0][0] * tr.matrix[0][0] + matrix[0][1] * tr.matrix[1][0] + matrix[0][2] * tr.matrix[2][0],
matrix[0][0] * tr.matrix[0][1] + matrix[0][1] * tr.matrix[1][1] + matrix[0][2] * tr.matrix[2][1],
matrix[0][0] * tr.matrix[0][2] + matrix[0][1] * tr.matrix[1][2] + matrix[0][2] * tr.matrix[2][2],
matrix[0][0] * tr.matrix[0][3] + matrix[0][1] * tr.matrix[1][3] + matrix[0][2] * tr.matrix[2][3] + matrix[0][3],
matrix[1][0] * tr.matrix[0][0] + matrix[1][1] * tr.matrix[1][0] + matrix[1][2] * tr.matrix[2][0],
matrix[1][0] * tr.matrix[0][1] + matrix[1][1] * tr.matrix[1][1] + matrix[1][2] * tr.matrix[2][1],
matrix[1][0] * tr.matrix[0][2] + matrix[1][1] * tr.matrix[1][2] + matrix[1][2] * tr.matrix[2][2],
matrix[1][0] * tr.matrix[0][3] + matrix[1][1] * tr.matrix[1][3] + matrix[1][2] * tr.matrix[2][3] + matrix[1][3],
matrix[2][0] * tr.matrix[0][0] + matrix[2][1] * tr.matrix[1][0] + matrix[2][2] * tr.matrix[2][0],
matrix[2][0] * tr.matrix[0][1] + matrix[2][1] * tr.matrix[1][1] + matrix[2][2] * tr.matrix[2][1],
matrix[2][0] * tr.matrix[0][2] + matrix[2][1] * tr.matrix[1][2] + matrix[2][2] * tr.matrix[2][2],
matrix[2][0] * tr.matrix[0][3] + matrix[2][1] * tr.matrix[1][3] + matrix[2][2] * tr.matrix[2][3] + matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Calculate the inverse of this transform.
*
* @return the inverse of this transform.
*/
public transform invert()
{
// transpose the upper 3x3 matrix and invert the translation
return new transform(
matrix[0][0], matrix[1][0], matrix[2][0], -(matrix[0][0] * matrix[0][3] + matrix[0][1] * matrix[1][3] + matrix[0][2] * matrix[2][3]),
matrix[0][1], matrix[1][1], matrix[2][1], -(matrix[1][1] * matrix[0][3] + matrix[1][1] * matrix[1][3] + matrix[1][2] * matrix[2][3]),
matrix[0][2], matrix[1][2], matrix[2][2], -(matrix[2][0] * matrix[0][3] + matrix[2][1] * matrix[1][3] + matrix[2][2] * matrix[2][3]));
}
// ----------------------------------------------------------------------
/**
* Move this transform in its own local space.
*
* This routine moves the transform according to its current frame of reference.
* Therefore, moving along the Z axis will move the transform forward in the direction in which it is pointed.
*
* @param vec vector to rotate and translate
* @return the resulting translated transform
*/
public transform move_l(vector vec)
{
return move_p(rotate_l2p(vec));
}
// ----------------------------------------------------------------------
/**
* Move this transform in its parent space.
*
* This routine moves the transform in its parent space, or the world space if the transform has no parent.
* Therefore, moving along the Z axis will move the transform forward along the Z axis of its parent space, not forward in the direction in which it is pointed.
*
* @param vec Displacement to move in parent space
* @return the resulting translated transform
*/
public transform move_p(vector vec)
{
return new transform(
matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3] + vec.x,
matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3] + vec.y,
matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3] + vec.z);
}
// ----------------------------------------------------------------------
/**
* Yaw this transform.
*
* This routine will rotate the transform around the Y axis by the specified number of radians.
* Positive rotations are clockwise when viewed looking at the origin from the positive side of the axis around which the transform is being rotated.
*
* @param radians amount to rotate, in radians
* @return the resulting yawed transform
*/
public transform yaw_l(float radians)
{
float sine = (float)Math.sin(radians);
float cosine = (float)Math.cos(radians);
return new transform(
matrix[0][0]*cosine + matrix[0][2]*(-sine), matrix[0][1], matrix[0][0]*sine + matrix[0][2]*cosine, matrix[0][3],
matrix[1][0]*cosine + matrix[1][2]*(-sine), matrix[1][1], matrix[1][0]*sine + matrix[1][2]*cosine, matrix[1][3],
matrix[2][0]*cosine + matrix[2][2]*(-sine), matrix[2][1], matrix[2][0]*sine + matrix[2][2]*cosine, matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Pitch this transform.
*
* This routine will rotate the transform around the X axis by the specified number of radians.
* Positive rotations are clockwise when viewed looking at the origin from the positive side of the axis around which the transform is being rotated.
*
* @param radians amount to rotate, in radians
* @return the resulting pitched transform
*/
public transform pitch_l(float radians)
{
float sine = (float)Math.sin(radians);
float cosine = (float)Math.cos(radians);
return new transform(
matrix[0][0], matrix[0][1]*cosine + matrix[0][2]*sine, matrix[0][1]*(-sine) + matrix[0][2]*cosine, matrix[0][3],
matrix[1][0], matrix[1][1]*cosine + matrix[1][2]*sine, matrix[1][1]*(-sine) + matrix[1][2]*cosine, matrix[1][3],
matrix[2][0], matrix[2][1]*cosine + matrix[2][2]*sine, matrix[2][1]*(-sine) + matrix[2][2]*cosine, matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Roll this transform.
*
* This routine will rotate the transform around the Z axis by the specified number of radians.
* Positive rotations are clockwise when viewed looking at the origin from the positive side of the axis around which the transform is being rotated.
*
* @param radians amount to rotate, in radians
* @return the resulting rolled transform
*/
public transform roll_l(float radians)
{
float sine = (float)Math.sin(radians);
float cosine = (float)Math.cos(radians);
return new transform(
matrix[0][0]*cosine + matrix[0][1]*sine, matrix[0][0]*(-sine) + matrix[0][1]*cosine, matrix[0][2], matrix[0][3],
matrix[1][0]*cosine + matrix[1][1]*sine, matrix[1][0]*(-sine) + matrix[1][1]*cosine, matrix[1][2], matrix[1][3],
matrix[2][0]*cosine + matrix[2][1]*sine, matrix[2][0]*(-sine) + matrix[2][1]*cosine, matrix[2][2], matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Set the orientation to the identity orientation, leaving the position intact.
*
* @return the resulting transform, with the identity orientation but the position of the original transform.
*/
public transform resetRotate_l2p()
{
return new transform(
1.0f, 0.0f, 0.0f, matrix[0][3],
0.0f, 1.0f, 0.0f, matrix[1][3],
0.0f, 0.0f, 1.0f, matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Get the position (translation component) from the transform.
*
* @return the position vector
*/
public vector getPosition_p()
{
return new vector(matrix[0][3], matrix[1][3], matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Set the position (translation component) of the transform.
*
* @param vec the new position vector
* @return the transform with its translation component set to the new position
*/
public transform setPosition_p(vector vec)
{
return new transform(
matrix[0][0], matrix[0][1], matrix[0][2], vec.x,
matrix[1][0], matrix[1][1], matrix[1][2], vec.y,
matrix[2][0], matrix[2][1], matrix[2][2], vec.z);
}
// ----------------------------------------------------------------------
/**
* Set the position (translation component) of the transform.
*
* @param x the x component of the new position vector
* @param y the y component of the new position vector
* @param z the z component of the new position vector
* @return the transform with its translation component set to the new position
*/
public transform setPosition_p(float x, float y, float z)
{
return new transform(
matrix[0][0], matrix[0][1], matrix[0][2], x,
matrix[1][0], matrix[1][1], matrix[1][2], y,
matrix[2][0], matrix[2][1], matrix[2][2], z);
}
// ----------------------------------------------------------------------
/**
* Get the parent-space vector pointing along the X axis of this frame of reference.
*
* The vector returned is in parent space, which is world space if the transform has no parent.
*
* @return the vector pointing along the X axis of the frame in parent space
*/
public vector getLocalFrameI_p()
{
return new vector(matrix[0][0], matrix[1][0], matrix[2][0]);
}
// ----------------------------------------------------------------------
/**
* Get the parent-space vector pointing along the Y axis of this frame of reference.
*
* The vector returned is in parent space, which is world space if the transform has no parent.
*
* @return the vector pointing along the Y axis of the frame in parent space
*/
public vector getLocalFrameJ_p()
{
return new vector(matrix[0][1], matrix[1][1], matrix[2][1]);
}
// ----------------------------------------------------------------------
/**
* Get the parent-space vector pointing along the Z axis of this frame of reference.
*
* The vector returned is in parent space, which is world space if the transform has no parent.
*
* @return the vector pointing along the Z axis of the frame in parent space
*/
public vector getLocalFrameK_p()
{
return new vector(matrix[0][2], matrix[1][2], matrix[2][2]);
}
// ----------------------------------------------------------------------
/**
* Set the transform matrix from the i, j, and k vectors.
*
* This routine assumes that i, j, and k are a left-handed orthonormal basis.
* If they are not, the transform must be reorthonormalized after this routine.
*
* @param i unit vector along the X axis
* @param j unit vector along the Y axis
* @param k unit vector along the Z axis
* @return the resulting vector with the newly set orientation and original translation
*/
public transform setLocalFrameIJK_p(vector i, vector j, vector k)
{
return new transform(i, j, k, getPosition_p());
}
// ----------------------------------------------------------------------
/**
* Set the transform matrix from the k and j vectors.
*
* This routine assumes that k and j are part of a left-handed orthonormal basis.
* If they are not, the transform must be reorthonormalized after this routine.
*
* @param k unit vector along the Z axis
* @param j unit vector along the Y axis
* @return the resulting vector with the newly set orientation and original translation
*/
public transform setLocalFrameKJ_p(vector k, vector j)
{
return new transform(j.cross(k), j, k, getPosition_p());
}
// ----------------------------------------------------------------------
/**
* Get the transform-space vector pointing along the X axis of the parent of reference.
*
* @return the vector pointing along the X axis of the parent's frame in local space
*/
public vector getParentFrameI_l()
{
return new vector(matrix[0][0], matrix[0][1], matrix[0][2]);
}
// ----------------------------------------------------------------------
/**
* Get the transform-space vector pointing along the Y axis of the parent of reference.
*
* @return the vector pointing along the Y axis of the parent's frame in local space
*/
public vector getParentFrameJ_l()
{
return new vector(matrix[1][0], matrix[1][1], matrix[1][2]);
}
// ----------------------------------------------------------------------
/**
* Get the transform-space vector pointing along the Z axis of the parent of reference.
*
* @return the vector pointing along the Z axis of the parent's frame in local space
*/
public vector getParentFrameK_l()
{
return new vector(matrix[2][0], matrix[2][1], matrix[2][2]);
}
// ----------------------------------------------------------------------
/**
* Rotate a vector from the transform's current frame to the parent frame.
*
* Pure rotation is most useful for vectors that are orientational, such as normals.
*
* @param vec the vector to rotate
* @return the vector in parent space
*/
public vector rotate_l2p(vector vec)
{
return new vector(
matrix[0][0] * vec.x + matrix[0][1] * vec.y + matrix[0][2] * vec.z,
matrix[1][0] * vec.x + matrix[1][1] * vec.y + matrix[1][2] * vec.z,
matrix[2][0] * vec.x + matrix[2][1] * vec.y + matrix[2][2] * vec.z);
}
// ----------------------------------------------------------------------
/**
* Transform a vector from the transform's current frame to the parent frame.
*
* Rotation and translation is most useful for vectors that are position, such as vertex data.
*
* @param vec vector to rotate and translate
* @return the vector in parent space
*/
public vector rotateTranslate_l2p(vector vec)
{
return new vector(
matrix[0][0] * vec.x + matrix[0][1] * vec.y + matrix[0][2] * vec.z + matrix[0][3],
matrix[1][0] * vec.x + matrix[1][1] * vec.y + matrix[1][2] * vec.z + matrix[1][3],
matrix[2][0] * vec.x + matrix[2][1] * vec.y + matrix[2][2] * vec.z + matrix[2][3]);
}
// ----------------------------------------------------------------------
/**
* Rotate a vector from the parent space to the local transform space.
*
* Pure rotation is most useful for vectors that are orientational, such as normals.
*
* @param vec the vector to rotate
* @return the vector in local space
*/
public vector rotate_p2l(vector vec)
{
return new vector(
matrix[0][0] * vec.x + matrix[1][0] * vec.y + matrix[2][0] * vec.z,
matrix[0][1] * vec.x + matrix[1][1] * vec.y + matrix[2][1] * vec.z,
matrix[0][2] * vec.x + matrix[1][2] * vec.y + matrix[2][2] * vec.z);
}
// ----------------------------------------------------------------------
/**
* Transform a vector from the parent space to to the local transform space.
*
* Rotation and translation is most useful for vectors that are position, such as vertex data.
*
* @param vec vector to rotate and translate
* @return the vector in local space
*/
public vector rotateTranslate_p2l(vector vec)
{
float x = vec.x - matrix[0][3];
float y = vec.y - matrix[1][3];
float z = vec.z - matrix[2][3];
return new vector(
matrix[0][0] * x + matrix[1][0] * y + matrix[2][0] * z,
matrix[0][1] * x + matrix[1][1] * y + matrix[2][1] * z,
matrix[0][2] * x + matrix[1][2] * y + matrix[2][2] * z);
}
// ----------------------------------------------------------------------
/**
* Transform a transform from local transform space to parent transform space.
*
* @param tr the transform to change from local space to parent space
* @return the transform in parent space
*/
public transform rotateTranslate_l2p(transform tr)
{
return multiply(tr).reorthonormalize();
}
// ----------------------------------------------------------------------
/**
* Transform a transform from parent transform space to the local transform space.
*
* @param tr the transform to change from parent space to local space
* @return the transform in local space
*/
public transform rotateTranslate_p2l(transform tr)
{
return tr.multiply(this).reorthonormalize();
}
// ----------------------------------------------------------------------
/**
* Determine whether this transform approximates another, within given tolerances.
*
* @param tr the transform to compare this against
* @param rotDelta the tolerance for the rotational comparison
* @param posDelta the tolerance for the positional comparison
* @return whether tr approximates this transform, within the given tolerances.
*/
public boolean approximates(transform tr, float rotDelta, float posDelta)
{
float rotDeltaCheck = 1-rotDelta;
for (int i = 0; i < 3; ++i)
if (matrix[0][i]*tr.matrix[0][i] + matrix[1][i]*tr.matrix[1][i]+matrix[2][i]*tr.matrix[2][i] < rotDeltaCheck)
return false;
float dx = matrix[0][3]-tr.matrix[0][3];
float dy = matrix[1][3]-tr.matrix[1][3];
float dz = matrix[2][3]-tr.matrix[2][3];
return dx*dx+dy*dy+dz*dz <= posDelta*posDelta;
}
// ----------------------------------------------------------------------
}
// ======================================================================