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