forked from LeenkxTeam/LNXSDK
365 lines
8.5 KiB
Haxe
365 lines
8.5 KiB
Haxe
|
package iron.object;
|
||
|
|
||
|
import iron.math.Mat4;
|
||
|
import iron.math.Vec4;
|
||
|
import iron.math.Quat;
|
||
|
|
||
|
class Transform {
|
||
|
/**
|
||
|
The world matrix (read-only).
|
||
|
**/
|
||
|
public var world: Mat4;
|
||
|
/**
|
||
|
Prevent applying parent matrix.
|
||
|
**/
|
||
|
public var localOnly = false;
|
||
|
/**
|
||
|
The local matrix. If you modify this, call `decompose()` to update the
|
||
|
`loc`, `rot` and `scale` fields, or `buildMatrix()` to update
|
||
|
everything.
|
||
|
**/
|
||
|
public var local: Mat4;
|
||
|
/**
|
||
|
The local translation. Changes to this field should be applied by
|
||
|
calling `buildMatrix()`.
|
||
|
**/
|
||
|
public var loc: Vec4;
|
||
|
/**
|
||
|
The local rotation. Changes to this field should be applied by
|
||
|
calling `buildMatrix()`.
|
||
|
**/
|
||
|
public var rot: Quat;
|
||
|
/**
|
||
|
The local scale. Changes to this field should be applied by
|
||
|
calling `buildMatrix()`.
|
||
|
**/
|
||
|
public var scale: Vec4;
|
||
|
/**
|
||
|
Uniform scale factor for `world` matrix.
|
||
|
**/
|
||
|
public var scaleWorld: kha.FastFloat = 1.0;
|
||
|
/**
|
||
|
The world matrix with `scaleWorld` applied (read-only).
|
||
|
**/
|
||
|
public var worldUnpack: Mat4;
|
||
|
/**
|
||
|
Flag to rebuild the `world` matrix on next update.
|
||
|
**/
|
||
|
public var dirty: Bool;
|
||
|
/**
|
||
|
The object that is effected by this transform.
|
||
|
**/
|
||
|
public var object: Object;
|
||
|
/**
|
||
|
The dimensions of the object in local space (without parent, prepended
|
||
|
or appended matrices applied).
|
||
|
**/
|
||
|
public var dim: Vec4;
|
||
|
/**
|
||
|
The radius of the smallest sphere that encompasses the object in local
|
||
|
space.
|
||
|
**/
|
||
|
public var radius: kha.FastFloat;
|
||
|
|
||
|
static var temp = Mat4.identity();
|
||
|
static var q = new Quat();
|
||
|
|
||
|
var boneParent: Mat4 = null;
|
||
|
var lastWorld: Mat4 = null;
|
||
|
|
||
|
// Wrong order returned from getEuler(), store last state for animation
|
||
|
var _eulerX: kha.FastFloat;
|
||
|
var _eulerY: kha.FastFloat;
|
||
|
var _eulerZ: kha.FastFloat;
|
||
|
|
||
|
// Animated delta transform
|
||
|
var dloc: Vec4 = null;
|
||
|
var drot: Quat = null;
|
||
|
var dscale: Vec4 = null;
|
||
|
var _deulerX: kha.FastFloat;
|
||
|
var _deulerY: kha.FastFloat;
|
||
|
var _deulerZ: kha.FastFloat;
|
||
|
|
||
|
public function new(object: Object) {
|
||
|
this.object = object;
|
||
|
reset();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Reset to a null transform: zero location and rotation, and a uniform
|
||
|
scale of one. Other fields such as prepended matrices and bone parents
|
||
|
will not be changed.
|
||
|
**/
|
||
|
public function reset() {
|
||
|
world = Mat4.identity();
|
||
|
worldUnpack = Mat4.identity();
|
||
|
local = Mat4.identity();
|
||
|
loc = new Vec4();
|
||
|
rot = new Quat();
|
||
|
scale = new Vec4(1.0, 1.0, 1.0);
|
||
|
dim = new Vec4(2.0, 2.0, 2.0);
|
||
|
radius = 1.0;
|
||
|
dirty = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Rebuild the matrices, if needed.
|
||
|
**/
|
||
|
public function update() {
|
||
|
if (dirty) buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Clear delta transforms. `dloc`, `drot` and `dscale` are set to `null`
|
||
|
**/
|
||
|
public function clearDelta() {
|
||
|
|
||
|
dloc = null;
|
||
|
drot = null;
|
||
|
dscale = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Reset delta transforms. `dloc`, `drot` and `dscale`
|
||
|
are set to `Vec4(0, 0, 0)`, `Quat(0, 0, 0, 0)` and `Vec4(1, 1, 1)` respectively
|
||
|
**/
|
||
|
public function resetDelta() {
|
||
|
dloc = new Vec4();
|
||
|
drot = new Quat();
|
||
|
_deulerX = _deulerY = _deulerZ = 0.0;
|
||
|
dscale = new Vec4().set(1, 1, 1);
|
||
|
}
|
||
|
|
||
|
function composeDelta() {
|
||
|
// Delta transform
|
||
|
var dl = new Vec4().addvecs(loc, dloc);
|
||
|
var ds = new Vec4().setFrom(scale);
|
||
|
ds.x *= dscale.x;
|
||
|
ds.y *= dscale.y;
|
||
|
ds.z *= dscale.z;
|
||
|
var dr = new Quat().fromEuler(_deulerX, _deulerY, _deulerZ);
|
||
|
dr.multquats(dr, rot);
|
||
|
dr.multquats(drot, dr);
|
||
|
local.compose(dl, dr, ds);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Update the transform matrix based on `loc`, `rot`, and `scale`. If any
|
||
|
change is made to `loc`, `rot`, or `scale` `buildMatrix()` must be
|
||
|
called to update the objects transform.
|
||
|
**/
|
||
|
public function buildMatrix() {
|
||
|
dloc == null ? local.compose(loc, rot, scale) : composeDelta();
|
||
|
|
||
|
if (boneParent != null) local.multmats(boneParent, local);
|
||
|
|
||
|
if (object.parent != null && !localOnly) {
|
||
|
world.multmats3x4(local, object.parent.transform.world);
|
||
|
}
|
||
|
else {
|
||
|
world.setFrom(local);
|
||
|
}
|
||
|
|
||
|
worldUnpack.setFrom(world);
|
||
|
if (scaleWorld != 1.0) {
|
||
|
worldUnpack._00 *= scaleWorld;
|
||
|
worldUnpack._01 *= scaleWorld;
|
||
|
worldUnpack._02 *= scaleWorld;
|
||
|
worldUnpack._03 *= scaleWorld;
|
||
|
worldUnpack._10 *= scaleWorld;
|
||
|
worldUnpack._11 *= scaleWorld;
|
||
|
worldUnpack._12 *= scaleWorld;
|
||
|
worldUnpack._13 *= scaleWorld;
|
||
|
worldUnpack._20 *= scaleWorld;
|
||
|
worldUnpack._21 *= scaleWorld;
|
||
|
worldUnpack._22 *= scaleWorld;
|
||
|
worldUnpack._23 *= scaleWorld;
|
||
|
}
|
||
|
|
||
|
// Constraints
|
||
|
if (object.constraints != null) for (c in object.constraints) c.apply(this);
|
||
|
|
||
|
computeDim();
|
||
|
|
||
|
// Update children
|
||
|
for (n in object.children) {
|
||
|
n.transform.buildMatrix();
|
||
|
}
|
||
|
|
||
|
dirty = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Move the game Object by the defined amount relative to its current location.
|
||
|
@param x Amount to move on the local x axis.
|
||
|
@param y Amount to move on the local y axis.
|
||
|
@param z Amount to move on the local z axis.
|
||
|
**/
|
||
|
public function translate(x: kha.FastFloat, y: kha.FastFloat, z: kha.FastFloat) {
|
||
|
loc.x += x;
|
||
|
loc.y += y;
|
||
|
loc.z += z;
|
||
|
buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Set the local matrix and update `loc`, `rot`, `scale` and `world`.
|
||
|
@param mat The new local matrix.
|
||
|
**/
|
||
|
public function setMatrix(mat: Mat4) {
|
||
|
local.setFrom(mat);
|
||
|
decompose();
|
||
|
buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Apply another transform to this one, i.e. multiply this transform's
|
||
|
local matrix by another.
|
||
|
@param mat The other transform to apply.
|
||
|
**/
|
||
|
public function multMatrix(mat: Mat4) {
|
||
|
local.multmat(mat);
|
||
|
decompose();
|
||
|
buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Update the `loc`, `rot` and `scale` fields according to the local
|
||
|
matrix. You may need to call this after directly mutating the local
|
||
|
matrix.
|
||
|
**/
|
||
|
public function decompose() {
|
||
|
local.decompose(loc, rot, scale);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Rotate around an axis.
|
||
|
@param axis The axis to rotate around.
|
||
|
@param f The magnitude of the rotation in radians.
|
||
|
**/
|
||
|
public function rotate(axis: Vec4, f: kha.FastFloat) {
|
||
|
q.fromAxisAngle(axis, f);
|
||
|
rot.multquats(q, rot);
|
||
|
buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Apply a scaled translation in local space.
|
||
|
@param axis The direction to move.
|
||
|
@param f A multiplier for the movement. If `axis` is a unit
|
||
|
vector, then this is the distance to move.
|
||
|
**/
|
||
|
public function move(axis: Vec4, f = 1.0) {
|
||
|
loc.addf(axis.x * f, axis.y * f, axis.z * f);
|
||
|
buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Set the rotation of the object in radians.
|
||
|
@param x Set the x axis rotation in radians.
|
||
|
@param y Set the y axis rotation in radians.
|
||
|
@param z Set the z axis rotation in radians.
|
||
|
**/
|
||
|
public function setRotation(x: kha.FastFloat, y: kha.FastFloat, z: kha.FastFloat) {
|
||
|
rot.fromEuler(x, y, z);
|
||
|
_eulerX = x;
|
||
|
_eulerY = y;
|
||
|
_eulerZ = z;
|
||
|
dirty = true;
|
||
|
}
|
||
|
|
||
|
function computeRadius() {
|
||
|
radius = Math.sqrt(dim.x * dim.x + dim.y * dim.y + dim.z * dim.z);
|
||
|
}
|
||
|
|
||
|
function computeDim() {
|
||
|
if (object.raw == null) {
|
||
|
computeRadius();
|
||
|
return;
|
||
|
}
|
||
|
var d = object.raw.dimensions;
|
||
|
if (d == null) dim.set(2 * scale.x, 2 * scale.y, 2 * scale.z);
|
||
|
else dim.set(d[0] * scale.x, d[1] * scale.y, d[2] * scale.z);
|
||
|
computeRadius();
|
||
|
}
|
||
|
|
||
|
public function applyParentInverse() {
|
||
|
var pt = object.parent.transform;
|
||
|
pt.buildMatrix();
|
||
|
temp.getInverse(pt.world);
|
||
|
this.local.multmat(temp);
|
||
|
this.decompose();
|
||
|
this.buildMatrix();
|
||
|
}
|
||
|
|
||
|
public function applyParent() {
|
||
|
var pt = object.parent.transform;
|
||
|
pt.buildMatrix();
|
||
|
this.local.multmat(pt.world);
|
||
|
this.decompose();
|
||
|
this.buildMatrix();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Check whether the transform has changed at all since the last time
|
||
|
this function was called.
|
||
|
@return `true` if the transform has changed.
|
||
|
**/
|
||
|
public function diff(): Bool {
|
||
|
if (lastWorld == null) {
|
||
|
lastWorld = Mat4.identity().setFrom(world);
|
||
|
return false;
|
||
|
}
|
||
|
var a = world;
|
||
|
var b = lastWorld;
|
||
|
var r = a._00 != b._00 || a._01 != b._01 || a._02 != b._02 || a._03 != b._03 ||
|
||
|
a._10 != b._10 || a._11 != b._11 || a._12 != b._12 || a._13 != b._13 ||
|
||
|
a._20 != b._20 || a._21 != b._21 || a._22 != b._22 || a._23 != b._23 ||
|
||
|
a._30 != b._30 || a._31 != b._31 || a._32 != b._32 || a._33 != b._33;
|
||
|
if (r) lastWorld.setFrom(world);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@return The look vector (positive local y axis) in world space.
|
||
|
**/
|
||
|
public inline function look(): Vec4 {
|
||
|
return world.look();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@return The right vector (positive local x axis) in world space.
|
||
|
**/
|
||
|
public inline function right(): Vec4 {
|
||
|
return world.right();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@return The up vector (positive local z axis) in world space.
|
||
|
**/
|
||
|
public inline function up(): Vec4 {
|
||
|
return world.up();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@return The world x location.
|
||
|
**/
|
||
|
public inline function worldx(): kha.FastFloat {
|
||
|
return world._30;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@return The world y location.
|
||
|
**/
|
||
|
public inline function worldy(): kha.FastFloat {
|
||
|
return world._31;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@return The world z location.
|
||
|
**/
|
||
|
public inline function worldz(): kha.FastFloat {
|
||
|
return world._32;
|
||
|
}
|
||
|
}
|