1169 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			1169 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								package iron.object;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import iron.object.Animation.ActionSampler;
							 | 
						||
| 
								 | 
							
								#if lnx_skin
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import kha.FastFloat;
							 | 
						||
| 
								 | 
							
								import kha.arrays.Float32Array;
							 | 
						||
| 
								 | 
							
								import iron.math.Vec4;
							 | 
						||
| 
								 | 
							
								import iron.math.Mat4;
							 | 
						||
| 
								 | 
							
								import iron.math.Quat;
							 | 
						||
| 
								 | 
							
								import iron.data.MeshData;
							 | 
						||
| 
								 | 
							
								import iron.data.SceneFormat;
							 | 
						||
| 
								 | 
							
								import iron.data.Armature;
							 | 
						||
| 
								 | 
							
								import iron.data.Data;
							 | 
						||
| 
								 | 
							
								import iron.math.Ray;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BoneAnimation extends Animation {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public static var skinMaxBones = 128;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Skinning
							 | 
						||
| 
								 | 
							
									public var object: MeshObject;
							 | 
						||
| 
								 | 
							
									public var armatureObject: Object;
							 | 
						||
| 
								 | 
							
									public var data: MeshData;
							 | 
						||
| 
								 | 
							
									public var skinBuffer: Float32Array;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var updateAnimation: Array<Mat4>->Void = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public var skeletonBones(default, null): Array<TObj> = null;
							 | 
						||
| 
								 | 
							
									public var skeletonMats(default, null): Array<Mat4> = null;
							 | 
						||
| 
								 | 
							
									//var skeletonBonesBlend: Array<TObj> = null;
							 | 
						||
| 
								 | 
							
									var absMats: Array<Mat4> = null;
							 | 
						||
| 
								 | 
							
									var applyParent: Array<Bool> = null;
							 | 
						||
| 
								 | 
							
									var matsFast: Array<Mat4> = [];
							 | 
						||
| 
								 | 
							
									var matsFastSort: Array<Int> = [];
							 | 
						||
| 
								 | 
							
									var matsFastBlend: Array<Mat4> = [];
							 | 
						||
| 
								 | 
							
									var matsFastBlendSort: Array<Int> = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var rootMotionCacheInit: Bool = false;
							 | 
						||
| 
								 | 
							
									public var rootMotion(default, null): TObj = null;
							 | 
						||
| 
								 | 
							
									public var rootMotionVelocity(default, null): Vec4 = null;
							 | 
						||
| 
								 | 
							
									public var rootMotionRotation(default, null): Quat = null;
							 | 
						||
| 
								 | 
							
									var rootMotionIndex: Int = -1;
							 | 
						||
| 
								 | 
							
									var rootMotionLockX: Bool = false;
							 | 
						||
| 
								 | 
							
									var rootMotionLockY: Bool = false;
							 | 
						||
| 
								 | 
							
									var rootMotionLockZ: Bool = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var delta: FastFloat = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var boneChildren: Map<String, Array<Object>> = null; // Parented to bone
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var constraintTargets: Array<Object> = null;
							 | 
						||
| 
								 | 
							
									var constraintTargetsI: Array<Mat4> = null;
							 | 
						||
| 
								 | 
							
									var constraintMats: Map<TObj, Mat4> = null;
							 | 
						||
| 
								 | 
							
									var relativeBoneConstraints: Bool = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static var m = Mat4.identity(); // Skinning matrix
							 | 
						||
| 
								 | 
							
									static var m1 = Mat4.identity();
							 | 
						||
| 
								 | 
							
									static var m2 = Mat4.identity();
							 | 
						||
| 
								 | 
							
									static var bm = Mat4.identity(); // Absolute bone matrix
							 | 
						||
| 
								 | 
							
									static var wm = Mat4.identity();
							 | 
						||
| 
								 | 
							
									static var vpos = new Vec4();
							 | 
						||
| 
								 | 
							
									static var vpos2 = new Vec4();
							 | 
						||
| 
								 | 
							
									static var vpos3 = new Vec4();
							 | 
						||
| 
								 | 
							
									static var vscl = new Vec4();
							 | 
						||
| 
								 | 
							
									static var vscl2 = new Vec4();
							 | 
						||
| 
								 | 
							
									static var vscl3 = new Vec4();
							 | 
						||
| 
								 | 
							
									static var q1 = new Quat();
							 | 
						||
| 
								 | 
							
									static var q2 = new Quat();
							 | 
						||
| 
								 | 
							
									static var q3 = new Quat();
							 | 
						||
| 
								 | 
							
									static var q4 = new Quat();
							 | 
						||
| 
								 | 
							
									static var v1 = new Vec4();
							 | 
						||
| 
								 | 
							
									static var v2 = new Vec4();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function new(armatureUid: Int, armatureObject: Object) {
							 | 
						||
| 
								 | 
							
										super();
							 | 
						||
| 
								 | 
							
										this.isSampled = false;
							 | 
						||
| 
								 | 
							
										this.armatureObject = armatureObject;
							 | 
						||
| 
								 | 
							
										for (a in Scene.active.armatures) {
							 | 
						||
| 
								 | 
							
											if (a.uid == armatureUid) {
							 | 
						||
| 
								 | 
							
												this.armature = a;
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function initMatsEmpty(): Array<Mat4> {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var mats = [];
							 | 
						||
| 
								 | 
							
										for(i in 0...skeletonMats.length) mats.push(Mat4.identity());
							 | 
						||
| 
								 | 
							
										return mats;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									public inline function getNumBones(): Int {
							 | 
						||
| 
								 | 
							
										if (skeletonBones == null) return 0;
							 | 
						||
| 
								 | 
							
										return skeletonBones.length;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function setSkin(mo: MeshObject) {
							 | 
						||
| 
								 | 
							
										this.object = mo;
							 | 
						||
| 
								 | 
							
										this.data = mo != null ? mo.data : null;
							 | 
						||
| 
								 | 
							
										this.isSkinned = data != null ? data.isSkinned : false;
							 | 
						||
| 
								 | 
							
										if (this.isSkinned) {
							 | 
						||
| 
								 | 
							
											var boneSize = 12; // Dual-quat skinning + scaling
							 | 
						||
| 
								 | 
							
											this.skinBuffer = new Float32Array(skinMaxBones * boneSize);
							 | 
						||
| 
								 | 
							
											for (i in 0...this.skinBuffer.length) this.skinBuffer[i] = 0;
							 | 
						||
| 
								 | 
							
											// Rotation is already applied to skin at export
							 | 
						||
| 
								 | 
							
											object.transform.rot.set(0, 0, 0, 1);
							 | 
						||
| 
								 | 
							
											object.transform.buildMatrix();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											var refs = mo.parent.raw.bone_actions;
							 | 
						||
| 
								 | 
							
											if (refs != null && refs.length > 0) {
							 | 
						||
| 
								 | 
							
												Data.getSceneRaw(refs[0], function(action: TSceneFormat) { play(action.name); });
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (armatureObject.raw.relative_bone_constraints) relativeBoneConstraints = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function addBoneChild(bone: String, o: Object) {
							 | 
						||
| 
								 | 
							
										if (boneChildren == null) boneChildren = new Map();
							 | 
						||
| 
								 | 
							
										var ar = boneChildren.get(bone);
							 | 
						||
| 
								 | 
							
										if (ar == null) {
							 | 
						||
| 
								 | 
							
											ar = [];
							 | 
						||
| 
								 | 
							
											boneChildren.set(bone, ar);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										ar.push(o);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									public function removeBoneChild(bone: String, o: Object) {
							 | 
						||
| 
								 | 
							
										if (boneChildren != null) {
							 | 
						||
| 
								 | 
							
											var ar = boneChildren.get(bone);
							 | 
						||
| 
								 | 
							
											if (ar != null) ar.remove(o);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								 
							 | 
						||
| 
								 | 
							
									@:access(iron.object.Transform)
							 | 
						||
| 
								 | 
							
									function updateBoneChildren(bone: TObj, bm: Mat4) {
							 | 
						||
| 
								 | 
							
										var ar = boneChildren.get(bone.name);
							 | 
						||
| 
								 | 
							
										if (ar == null) return;
							 | 
						||
| 
								 | 
							
										for (o in ar) {
							 | 
						||
| 
								 | 
							
											var t = o.transform;
							 | 
						||
| 
								 | 
							
											if (t.boneParent == null) t.boneParent = Mat4.identity();
							 | 
						||
| 
								 | 
							
											if (o.raw.parent_bone_tail != null) {
							 | 
						||
| 
								 | 
							
												if (o.raw.parent_bone_connected || isSkinned) {
							 | 
						||
| 
								 | 
							
													var v = o.raw.parent_bone_tail;
							 | 
						||
| 
								 | 
							
													t.boneParent.initTranslate(v[0], v[1], v[2]);
							 | 
						||
| 
								 | 
							
													t.boneParent.multmat(bm);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else {
							 | 
						||
| 
								 | 
							
													var v = o.raw.parent_bone_tail_pose;
							 | 
						||
| 
								 | 
							
													t.boneParent.setFrom(bm);
							 | 
						||
| 
								 | 
							
													t.boneParent.translate(v[0], v[1], v[2]);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else t.boneParent.setFrom(bm);
							 | 
						||
| 
								 | 
							
											t.buildMatrix();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function numParents(b: TObj): Int {
							 | 
						||
| 
								 | 
							
										var i = 0;
							 | 
						||
| 
								 | 
							
										var p = b.parent;
							 | 
						||
| 
								 | 
							
										while (p != null) {
							 | 
						||
| 
								 | 
							
											i++;
							 | 
						||
| 
								 | 
							
											p = p.parent;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return i;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function setMats() {
							 | 
						||
| 
								 | 
							
										while (matsFast.length < skeletonBones.length) {
							 | 
						||
| 
								 | 
							
											matsFast.push(Mat4.identity());
							 | 
						||
| 
								 | 
							
											matsFastSort.push(matsFastSort.length);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										// Calc bones with 0 parents first
							 | 
						||
| 
								 | 
							
										matsFastSort.sort(function(a, b) {
							 | 
						||
| 
								 | 
							
											var i = numParents(skeletonBones[a]);
							 | 
						||
| 
								 | 
							
											var j = numParents(skeletonBones[b]);
							 | 
						||
| 
								 | 
							
											return i < j ? -1 : i > j ? 1 : 0;
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function setAction(action: String) {
							 | 
						||
| 
								 | 
							
										armature.initMats();
							 | 
						||
| 
								 | 
							
										var a = armature.getAction(action);
							 | 
						||
| 
								 | 
							
										skeletonBones = a.bones;
							 | 
						||
| 
								 | 
							
										skeletonMats = a.mats;
							 | 
						||
| 
								 | 
							
										if(! rootMotionCacheInit) skeletonMats.push(Mat4.identity());
							 | 
						||
| 
								 | 
							
										rootMotionCacheInit = true;
							 | 
						||
| 
								 | 
							
										setMats();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getAction(action: String): Array<TObj> {
							 | 
						||
| 
								 | 
							
										armature.initMats();
							 | 
						||
| 
								 | 
							
										return armature.getAction(action).bones;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function multParent(i: Int, fasts: Array<Mat4>, bones: Array<TObj>, mats: Array<Mat4>) {
							 | 
						||
| 
								 | 
							
										var f = fasts[i];
							 | 
						||
| 
								 | 
							
										if (applyParent != null && !applyParent[i]) {
							 | 
						||
| 
								 | 
							
											f.setFrom(mats[i]);
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var p = bones[i].parent;
							 | 
						||
| 
								 | 
							
										var bi = getBoneIndex(p, bones);
							 | 
						||
| 
								 | 
							
										(p == null || bi == -1) ? f.setFrom(mats[i]) : f.multmats(fasts[bi], mats[i]);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function multVecs(vec1: Vec4, vec2: Vec4): Vec4 {
							 | 
						||
| 
								 | 
							
										var res = new Vec4().setFrom(vec1);
							 | 
						||
| 
								 | 
							
										res.x *= vec2.x;
							 | 
						||
| 
								 | 
							
										res.y *= vec2.y;
							 | 
						||
| 
								 | 
							
										res.z *= vec2.z;
							 | 
						||
| 
								 | 
							
										res.w *= vec2.w;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return res;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Do animation here
							 | 
						||
| 
								 | 
							
									public function setAnimationLoop(f: Array<Mat4>->Void) {
							 | 
						||
| 
								 | 
							
										updateAnimation = f;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									override public function play(action = "", onComplete: Void->Void = null, blendTime = 0.2, speed = 1.0, loop = true) {
							 | 
						||
| 
								 | 
							
										super.play(action, onComplete, blendTime, speed, loop);
							 | 
						||
| 
								 | 
							
										if (action != "") {
							 | 
						||
| 
								 | 
							
											setAction(action);
							 | 
						||
| 
								 | 
							
											var tempAnimParam = new ActionSampler(action);
							 | 
						||
| 
								 | 
							
											registerAction("tempAction", tempAnimParam);
							 | 
						||
| 
								 | 
							
											updateAnimation = function(mats){
							 | 
						||
| 
								 | 
							
												sampleAction(tempAnimParam, mats);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									override public function update(delta: FastFloat) {
							 | 
						||
| 
								 | 
							
										this.delta = delta;
							 | 
						||
| 
								 | 
							
										if (!isSkinned && skeletonBones == null) setAction(armature.actions[0].name);
							 | 
						||
| 
								 | 
							
										if (object != null && (!object.visible || object.culled)) return;
							 | 
						||
| 
								 | 
							
										if (skeletonBones == null || skeletonBones.length == 0) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#if lnx_debug
							 | 
						||
| 
								 | 
							
										Animation.beginProfile();
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										super.update(delta);
							 | 
						||
| 
								 | 
							
										if(updateAnimation != null) {
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											updateAnimation(skeletonMats);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										updateConstraints();
							 | 
						||
| 
								 | 
							
										// Do forward kinematics and inverse kinematics here
							 | 
						||
| 
								 | 
							
										if (onUpdates != null) {
							 | 
						||
| 
								 | 
							
											var i = 0;
							 | 
						||
| 
								 | 
							
											var l = onUpdates.length;
							 | 
						||
| 
								 | 
							
											while (i < l) {
							 | 
						||
| 
								 | 
							
												onUpdates[i]();
							 | 
						||
| 
								 | 
							
												l <= onUpdates.length ? i++ : l = onUpdates.length;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calc absolute bones
							 | 
						||
| 
								 | 
							
										for (i in 0...skeletonBones.length) {
							 | 
						||
| 
								 | 
							
											// Take bones with 0 parents first
							 | 
						||
| 
								 | 
							
											multParent(matsFastSort[i], matsFast, skeletonBones, skeletonMats);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (isSkinned) updateSkinGpu();
							 | 
						||
| 
								 | 
							
										else updateBonesOnly();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#if lnx_debug
							 | 
						||
| 
								 | 
							
										Animation.endProfile();
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function evaluateRootMotion(actionMats: Array<Mat4>): Vec4{
							 | 
						||
| 
								 | 
							
										if(rootMotionIndex < 0) return new Vec4();
							 | 
						||
| 
								 | 
							
										var scl = armatureObject.transform.scale;
							 | 
						||
| 
								 | 
							
										wm = getRootMotionWorldMat(actionMats, rootMotion);
							 | 
						||
| 
								 | 
							
										wm.decompose(vpos, q1, vscl);
							 | 
						||
| 
								 | 
							
										vpos = multVecs(vpos, scl);
							 | 
						||
| 
								 | 
							
										rootMotionVelocity.setFrom(vpos);
							 | 
						||
| 
								 | 
							
										rootMotionRotation.setFrom(q1);
							 | 
						||
| 
								 | 
							
										return new Vec4().setFrom(rootMotionVelocity);
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function setRootMotion(bone: TObj, lockX: Bool = false, lockY: Bool = false, lockZ: Bool = false) {
							 | 
						||
| 
								 | 
							
										rootMotion = bone;
							 | 
						||
| 
								 | 
							
										rootMotionIndex = getBoneIndex(rootMotion);
							 | 
						||
| 
								 | 
							
										rootMotionLockX	= lockX;
							 | 
						||
| 
								 | 
							
										rootMotionLockY	= lockY;
							 | 
						||
| 
								 | 
							
										rootMotionLockZ	= lockZ;
							 | 
						||
| 
								 | 
							
										rootMotionVelocity = new Vec4();
							 | 
						||
| 
								 | 
							
										rootMotionRotation = new Quat();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function multParents(m: Mat4, i: Int, bones: Array<TObj>, mats: Array<Mat4>) {
							 | 
						||
| 
								 | 
							
										var bone = bones[i];
							 | 
						||
| 
								 | 
							
										var p = bone.parent;
							 | 
						||
| 
								 | 
							
										while (p != null) {
							 | 
						||
| 
								 | 
							
											var i = getBoneIndex(p, bones);
							 | 
						||
| 
								 | 
							
											if (i == -1) continue;
							 | 
						||
| 
								 | 
							
											m.multmat(mats[i]);
							 | 
						||
| 
								 | 
							
											p = p.parent;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getConstraintsFromScene(cs: Array<TConstraint>) {
							 | 
						||
| 
								 | 
							
										// Init constraints
							 | 
						||
| 
								 | 
							
										if (constraintTargets == null) {
							 | 
						||
| 
								 | 
							
											constraintTargets = [];
							 | 
						||
| 
								 | 
							
											constraintTargetsI = [];
							 | 
						||
| 
								 | 
							
											for (c in cs) {
							 | 
						||
| 
								 | 
							
												var o = Scene.active.getChild(c.target);
							 | 
						||
| 
								 | 
							
												constraintTargets.push(o);
							 | 
						||
| 
								 | 
							
												var m: Mat4 = null;
							 | 
						||
| 
								 | 
							
												if (o != null) {
							 | 
						||
| 
								 | 
							
													m = Mat4.identity().setFrom(o.transform.world);
							 | 
						||
| 
								 | 
							
													m.getInverse(m);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												constraintTargetsI.push(m);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											constraintMats = new Map();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getConstraintsFromParentRelative(cs: Array<TConstraint>) {
							 | 
						||
| 
								 | 
							
										// Init constraints
							 | 
						||
| 
								 | 
							
										if (constraintTargets == null) {
							 | 
						||
| 
								 | 
							
											constraintTargets = [];
							 | 
						||
| 
								 | 
							
											constraintTargetsI = [];
							 | 
						||
| 
								 | 
							
											// MeshObject -> ArmatureObject -> Collection/Empty
							 | 
						||
| 
								 | 
							
											var conParent = armatureObject.parent;
							 | 
						||
| 
								 | 
							
											if (conParent == null) return;
							 | 
						||
| 
								 | 
							
											for (c in cs) {
							 | 
						||
| 
								 | 
							
												var o = conParent.getChild(c.target);
							 | 
						||
| 
								 | 
							
												constraintTargets.push(o);
							 | 
						||
| 
								 | 
							
												var m: Mat4 = null;
							 | 
						||
| 
								 | 
							
												if (o != null) {
							 | 
						||
| 
								 | 
							
													m = Mat4.identity().setFrom(o.transform.world);
							 | 
						||
| 
								 | 
							
													m.getInverse(m);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												constraintTargetsI.push(m);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											constraintMats = new Map();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function updateConstraints() {
							 | 
						||
| 
								 | 
							
										if (data == null) return;
							 | 
						||
| 
								 | 
							
										var cs = data.raw.skin.constraints;
							 | 
						||
| 
								 | 
							
										if (cs == null) return;
							 | 
						||
| 
								 | 
							
										if (relativeBoneConstraints) {
							 | 
						||
| 
								 | 
							
											getConstraintsFromParentRelative(cs);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											getConstraintsFromScene(cs);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										// Update matrices
							 | 
						||
| 
								 | 
							
										for (i in 0...cs.length) {
							 | 
						||
| 
								 | 
							
											var c = cs[i];
							 | 
						||
| 
								 | 
							
											var bone = getBone(c.bone);
							 | 
						||
| 
								 | 
							
											if (bone == null) continue;
							 | 
						||
| 
								 | 
							
											var o = constraintTargets[i];
							 | 
						||
| 
								 | 
							
											if (o == null) continue;
							 | 
						||
| 
								 | 
							
											if (c.type == "CHILD_OF") {
							 | 
						||
| 
								 | 
							
												var m = constraintMats.get(bone);
							 | 
						||
| 
								 | 
							
												if (m == null) {
							 | 
						||
| 
								 | 
							
													m = Mat4.identity();
							 | 
						||
| 
								 | 
							
													constraintMats.set(bone, m);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												m.setFrom(armatureObject.transform.world); // Armature transform
							 | 
						||
| 
								 | 
							
												m.multmat(constraintTargetsI[i]); // Roll back initial hitbox transform
							 | 
						||
| 
								 | 
							
												m.multmat(o.transform.world); // Current hitbox transform
							 | 
						||
| 
								 | 
							
												m1.getInverse(armatureObject.transform.world); // Roll back armature transform
							 | 
						||
| 
								 | 
							
												m.multmat(m1);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var onUpdates: Array<Void->Void> = null;
							 | 
						||
| 
								 | 
							
									public function notifyOnUpdate(f: Void->Void) {
							 | 
						||
| 
								 | 
							
										if (onUpdates == null) onUpdates = [];
							 | 
						||
| 
								 | 
							
										onUpdates.push(f);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function removeUpdate(f: Void->Void) {
							 | 
						||
| 
								 | 
							
										onUpdates.remove(f);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									override public function updateActionTrack(sampler: ActionSampler) {
							 | 
						||
| 
								 | 
							
										if(sampler.paused) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(! sampler.actionDataInit) {
							 | 
						||
| 
								 | 
							
											var bones = getAction(sampler.action);
							 | 
						||
| 
								 | 
							
											sampler.setBoneAction(bones);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										var bones = sampler.getBoneAction();
							 | 
						||
| 
								 | 
							
										for(b in bones){
							 | 
						||
| 
								 | 
							
											if (b.anim != null) {
							 | 
						||
| 
								 | 
							
												updateTrack(b.anim, sampler);
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function sampleAction(sampler: ActionSampler, actionMats: Array<Mat4>) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(! sampler.actionDataInit) {
							 | 
						||
| 
								 | 
							
											var bones = getAction(sampler.action);
							 | 
						||
| 
								 | 
							
											sampler.setBoneAction(bones);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										var bones = sampler.getBoneAction();
							 | 
						||
| 
								 | 
							
										actionMats[skeletonBones.length].setIdentity();
							 | 
						||
| 
								 | 
							
										var rootMotionEnabled = sampler.rootMotionPos || sampler.rootMotionRot;
							 | 
						||
| 
								 | 
							
										for (i in 0...bones.length) {
							 | 
						||
| 
								 | 
							
											if (i == rootMotionIndex && rootMotionEnabled){
							 | 
						||
| 
								 | 
							
												updateAnimSampledRootMotion(bones[i].anim, actionMats[i], actionMats[skeletonBones.length], sampler);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else {
							 | 
						||
| 
								 | 
							
												updateAnimSampled(bones[i].anim, actionMats[i], sampler);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function updateAnimSampled(anim: TAnimation, mm: Mat4, sampler: ActionSampler) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(anim == null) return;
							 | 
						||
| 
								 | 
							
										var track = anim.tracks[0];
							 | 
						||
| 
								 | 
							
										var sign = sampler.speed > 0 ? 1 : -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var t = sampler.time;
							 | 
						||
| 
								 | 
							
										//t = t < 0 ? 0.1 : t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var ti = sampler.offset;
							 | 
						||
| 
								 | 
							
										//ti = ti < 0 ? 1 : ti;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										interpolateSample(track, mm, t, ti, sign);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function updateAnimSampledRootMotion(anim: TAnimation, mm: Mat4, rm: Mat4, sampler: ActionSampler) {
							 | 
						||
| 
								 | 
							
										if(anim == null) return;
							 | 
						||
| 
								 | 
							
										var track = anim.tracks[0];
							 | 
						||
| 
								 | 
							
										var sign = sampler.speed > 0 ? 1 : -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var t0 = speed > 0 ? 0 : track.frames.length - 1;
							 | 
						||
| 
								 | 
							
										var t = sampler.time;
							 | 
						||
| 
								 | 
							
										var ti = sampler.offset;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(sampler.trackEnd || !sampler.cacheInit) {
							 | 
						||
| 
								 | 
							
											interpolateSample(track, bm, t, ti, sign);
							 | 
						||
| 
								 | 
							
											//bm.setF32(track.values, t0);
							 | 
						||
| 
								 | 
							
											sampler.setActionCache(bm);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Interpolated action for current frame
							 | 
						||
| 
								 | 
							
										interpolateSample(track, m1, t, ti, sign);
							 | 
						||
| 
								 | 
							
										// Action at first frame
							 | 
						||
| 
								 | 
							
										m.setF32(track.values, t0);
							 | 
						||
| 
								 | 
							
										// Action at previous frame
							 | 
						||
| 
								 | 
							
										sampler.getActionCache(m2);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										m.decompose(vpos, q1, vscl);
							 | 
						||
| 
								 | 
							
										m1.decompose(vpos2, q2, vscl2);
							 | 
						||
| 
								 | 
							
										m2.decompose(vpos3, q3, vscl3);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Compose
							 | 
						||
| 
								 | 
							
										if(sampler.rootMotionRot) {
							 | 
						||
| 
								 | 
							
											// Bone matrix
							 | 
						||
| 
								 | 
							
											mm.fromQuat(q1);
							 | 
						||
| 
								 | 
							
											mm.scale(vscl2);
							 | 
						||
| 
								 | 
							
											// Root motion matrix
							 | 
						||
| 
								 | 
							
											q2.mult(q4.inverse(q3));
							 | 
						||
| 
								 | 
							
											rm.fromQuat(q2);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											// Bone matrix
							 | 
						||
| 
								 | 
							
											mm.fromQuat(q2);
							 | 
						||
| 
								 | 
							
											mm.scale(vscl2);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(sampler.rootMotionPos) {
							 | 
						||
| 
								 | 
							
											// Bone matrix
							 | 
						||
| 
								 | 
							
											mm._30 = vpos.x;
							 | 
						||
| 
								 | 
							
											mm._31 = vpos.y;
							 | 
						||
| 
								 | 
							
											mm._32 = vpos.z;
							 | 
						||
| 
								 | 
							
											// Root motion matrix
							 | 
						||
| 
								 | 
							
											rm._30 = vpos2.x - vpos3.x;
							 | 
						||
| 
								 | 
							
											rm._31 = vpos2.y - vpos3.y;
							 | 
						||
| 
								 | 
							
											rm._32 = vpos2.z - vpos3.z;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											// Bone matrix
							 | 
						||
| 
								 | 
							
											mm._30 = vpos2.x;
							 | 
						||
| 
								 | 
							
											mm._31 = vpos2.y;
							 | 
						||
| 
								 | 
							
											mm._32 = vpos2.z;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										sampler.setActionCache(m1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function interpolateSample(track: TTrack, m: Mat4, t: FastFloat, ti: Int, sign: Int) {
							 | 
						||
| 
								 | 
							
										var t1 = track.frames[ti] * frameTime;
							 | 
						||
| 
								 | 
							
										var t2 = track.frames[ti + sign] * frameTime;
							 | 
						||
| 
								 | 
							
										var s: FastFloat = (t - t1) / (t2 - t1); // Linear
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										m1.setF32(track.values, ti * 16); // Offset to 4x4 matrix array
							 | 
						||
| 
								 | 
							
										m2.setF32(track.values, (ti + sign) * 16);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Decompose
							 | 
						||
| 
								 | 
							
										m1.decompose(vpos, q1, vscl);
							 | 
						||
| 
								 | 
							
										m2.decompose(vpos2, q2, vscl2);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Lerp
							 | 
						||
| 
								 | 
							
										v1.lerp(vpos, vpos2, s);
							 | 
						||
| 
								 | 
							
										v2.lerp(vscl, vscl2, s);
							 | 
						||
| 
								 | 
							
										q3.lerp(q1, q2, s);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Compose
							 | 
						||
| 
								 | 
							
										m.fromQuat(q3);
							 | 
						||
| 
								 | 
							
										m.scale(v2);
							 | 
						||
| 
								 | 
							
										m._30 = v1.x;
							 | 
						||
| 
								 | 
							
										m._31 = v1.y;
							 | 
						||
| 
								 | 
							
										m._32 = v1.z;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function updateBonesOnly() {
							 | 
						||
| 
								 | 
							
										if (boneChildren != null) {
							 | 
						||
| 
								 | 
							
											for (i in 0...skeletonBones.length) {
							 | 
						||
| 
								 | 
							
												var b = skeletonBones[i]; // TODO: blendTime > 0
							 | 
						||
| 
								 | 
							
												m.setFrom(matsFast[i]);
							 | 
						||
| 
								 | 
							
												updateBoneChildren(b, m);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function updateSkinGpu() {
							 | 
						||
| 
								 | 
							
										var bones = skeletonBones;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Update skin buffer
							 | 
						||
| 
								 | 
							
										for (i in 0...bones.length) {
							 | 
						||
| 
								 | 
							
											if (constraintMats != null) {
							 | 
						||
| 
								 | 
							
												var m = constraintMats.get(bones[i]);
							 | 
						||
| 
								 | 
							
												if (m != null) {
							 | 
						||
| 
								 | 
							
													updateSkinBuffer(m, i);
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											m.setFrom(matsFast[i]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (absMats != null && i < absMats.length) absMats[i].setFrom(m);
							 | 
						||
| 
								 | 
							
											if (boneChildren != null) updateBoneChildren(bones[i], m);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											m.multmats(m, data.geom.skeletonTransformsI[i]);
							 | 
						||
| 
								 | 
							
											updateSkinBuffer(m, i);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function updateSkinBuffer(m: Mat4, i: Int) {
							 | 
						||
| 
								 | 
							
										// Dual quat skinning
							 | 
						||
| 
								 | 
							
										m.decompose(vpos, q1, vscl);
							 | 
						||
| 
								 | 
							
										q1.normalize();
							 | 
						||
| 
								 | 
							
										q2.set(vpos.x, vpos.y, vpos.z, 0.0);
							 | 
						||
| 
								 | 
							
										q2.multquats(q2, q1);
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12] = q1.x; // Real
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 1] = q1.y;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 2] = q1.z;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 3] = q1.w;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 4] = q2.x * 0.5; // Dual
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 5] = q2.y * 0.5;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 6] = q2.z * 0.5;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 7] = q2.w * 0.5;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 8] = vscl.x;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 9] = vscl.y;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 10] = vscl.z;
							 | 
						||
| 
								 | 
							
										skinBuffer[i * 12 + 11] = 1.0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public override function getTotalFrames(sampler: ActionSampler): Int {
							 | 
						||
| 
								 | 
							
										var bones = getAction(sampler.action);
							 | 
						||
| 
								 | 
							
										var track = bones[0].anim.tracks[0];
							 | 
						||
| 
								 | 
							
										return Std.int(track.frames[track.frames.length - 1] - track.frames[0]);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function getBone(name: String): TObj {
							 | 
						||
| 
								 | 
							
										if (skeletonBones == null) return null;
							 | 
						||
| 
								 | 
							
										for (b in skeletonBones) if (b.name == name) return b;
							 | 
						||
| 
								 | 
							
										return null;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getBoneIndex(bone: TObj, bones: Array<TObj> = null): Int {
							 | 
						||
| 
								 | 
							
										if (bones == null) bones = skeletonBones;
							 | 
						||
| 
								 | 
							
										if (bones != null) for (i in 0...bones.length) if (bones[i] == bone) return i;
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function getBoneMat(actionMats: Array<Mat4>, bone: TObj): Mat4 {
							 | 
						||
| 
								 | 
							
										return actionMats != null ? actionMats[getBoneIndex(bone)] : null;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getWorldMat(actionMats: Array<Mat4>, bone: TObj): Mat4 {
							 | 
						||
| 
								 | 
							
										if (applyParent == null) {
							 | 
						||
| 
								 | 
							
											applyParent = [];
							 | 
						||
| 
								 | 
							
											for (m in actionMats) applyParent.push(true);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var i = getBoneIndex(bone);
							 | 
						||
| 
								 | 
							
										wm.setFrom(actionMats[i]);
							 | 
						||
| 
								 | 
							
										multParents(wm, i, skeletonBones, actionMats);
							 | 
						||
| 
								 | 
							
										return wm;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getRootMotionWorldMat(actionMats: Array<Mat4>, bone: TObj): Mat4 {
							 | 
						||
| 
								 | 
							
										// Get root motion bone index
							 | 
						||
| 
								 | 
							
										var i = getBoneIndex(bone);
							 | 
						||
| 
								 | 
							
										// Store current bone matrix in temp
							 | 
						||
| 
								 | 
							
										var tempMat = Mat4.identity().setFrom(actionMats[i]);
							 | 
						||
| 
								 | 
							
										// Move root motion cache to bone position
							 | 
						||
| 
								 | 
							
										actionMats[i].setFrom(actionMats[skeletonBones.length]);
							 | 
						||
| 
								 | 
							
										// Calculate world matrix
							 | 
						||
| 
								 | 
							
										wm.setFrom(getWorldMat(actionMats, bone));
							 | 
						||
| 
								 | 
							
										// Revert to old value
							 | 
						||
| 
								 | 
							
										actionMats[i].setFrom(tempMat);
							 | 
						||
| 
								 | 
							
										return wm;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Returns bone matrix in world space
							 | 
						||
| 
								 | 
							
									public function getAbsWorldMat(actionMats: Array<Mat4>, bone: TObj): Mat4 {
							 | 
						||
| 
								 | 
							
										var wm = getWorldMat(actionMats, bone);
							 | 
						||
| 
								 | 
							
										wm.multmat(armatureObject.transform.world);
							 | 
						||
| 
								 | 
							
										return wm;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Returns an array of bone matrices in world space
							 | 
						||
| 
								 | 
							
									public function getWorldMatsFast(actionMats: Array<Mat4>, tip: TObj, chainLength: Int): Array<Mat4> {
							 | 
						||
| 
								 | 
							
										var wmArray: Array<Mat4> = [];
							 | 
						||
| 
								 | 
							
										var root = tip;
							 | 
						||
| 
								 | 
							
										var numP = chainLength;
							 | 
						||
| 
								 | 
							
										for (i in 0...chainLength) {
							 | 
						||
| 
								 | 
							
											var wm = getAbsWorldMat(actionMats, root);
							 | 
						||
| 
								 | 
							
											wmArray[chainLength - 1 - i] = wm.clone();
							 | 
						||
| 
								 | 
							
											root = root.parent;
							 | 
						||
| 
								 | 
							
											numP--;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Root bone at [0]
							 | 
						||
| 
								 | 
							
										return wmArray;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Set bone transforms in world space
							 | 
						||
| 
								 | 
							
									public function setBoneMatFromWorldMat(actionMats: Array<Mat4>, wm: Mat4, bone: TObj) {
							 | 
						||
| 
								 | 
							
										var invMat = Mat4.identity();
							 | 
						||
| 
								 | 
							
										var tempMat = wm.clone();
							 | 
						||
| 
								 | 
							
										invMat.getInverse(armatureObject.transform.world);
							 | 
						||
| 
								 | 
							
										tempMat.multmat(invMat);
							 | 
						||
| 
								 | 
							
										var bones: Array<TObj> = [];
							 | 
						||
| 
								 | 
							
										var pBone = bone;
							 | 
						||
| 
								 | 
							
										while (pBone.parent != null) {
							 | 
						||
| 
								 | 
							
											bones.push(pBone.parent);
							 | 
						||
| 
								 | 
							
											pBone = pBone.parent;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for (i in 0...bones.length) {
							 | 
						||
| 
								 | 
							
											var x = bones.length - 1;
							 | 
						||
| 
								 | 
							
											invMat.getInverse(getBoneMat(actionMats, bones[x - i]));
							 | 
						||
| 
								 | 
							
											tempMat.multmat(invMat);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										getBoneMat(actionMats, bone).setFrom(tempMat);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function blendAction(actionMats1: Array<Mat4>, actionMats2: Array<Mat4>, resultMat: Array<Mat4>, factor: FastFloat = 0.0, layerMask: Int = -1, threshold: FastFloat = 0.1) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(factor < threshold) {
							 | 
						||
| 
								 | 
							
											for(i in 0...actionMats1.length){
							 | 
						||
| 
								 | 
							
												// Use Action 1
							 | 
						||
| 
								 | 
							
												resultMat[i].setFrom(actionMats1[i]);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else if(factor > 1.0 - threshold){
							 | 
						||
| 
								 | 
							
											for(i in 0...actionMats2.length){
							 | 
						||
| 
								 | 
							
												// If root motion cache -> use root motion index, else use current index
							 | 
						||
| 
								 | 
							
												var j = i == skeletonBones.length ? rootMotionIndex : i;
							 | 
						||
| 
								 | 
							
												// Skip if root motion is disabled
							 | 
						||
| 
								 | 
							
												if(j < 0) continue;
							 | 
						||
| 
								 | 
							
												// Use Action 2
							 | 
						||
| 
								 | 
							
												if(skeletonBones[j].bone_layers[layerMask] || layerMask < 0){
							 | 
						||
| 
								 | 
							
													resultMat[i].setFrom(actionMats2[i]);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												// Use Action 1 if not in layer
							 | 
						||
| 
								 | 
							
												else {
							 | 
						||
| 
								 | 
							
													resultMat[i].setFrom(actionMats1[i]);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											for(i in 0...actionMats1.length){
							 | 
						||
| 
								 | 
							
												// If root motion cache -> use root motion index, else use current index
							 | 
						||
| 
								 | 
							
												var j = i == skeletonBones.length ? rootMotionIndex : i;
							 | 
						||
| 
								 | 
							
												// Skip if root motion is disabled
							 | 
						||
| 
								 | 
							
												if(j < 0) continue;
							 | 
						||
| 
								 | 
							
												// Blend
							 | 
						||
| 
								 | 
							
												if(skeletonBones[j].bone_layers[j] || layerMask < 0) {
							 | 
						||
| 
								 | 
							
													// Decompose
							 | 
						||
| 
								 | 
							
													m.setFrom(actionMats1[i]);
							 | 
						||
| 
								 | 
							
													m1.setFrom(actionMats2[i]);
							 | 
						||
| 
								 | 
							
													m.decompose(vpos, q1, vscl);
							 | 
						||
| 
								 | 
							
													m1.decompose(vpos2, q2, vscl2);
							 | 
						||
| 
								 | 
							
													// Lerp
							 | 
						||
| 
								 | 
							
													v1.lerp(vpos, vpos2, factor);
							 | 
						||
| 
								 | 
							
													v2.lerp(vscl, vscl2, factor);
							 | 
						||
| 
								 | 
							
													q3.lerp(q1, q2, factor);
							 | 
						||
| 
								 | 
							
													// Compose
							 | 
						||
| 
								 | 
							
													m2.fromQuat(q3);
							 | 
						||
| 
								 | 
							
													m2.scale(v2);
							 | 
						||
| 
								 | 
							
													m2._30 = v1.x;
							 | 
						||
| 
								 | 
							
													m2._31 = v1.y;
							 | 
						||
| 
								 | 
							
													m2._32 = v1.z;
							 | 
						||
| 
								 | 
							
													// Lock root motion to one action on certain axis to conserve looping
							 | 
						||
| 
								 | 
							
													if(i == skeletonBones.length){
							 | 
						||
| 
								 | 
							
														m2._30 = rootMotionLockX ? vpos2.x : m2._30;
							 | 
						||
| 
								 | 
							
														m2._31 = rootMotionLockY ? vpos2.y : m2._31;
							 | 
						||
| 
								 | 
							
														m2._32 = rootMotionLockZ ? vpos2.z : m2._32;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
													// Return Result
							 | 
						||
| 
								 | 
							
													resultMat[i].setFrom(m2);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												// Use Action 1 if not in layer
							 | 
						||
| 
								 | 
							
												else {
							 | 
						||
| 
								 | 
							
													resultMat[i].setFrom(actionMats1[i]);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function additiveBlendAction(baseActionMats: Array<Mat4>, addActionMats: Array<Mat4>, restPoseMats: Array<Mat4>, resultMat: Array<Mat4>, factor: FastFloat, layerMask: Int = -1, threshold: FastFloat = 0.1) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(factor < threshold) {
							 | 
						||
| 
								 | 
							
											for(i in 0...baseActionMats.length){
							 | 
						||
| 
								 | 
							
												resultMat[i].setFrom(baseActionMats[i]);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else{
							 | 
						||
| 
								 | 
							
											for(i in 0...baseActionMats.length){
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if(skeletonBones[i].bone_layers[layerMask] || layerMask < 0) {
							 | 
						||
| 
								 | 
							
													// Decompose
							 | 
						||
| 
								 | 
							
													m.setFrom(baseActionMats[i]);
							 | 
						||
| 
								 | 
							
													m1.setFrom(addActionMats[i]);
							 | 
						||
| 
								 | 
							
													bm.setFrom(restPoseMats[i]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													m.decompose(vpos, q1, vscl);
							 | 
						||
| 
								 | 
							
													m1.decompose(vpos2, q2, vscl2);
							 | 
						||
| 
								 | 
							
													bm.decompose(vpos3, q3, vscl3);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Add Transforms
							 | 
						||
| 
								 | 
							
													v1.setFrom(vpos);
							 | 
						||
| 
								 | 
							
													v2.setFrom(vpos2);
							 | 
						||
| 
								 | 
							
													v2.sub(vpos3);
							 | 
						||
| 
								 | 
							
													v2.mult(factor);
							 | 
						||
| 
								 | 
							
													v1.add(v2);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Add Scales
							 | 
						||
| 
								 | 
							
													vscl2.mult(factor);
							 | 
						||
| 
								 | 
							
													v2.set(1-factor, 1-factor, 1-factor, 1);
							 | 
						||
| 
								 | 
							
													v2.add(vscl2);
							 | 
						||
| 
								 | 
							
													v2.x *= vscl.x;
							 | 
						||
| 
								 | 
							
													v2.y *= vscl.y;
							 | 
						||
| 
								 | 
							
													v2.z *= vscl.z;
							 | 
						||
| 
								 | 
							
													v2.w = 1.0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Add rotations
							 | 
						||
| 
								 | 
							
													q2.lerp(q3, q2, factor);
							 | 
						||
| 
								 | 
							
													wm.fromQuat(q3);
							 | 
						||
| 
								 | 
							
													wm.getInverse(wm);
							 | 
						||
| 
								 | 
							
													q3.fromMat(wm).normalize();
							 | 
						||
| 
								 | 
							
													q3.multquats(q3, q2);
							 | 
						||
| 
								 | 
							
													q3.multquats(q1, q3);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Compose
							 | 
						||
| 
								 | 
							
													m2.fromQuat(q3);
							 | 
						||
| 
								 | 
							
													m2.scale(v2);
							 | 
						||
| 
								 | 
							
													m2._30 = v1.x;
							 | 
						||
| 
								 | 
							
													m2._31 = v1.y;
							 | 
						||
| 
								 | 
							
													m2._32 = v1.z;
							 | 
						||
| 
								 | 
							
													// Return Result
							 | 
						||
| 
								 | 
							
													resultMat[i].setFrom(m2);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else{
							 | 
						||
| 
								 | 
							
													resultMat[i].setFrom(baseActionMats[i]);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function solveIK(actionMats: Array<Mat4>, effector: TObj, goal: Vec4, precision = 0.01, maxIterations = 100, chainLenght = 100, pole: Vec4 = null, rollAngle = 0.0) {
							 | 
						||
| 
								 | 
							
										// Array of bones to solve IK for, effector at 0
							 | 
						||
| 
								 | 
							
										var bones: Array<TObj> = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Array of bones lengths, effector length at 0
							 | 
						||
| 
								 | 
							
										var lengths: Array<FastFloat> = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Array of bones matrices in world coordinates, effector at 0
							 | 
						||
| 
								 | 
							
										var boneWorldMats: Array<Mat4>;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var tempLoc = new Vec4();
							 | 
						||
| 
								 | 
							
										var tempRot = new Quat();
							 | 
						||
| 
								 | 
							
										var tempRot2 = new Quat();
							 | 
						||
| 
								 | 
							
										var tempScl = new Vec4();
							 | 
						||
| 
								 | 
							
										var roll = new Quat().fromEuler(0, rollAngle, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Store all bones and lengths in array
							 | 
						||
| 
								 | 
							
										var tip = effector;
							 | 
						||
| 
								 | 
							
										bones.push(tip);
							 | 
						||
| 
								 | 
							
										var root = tip;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										while (root.parent != null) {
							 | 
						||
| 
								 | 
							
											if (bones.length > chainLenght - 1) break;
							 | 
						||
| 
								 | 
							
											bones.push(root.parent);
							 | 
						||
| 
								 | 
							
											root = root.parent;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get all bone mats in world space
							 | 
						||
| 
								 | 
							
										boneWorldMats = getWorldMatsFast(actionMats, effector, bones.length);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var tempIndex = 0;
							 | 
						||
| 
								 | 
							
										for(b in bones){
							 | 
						||
| 
								 | 
							
											lengths.push(b.bone_length * boneWorldMats[boneWorldMats.length - 1 - tempIndex].getScale().x);
							 | 
						||
| 
								 | 
							
											tempIndex++;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Root bone
							 | 
						||
| 
								 | 
							
										root = bones[bones.length - 1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// World matrix of root bone
							 | 
						||
| 
								 | 
							
										var rootWorldMat = getAbsWorldMat(actionMats, root).clone();
							 | 
						||
| 
								 | 
							
										// Distance from root to goal
							 | 
						||
| 
								 | 
							
										var dist = Vec4.distance(goal, rootWorldMat.getLoc());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Total bones length
							 | 
						||
| 
								 | 
							
										var totalLength: FastFloat = 0.0;
							 | 
						||
| 
								 | 
							
										for (l in lengths) totalLength += l;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Unreachable distance
							 | 
						||
| 
								 | 
							
										if (dist > totalLength) {
							 | 
						||
| 
								 | 
							
											// Calculate unit vector from root to goal
							 | 
						||
| 
								 | 
							
											var newLook = goal.clone();
							 | 
						||
| 
								 | 
							
											newLook.sub(rootWorldMat.getLoc());
							 | 
						||
| 
								 | 
							
											newLook.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Rotate root bone to point at goal
							 | 
						||
| 
								 | 
							
											rootWorldMat.decompose(tempLoc, tempRot, tempScl);
							 | 
						||
| 
								 | 
							
											tempRot2.fromTo(rootWorldMat.look().normalize(), newLook);
							 | 
						||
| 
								 | 
							
											tempRot2.mult(tempRot);
							 | 
						||
| 
								 | 
							
											tempRot2.mult(roll);
							 | 
						||
| 
								 | 
							
											rootWorldMat.compose(tempLoc, tempRot2, tempScl);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Set bone matrix in local space from world space
							 | 
						||
| 
								 | 
							
											setBoneMatFromWorldMat(actionMats, rootWorldMat, root);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Set child bone rotations to zero
							 | 
						||
| 
								 | 
							
											for (i in 0...bones.length - 1) {
							 | 
						||
| 
								 | 
							
												getBoneMat(actionMats, bones[i]).decompose(tempLoc, tempRot, tempScl);
							 | 
						||
| 
								 | 
							
												getBoneMat(actionMats, bones[i]).compose(tempLoc, roll, tempScl);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Array of bone locations in world space, root location at [0]
							 | 
						||
| 
								 | 
							
										var boneWorldLocs: Array<Vec4> = [];
							 | 
						||
| 
								 | 
							
										for (b in boneWorldMats) boneWorldLocs.push(b.getLoc());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Solve FABRIK
							 | 
						||
| 
								 | 
							
										var vec = new Vec4();
							 | 
						||
| 
								 | 
							
										var startLoc = boneWorldLocs[0].clone();
							 | 
						||
| 
								 | 
							
										var l = boneWorldLocs.length;
							 | 
						||
| 
								 | 
							
										var testLength = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for (iter in 0...maxIterations) {
							 | 
						||
| 
								 | 
							
											// Backward
							 | 
						||
| 
								 | 
							
											vec.setFrom(goal);
							 | 
						||
| 
								 | 
							
											vec.sub(boneWorldLocs[l - 1]);
							 | 
						||
| 
								 | 
							
											vec.normalize();
							 | 
						||
| 
								 | 
							
											vec.mult(lengths[0]);
							 | 
						||
| 
								 | 
							
											boneWorldLocs[l - 1].setFrom(goal);
							 | 
						||
| 
								 | 
							
											boneWorldLocs[l - 1].sub(vec);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for (j in 1...l) {
							 | 
						||
| 
								 | 
							
												vec.setFrom(boneWorldLocs[l - 1 - j]);
							 | 
						||
| 
								 | 
							
												vec.sub(boneWorldLocs[l - j]);
							 | 
						||
| 
								 | 
							
												vec.normalize();
							 | 
						||
| 
								 | 
							
												vec.mult(lengths[j]);
							 | 
						||
| 
								 | 
							
												boneWorldLocs[l - 1 - j].setFrom(boneWorldLocs[l - j]);
							 | 
						||
| 
								 | 
							
												boneWorldLocs[l - 1 - j].add(vec);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Forward
							 | 
						||
| 
								 | 
							
											boneWorldLocs[0].setFrom(startLoc);
							 | 
						||
| 
								 | 
							
											for (j in 1...l) {
							 | 
						||
| 
								 | 
							
												vec.setFrom(boneWorldLocs[j]);
							 | 
						||
| 
								 | 
							
												vec.sub(boneWorldLocs[j - 1]);
							 | 
						||
| 
								 | 
							
												vec.normalize();
							 | 
						||
| 
								 | 
							
												vec.mult(lengths[l - j]);
							 | 
						||
| 
								 | 
							
												boneWorldLocs[j].setFrom(boneWorldLocs[j - 1]);
							 | 
						||
| 
								 | 
							
												boneWorldLocs[j].add(vec);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (Vec4.distance(boneWorldLocs[l - 1], goal) - lengths[0] <= precision) break;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Pole rotation implementation
							 | 
						||
| 
								 | 
							
										if (pole != null) {
							 | 
						||
| 
								 | 
							
											for (i in 1...boneWorldLocs.length - 1) {
							 | 
						||
| 
								 | 
							
												boneWorldLocs[i] = moveTowardPole(boneWorldLocs[i - 1].clone(), boneWorldLocs[i].clone(), boneWorldLocs[i + 1].clone(), pole.clone());
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Correct rotations
							 | 
						||
| 
								 | 
							
										// Applying locations and rotations
							 | 
						||
| 
								 | 
							
										var tempLook = new Vec4();
							 | 
						||
| 
								 | 
							
										var tempLoc2 = new Vec4();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for (i in 0...l - 1){
							 | 
						||
| 
								 | 
							
											// Decompose matrix
							 | 
						||
| 
								 | 
							
											boneWorldMats[i].decompose(tempLoc, tempRot, tempScl);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Rotate to point to parent bone
							 | 
						||
| 
								 | 
							
											tempLoc2.setFrom(boneWorldLocs[i + 1]);
							 | 
						||
| 
								 | 
							
											tempLoc2.sub(boneWorldLocs[i]);
							 | 
						||
| 
								 | 
							
											tempLoc2.normalize();
							 | 
						||
| 
								 | 
							
											tempLook.setFrom(boneWorldMats[i].look());
							 | 
						||
| 
								 | 
							
											tempLook.normalize();
							 | 
						||
| 
								 | 
							
											tempRot2.fromTo(tempLook, tempLoc2);
							 | 
						||
| 
								 | 
							
											tempRot2.mult(tempRot);
							 | 
						||
| 
								 | 
							
											tempRot2.mult(roll);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Compose matrix with new rotation and location
							 | 
						||
| 
								 | 
							
											boneWorldMats[i].compose(boneWorldLocs[i], tempRot2, tempScl);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Set bone matrix in local space from world space
							 | 
						||
| 
								 | 
							
											setBoneMatFromWorldMat(actionMats, boneWorldMats[i], bones[bones.length - 1 - i]);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Decompose matrix
							 | 
						||
| 
								 | 
							
										boneWorldMats[l - 1].decompose(tempLoc, tempRot, tempScl);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Rotate to point to goal
							 | 
						||
| 
								 | 
							
										tempLoc2.setFrom(goal);
							 | 
						||
| 
								 | 
							
										tempLoc2.sub(tempLoc);
							 | 
						||
| 
								 | 
							
										tempLoc2.normalize();
							 | 
						||
| 
								 | 
							
										tempLook.setFrom(boneWorldMats[l - 1].look());
							 | 
						||
| 
								 | 
							
										tempLook.normalize();
							 | 
						||
| 
								 | 
							
										tempRot2.fromTo(tempLook, tempLoc2);
							 | 
						||
| 
								 | 
							
										tempRot2.mult(tempRot);
							 | 
						||
| 
								 | 
							
										tempRot2.mult(roll);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Compose matrix with new rotation and location
							 | 
						||
| 
								 | 
							
										boneWorldMats[l - 1].compose(boneWorldLocs[l - 1], tempRot2, tempScl);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Set bone matrix in local space from world space
							 | 
						||
| 
								 | 
							
										setBoneMatFromWorldMat(actionMats, boneWorldMats[l - 1], bones[0]);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function moveTowardPole(bone0Pos: Vec4, bone1Pos: Vec4, bone2Pos: Vec4, polePos: Vec4): Vec4 {
							 | 
						||
| 
								 | 
							
										// Setup projection plane at current bone's parent
							 | 
						||
| 
								 | 
							
										var plane = new Plane();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Plane normal from parent of current bone to child of current bone
							 | 
						||
| 
								 | 
							
										var planeNormal = new Vec4().setFrom(bone2Pos);
							 | 
						||
| 
								 | 
							
										planeNormal.sub(bone0Pos);
							 | 
						||
| 
								 | 
							
										planeNormal.normalize();
							 | 
						||
| 
								 | 
							
										plane.set(planeNormal, bone0Pos);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Create and project ray from current bone to plane
							 | 
						||
| 
								 | 
							
										var rayPos = new Vec4();
							 | 
						||
| 
								 | 
							
										rayPos.setFrom(bone1Pos);
							 | 
						||
| 
								 | 
							
										var rayDir = new Vec4();
							 | 
						||
| 
								 | 
							
										rayDir.sub(planeNormal);
							 | 
						||
| 
								 | 
							
										rayDir.normalize();
							 | 
						||
| 
								 | 
							
										var rayBone = new Ray(rayPos, rayDir);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Projection point of current bone on plane
							 | 
						||
| 
								 | 
							
										// If pole does not project on the plane
							 | 
						||
| 
								 | 
							
										if (!rayBone.intersectsPlane(plane)) {
							 | 
						||
| 
								 | 
							
											rayBone.direction = planeNormal;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var bone1Proj = rayBone.intersectPlane(plane);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Create and project ray from pole to plane
							 | 
						||
| 
								 | 
							
										rayPos.setFrom(polePos);
							 | 
						||
| 
								 | 
							
										var rayPole = new Ray(rayPos, rayDir);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// If pole does not project on the plane
							 | 
						||
| 
								 | 
							
										if (!rayPole.intersectsPlane(plane)) {
							 | 
						||
| 
								 | 
							
											rayPole.direction = planeNormal;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Projection point of pole on plane
							 | 
						||
| 
								 | 
							
										var poleProj = rayPole.intersectPlane(plane);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Caclulate unit vectors from pole projection to parent bone
							 | 
						||
| 
								 | 
							
										var poleProjNormal = new Vec4();
							 | 
						||
| 
								 | 
							
										poleProjNormal.setFrom(bone0Pos);
							 | 
						||
| 
								 | 
							
										poleProjNormal.sub(poleProj);
							 | 
						||
| 
								 | 
							
										poleProjNormal.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calculate unit vector from current bone projection to parent bone
							 | 
						||
| 
								 | 
							
										var bone1ProjNormal = new Vec4();
							 | 
						||
| 
								 | 
							
										bone1ProjNormal.setFrom(bone0Pos);
							 | 
						||
| 
								 | 
							
										bone1ProjNormal.sub(bone1Proj);
							 | 
						||
| 
								 | 
							
										bone1ProjNormal.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calculate rotation quaternion
							 | 
						||
| 
								 | 
							
										var rotQuat = new Quat();
							 | 
						||
| 
								 | 
							
										rotQuat.fromTo(bone1ProjNormal, poleProjNormal);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Apply quaternion to current bone location
							 | 
						||
| 
								 | 
							
										var bone1Res = new Vec4().setFrom(bone1Pos);
							 | 
						||
| 
								 | 
							
										bone1Res.sub(bone0Pos);
							 | 
						||
| 
								 | 
							
										bone1Res.applyQuat(rotQuat);
							 | 
						||
| 
								 | 
							
										bone1Res.add(bone0Pos);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Return new location of current bone
							 | 
						||
| 
								 | 
							
										return bone1Res;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function solveTwoBoneIK(actionMats : Array<Mat4>, effector: TObj, goal: Vec4, pole: Vec4 = null, rollAngle = 0.0) {		
							 | 
						||
| 
								 | 
							
										var roll = new Quat().fromEuler(0, rollAngle, 0);
							 | 
						||
| 
								 | 
							
										var root = effector.parent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get bone transforms in world space
							 | 
						||
| 
								 | 
							
										var effectorMat = getAbsWorldMat(actionMats, effector).clone();
							 | 
						||
| 
								 | 
							
										var rootMat = getAbsWorldMat(actionMats, root).clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get bone lenghts
							 | 
						||
| 
								 | 
							
										var effectorLen = effector.bone_length * effectorMat.getScale().x;
							 | 
						||
| 
								 | 
							
										var rootLen = root.bone_length * rootMat.getScale().x;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get distance form root to goal
							 | 
						||
| 
								 | 
							
										var goalLen = Math.abs(Vec4.distance(rootMat.getLoc(), goal));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var totalLength = effectorLen + rootLen;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get tip location of effector bone
							 | 
						||
| 
								 | 
							
										var effectorTipPos = new Vec4().setFrom(effectorMat.look()).normalize();
							 | 
						||
| 
								 | 
							
										effectorTipPos.mult(effectorLen);
							 | 
						||
| 
								 | 
							
										effectorTipPos.add(effectorMat.getLoc());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get unit vector from root to effector tip
							 | 
						||
| 
								 | 
							
										var vectorRootEffector = new Vec4().setFrom(effectorTipPos).sub(rootMat.getLoc());
							 | 
						||
| 
								 | 
							
										vectorRootEffector.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get unit vector from root to goal
							 | 
						||
| 
								 | 
							
										var vectorGoal = new Vec4().setFrom(goal).sub(rootMat.getLoc());
							 | 
						||
| 
								 | 
							
										vectorGoal.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get unit vector of root bone
							 | 
						||
| 
								 | 
							
										var vectorRoot = new Vec4().setFrom(rootMat.look()).normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get unit vector of effector bone
							 | 
						||
| 
								 | 
							
										var vectorEffector = new Vec4().setFrom(effectorMat.look()).normalize();		
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										// Get dot product of vectors
							 | 
						||
| 
								 | 
							
										var dot = new Vec4().setFrom(vectorRootEffector).dot(vectorRoot);
							 | 
						||
| 
								 | 
							
										// Calmp between -1 and 1
							 | 
						||
| 
								 | 
							
										dot = dot < -1.0 ? -1.0 : dot > 1.0 ? 1.0 : dot;
							 | 
						||
| 
								 | 
							
										// Gat angle A1
							 | 
						||
| 
								 | 
							
										var angleA1 = Math.acos(dot);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get angle A2
							 | 
						||
| 
								 | 
							
										dot = new Vec4().setFrom(vectorRoot).mult(-1.0).dot(vectorEffector);
							 | 
						||
| 
								 | 
							
										dot = dot < -1.0 ? -1.0 : dot > 1.0 ? 1.0 : dot;
							 | 
						||
| 
								 | 
							
										var angleA2 = Math.acos(dot);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get angle A3
							 | 
						||
| 
								 | 
							
										dot = new Vec4().setFrom(vectorRootEffector).dot(vectorGoal);
							 | 
						||
| 
								 | 
							
										dot = dot < -1.0 ? -1.0 : dot > 1.0 ? 1.0 : dot;
							 | 
						||
| 
								 | 
							
										var angleA3 = Math.acos(dot);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get angle B1
							 | 
						||
| 
								 | 
							
										dot = (effectorLen * effectorLen - rootLen * rootLen - goalLen * goalLen) / (-2 * rootLen * goalLen);
							 | 
						||
| 
								 | 
							
										dot = dot < -1.0 ? -1.0 : dot > 1.0 ? 1.0 : dot;
							 | 
						||
| 
								 | 
							
										var angleB1 = Math.acos(dot);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Get angle B2
							 | 
						||
| 
								 | 
							
										dot = (goalLen * goalLen - rootLen * rootLen - effectorLen * effectorLen) / (-2 * rootLen * effectorLen);
							 | 
						||
| 
								 | 
							
										dot = dot < -1.0 ? -1.0 : dot > 1.0 ? 1.0 : dot;
							 | 
						||
| 
								 | 
							
										var angleB2 = Math.acos(dot);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Calculate rotation axes
							 | 
						||
| 
								 | 
							
										var axis0 = new Vec4().setFrom(vectorRootEffector).cross(vectorRoot).normalize();
							 | 
						||
| 
								 | 
							
										var axis1 = new Vec4().setFrom(vectorRootEffector).cross(vectorGoal).normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Apply rotations to effector bone
							 | 
						||
| 
								 | 
							
										vpos.setFrom(effectorMat.getLoc());
							 | 
						||
| 
								 | 
							
										effectorMat.setLoc(new Vec4());
							 | 
						||
| 
								 | 
							
										effectorMat.applyQuat(new Quat().fromAxisAngle(axis0, angleB2 - angleA2));
							 | 
						||
| 
								 | 
							
										effectorMat.setLoc(vpos);
							 | 
						||
| 
								 | 
							
										setBoneMatFromWorldMat(actionMats, effectorMat, effector);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Apply rotations to root bone
							 | 
						||
| 
								 | 
							
										vpos.setFrom(rootMat.getLoc());
							 | 
						||
| 
								 | 
							
										rootMat.setLoc(new Vec4());
							 | 
						||
| 
								 | 
							
										rootMat.applyQuat(new Quat().fromAxisAngle(axis0, angleB1 - angleA1));
							 | 
						||
| 
								 | 
							
										rootMat.applyQuat(new Quat().fromAxisAngle(axis1, angleA3));
							 | 
						||
| 
								 | 
							
										rootMat.setLoc(vpos);
							 | 
						||
| 
								 | 
							
										setBoneMatFromWorldMat(actionMats, rootMat, root);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Recalculate new effector matrix
							 | 
						||
| 
								 | 
							
										effectorMat.setFrom(getAbsWorldMat(actionMats, effector));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Check if pole present
							 | 
						||
| 
								 | 
							
										if((pole != null) && (goalLen < totalLength)) {
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
											// Calculate new effector tip position
							 | 
						||
| 
								 | 
							
											vscl.setFrom(effectorMat.look()).normalize();
							 | 
						||
| 
								 | 
							
											vscl.mult(effectorLen);
							 | 
						||
| 
								 | 
							
											vscl.add(effectorMat.getLoc());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Calculate new effector position from pole
							 | 
						||
| 
								 | 
							
											vpos2 = moveTowardPole(rootMat.getLoc(), effectorMat.getLoc(), vscl, pole);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Orient root bone to new effector position
							 | 
						||
| 
								 | 
							
											vpos.setFrom(rootMat.getLoc());
							 | 
						||
| 
								 | 
							
											rootMat.setLoc(new Vec4());
							 | 
						||
| 
								 | 
							
											vpos3.setFrom(vpos2).sub(vpos).normalize();
							 | 
						||
| 
								 | 
							
											rootMat.applyQuat(new Quat().fromTo(rootMat.look().normalize(), vpos3));
							 | 
						||
| 
								 | 
							
											rootMat.setLoc(vpos);
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Orient effector bone to new position
							 | 
						||
| 
								 | 
							
											vpos.setFrom(effectorMat.getLoc());
							 | 
						||
| 
								 | 
							
											effectorMat.setLoc(new Vec4());
							 | 
						||
| 
								 | 
							
											vpos3.setFrom(vscl).sub(vpos2).normalize();
							 | 
						||
| 
								 | 
							
											effectorMat.applyQuat(new Quat().fromTo(effectorMat.look().normalize(), vpos3));
							 | 
						||
| 
								 | 
							
											effectorMat.setLoc(vpos2);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Apply roll to root bone
							 | 
						||
| 
								 | 
							
										vpos.setFrom(rootMat.getLoc());
							 | 
						||
| 
								 | 
							
										rootMat.setLoc(new Vec4());
							 | 
						||
| 
								 | 
							
										rootMat.applyQuat(new Quat().fromAxisAngle(rootMat.look().normalize(), rollAngle));
							 | 
						||
| 
								 | 
							
										rootMat.setLoc(vpos);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Apply roll to effector bone
							 | 
						||
| 
								 | 
							
										vpos.setFrom(effectorMat.getLoc());
							 | 
						||
| 
								 | 
							
										effectorMat.setLoc(new Vec4());
							 | 
						||
| 
								 | 
							
										effectorMat.applyQuat(new Quat().fromAxisAngle(effectorMat.look().normalize(), rollAngle));
							 | 
						||
| 
								 | 
							
										effectorMat.setLoc(vpos);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Finally set root and effector matrices in local space
							 | 
						||
| 
								 | 
							
										setBoneMatFromWorldMat(actionMats, rootMat, root);
							 | 
						||
| 
								 | 
							
										setBoneMatFromWorldMat(actionMats, effectorMat, effector);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#end
							 |