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;
 | |
| 	}
 | |
| }
 |