forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
489
leenkx/Sources/leenkx/object/AnimationExtension.hx
Normal file
489
leenkx/Sources/leenkx/object/AnimationExtension.hx
Normal file
@ -0,0 +1,489 @@
|
||||
package leenkx.object;
|
||||
|
||||
import iron.math.Quat;
|
||||
import iron.Scene;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.App;
|
||||
import iron.system.Tween;
|
||||
import kha.FastFloat;
|
||||
import iron.math.Mat4;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Vec3;
|
||||
import iron.math.Vec2;
|
||||
import haxe.ds.Vector;
|
||||
import iron.object.Object;
|
||||
import iron.object.Animation;
|
||||
import iron.object.BoneAnimation;
|
||||
import iron.object.ObjectAnimation;
|
||||
|
||||
class AnimationExtension {
|
||||
|
||||
public static function solveIKBlend(boneAnimation: BoneAnimation, actionMats: Array<Mat4>, effector: TObj, goal: Vec4, precision = 0.01, maxIterations = 100, chainLenght = 100, pole: Vec4 = null, rollAngle = 0.0, influence = 0.0, layerMask: Null<Int> = null, threshold: FastFloat = 0.1) {
|
||||
|
||||
var matsBlend = boneAnimation.initMatsEmpty();
|
||||
|
||||
var i = 0;
|
||||
for (mat in matsBlend){
|
||||
mat.setFrom(actionMats[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
boneAnimation.solveIK(actionMats, effector, goal, precision, maxIterations, chainLenght, pole, rollAngle);
|
||||
boneAnimation.blendAction(matsBlend, actionMats, actionMats, influence, layerMask, threshold);
|
||||
}
|
||||
|
||||
public static function solveTwoBoneIKBlend(boneAnimation: BoneAnimation, actionMats: Array<Mat4>, effector: TObj, goal: Vec4, pole: Vec4 = null, rollAngle = 0.0, influence = 0.0, layerMask: Null<Int> = null, threshold: FastFloat = 0.1) {
|
||||
|
||||
var matsBlend = boneAnimation.initMatsEmpty();
|
||||
|
||||
var i = 0;
|
||||
for (mat in matsBlend){
|
||||
mat.setFrom(actionMats[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
boneAnimation.solveTwoBoneIK(actionMats, effector, goal, pole, rollAngle);
|
||||
boneAnimation.blendAction(matsBlend, actionMats, actionMats, influence, layerMask, threshold);
|
||||
}
|
||||
|
||||
static inline function sortWeights(vecs: Array<Vec2>, sampleVec: Vec2): Map<Int, Vec2> {
|
||||
var weightIndex: Array<WeightIndex> = [];
|
||||
var i = 0;
|
||||
for (vec in vecs){
|
||||
weightIndex.push({weight: Vec2.distance(vec, sampleVec), index: i} );
|
||||
i++;
|
||||
}
|
||||
|
||||
weightIndex.sort(sortCompare);
|
||||
|
||||
var weightsSorted = new Map<Int, Vec2>();
|
||||
for (i in 0...3) {
|
||||
var index = weightIndex[i].index;
|
||||
weightsSorted.set(index, vecs[index]);
|
||||
}
|
||||
|
||||
return weightsSorted;
|
||||
}
|
||||
|
||||
static inline function sortCompare(a: WeightIndex, b: WeightIndex): Int {
|
||||
return Reflect.compare(a.weight, b.weight);
|
||||
}
|
||||
|
||||
public static function getBlend2DWeights(actionCoords: Array<Vec2>, sampleCoords: Vec2): Map<Int, FastFloat> {
|
||||
|
||||
var weightsMap = sortWeights(actionCoords, sampleCoords);
|
||||
|
||||
var weights = new Vector<FastFloat>(3);
|
||||
var tempWeights = new Vector<FastFloat>(2);
|
||||
|
||||
// Gradient Band Interpolation
|
||||
var keys1 = weightsMap.keys();
|
||||
var i = 0;
|
||||
for (key1 in keys1){
|
||||
var v1 = new Vec2().setFrom(sampleCoords).sub(weightsMap[key1]);
|
||||
var k = 0;
|
||||
var keys2 = weightsMap.keys();
|
||||
for (key2 in keys2){
|
||||
if (key1 == key2) continue;
|
||||
var v2 = new Vec2().setFrom(weightsMap[key2]).sub(weightsMap[key1]);
|
||||
var len = new Vec2().setFrom(v2).dot(v2);
|
||||
var w = 1.0 - ((new Vec2().setFrom(v1).dot(v2)) / len);
|
||||
|
||||
w = w < 0 ? 0 : w > 1.0 ? 1.0 : w;
|
||||
tempWeights.set(k, w);
|
||||
k++;
|
||||
}
|
||||
weights.set(i, Math.min(tempWeights.get(0), tempWeights.get(1)));
|
||||
i++;
|
||||
}
|
||||
|
||||
var res = new Vec3(weights.get(0), weights.get(1), weights.get(2));
|
||||
res.mult(1.0 / (res.x + res.y + res.z));
|
||||
|
||||
var resultMap = new Map<Int, FastFloat>();
|
||||
var keys = weightsMap.keys();
|
||||
resultMap.set(keys.next(), res.x );
|
||||
resultMap.set(keys.next(), res.y );
|
||||
resultMap.set(keys.next(), res.z );
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OneShotOperator {
|
||||
|
||||
var boneAnimation: BoneAnimation;
|
||||
var objectAnimation: ObjectAnimation;
|
||||
var isArmature: Bool;
|
||||
var oneShotAction: ActionSampler;
|
||||
var restart: Bool;
|
||||
var blendInTime: FastFloat;
|
||||
var blendOutTime: FastFloat;
|
||||
var frameTime: FastFloat;
|
||||
var boneLayer: Null<Int>;
|
||||
var doneOneShot: Null<Void -> Void> = null;
|
||||
var tempMatsBone: Array<Mat4>;
|
||||
var tempMatsObject: Map<String, FastFloat>;
|
||||
// Internal
|
||||
var isDone: Bool = true;
|
||||
var totalFrames: Int;
|
||||
var blendFactor: FastFloat;
|
||||
var tween: TAnim = null;
|
||||
var blendOutFrame : Int;
|
||||
|
||||
public function new(animation: Animation, oneShotAction: ActionSampler) {
|
||||
|
||||
var animation = animation;
|
||||
this.oneShotAction = oneShotAction;
|
||||
if(Std.isOfType(animation, BoneAnimation)) {
|
||||
boneAnimation = cast animation;
|
||||
tempMatsBone = boneAnimation.initMatsEmpty();
|
||||
this.isArmature = true;
|
||||
}
|
||||
else {
|
||||
objectAnimation = cast animation;
|
||||
tempMatsObject = objectAnimation.initTransformMap();
|
||||
this.isArmature = false;
|
||||
}
|
||||
initOneShot();
|
||||
}
|
||||
|
||||
function initOneShot() {
|
||||
if(isArmature) {
|
||||
totalFrames = boneAnimation.getTotalFrames(oneShotAction) - 1;
|
||||
}
|
||||
else {
|
||||
totalFrames = objectAnimation.getTotalFrames(oneShotAction) - 1;
|
||||
}
|
||||
blendFactor = 0.0;
|
||||
blendOutFrame = getBlendOutFrame(blendOutTime);
|
||||
}
|
||||
|
||||
function tweenIn() {
|
||||
if(tween != null){
|
||||
Tween.stop(tween);
|
||||
|
||||
}
|
||||
isDone = false;
|
||||
tween = Tween.to({
|
||||
target: this,
|
||||
props: { blendFactor: 1.0 },
|
||||
duration: blendInTime,
|
||||
ease: Ease.Linear
|
||||
});
|
||||
|
||||
App.removeUpdate(tweenOut);
|
||||
App.notifyOnUpdate(tweenOut);
|
||||
}
|
||||
|
||||
function tweenOut() {
|
||||
if(oneShotAction.offset >= totalFrames){
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if(oneShotAction.offset >= blendOutFrame){
|
||||
if(tween != null){
|
||||
Tween.stop(tween);
|
||||
}
|
||||
tween = Tween.to({
|
||||
target: this,
|
||||
props: { blendFactor: 0.0 },
|
||||
duration: blendOutTime,
|
||||
ease: Ease.Linear,
|
||||
done: done
|
||||
});
|
||||
App.removeUpdate(tweenOut);
|
||||
}
|
||||
}
|
||||
|
||||
function stopTween() {
|
||||
App.removeUpdate(tweenOut);
|
||||
if(tween != null){
|
||||
Tween.stop(tween);
|
||||
}
|
||||
isDone = true;
|
||||
oneShotAction.setFrameOffset(0);
|
||||
oneShotAction.paused = true;
|
||||
blendFactor = 0.0;
|
||||
|
||||
}
|
||||
|
||||
function done() {
|
||||
stopTween();
|
||||
if(doneOneShot != null) doneOneShot();
|
||||
}
|
||||
|
||||
function getBlendOutFrame(blendOutTime: FastFloat): Int {
|
||||
var frameTime: FastFloat = Scene.active.raw.frame_time;
|
||||
return totalFrames - Std.int(blendOutTime / frameTime);
|
||||
}
|
||||
|
||||
public overload extern inline function update(mainMats: Array<Mat4>, resultMats: Array<Mat4>) {
|
||||
#if lnx_skin
|
||||
boneAnimation.sampleAction(oneShotAction, tempMatsBone);
|
||||
boneAnimation.blendAction(mainMats, tempMatsBone, resultMats, blendFactor, boneLayer);
|
||||
#end
|
||||
}
|
||||
|
||||
public overload extern inline function update(mainMats: Map<String, FastFloat>, resultMats: Map<String, FastFloat>) {
|
||||
|
||||
objectAnimation.sampleAction(oneShotAction, tempMatsObject);
|
||||
objectAnimation.blendActionObject(mainMats, tempMatsObject, resultMats, blendFactor);
|
||||
}
|
||||
|
||||
public function startOneShotAction(blendInTime: FastFloat, blendOutTime: FastFloat, restart: Bool = false, done: Null<Void -> Void> = null, boneLayer: Null<Int> = null) {
|
||||
if(getBlendOutFrame(blendOutTime) < 1) return;
|
||||
|
||||
if(! restart && ! isDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.restart = restart;
|
||||
this.blendInTime = blendInTime;
|
||||
this.blendOutTime = blendOutTime;
|
||||
this.boneLayer = boneLayer;
|
||||
this.doneOneShot = done;
|
||||
initOneShot();
|
||||
oneShotAction.restartAction();
|
||||
tweenIn();
|
||||
}
|
||||
|
||||
public function stopOneShotAction() {
|
||||
stopTween();
|
||||
}
|
||||
}
|
||||
|
||||
class SwitchActionOperator {
|
||||
|
||||
var boneAnimation: BoneAnimation;
|
||||
var objectAnimation: ObjectAnimation;
|
||||
var isArmature: Bool;
|
||||
var boneLayer: Null<Int>;
|
||||
var done: Null<Void -> Void> = null;
|
||||
// Internal
|
||||
var isDone: Bool = true;
|
||||
var blendFactor: FastFloat = 0;
|
||||
var tween: TAnim = null;
|
||||
|
||||
public function new(animation: Animation) {
|
||||
|
||||
if(Std.isOfType(animation, BoneAnimation)) {
|
||||
boneAnimation = cast animation;
|
||||
this.isArmature = true;
|
||||
}
|
||||
else {
|
||||
objectAnimation = cast animation;
|
||||
this.isArmature = false;
|
||||
}
|
||||
}
|
||||
|
||||
public overload extern inline function update(action1: Array<Mat4>, action2: Array<Mat4>, resultMats: Array<Mat4>) {
|
||||
#if lnx_skin
|
||||
boneAnimation.blendAction(action1, action2, resultMats, blendFactor, boneLayer);
|
||||
#end
|
||||
}
|
||||
|
||||
public overload extern inline function update(action1: Map<String, FastFloat>, action2: Map<String, FastFloat>, resultMats: Map<String, FastFloat>) {
|
||||
|
||||
objectAnimation.blendActionObject(action1, action2, resultMats, blendFactor);
|
||||
}
|
||||
|
||||
public function switchAction(toAction: SelectAction, duration: FastFloat, restart: Bool = false, done: Null<Void -> Void> = null, boneLayer: Null<Int> = null) {
|
||||
|
||||
this.done = done;
|
||||
this.boneLayer = boneLayer;
|
||||
|
||||
switch(toAction){
|
||||
case action1:
|
||||
if(tween != null){
|
||||
if(! restart && ! isDone) {
|
||||
|
||||
return;
|
||||
}
|
||||
Tween.stop(tween);
|
||||
}
|
||||
isDone = false;
|
||||
tween = Tween.to({
|
||||
target: this,
|
||||
props: { blendFactor: 0.0 },
|
||||
duration: blendFactor * duration,
|
||||
ease: Ease.Linear,
|
||||
done: doneSwitch
|
||||
});
|
||||
|
||||
case action2:
|
||||
if(tween != null){
|
||||
if(! restart && ! isDone) {
|
||||
|
||||
return;
|
||||
}
|
||||
Tween.stop(tween);
|
||||
}
|
||||
isDone = false;
|
||||
tween = Tween.to({
|
||||
target: this,
|
||||
props: { blendFactor: 1.0 },
|
||||
duration: (1.0 - blendFactor) * duration,
|
||||
ease: Ease.Linear,
|
||||
done: doneSwitch
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function doneSwitch() {
|
||||
isDone = true;
|
||||
if(done != null) done();
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleBiPedalIK {
|
||||
|
||||
var object: Object;
|
||||
var boneAnimation: BoneAnimation;
|
||||
var leftBone: TObj; //2
|
||||
var rightBone: TObj; //3
|
||||
var leftBoneParent: TObj;
|
||||
var rightBoneParent: TObj;
|
||||
|
||||
public function new(armatureObject: Object, boneAnimation: BoneAnimation, leftBoneName: String, rightBoneName: String) {
|
||||
this.object = armatureObject;
|
||||
this.boneAnimation = boneAnimation;
|
||||
this.leftBone = boneAnimation.getBone(leftBoneName);
|
||||
this.rightBone = boneAnimation.getBone(rightBoneName);
|
||||
this.leftBoneParent = boneAnimation.getBone(leftBoneName).parent;
|
||||
this.rightBoneParent = boneAnimation.getBone(rightBoneName).parent;
|
||||
|
||||
}
|
||||
|
||||
public function resetArmaturePosition(heightOffset: FastFloat, interpSpeed: FastFloat) {
|
||||
var currentZ = object.transform.loc.z;
|
||||
var res = deltaInterpolate(currentZ, heightOffset, interpSpeed);
|
||||
object.transform.loc.set(0, 0, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update position of the foot
|
||||
* @param animMats Input bone transforms
|
||||
* @param heightOffset Height of the player mesh relative to hit height
|
||||
* @param footOffset Height of the foot bone relative to hit height
|
||||
* @param hitPointLeft Hit point left foot
|
||||
* @param hitPointRight Hit point right foot
|
||||
* @param offsetThreshold Threshold to stop IK if distnace between left and right hit points are greater than this value
|
||||
* @param interpSpeed Interpolation speed
|
||||
* @param poleLeft Pole target for left foot
|
||||
* @param poleRight Pole target for right foot
|
||||
* @param influence Influence of IK
|
||||
* @param layerMask Bone layer for IK blending
|
||||
**/
|
||||
|
||||
public function updatePosition(animMats: Array<Mat4>, heightOffset: FastFloat, footOffset: FastFloat, hitPointLeft: Null<Vec4>, hitPointRight: Null<Vec4>, offsetThreshold: FastFloat, interpSpeed: FastFloat, poleLeft: Null<Vec4> = null, poleRight: Null<Vec4> = null, influence : FastFloat = 1.0, layerMask: Int = -1){
|
||||
|
||||
if(hitPointRight == null || hitPointLeft == null) {
|
||||
resetArmaturePosition(heightOffset, interpSpeed);
|
||||
}
|
||||
else {
|
||||
var currentLoc = object.transform.world.getLoc();
|
||||
|
||||
// Lower the armature to ground
|
||||
var rightZ = hitPointRight.z;
|
||||
var leftZ = hitPointLeft.z;
|
||||
if(Math.abs(rightZ - leftZ) > offsetThreshold) return;
|
||||
var minZ = Math.min(rightZ, leftZ);
|
||||
var interp = deltaInterpolate(currentLoc.z, minZ + heightOffset, interpSpeed);
|
||||
var newLoc = new Vec4(currentLoc.x, currentLoc.y, interp);
|
||||
setWorldLocation(newLoc);
|
||||
}
|
||||
|
||||
if(hitPointLeft != null){
|
||||
var leftFootLoc = boneAnimation.getAbsWorldMat(animMats, leftBone).getLoc();
|
||||
var leftZ = hitPointLeft.z;
|
||||
if(leftFootLoc.z - footOffset < leftZ){
|
||||
var goal = new Vec4(leftFootLoc.x, leftFootLoc.y, leftZ + footOffset);
|
||||
AnimationExtension.solveTwoBoneIKBlend(boneAnimation, animMats, leftBoneParent, goal, poleLeft, 0.0, influence, layerMask);
|
||||
}
|
||||
}
|
||||
|
||||
if(hitPointRight != null){
|
||||
var rightFootLoc = boneAnimation.getAbsWorldMat(animMats, rightBone).getLoc();
|
||||
var rightZ = hitPointRight.z;
|
||||
if(rightFootLoc.z - footOffset < rightZ){
|
||||
var goal = new Vec4(rightFootLoc.x, rightFootLoc.y, rightZ + footOffset);
|
||||
AnimationExtension.solveTwoBoneIKBlend(boneAnimation, animMats, rightBoneParent, goal, poleRight, 0.0, influence, layerMask);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function updateRotation(animMats: Array<Mat4>, defaultFootVecLeft: Vec4, defaultFootVecRight: Vec4, groundNormalLeft: Null<Vec4>, groundNormalRight: Null<Vec4>){
|
||||
|
||||
if(groundNormalLeft != null){
|
||||
var m = boneAnimation.getAbsWorldMat(animMats, leftBone);
|
||||
var leftMat = Mat4.identity().setFrom(m);
|
||||
var currentLeftLook = leftMat.look().normalize();
|
||||
var quatLeft = new Quat().fromTo(new Vec4(0, 0, 1), groundNormalLeft.normalize()).normalize();
|
||||
var requiredVecLeft = defaultFootVecLeft.applyQuat(quatLeft).normalize();
|
||||
var requiredQuatLeft = new Quat().fromTo(currentLeftLook, requiredVecLeft).normalize();
|
||||
applyScaledQuat(leftMat, requiredQuatLeft);
|
||||
boneAnimation.setBoneMatFromWorldMat(animMats, leftMat, leftBone);
|
||||
}
|
||||
|
||||
if(groundNormalRight != null){
|
||||
var m = boneAnimation.getAbsWorldMat(animMats, rightBone);
|
||||
var rightMat = Mat4.identity().setFrom(m);
|
||||
var currentRightLook = rightMat.look().normalize();
|
||||
var quatRight = new Quat().fromTo(new Vec4(0, 0, 1), groundNormalRight.normalize()).normalize();
|
||||
var requiredVecRight = defaultFootVecRight.applyQuat(quatRight).normalize();
|
||||
var requiredQuatRight = new Quat().fromTo(currentRightLook, requiredVecRight).normalize();
|
||||
applyScaledQuat(rightMat, requiredQuatRight);
|
||||
boneAnimation.setBoneMatFromWorldMat(animMats, rightMat, rightBone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline function applyScaledQuat(mat: Mat4, quat: Quat) {
|
||||
|
||||
var loc = new Vec4();
|
||||
var rot = new Quat();
|
||||
var scl = new Vec4();
|
||||
|
||||
mat.decompose(loc, rot, scl);
|
||||
quat.mult(rot);
|
||||
mat.compose(loc, quat.normalize(), scl);
|
||||
|
||||
}
|
||||
|
||||
inline function deltaInterpolate(from: FastFloat, to: FastFloat, interpSpeed: FastFloat): FastFloat {
|
||||
|
||||
var sign = to > from ? 1.0 : -1.0;
|
||||
var value = from + interpSpeed * sign;
|
||||
var min = Math.min(to, from);
|
||||
var max = Math.max(to, from);
|
||||
return value < min ? min : value > max ? max : value;
|
||||
}
|
||||
|
||||
inline function setWorldLocation(currentPos: Vec4) {
|
||||
var loc = new Vec4().setFrom(currentPos);
|
||||
// Remove parent location influence
|
||||
loc.sub(object.parent.transform.world.getLoc());
|
||||
// Convert vec to parent local space
|
||||
var dotX = loc.dot(object.parent.transform.right());
|
||||
var dotY = loc.dot(object.parent.transform.look());
|
||||
var dotZ = loc.dot(object.parent.transform.up());
|
||||
var vec = new Vec4(dotX, dotY, dotZ);
|
||||
object.transform.loc.setFrom(vec);
|
||||
object.transform.buildMatrix();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@:enum abstract SelectAction(Int) from Int to Int {
|
||||
var action1 = 0;
|
||||
var action2 = 1;
|
||||
}
|
||||
|
||||
typedef WeightIndex = {
|
||||
var weight: FastFloat;
|
||||
var index: Int;
|
||||
}
|
72
leenkx/Sources/leenkx/object/TransformExtension.hx
Normal file
72
leenkx/Sources/leenkx/object/TransformExtension.hx
Normal file
@ -0,0 +1,72 @@
|
||||
package leenkx.object;
|
||||
|
||||
import iron.math.Mat4;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Quat;
|
||||
import iron.object.Transform;
|
||||
|
||||
class TransformExtension {
|
||||
|
||||
public static function overlap(t1: Transform, t2: Transform): Bool {
|
||||
return t1.worldx() + t1.dim.x / 2 > t2.worldx() - t2.dim.x / 2 && t1.worldx() - t1.dim.x / 2 < t2.worldx() + t2.dim.x / 2 &&
|
||||
t1.worldy() + t1.dim.y / 2 > t2.worldy() - t2.dim.y / 2 && t1.worldy() - t1.dim.y / 2 < t2.worldy() + t2.dim.y / 2 &&
|
||||
t1.worldz() + t1.dim.z / 2 > t2.worldz() - t2.dim.z / 2 && t1.worldz() - t1.dim.z / 2 < t2.worldz() + t2.dim.z / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world (global) position.
|
||||
* @return Vec4
|
||||
*/
|
||||
public static inline function getWorldPosition(t: Transform): Vec4 {
|
||||
return new Vec4(t.worldx(), t.worldy(), t.worldz(), 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given local vector in world coordinates
|
||||
* @param localVec
|
||||
* @return Vec4
|
||||
*/
|
||||
public static inline function getWorldVecFromLocal(t: Transform, localVec: Vec4): Vec4 {
|
||||
return localVec.clone().applymat4(t.worldUnpack);
|
||||
}
|
||||
/**
|
||||
* Returns the given world vector in local coordinates
|
||||
* @param worldVec
|
||||
* @return Vec4
|
||||
*/
|
||||
public static inline function getLocalVecFromWorld(t: Transform, worldVec: Vec4): Vec4 {
|
||||
return worldVec.clone().applymat4(Mat4.identity().getInverse(t.worldUnpack));
|
||||
}
|
||||
/**
|
||||
* Returns the given world vector in transform orientation
|
||||
* @param worldVec
|
||||
* @return Vec4
|
||||
**/
|
||||
public static inline function worldVecToOrientation(t: Transform, worldVec: Vec4): Vec4 {
|
||||
var right = t.right().normalize();
|
||||
right.mult(worldVec.x);
|
||||
|
||||
var look = t.look().normalize();
|
||||
look.mult(worldVec.y);
|
||||
|
||||
var up = t.up().normalize();
|
||||
up.mult(worldVec.z);
|
||||
|
||||
return new Vec4().add(right).add(look).add(up);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given world vector in local orientation
|
||||
* @param worldVec Vector in world orientation
|
||||
* @return Local vector
|
||||
**/
|
||||
public static inline function getWorldVectorAlongLocalAxis(t: Transform, worldVec: Vec4): Vec4 {
|
||||
|
||||
var localVec = new Vec4();
|
||||
localVec.x = worldVec.dot(t.right().normalize());
|
||||
localVec.y = worldVec.dot(t.look().normalize());
|
||||
localVec.z = worldVec.dot(t.up().normalize());
|
||||
|
||||
return localVec;
|
||||
}
|
||||
}
|
249
leenkx/Sources/leenkx/object/Uniforms.hx
Normal file
249
leenkx/Sources/leenkx/object/Uniforms.hx
Normal file
@ -0,0 +1,249 @@
|
||||
package leenkx.object;
|
||||
|
||||
import iron.Scene;
|
||||
import iron.object.Object;
|
||||
import iron.data.MaterialData;
|
||||
import iron.math.Vec4;
|
||||
|
||||
import kha.arrays.Float32Array;
|
||||
|
||||
import leenkx.renderpath.Postprocess;
|
||||
|
||||
using StringTools;
|
||||
|
||||
// Structure for setting shader uniforms
|
||||
class Uniforms {
|
||||
|
||||
public static function register() {
|
||||
iron.object.Uniforms.externalTextureLinks = [textureLink];
|
||||
iron.object.Uniforms.externalVec2Links = [vec2Link];
|
||||
iron.object.Uniforms.externalVec3Links = [vec3Link];
|
||||
iron.object.Uniforms.externalVec4Links = [];
|
||||
iron.object.Uniforms.externalFloatLinks = [floatLink];
|
||||
iron.object.Uniforms.externalFloatsLinks = [floatsLink];
|
||||
iron.object.Uniforms.externalIntLinks = [intLink];
|
||||
}
|
||||
|
||||
public static function textureLink(object: Object, mat: MaterialData, link: String): Null<kha.Image> {
|
||||
switch (link) {
|
||||
case "_nishitaLUT": {
|
||||
if (leenkx.renderpath.Nishita.data == null) leenkx.renderpath.Nishita.recompute(Scene.active.world);
|
||||
return leenkx.renderpath.Nishita.data.lut;
|
||||
}
|
||||
#if lnx_ltc
|
||||
case "_ltcMat": {
|
||||
if (leenkx.data.ConstData.ltcMatTex == null) leenkx.data.ConstData.initLTC();
|
||||
return leenkx.data.ConstData.ltcMatTex;
|
||||
}
|
||||
case "_ltcMag": {
|
||||
if (leenkx.data.ConstData.ltcMagTex == null) leenkx.data.ConstData.initLTC();
|
||||
return leenkx.data.ConstData.ltcMagTex;
|
||||
}
|
||||
#end
|
||||
#if lnx_morph_target
|
||||
case "_morphDataPos": {
|
||||
return cast(object, iron.object.MeshObject).morphTarget.morphDataPos;
|
||||
}
|
||||
case "_morphDataNor": {
|
||||
return cast(object, iron.object.MeshObject).morphTarget.morphDataNor;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
var target = iron.RenderPath.active.renderTargets.get(link.endsWith("_depth") ? link.substr(0, link.length - 6) : link);
|
||||
return target != null ? target.image : null;
|
||||
}
|
||||
|
||||
public static function vec3Link(object: Object, mat: MaterialData, link: String): Null<iron.math.Vec4> {
|
||||
var v: Vec4 = null;
|
||||
switch (link) {
|
||||
#if lnx_hosek
|
||||
case "_hosekA": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.A.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.A.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.A.z;
|
||||
}
|
||||
}
|
||||
case "_hosekB": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.B.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.B.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.B.z;
|
||||
}
|
||||
}
|
||||
case "_hosekC": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.C.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.C.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.C.z;
|
||||
}
|
||||
}
|
||||
case "_hosekD": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.D.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.D.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.D.z;
|
||||
}
|
||||
}
|
||||
case "_hosekE": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.E.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.E.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.E.z;
|
||||
}
|
||||
}
|
||||
case "_hosekF": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.F.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.F.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.F.z;
|
||||
}
|
||||
}
|
||||
case "_hosekG": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.G.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.G.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.G.z;
|
||||
}
|
||||
}
|
||||
case "_hosekH": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.H.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.H.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.H.z;
|
||||
}
|
||||
}
|
||||
case "_hosekI": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.I.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.I.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.I.z;
|
||||
}
|
||||
}
|
||||
case "_hosekZ": {
|
||||
if (leenkx.renderpath.HosekWilkie.data == null) {
|
||||
leenkx.renderpath.HosekWilkie.recompute(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.HosekWilkie.data != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.HosekWilkie.data.Z.x;
|
||||
v.y = leenkx.renderpath.HosekWilkie.data.Z.y;
|
||||
v.z = leenkx.renderpath.HosekWilkie.data.Z.z;
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function vec2Link(object: Object, mat: MaterialData, link: String): Null<iron.math.Vec4> {
|
||||
var v: Vec4 = null;
|
||||
switch (link) {
|
||||
case "_nishitaDensity": {
|
||||
var w = Scene.active.world;
|
||||
if (w != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
// We only need Rayleigh and Mie density in the sky shader -> Vec2
|
||||
v.x = w.raw.nishita_density[0];
|
||||
v.y = w.raw.nishita_density[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
|
||||
switch (link) {
|
||||
#if rp_dynres
|
||||
case "_dynamicScale": {
|
||||
return leenkx.renderpath.DynamicResolutionScale.dynamicScale;
|
||||
}
|
||||
#end
|
||||
#if lnx_debug
|
||||
case "_debugFloat": {
|
||||
return leenkx.trait.internal.DebugConsole.debugFloat;
|
||||
}
|
||||
#end
|
||||
#if rp_bloom
|
||||
case "_bloomSampleScale": {
|
||||
return Postprocess.bloom_uniforms[3];
|
||||
}
|
||||
#end
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function floatsLink(object: Object, mat: MaterialData, link: String): Float32Array {
|
||||
switch (link) {
|
||||
#if (rp_voxels != "Off")
|
||||
case "_clipmaps": {
|
||||
var clipmaps = iron.RenderPath.clipmaps;
|
||||
var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10);
|
||||
for (i in 0...Main.voxelgiClipmapCount) {
|
||||
fa[i * 10] = clipmaps[i].voxelSize;
|
||||
fa[i * 10 + 1] = clipmaps[i].extents.x;
|
||||
fa[i * 10 + 2] = clipmaps[i].extents.y;
|
||||
fa[i * 10 + 3] = clipmaps[i].extents.z;
|
||||
fa[i * 10 + 4] = clipmaps[i].center.x;
|
||||
fa[i * 10 + 5] = clipmaps[i].center.y;
|
||||
fa[i * 10 + 6] = clipmaps[i].center.z;
|
||||
fa[i * 10 + 7] = clipmaps[i].offset_prev.x;
|
||||
fa[i * 10 + 8] = clipmaps[i].offset_prev.y;
|
||||
fa[i * 10 + 9] = clipmaps[i].offset_prev.z;
|
||||
}
|
||||
return fa;
|
||||
}
|
||||
#end
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function intLink(object: Object, mat: MaterialData, link: String): Null<Int> {
|
||||
switch (link) {
|
||||
#if (rp_voxels != "Off")
|
||||
case "_clipmapLevel": {
|
||||
return iron.RenderPath.clipmapLevel;
|
||||
}
|
||||
#end
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user