forked from LeenkxTeam/LNXSDK
Merge pull request 'moisesjpelaez - FixedUpdate - Physics Improvements - Private Fields' (#58) from Onek8/LNXSDK:main into main
Reviewed-on: LeenkxTeam/LNXSDK#58
This commit is contained in:
@ -12,6 +12,7 @@ class App {
|
|||||||
static var traitInits: Array<Void->Void> = [];
|
static var traitInits: Array<Void->Void> = [];
|
||||||
static var traitUpdates: Array<Void->Void> = [];
|
static var traitUpdates: Array<Void->Void> = [];
|
||||||
static var traitLateUpdates: Array<Void->Void> = [];
|
static var traitLateUpdates: Array<Void->Void> = [];
|
||||||
|
static var traitFixedUpdates: Array<Void->Void> = [];
|
||||||
static var traitRenders: Array<kha.graphics4.Graphics->Void> = [];
|
static var traitRenders: Array<kha.graphics4.Graphics->Void> = [];
|
||||||
static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = [];
|
static var traitRenders2D: Array<kha.graphics2.Graphics->Void> = [];
|
||||||
public static var framebuffer: kha.Framebuffer;
|
public static var framebuffer: kha.Framebuffer;
|
||||||
@ -23,6 +24,8 @@ class App {
|
|||||||
public static var renderPathTime: Float;
|
public static var renderPathTime: Float;
|
||||||
public static var endFrameCallbacks: Array<Void->Void> = [];
|
public static var endFrameCallbacks: Array<Void->Void> = [];
|
||||||
#end
|
#end
|
||||||
|
static var last = 0.0;
|
||||||
|
static var time = 0.0;
|
||||||
static var lastw = -1;
|
static var lastw = -1;
|
||||||
static var lasth = -1;
|
static var lasth = -1;
|
||||||
public static var onResize: Void->Void = null;
|
public static var onResize: Void->Void = null;
|
||||||
@ -34,13 +37,14 @@ class App {
|
|||||||
function new(done: Void->Void) {
|
function new(done: Void->Void) {
|
||||||
done();
|
done();
|
||||||
kha.System.notifyOnFrames(render);
|
kha.System.notifyOnFrames(render);
|
||||||
kha.Scheduler.addTimeTask(update, 0, iron.system.Time.delta);
|
kha.Scheduler.addTimeTask(update, 0, iron.system.Time.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function reset() {
|
public static function reset() {
|
||||||
traitInits = [];
|
traitInits = [];
|
||||||
traitUpdates = [];
|
traitUpdates = [];
|
||||||
traitLateUpdates = [];
|
traitLateUpdates = [];
|
||||||
|
traitFixedUpdates = [];
|
||||||
traitRenders = [];
|
traitRenders = [];
|
||||||
traitRenders2D = [];
|
traitRenders2D = [];
|
||||||
if (onResets != null) for (f in onResets) f();
|
if (onResets != null) for (f in onResets) f();
|
||||||
@ -50,12 +54,22 @@ class App {
|
|||||||
if (Scene.active == null || !Scene.active.ready) return;
|
if (Scene.active == null || !Scene.active.ready) return;
|
||||||
if (pauseUpdates) return;
|
if (pauseUpdates) return;
|
||||||
|
|
||||||
|
iron.system.Time.update();
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
startTime = kha.Scheduler.realTime();
|
startTime = kha.Scheduler.realTime();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
Scene.active.updateFrame();
|
Scene.active.updateFrame();
|
||||||
|
|
||||||
|
|
||||||
|
time += iron.system.Time.delta;
|
||||||
|
|
||||||
|
while (time >= iron.system.Time.fixedStep) {
|
||||||
|
for (f in traitFixedUpdates) f();
|
||||||
|
time -= iron.system.Time.fixedStep;
|
||||||
|
}
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var l = traitUpdates.length;
|
var l = traitUpdates.length;
|
||||||
while (i < l) {
|
while (i < l) {
|
||||||
@ -106,7 +120,7 @@ class App {
|
|||||||
var frame = frames[0];
|
var frame = frames[0];
|
||||||
framebuffer = frame;
|
framebuffer = frame;
|
||||||
|
|
||||||
iron.system.Time.update();
|
iron.system.Time.render();
|
||||||
|
|
||||||
if (Scene.active == null || !Scene.active.ready) {
|
if (Scene.active == null || !Scene.active.ready) {
|
||||||
render2D(frame);
|
render2D(frame);
|
||||||
@ -172,6 +186,14 @@ class App {
|
|||||||
traitLateUpdates.remove(f);
|
traitLateUpdates.remove(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function notifyOnFixedUpdate(f: Void->Void) {
|
||||||
|
traitFixedUpdates.push(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function removeFixedUpdate(f: Void->Void) {
|
||||||
|
traitFixedUpdates.remove(f);
|
||||||
|
}
|
||||||
|
|
||||||
public static function notifyOnRender(f: kha.graphics4.Graphics->Void) {
|
public static function notifyOnRender(f: kha.graphics4.Graphics->Void) {
|
||||||
traitRenders.push(f);
|
traitRenders.push(f);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ class Trait {
|
|||||||
var _remove: Array<Void->Void> = null;
|
var _remove: Array<Void->Void> = null;
|
||||||
var _update: Array<Void->Void> = null;
|
var _update: Array<Void->Void> = null;
|
||||||
var _lateUpdate: Array<Void->Void> = null;
|
var _lateUpdate: Array<Void->Void> = null;
|
||||||
|
var _fixedUpdate: Array<Void->Void> = null;
|
||||||
var _render: Array<kha.graphics4.Graphics->Void> = null;
|
var _render: Array<kha.graphics4.Graphics->Void> = null;
|
||||||
var _render2D: Array<kha.graphics2.Graphics->Void> = null;
|
var _render2D: Array<kha.graphics2.Graphics->Void> = null;
|
||||||
|
|
||||||
@ -87,6 +88,23 @@ class Trait {
|
|||||||
App.removeLateUpdate(f);
|
App.removeLateUpdate(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Add fixed game logic handler.
|
||||||
|
**/
|
||||||
|
public function notifyOnFixedUpdate(f: Void->Void) {
|
||||||
|
if (_fixedUpdate == null) _fixedUpdate = [];
|
||||||
|
_fixedUpdate.push(f);
|
||||||
|
App.notifyOnFixedUpdate(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Remove fixed game logic handler.
|
||||||
|
**/
|
||||||
|
public function removeFixedUpdate(f: Void->Void) {
|
||||||
|
_fixedUpdate.remove(f);
|
||||||
|
App.removeFixedUpdate(f);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add render handler.
|
Add render handler.
|
||||||
**/
|
**/
|
||||||
|
@ -392,6 +392,8 @@ typedef TParticleData = {
|
|||||||
#end
|
#end
|
||||||
public var name: String;
|
public var name: String;
|
||||||
public var type: Int; // 0 - Emitter, Hair
|
public var type: Int; // 0 - Emitter, Hair
|
||||||
|
public var auto_start: Bool;
|
||||||
|
public var is_unique: Bool;
|
||||||
public var loop: Bool;
|
public var loop: Bool;
|
||||||
public var count: Int;
|
public var count: Int;
|
||||||
public var frame_start: FastFloat;
|
public var frame_start: FastFloat;
|
||||||
|
@ -159,9 +159,17 @@ class Animation {
|
|||||||
if(markerEvents.get(sampler) != null){
|
if(markerEvents.get(sampler) != null){
|
||||||
for (i in 0...anim.marker_frames.length) {
|
for (i in 0...anim.marker_frames.length) {
|
||||||
if (frameIndex == anim.marker_frames[i]) {
|
if (frameIndex == anim.marker_frames[i]) {
|
||||||
var marketAct = markerEvents.get(sampler);
|
var markerAct = markerEvents.get(sampler);
|
||||||
var ar = marketAct.get(anim.marker_names[i]);
|
var ar = markerAct.get(anim.marker_names[i]);
|
||||||
if (ar != null) for (f in ar) f();
|
if (ar != null) for (f in ar) f();
|
||||||
|
} else {
|
||||||
|
for (j in 0...(frameIndex - lastFrameIndex)) {
|
||||||
|
if (lastFrameIndex + j + 1 == anim.marker_frames[i]) {
|
||||||
|
var markerAct = markerEvents.get(sampler);
|
||||||
|
var ar = markerAct.get(anim.marker_names[i]);
|
||||||
|
if (ar != null) for (f in ar) f();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastFrameIndex = frameIndex;
|
lastFrameIndex = frameIndex;
|
||||||
|
@ -172,6 +172,10 @@ class Object {
|
|||||||
for (f in t._init) App.removeInit(f);
|
for (f in t._init) App.removeInit(f);
|
||||||
t._init = null;
|
t._init = null;
|
||||||
}
|
}
|
||||||
|
if (t._fixedUpdate != null) {
|
||||||
|
for (f in t._fixedUpdate) App.removeFixedUpdate(f);
|
||||||
|
t._fixedUpdate = null;
|
||||||
|
}
|
||||||
if (t._update != null) {
|
if (t._update != null) {
|
||||||
for (f in t._update) App.removeUpdate(f);
|
for (f in t._update) App.removeUpdate(f);
|
||||||
t._update = null;
|
t._update = null;
|
||||||
|
@ -2,6 +2,7 @@ package iron.object;
|
|||||||
|
|
||||||
#if lnx_particles
|
#if lnx_particles
|
||||||
|
|
||||||
|
import kha.FastFloat;
|
||||||
import kha.graphics4.Usage;
|
import kha.graphics4.Usage;
|
||||||
import kha.arrays.Float32Array;
|
import kha.arrays.Float32Array;
|
||||||
import iron.data.Data;
|
import iron.data.Data;
|
||||||
@ -16,39 +17,45 @@ import iron.math.Vec4;
|
|||||||
class ParticleSystem {
|
class ParticleSystem {
|
||||||
public var data: ParticleData;
|
public var data: ParticleData;
|
||||||
public var speed = 1.0;
|
public var speed = 1.0;
|
||||||
|
var currentSpeed = 0.0;
|
||||||
var particles: Array<Particle>;
|
var particles: Array<Particle>;
|
||||||
var ready: Bool;
|
var ready: Bool;
|
||||||
public var frameRate = 24;
|
var frameRate = 24;
|
||||||
public var lifetime = 0.0;
|
var lifetime = 0.0;
|
||||||
public var animtime = 0.0;
|
var animtime = 0.0;
|
||||||
public var time = 0.0;
|
var time = 0.0;
|
||||||
public var spawnRate = 0.0;
|
var spawnRate = 0.0;
|
||||||
|
var looptime = 0.0;
|
||||||
var seed = 0;
|
var seed = 0;
|
||||||
|
|
||||||
public var r: TParticleData;
|
var r: TParticleData;
|
||||||
public var gx: Float;
|
var gx: Float;
|
||||||
public var gy: Float;
|
var gy: Float;
|
||||||
public var gz: Float;
|
var gz: Float;
|
||||||
public var alignx: Float;
|
var alignx: Float;
|
||||||
public var aligny: Float;
|
var aligny: Float;
|
||||||
public var alignz: Float;
|
var alignz: Float;
|
||||||
var dimx: Float;
|
var dimx: Float;
|
||||||
var dimy: Float;
|
var dimy: Float;
|
||||||
var tilesx: Int;
|
var tilesx: Int;
|
||||||
var tilesy: Int;
|
var tilesy: Int;
|
||||||
var tilesFramerate: Int;
|
var tilesFramerate: Int;
|
||||||
|
|
||||||
public var count = 0;
|
var count = 0;
|
||||||
public var lap = 0;
|
var lap = 0;
|
||||||
public var lapTime = 0.0;
|
var lapTime = 0.0;
|
||||||
var m = Mat4.identity();
|
var m = Mat4.identity();
|
||||||
|
|
||||||
var ownerLoc = new Vec4();
|
var ownerLoc = new Vec4();
|
||||||
var ownerRot = new Quat();
|
var ownerRot = new Quat();
|
||||||
var ownerScl = new Vec4();
|
var ownerScl = new Vec4();
|
||||||
|
|
||||||
|
var random = 0.0;
|
||||||
|
|
||||||
public function new(sceneName: String, pref: TParticleReference) {
|
public function new(sceneName: String, pref: TParticleReference) {
|
||||||
seed = pref.seed;
|
seed = pref.seed;
|
||||||
|
currentSpeed = speed;
|
||||||
|
speed = 0;
|
||||||
particles = [];
|
particles = [];
|
||||||
ready = false;
|
ready = false;
|
||||||
|
|
||||||
@ -65,34 +72,61 @@ class ParticleSystem {
|
|||||||
gy = 0;
|
gy = 0;
|
||||||
gz = -9.81 * r.weight_gravity;
|
gz = -9.81 * r.weight_gravity;
|
||||||
}
|
}
|
||||||
alignx = r.object_align_factor[0] / 2;
|
alignx = r.object_align_factor[0];
|
||||||
aligny = r.object_align_factor[1] / 2;
|
aligny = r.object_align_factor[1];
|
||||||
alignz = r.object_align_factor[2] / 2;
|
alignz = r.object_align_factor[2];
|
||||||
|
looptime = (r.frame_end - r.frame_start) / frameRate;
|
||||||
lifetime = r.lifetime / frameRate;
|
lifetime = r.lifetime / frameRate;
|
||||||
animtime = (r.frame_end - r.frame_start) / frameRate;
|
animtime = r.loop ? looptime : looptime + lifetime;
|
||||||
spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate;
|
spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate;
|
||||||
|
|
||||||
for (i in 0...r.count) {
|
for (i in 0...r.count) {
|
||||||
var particle = new Particle(i);
|
particles.push(new Particle(i));
|
||||||
particle.sr = 1 - Math.random() * r.size_random;
|
|
||||||
particles.push(particle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ready = true;
|
ready = true;
|
||||||
|
if (r.auto_start){
|
||||||
|
start();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function start() {
|
||||||
|
if (r.is_unique) random = Math.random();
|
||||||
|
lifetime = r.lifetime / frameRate;
|
||||||
|
time = 0;
|
||||||
|
lap = 0;
|
||||||
|
lapTime = 0;
|
||||||
|
speed = currentSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
public function pause() {
|
public function pause() {
|
||||||
lifetime = 0;
|
speed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resume() {
|
public function resume() {
|
||||||
lifetime = r.lifetime / frameRate;
|
lifetime = r.lifetime / frameRate;
|
||||||
|
speed = currentSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: interrupt smoothly
|
||||||
|
public function stop() {
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
|
function end() {
|
||||||
|
lifetime = 0;
|
||||||
|
speed = 0;
|
||||||
|
lap = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(object: MeshObject, owner: MeshObject) {
|
public function update(object: MeshObject, owner: MeshObject) {
|
||||||
if (!ready || object == null || speed == 0.0) return;
|
if (!ready || object == null || speed == 0.0) return;
|
||||||
|
|
||||||
|
var prevLap = lap;
|
||||||
|
|
||||||
// Copy owner world transform but discard scale
|
// Copy owner world transform but discard scale
|
||||||
owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
|
owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
|
||||||
object.transform.loc = ownerLoc;
|
object.transform.loc = ownerLoc;
|
||||||
@ -115,17 +149,21 @@ class ParticleSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Animate
|
// Animate
|
||||||
time += Time.delta * speed;
|
time += Time.renderDelta * speed; // realDelta to renderDelta
|
||||||
lap = Std.int(time / animtime);
|
lap = Std.int(time / animtime);
|
||||||
lapTime = time - lap * animtime;
|
lapTime = time - lap * animtime;
|
||||||
count = Std.int(lapTime / spawnRate);
|
count = Std.int(lapTime / spawnRate);
|
||||||
|
|
||||||
|
if (lap > prevLap && !r.loop) {
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
updateGpu(object, owner);
|
updateGpu(object, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getData(): Mat4 {
|
public function getData(): Mat4 {
|
||||||
var hair = r.type == 1;
|
var hair = r.type == 1;
|
||||||
m._00 = r.loop ? animtime : -animtime;
|
m._00 = animtime;
|
||||||
m._01 = hair ? 1 / particles.length : spawnRate;
|
m._01 = hair ? 1 / particles.length : spawnRate;
|
||||||
m._02 = hair ? 1 : lifetime;
|
m._02 = hair ? 1 : lifetime;
|
||||||
m._03 = particles.length;
|
m._03 = particles.length;
|
||||||
@ -133,9 +171,9 @@ class ParticleSystem {
|
|||||||
m._11 = hair ? 0 : aligny;
|
m._11 = hair ? 0 : aligny;
|
||||||
m._12 = hair ? 0 : alignz;
|
m._12 = hair ? 0 : alignz;
|
||||||
m._13 = hair ? 0 : r.factor_random;
|
m._13 = hair ? 0 : r.factor_random;
|
||||||
m._20 = hair ? 0 : gx * r.mass;
|
m._20 = hair ? 0 : gx;
|
||||||
m._21 = hair ? 0 : gy * r.mass;
|
m._21 = hair ? 0 : gy;
|
||||||
m._22 = hair ? 0 : gz * r.mass;
|
m._22 = hair ? 0 : gz;
|
||||||
m._23 = hair ? 0 : r.lifetime_random;
|
m._23 = hair ? 0 : r.lifetime_random;
|
||||||
m._30 = tilesx;
|
m._30 = tilesx;
|
||||||
m._31 = tilesy;
|
m._31 = tilesy;
|
||||||
@ -144,13 +182,25 @@ class ParticleSystem {
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSizeRandom(): FastFloat {
|
||||||
|
return r.size_random;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRandom(): FastFloat {
|
||||||
|
return random;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize(): FastFloat {
|
||||||
|
return r.particle_size;
|
||||||
|
}
|
||||||
|
|
||||||
function updateGpu(object: MeshObject, owner: MeshObject) {
|
function updateGpu(object: MeshObject, owner: MeshObject) {
|
||||||
if (!object.data.geom.instanced) setupGeomGpu(object, owner);
|
if (!object.data.geom.instanced) setupGeomGpu(object, owner);
|
||||||
// GPU particles transform is attached to owner object
|
// GPU particles transform is attached to owner object
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setupGeomGpu(object: MeshObject, owner: MeshObject) {
|
function setupGeomGpu(object: MeshObject, owner: MeshObject) {
|
||||||
var instancedData = new Float32Array(particles.length * 6);
|
var instancedData = new Float32Array(particles.length * 3);
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
var normFactor = 1 / 32767; // pa.values are not normalized
|
var normFactor = 1 / 32767; // pa.values are not normalized
|
||||||
@ -169,10 +219,6 @@ class ParticleSystem {
|
|||||||
instancedData.set(i, pa.values[j * pa.size ] * normFactor * scaleFactor.x); i++;
|
instancedData.set(i, pa.values[j * pa.size ] * normFactor * scaleFactor.x); i++;
|
||||||
instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++;
|
instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++;
|
||||||
instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++;
|
instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++;
|
||||||
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1: // Face
|
case 1: // Face
|
||||||
@ -196,10 +242,6 @@ class ParticleSystem {
|
|||||||
instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++;
|
instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++;
|
||||||
instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++;
|
instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++;
|
||||||
instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++;
|
instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++;
|
||||||
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 2: // Volume
|
case 2: // Volume
|
||||||
@ -210,13 +252,9 @@ class ParticleSystem {
|
|||||||
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++;
|
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++;
|
||||||
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++;
|
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++;
|
||||||
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++;
|
instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++;
|
||||||
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
instancedData.set(i, p.sr); i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object.data.geom.setupInstanced(instancedData, 3, Usage.StaticUsage);
|
object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fhash(n: Int): Float {
|
function fhash(n: Int): Float {
|
||||||
@ -255,10 +293,11 @@ class ParticleSystem {
|
|||||||
|
|
||||||
class Particle {
|
class Particle {
|
||||||
public var i: Int;
|
public var i: Int;
|
||||||
public var px = 0.0;
|
|
||||||
public var py = 0.0;
|
public var x = 0.0;
|
||||||
public var pz = 0.0;
|
public var y = 0.0;
|
||||||
public var sr = 1.0; // Size random
|
public var z = 0.0;
|
||||||
|
|
||||||
public var cameraDistance: Float;
|
public var cameraDistance: Float;
|
||||||
|
|
||||||
public function new(i: Int) {
|
public function new(i: Int) {
|
||||||
|
@ -80,7 +80,7 @@ class Tilesheet {
|
|||||||
function update() {
|
function update() {
|
||||||
if (!ready || paused || action.start >= action.end) return;
|
if (!ready || paused || action.start >= action.end) return;
|
||||||
|
|
||||||
time += Time.realDelta;
|
time += Time.renderDelta;
|
||||||
|
|
||||||
var frameTime = 1 / raw.framerate;
|
var frameTime = 1 / raw.framerate;
|
||||||
var framesToAdvance = 0;
|
var framesToAdvance = 0;
|
||||||
|
@ -1109,6 +1109,26 @@ class Uniforms {
|
|||||||
case "_texUnpack": {
|
case "_texUnpack": {
|
||||||
f = texUnpack != null ? texUnpack : 1.0;
|
f = texUnpack != null ? texUnpack : 1.0;
|
||||||
}
|
}
|
||||||
|
#if lnx_particles
|
||||||
|
case "_particleSizeRandom": {
|
||||||
|
var mo = cast(object, MeshObject);
|
||||||
|
if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) {
|
||||||
|
f = mo.particleOwner.particleSystems[mo.particleIndex].getSizeRandom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "_particleRandom": {
|
||||||
|
var mo = cast(object, MeshObject);
|
||||||
|
if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) {
|
||||||
|
f = mo.particleOwner.particleSystems[mo.particleIndex].getRandom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "_particleSize": {
|
||||||
|
var mo = cast(object, MeshObject);
|
||||||
|
if (mo.particleOwner != null && mo.particleOwner.particleSystems != null) {
|
||||||
|
f = mo.particleOwner.particleSystems[mo.particleIndex].getSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f == null && externalFloatLinks != null) {
|
if (f == null && externalFloatLinks != null) {
|
||||||
|
@ -8,15 +8,23 @@ class Time {
|
|||||||
return 1 / frequency;
|
return 1 / frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var scale = 1.0;
|
static var _fixedStep: Null<Float>;
|
||||||
public static var delta(get, never): Float;
|
public static var fixedStep(get, never): Float;
|
||||||
static function get_delta(): Float {
|
static function get_fixedStep(): Float {
|
||||||
if (frequency == null) initFrequency();
|
return _fixedStep;
|
||||||
return (1 / frequency) * scale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static var last = 0.0;
|
|
||||||
public static var realDelta = 0.0;
|
public static function initFixedStep(value: Float = 1 / 60) {
|
||||||
|
_fixedStep = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static var lastTime = 0.0;
|
||||||
|
public static var delta = 0.0;
|
||||||
|
|
||||||
|
static var lastRenderTime = 0.0;
|
||||||
|
public static var renderDelta = 0.0;
|
||||||
public static inline function time(): Float {
|
public static inline function time(): Float {
|
||||||
return kha.Scheduler.time();
|
return kha.Scheduler.time();
|
||||||
}
|
}
|
||||||
@ -31,7 +39,12 @@ class Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function update() {
|
public static function update() {
|
||||||
realDelta = realTime() - last;
|
delta = (realTime() - lastTime) * scale;
|
||||||
last = realTime();
|
lastTime = realTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render() {
|
||||||
|
renderDelta = (realTime() - lastRenderTime) * scale;
|
||||||
|
lastRenderTime = realTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class AddParticleToObjectNode extends LogicNode {
|
|||||||
|
|
||||||
var mobjTo = cast(objTo, iron.object.MeshObject);
|
var mobjTo = cast(objTo, iron.object.MeshObject);
|
||||||
|
|
||||||
mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: psys.r.name});
|
mobjTo.setupParticleSystem(iron.Scene.active.raw.name, {name: 'LnxPS', seed: 0, particle: @:privateAccess psys.r.name});
|
||||||
|
|
||||||
mobjTo.render_emitter = inputs[4].get();
|
mobjTo.render_emitter = inputs[4].get();
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ class AddParticleToObjectNode extends LogicNode {
|
|||||||
|
|
||||||
var oslot: Int = mobjTo.particleSystems.length-1;
|
var oslot: Int = mobjTo.particleSystems.length-1;
|
||||||
var opsys = mobjTo.particleSystems[oslot];
|
var opsys = mobjTo.particleSystems[oslot];
|
||||||
opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo);
|
@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var sceneName: String = inputs[1].get();
|
var sceneName: String = inputs[1].get();
|
||||||
@ -82,7 +82,7 @@ class AddParticleToObjectNode extends LogicNode {
|
|||||||
|
|
||||||
var oslot: Int = mobjTo.particleSystems.length-1;
|
var oslot: Int = mobjTo.particleSystems.length-1;
|
||||||
var opsys = mobjTo.particleSystems[oslot];
|
var opsys = mobjTo.particleSystems[oslot];
|
||||||
opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo);
|
@:privateAccess opsys.setupGeomGpu(mobjTo.particleChildren[oslot], mobjTo);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ class GetFPSNode extends LogicNode {
|
|||||||
|
|
||||||
override function get(from: Int): Dynamic {
|
override function get(from: Int): Dynamic {
|
||||||
if (from == 0) {
|
if (from == 0) {
|
||||||
var fps = Math.round(1 / iron.system.Time.realDelta);
|
var fps = Math.round(1 / iron.system.Time.renderDelta);
|
||||||
if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) {
|
if ((fps == Math.POSITIVE_INFINITY) || (fps == Math.NEGATIVE_INFINITY) || (Math.isNaN(fps))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -25,37 +25,37 @@ class GetParticleDataNode extends LogicNode {
|
|||||||
|
|
||||||
return switch (from) {
|
return switch (from) {
|
||||||
case 0:
|
case 0:
|
||||||
psys.r.name;
|
@:privateAccess psys.r.name;
|
||||||
case 1:
|
case 1:
|
||||||
psys.r.particle_size;
|
@:privateAccess psys.r.particle_size;
|
||||||
case 2:
|
case 2:
|
||||||
psys.r.frame_start;
|
@:privateAccess psys.r.frame_start;
|
||||||
case 3:
|
case 3:
|
||||||
psys.r.frame_end;
|
@:privateAccess psys.r.frame_end;
|
||||||
case 4:
|
case 4:
|
||||||
psys.lifetime;
|
@:privateAccess psys.lifetime;
|
||||||
case 5:
|
case 5:
|
||||||
psys.r.lifetime;
|
@:privateAccess psys.r.lifetime;
|
||||||
case 6:
|
case 6:
|
||||||
psys.r.emit_from;
|
@:privateAccess psys.r.emit_from;
|
||||||
case 7:
|
case 7:
|
||||||
new iron.math.Vec3(psys.alignx*2, psys.aligny*2, psys.alignz*2);
|
new iron.math.Vec3(@:privateAccess psys.alignx*2, @:privateAccess psys.aligny*2, @:privateAccess psys.alignz*2);
|
||||||
case 8:
|
case 8:
|
||||||
psys.r.factor_random;
|
@:privateAccess psys.r.factor_random;
|
||||||
case 9:
|
case 9:
|
||||||
new iron.math.Vec3(psys.gx, psys.gy, psys.gz);
|
new iron.math.Vec3(@:privateAccess psys.gx, @:privateAccess psys.gy, @:privateAccess psys.gz);
|
||||||
case 10:
|
case 10:
|
||||||
psys.r.weight_gravity;
|
@:privateAccess psys.r.weight_gravity;
|
||||||
case 11:
|
case 11:
|
||||||
psys.speed;
|
psys.speed;
|
||||||
case 12:
|
case 12:
|
||||||
psys.time;
|
@:privateAccess psys.time;
|
||||||
case 13:
|
case 13:
|
||||||
psys.lap;
|
@:privateAccess psys.lap;
|
||||||
case 14:
|
case 14:
|
||||||
psys.lapTime;
|
@:privateAccess psys.lapTime;
|
||||||
case 15:
|
case 15:
|
||||||
psys.count;
|
@:privateAccess psys.count;
|
||||||
default:
|
default:
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class GetParticleNode extends LogicNode {
|
|||||||
var names: Array<String> = [];
|
var names: Array<String> = [];
|
||||||
if (mo.particleSystems != null)
|
if (mo.particleSystems != null)
|
||||||
for (psys in mo.particleSystems)
|
for (psys in mo.particleSystems)
|
||||||
names.push(psys.r.name);
|
names.push(@:privateAccess psys.r.name);
|
||||||
return names;
|
return names;
|
||||||
case 1:
|
case 1:
|
||||||
return mo.particleSystems != null ? mo.particleSystems.length : 0;
|
return mo.particleSystems != null ? mo.particleSystems.length : 0;
|
||||||
|
@ -33,7 +33,7 @@ class RemoveParticleFromObjectNode extends LogicNode {
|
|||||||
if (property0 == 'Name'){
|
if (property0 == 'Name'){
|
||||||
var name: String = inputs[2].get();
|
var name: String = inputs[2].get();
|
||||||
for (i => psys in mo.particleSystems){
|
for (i => psys in mo.particleSystems){
|
||||||
if (psys.r.name == name){ slot = i; break; }
|
if (@:privateAccess psys.r.name == name){ slot = i; break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else slot = inputs[2].get();
|
else slot = inputs[2].get();
|
||||||
|
@ -24,43 +24,43 @@ class SetParticleDataNode extends LogicNode {
|
|||||||
|
|
||||||
switch (property0) {
|
switch (property0) {
|
||||||
case 'Particle Size':
|
case 'Particle Size':
|
||||||
psys.r.particle_size = inputs[3].get();
|
@:privateAccess psys.r.particle_size = inputs[3].get();
|
||||||
case 'Frame Start':
|
case 'Frame Start':
|
||||||
psys.r.frame_start = inputs[3].get();
|
@:privateAccess psys.r.frame_start = inputs[3].get();
|
||||||
psys.animtime = (psys.r.frame_end - psys.r.frame_start) / psys.frameRate;
|
@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate;
|
||||||
psys.spawnRate = ((psys.r.frame_end - psys.r.frame_start) / psys.count) / psys.frameRate;
|
@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate;
|
||||||
case 'Frame End':
|
case 'Frame End':
|
||||||
psys.r.frame_end = inputs[3].get();
|
@:privateAccess psys.r.frame_end = inputs[3].get();
|
||||||
psys.animtime = (psys.r.frame_end - psys.r.frame_start) / psys.frameRate;
|
@:privateAccess psys.animtime = (@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.frameRate;
|
||||||
psys.spawnRate = ((psys.r.frame_end - psys.r.frame_start) / psys.count) / psys.frameRate;
|
@:privateAccess psys.spawnRate = ((@:privateAccess psys.r.frame_end - @:privateAccess psys.r.frame_start) / @:privateAccess psys.count) / @:privateAccess psys.frameRate;
|
||||||
case 'Lifetime':
|
case 'Lifetime':
|
||||||
psys.lifetime = inputs[3].get() / psys.frameRate;
|
@:privateAccess psys.lifetime = inputs[3].get() / @:privateAccess psys.frameRate;
|
||||||
case 'Lifetime Random':
|
case 'Lifetime Random':
|
||||||
psys.r.lifetime_random = inputs[3].get();
|
@:privateAccess psys.r.lifetime_random = inputs[3].get();
|
||||||
case 'Emit From':
|
case 'Emit From':
|
||||||
var emit_from: Int = inputs[3].get();
|
var emit_from: Int = inputs[3].get();
|
||||||
if (emit_from == 0 || emit_from == 1 || emit_from == 2) {
|
if (emit_from == 0 || emit_from == 1 || emit_from == 2) {
|
||||||
psys.r.emit_from = emit_from;
|
@:privateAccess psys.r.emit_from = emit_from;
|
||||||
psys.setupGeomGpu(mo.particleChildren != null ? mo.particleChildren[slot] : cast(iron.Scene.active.getChild(psys.data.raw.instance_object), iron.object.MeshObject), mo);
|
@:privateAccess psys.setupGeomGpu(mo.particleChildren != null ? mo.particleChildren[slot] : cast(iron.Scene.active.getChild(psys.data.raw.instance_object), iron.object.MeshObject), mo);
|
||||||
}
|
}
|
||||||
case 'Velocity':
|
case 'Velocity':
|
||||||
var vel: iron.math.Vec3 = inputs[3].get();
|
var vel: iron.math.Vec3 = inputs[3].get();
|
||||||
psys.alignx = vel.x / 2;
|
@:privateAccess psys.alignx = vel.x / 2;
|
||||||
psys.aligny = vel.y / 2;
|
@:privateAccess psys.aligny = vel.y / 2;
|
||||||
psys.alignz = vel.z / 2;
|
@:privateAccess psys.alignz = vel.z / 2;
|
||||||
case 'Velocity Random':
|
case 'Velocity Random':
|
||||||
psys.r.factor_random = inputs[3].get();
|
psys.r.factor_random = inputs[3].get();
|
||||||
case 'Weight Gravity':
|
case 'Weight Gravity':
|
||||||
psys.r.weight_gravity = inputs[3].get();
|
psys.r.weight_gravity = inputs[3].get();
|
||||||
if (iron.Scene.active.raw.gravity != null) {
|
if (iron.Scene.active.raw.gravity != null) {
|
||||||
psys.gx = iron.Scene.active.raw.gravity[0] * psys.r.weight_gravity;
|
@:privateAccess psys.gx = iron.Scene.active.raw.gravity[0] * @:privateAccess psys.r.weight_gravity;
|
||||||
psys.gy = iron.Scene.active.raw.gravity[1] * psys.r.weight_gravity;
|
@:privateAccess psys.gy = iron.Scene.active.raw.gravity[1] * @:privateAccess psys.r.weight_gravity;
|
||||||
psys.gz = iron.Scene.active.raw.gravity[2] * psys.r.weight_gravity;
|
@:privateAccess psys.gz = iron.Scene.active.raw.gravity[2] * @:privateAccess psys.r.weight_gravity;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
psys.gx = 0;
|
@:privateAccess psys.gx = 0;
|
||||||
psys.gy = 0;
|
@:privateAccess psys.gy = 0;
|
||||||
psys.gz = -9.81 * psys.r.weight_gravity;
|
@:privateAccess psys.gz = -9.81 * @:privateAccess psys.r.weight_gravity;
|
||||||
}
|
}
|
||||||
case 'Speed':
|
case 'Speed':
|
||||||
psys.speed = inputs[3].get();
|
psys.speed = inputs[3].get();
|
||||||
|
@ -101,7 +101,7 @@ class PhysicsWorld extends Trait {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10, debugDrawMode: DebugDrawMode = NoDebug) {
|
public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10, fixedStep = 1 / 60, debugDrawMode: DebugDrawMode = NoDebug) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (nullvec) {
|
if (nullvec) {
|
||||||
@ -120,6 +120,7 @@ class PhysicsWorld extends Trait {
|
|||||||
this.timeScale = timeScale;
|
this.timeScale = timeScale;
|
||||||
this.maxSteps = maxSteps;
|
this.maxSteps = maxSteps;
|
||||||
this.solverIterations = solverIterations;
|
this.solverIterations = solverIterations;
|
||||||
|
Time.initFixedStep(fixedStep);
|
||||||
|
|
||||||
// First scene
|
// First scene
|
||||||
if (active == null) {
|
if (active == null) {
|
||||||
@ -136,9 +137,9 @@ class PhysicsWorld extends Trait {
|
|||||||
conMap = new Map();
|
conMap = new Map();
|
||||||
active = this;
|
active = this;
|
||||||
|
|
||||||
// Ensure physics are updated first in the lateUpdate list
|
// Ensure physics are updated first in the fixedUpdate list
|
||||||
_lateUpdate = [lateUpdate];
|
_fixedUpdate = [fixedUpdate];
|
||||||
@:privateAccess iron.App.traitLateUpdates.insert(0, lateUpdate);
|
@:privateAccess iron.App.traitFixedUpdates.insert(0, fixedUpdate);
|
||||||
|
|
||||||
setDebugDrawMode(debugDrawMode);
|
setDebugDrawMode(debugDrawMode);
|
||||||
|
|
||||||
@ -298,8 +299,8 @@ class PhysicsWorld extends Trait {
|
|||||||
return rb;
|
return rb;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lateUpdate() {
|
function fixedUpdate() {
|
||||||
var t = Time.delta * timeScale;
|
var t = Time.fixedStep * timeScale * Time.scale;
|
||||||
if (t == 0.0) return; // Simulation paused
|
if (t == 0.0) return; // Simulation paused
|
||||||
|
|
||||||
#if lnx_debug
|
#if lnx_debug
|
||||||
@ -308,13 +309,10 @@ class PhysicsWorld extends Trait {
|
|||||||
|
|
||||||
if (preUpdates != null) for (f in preUpdates) f();
|
if (preUpdates != null) for (f in preUpdates) f();
|
||||||
|
|
||||||
//Bullet physics fixed timescale
|
|
||||||
var fixedTime = 1.0 / 60;
|
|
||||||
|
|
||||||
//This condition must be satisfied to not loose time
|
//This condition must be satisfied to not loose time
|
||||||
var currMaxSteps = t < (fixedTime * maxSteps) ? maxSteps : 1;
|
var currMaxSteps = t < (Time.fixedStep * maxSteps) ? maxSteps : 1;
|
||||||
|
|
||||||
world.stepSimulation(t, currMaxSteps, fixedTime);
|
world.stepSimulation(t, currMaxSteps, Time.fixedStep);
|
||||||
updateContacts();
|
updateContacts();
|
||||||
|
|
||||||
for (rb in rbMap) @:privateAccess rb.physicsUpdate();
|
for (rb in rbMap) @:privateAccess rb.physicsUpdate();
|
||||||
|
@ -2,11 +2,13 @@ package leenkx.trait.physics.bullet;
|
|||||||
|
|
||||||
#if lnx_bullet
|
#if lnx_bullet
|
||||||
|
|
||||||
|
import leenkx.math.Helper;
|
||||||
|
import iron.data.MeshData;
|
||||||
import iron.math.Vec4;
|
import iron.math.Vec4;
|
||||||
import iron.math.Quat;
|
import iron.math.Quat;
|
||||||
import iron.object.Transform;
|
import iron.object.Transform;
|
||||||
import iron.object.MeshObject;
|
import iron.object.MeshObject;
|
||||||
import iron.data.MeshData;
|
import iron.system.Time;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
RigidBody is used to allow objects to interact with Physics in your game including collisions and gravity.
|
RigidBody is used to allow objects to interact with Physics in your game including collisions and gravity.
|
||||||
@ -76,6 +78,14 @@ class RigidBody extends iron.Trait {
|
|||||||
static var triangleMeshCache = new Map<MeshData, bullet.Bt.TriangleMesh>();
|
static var triangleMeshCache = new Map<MeshData, bullet.Bt.TriangleMesh>();
|
||||||
static var usersCache = new Map<MeshData, Int>();
|
static var usersCache = new Map<MeshData, Int>();
|
||||||
|
|
||||||
|
// Interpolation
|
||||||
|
var interpolate: Bool = false;
|
||||||
|
var time: Float = 0.0;
|
||||||
|
var currentPos: bullet.Bt.Vector3 = new bullet.Bt.Vector3(0, 0, 0);
|
||||||
|
var prevPos: bullet.Bt.Vector3 = new bullet.Bt.Vector3(0, 0, 0);
|
||||||
|
var currentRot: bullet.Bt.Quaternion = new bullet.Bt.Quaternion(0, 0, 0, 1);
|
||||||
|
var prevRot: bullet.Bt.Quaternion = new bullet.Bt.Quaternion(0, 0, 0, 1);
|
||||||
|
|
||||||
public function new(shape = Shape.Box, mass = 1.0, friction = 0.5, restitution = 0.0, group = 1, mask = 1,
|
public function new(shape = Shape.Box, mass = 1.0, friction = 0.5, restitution = 0.0, group = 1, mask = 1,
|
||||||
params: RigidBodyParams = null, flags: RigidBodyFlags = null) {
|
params: RigidBodyParams = null, flags: RigidBodyFlags = null) {
|
||||||
super();
|
super();
|
||||||
@ -85,7 +95,7 @@ class RigidBody extends iron.Trait {
|
|||||||
vec1 = new bullet.Bt.Vector3(0, 0, 0);
|
vec1 = new bullet.Bt.Vector3(0, 0, 0);
|
||||||
vec2 = new bullet.Bt.Vector3(0, 0, 0);
|
vec2 = new bullet.Bt.Vector3(0, 0, 0);
|
||||||
vec3 = new bullet.Bt.Vector3(0, 0, 0);
|
vec3 = new bullet.Bt.Vector3(0, 0, 0);
|
||||||
quat1 = new bullet.Bt.Quaternion(0, 0, 0, 0);
|
quat1 = new bullet.Bt.Quaternion(0, 0, 0, 1);
|
||||||
trans1 = new bullet.Bt.Transform();
|
trans1 = new bullet.Bt.Transform();
|
||||||
trans2 = new bullet.Bt.Transform();
|
trans2 = new bullet.Bt.Transform();
|
||||||
}
|
}
|
||||||
@ -117,6 +127,7 @@ class RigidBody extends iron.Trait {
|
|||||||
animated: false,
|
animated: false,
|
||||||
trigger: false,
|
trigger: false,
|
||||||
ccd: false,
|
ccd: false,
|
||||||
|
interpolate: false,
|
||||||
staticObj: false,
|
staticObj: false,
|
||||||
useDeactivation: true
|
useDeactivation: true
|
||||||
};
|
};
|
||||||
@ -131,6 +142,7 @@ class RigidBody extends iron.Trait {
|
|||||||
this.animated = flags.animated;
|
this.animated = flags.animated;
|
||||||
this.trigger = flags.trigger;
|
this.trigger = flags.trigger;
|
||||||
this.ccd = flags.ccd;
|
this.ccd = flags.ccd;
|
||||||
|
this.interpolate = flags.interpolate;
|
||||||
this.staticObj = flags.staticObj;
|
this.staticObj = flags.staticObj;
|
||||||
this.useDeactivation = flags.useDeactivation;
|
this.useDeactivation = flags.useDeactivation;
|
||||||
|
|
||||||
@ -153,6 +165,7 @@ class RigidBody extends iron.Trait {
|
|||||||
if (!Std.isOfType(object, MeshObject)) return; // No mesh data
|
if (!Std.isOfType(object, MeshObject)) return; // No mesh data
|
||||||
|
|
||||||
transform = object.transform;
|
transform = object.transform;
|
||||||
|
transform.buildMatrix();
|
||||||
physics = leenkx.trait.physics.PhysicsWorld.active;
|
physics = leenkx.trait.physics.PhysicsWorld.active;
|
||||||
|
|
||||||
if (shape == Shape.Box) {
|
if (shape == Shape.Box) {
|
||||||
@ -244,6 +257,9 @@ class RigidBody extends iron.Trait {
|
|||||||
quat1.setValue(quat.x, quat.y, quat.z, quat.w);
|
quat1.setValue(quat.x, quat.y, quat.z, quat.w);
|
||||||
trans1.setRotation(quat1);
|
trans1.setRotation(quat1);
|
||||||
|
|
||||||
|
currentPos.setValue(vec1.x(), vec1.y(), vec1.z());
|
||||||
|
currentRot.setValue(quat.x, quat.y, quat.z, quat.w);
|
||||||
|
|
||||||
var centerOfMassOffset = trans2;
|
var centerOfMassOffset = trans2;
|
||||||
centerOfMassOffset.setIdentity();
|
centerOfMassOffset.setIdentity();
|
||||||
motionState = new bullet.Bt.DefaultMotionState(trans1, centerOfMassOffset);
|
motionState = new bullet.Bt.DefaultMotionState(trans1, centerOfMassOffset);
|
||||||
@ -307,6 +323,7 @@ class RigidBody extends iron.Trait {
|
|||||||
|
|
||||||
physics.addRigidBody(this);
|
physics.addRigidBody(this);
|
||||||
notifyOnRemove(removeFromWorld);
|
notifyOnRemove(removeFromWorld);
|
||||||
|
if (!animated) notifyOnUpdate(update);
|
||||||
|
|
||||||
if (onReady != null) onReady();
|
if (onReady != null) onReady();
|
||||||
|
|
||||||
@ -317,26 +334,71 @@ class RigidBody extends iron.Trait {
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
function physicsUpdate() {
|
|
||||||
if (!ready) return;
|
|
||||||
if (animated) {
|
|
||||||
syncTransform();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var trans = body.getWorldTransform();
|
|
||||||
var p = trans.getOrigin();
|
|
||||||
var q = trans.getRotation();
|
|
||||||
|
|
||||||
transform.loc.set(p.x(), p.y(), p.z());
|
function update() {
|
||||||
transform.rot.set(q.x(), q.y(), q.z(), q.w());
|
if (interpolate) {
|
||||||
|
time += Time.delta;
|
||||||
|
|
||||||
|
while (time >= Time.fixedStep) {
|
||||||
|
time -= Time.fixedStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
var t: Float = time / Time.fixedStep;
|
||||||
|
t = Helper.clamp(t, 0, 1);
|
||||||
|
|
||||||
|
var tx: Float = prevPos.x() * (1.0 - t) + currentPos.x() * t;
|
||||||
|
var ty: Float = prevPos.y() * (1.0 - t) + currentPos.y() * t;
|
||||||
|
var tz: Float = prevPos.z() * (1.0 - t) + currentPos.z() * t;
|
||||||
|
|
||||||
|
var tRot: bullet.Bt.Quaternion = nlerp(prevRot, currentRot, t);
|
||||||
|
|
||||||
|
transform.loc.set(tx, ty, tz, 1.0);
|
||||||
|
transform.rot.set(tRot.x(), tRot.y(), tRot.z(), tRot.w());
|
||||||
|
} else {
|
||||||
|
transform.loc.set(currentPos.x(), currentPos.y(), currentPos.z(), 1.0);
|
||||||
|
transform.rot.set(currentRot.x(), currentRot.y(), currentRot.z(), currentRot.w());
|
||||||
|
}
|
||||||
|
|
||||||
if (object.parent != null) {
|
if (object.parent != null) {
|
||||||
var ptransform = object.parent.transform;
|
var ptransform = object.parent.transform;
|
||||||
transform.loc.x -= ptransform.worldx();
|
transform.loc.x -= ptransform.worldx();
|
||||||
transform.loc.y -= ptransform.worldy();
|
transform.loc.y -= ptransform.worldy();
|
||||||
transform.loc.z -= ptransform.worldz();
|
transform.loc.z -= ptransform.worldz();
|
||||||
}
|
}
|
||||||
transform.clearDelta();
|
|
||||||
transform.buildMatrix();
|
transform.buildMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nlerp(q1: bullet.Bt.Quaternion, q2: bullet.Bt.Quaternion, t: Float): bullet.Bt.Quaternion {
|
||||||
|
var dot = q1.x() * q2.x() + q1.y() * q2.y() + q1.z() * q2.z() + q1.w() * q2.w();
|
||||||
|
var _q2 = dot < 0 ? new bullet.Bt.Quaternion(-q2.x(), -q2.y(), -q2.z(), -q2.w()) : q2;
|
||||||
|
|
||||||
|
var x = q1.x() * (1.0 - t) + _q2.x() * t;
|
||||||
|
var y = q1.y() * (1.0 - t) + _q2.y() * t;
|
||||||
|
var z = q1.z() * (1.0 - t) + _q2.z() * t;
|
||||||
|
var w = q1.w() * (1.0 - t) + _q2.w() * t;
|
||||||
|
|
||||||
|
var len = Math.sqrt(x * x + y * y + z * z + w * w);
|
||||||
|
return new bullet.Bt.Quaternion(x / len, y / len, z / len, w / len);
|
||||||
|
}
|
||||||
|
|
||||||
|
function physicsUpdate() {
|
||||||
|
if (!ready) return;
|
||||||
|
if (animated) {
|
||||||
|
syncTransform();
|
||||||
|
} else {
|
||||||
|
if (interpolate) {
|
||||||
|
prevPos.setValue(currentPos.x(), currentPos.y(), currentPos.z());
|
||||||
|
prevRot.setValue(currentRot.x(), currentRot.y(), currentRot.z(), currentRot.w());
|
||||||
|
}
|
||||||
|
var trans = body.getWorldTransform();
|
||||||
|
var p = trans.getOrigin();
|
||||||
|
var q = trans.getRotation();
|
||||||
|
transform.clearDelta();
|
||||||
|
// transform.buildMatrix();
|
||||||
|
currentPos.setValue(p.x(), p.y(), p.z());
|
||||||
|
currentRot.setValue(q.x(), q.y(), q.z(), q.w());
|
||||||
|
|
||||||
|
|
||||||
#if hl
|
#if hl
|
||||||
p.delete();
|
p.delete();
|
||||||
@ -689,6 +751,7 @@ typedef RigidBodyFlags = {
|
|||||||
var animated: Bool;
|
var animated: Bool;
|
||||||
var trigger: Bool;
|
var trigger: Bool;
|
||||||
var ccd: Bool;
|
var ccd: Bool;
|
||||||
|
var interpolate: Bool;
|
||||||
var staticObj: Bool;
|
var staticObj: Bool;
|
||||||
var useDeactivation: Bool;
|
var useDeactivation: Bool;
|
||||||
}
|
}
|
||||||
|
@ -2297,6 +2297,8 @@ class LeenkxExporter:
|
|||||||
out_particlesys = {
|
out_particlesys = {
|
||||||
'name': particleRef[1]["structName"],
|
'name': particleRef[1]["structName"],
|
||||||
'type': 0 if psettings.type == 'EMITTER' else 1, # HAIR
|
'type': 0 if psettings.type == 'EMITTER' else 1, # HAIR
|
||||||
|
'auto_start': psettings.lnx_auto_start,
|
||||||
|
'is_unique': psettings.lnx_is_unique,
|
||||||
'loop': psettings.lnx_loop,
|
'loop': psettings.lnx_loop,
|
||||||
# Emission
|
# Emission
|
||||||
'count': int(psettings.count * psettings.lnx_count_mult),
|
'count': int(psettings.count * psettings.lnx_count_mult),
|
||||||
@ -2813,6 +2815,7 @@ class LeenkxExporter:
|
|||||||
body_flags['animated'] = rb.kinematic
|
body_flags['animated'] = rb.kinematic
|
||||||
body_flags['trigger'] = bobject.lnx_rb_trigger
|
body_flags['trigger'] = bobject.lnx_rb_trigger
|
||||||
body_flags['ccd'] = bobject.lnx_rb_ccd
|
body_flags['ccd'] = bobject.lnx_rb_ccd
|
||||||
|
body_flags['interpolate'] = bobject.lnx_rb_interpolate
|
||||||
body_flags['staticObj'] = is_static
|
body_flags['staticObj'] = is_static
|
||||||
body_flags['useDeactivation'] = rb.use_deactivation
|
body_flags['useDeactivation'] = rb.use_deactivation
|
||||||
x['parameters'].append(lnx.utils.get_haxe_json_string(body_params))
|
x['parameters'].append(lnx.utils.get_haxe_json_string(body_params))
|
||||||
@ -3037,7 +3040,7 @@ class LeenkxExporter:
|
|||||||
|
|
||||||
rbw = self.scene.rigidbody_world
|
rbw = self.scene.rigidbody_world
|
||||||
if rbw is not None and rbw.enabled:
|
if rbw is not None and rbw.enabled:
|
||||||
out_trait['parameters'] = [str(rbw.time_scale), str(rbw.substeps_per_frame), str(rbw.solver_iterations)]
|
out_trait['parameters'] = [str(rbw.time_scale), str(rbw.substeps_per_frame), str(rbw.solver_iterations), str(wrd.lnx_physics_fixed_step)]
|
||||||
|
|
||||||
if phys_pkg == 'bullet' or phys_pkg == 'oimo':
|
if phys_pkg == 'bullet' or phys_pkg == 'oimo':
|
||||||
debug_draw_mode = 1 if wrd.lnx_physics_dbg_draw_wireframe else 0
|
debug_draw_mode = 1 if wrd.lnx_physics_dbg_draw_wireframe else 0
|
||||||
|
@ -87,6 +87,7 @@ def on_operator_post(operator_id: str) -> None:
|
|||||||
target_obj.lnx_rb_trigger = source_obj.lnx_rb_trigger
|
target_obj.lnx_rb_trigger = source_obj.lnx_rb_trigger
|
||||||
target_obj.lnx_rb_deactivation_time = source_obj.lnx_rb_deactivation_time
|
target_obj.lnx_rb_deactivation_time = source_obj.lnx_rb_deactivation_time
|
||||||
target_obj.lnx_rb_ccd = source_obj.lnx_rb_ccd
|
target_obj.lnx_rb_ccd = source_obj.lnx_rb_ccd
|
||||||
|
target_obj.lnx_rb_interpolate = source_obj.lnx_rb_interpolate
|
||||||
target_obj.lnx_rb_collision_filter_mask = source_obj.lnx_rb_collision_filter_mask
|
target_obj.lnx_rb_collision_filter_mask = source_obj.lnx_rb_collision_filter_mask
|
||||||
|
|
||||||
elif operator_id == "NODE_OT_new_node_tree":
|
elif operator_id == "NODE_OT_new_node_tree":
|
||||||
|
@ -82,25 +82,34 @@ def parse_clamp(node: bpy.types.ShaderNodeClamp, out_socket: bpy.types.NodeSocke
|
|||||||
|
|
||||||
|
|
||||||
def parse_valtorgb(node: bpy.types.ShaderNodeValToRGB, out_socket: bpy.types.NodeSocket, state: ParserState) -> Union[floatstr, vec3str]:
|
def parse_valtorgb(node: bpy.types.ShaderNodeValToRGB, out_socket: bpy.types.NodeSocket, state: ParserState) -> Union[floatstr, vec3str]:
|
||||||
# Alpha (TODO: make ColorRamp calculation vec4-based and split afterwards)
|
|
||||||
if out_socket == node.outputs[1]:
|
|
||||||
return '1.0'
|
|
||||||
|
|
||||||
input_fac: bpy.types.NodeSocket = node.inputs[0]
|
input_fac: bpy.types.NodeSocket = node.inputs[0]
|
||||||
|
alpha_out = out_socket == node.outputs[1]
|
||||||
fac: str = c.parse_value_input(input_fac) if input_fac.is_linked else c.to_vec1(input_fac.default_value)
|
fac: str = c.parse_value_input(input_fac) if input_fac.is_linked else c.to_vec1(input_fac.default_value)
|
||||||
interp = node.color_ramp.interpolation
|
interp = node.color_ramp.interpolation
|
||||||
elems = node.color_ramp.elements
|
elems = node.color_ramp.elements
|
||||||
|
|
||||||
if len(elems) == 1:
|
if len(elems) == 1:
|
||||||
return c.to_vec3(elems[0].color)
|
if alpha_out:
|
||||||
|
return c.to_vec1(elems[0].color[3]) # Return alpha from the color
|
||||||
|
else:
|
||||||
|
return c.to_vec3(elems[0].color) # Return RGB
|
||||||
|
|
||||||
# Write color array
|
name_prefix = c.node_name(node.name).upper()
|
||||||
# The last entry is included twice so that the interpolation
|
|
||||||
# between indices works (no out of bounds error)
|
if alpha_out:
|
||||||
cols_var = c.node_name(node.name).upper() + '_COLS'
|
cols_var = name_prefix + '_ALPHAS'
|
||||||
|
else:
|
||||||
|
cols_var = name_prefix + '_COLS'
|
||||||
|
|
||||||
if state.current_pass == ParserPass.REGULAR:
|
if state.current_pass == ParserPass.REGULAR:
|
||||||
|
if alpha_out:
|
||||||
|
cols_entries = ', '.join(f'{elem.color[3]}' for elem in elems)
|
||||||
|
# Add last value twice to avoid out of bounds access
|
||||||
|
cols_entries += f', {elems[len(elems) - 1].color[3]}'
|
||||||
|
state.curshader.add_const("float", cols_var, cols_entries, array_size=len(elems) + 1)
|
||||||
|
else:
|
||||||
|
# Create array of RGB values for color output
|
||||||
cols_entries = ', '.join(f'vec3({elem.color[0]}, {elem.color[1]}, {elem.color[2]})' for elem in elems)
|
cols_entries = ', '.join(f'vec3({elem.color[0]}, {elem.color[1]}, {elem.color[2]})' for elem in elems)
|
||||||
cols_entries += f', vec3({elems[len(elems) - 1].color[0]}, {elems[len(elems) - 1].color[1]}, {elems[len(elems) - 1].color[2]})'
|
cols_entries += f', vec3({elems[len(elems) - 1].color[0]}, {elems[len(elems) - 1].color[1]}, {elems[len(elems) - 1].color[2]})'
|
||||||
state.curshader.add_const("vec3", cols_var, cols_entries, array_size=len(elems) + 1)
|
state.curshader.add_const("vec3", cols_var, cols_entries, array_size=len(elems) + 1)
|
||||||
@ -121,21 +130,22 @@ def parse_valtorgb(node: bpy.types.ShaderNodeValToRGB, out_socket: bpy.types.Nod
|
|||||||
|
|
||||||
# Linear interpolation
|
# Linear interpolation
|
||||||
else:
|
else:
|
||||||
# Write factor array
|
# Write factor array - same for both color and alpha
|
||||||
facs_var = c.node_name(node.name).upper() + '_FACS'
|
facs_var = name_prefix + '_FACS'
|
||||||
if state.current_pass == ParserPass.REGULAR:
|
if state.current_pass == ParserPass.REGULAR:
|
||||||
facs_entries = ', '.join(str(elem.position) for elem in elems)
|
facs_entries = ', '.join(str(elem.position) for elem in elems)
|
||||||
# Add one more entry at the rightmost position so that the
|
# Add one more entry at the rightmost position to avoid out of bounds access
|
||||||
# interpolation between indices works (no out of bounds error)
|
|
||||||
facs_entries += ', 1.0'
|
facs_entries += ', 1.0'
|
||||||
state.curshader.add_const("float", facs_var, facs_entries, array_size=len(elems) + 1)
|
state.curshader.add_const("float", facs_var, facs_entries, array_size=len(elems) + 1)
|
||||||
|
|
||||||
# Mix color
|
# Calculation for interpolation position
|
||||||
prev_stop_fac = f'{facs_var}[{index_var}]'
|
prev_stop_fac = f'{facs_var}[{index_var}]'
|
||||||
next_stop_fac = f'{facs_var}[{index_var} + 1]'
|
next_stop_fac = f'{facs_var}[{index_var} + 1]'
|
||||||
prev_stop_col = f'{cols_var}[{index_var}]'
|
prev_stop_col = f'{cols_var}[{index_var}]'
|
||||||
next_stop_col = f'{cols_var}[{index_var} + 1]'
|
next_stop_col = f'{cols_var}[{index_var} + 1]'
|
||||||
rel_pos = f'({fac_var} - {prev_stop_fac}) * (1.0 / ({next_stop_fac} - {prev_stop_fac}))'
|
rel_pos = f'({fac_var} - {prev_stop_fac}) * (1.0 / ({next_stop_fac} - {prev_stop_fac}))'
|
||||||
|
|
||||||
|
# Use mix function for both alpha and color outputs (mix works on floats too)
|
||||||
return f'mix({prev_stop_col}, {next_stop_col}, max({rel_pos}, 0.0))'
|
return f'mix({prev_stop_col}, {next_stop_col}, max({rel_pos}, 0.0))'
|
||||||
|
|
||||||
if bpy.app.version > (3, 2, 0):
|
if bpy.app.version > (3, 2, 0):
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import bpy
|
||||||
import lnx.utils
|
import lnx.utils
|
||||||
import lnx.material.mat_state as mat_state
|
import lnx.material.mat_state as mat_state
|
||||||
|
|
||||||
@ -10,6 +11,48 @@ else:
|
|||||||
|
|
||||||
def write(vert, particle_info=None, shadowmap=False):
|
def write(vert, particle_info=None, shadowmap=False):
|
||||||
|
|
||||||
|
ramp_el_len = 0
|
||||||
|
|
||||||
|
ramp_positions = []
|
||||||
|
ramp_colors_b = []
|
||||||
|
size_over_time_factor = 0
|
||||||
|
|
||||||
|
use_rotations = False
|
||||||
|
rotation_mode = 'NONE'
|
||||||
|
rotation_factor_random = 0
|
||||||
|
phase_factor = 0
|
||||||
|
phase_factor_random = 0
|
||||||
|
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
for psys in obj.particle_systems:
|
||||||
|
psettings = psys.settings
|
||||||
|
|
||||||
|
if psettings.instance_object:
|
||||||
|
if psettings.instance_object.active_material:
|
||||||
|
# FIXME: Different particle systems may share the same particle object. This ideally should check the correct `ParticleSystem` using an id or name in the particle's object material.
|
||||||
|
if psettings.instance_object.active_material.name.replace(".", "_") == vert.context.matname:
|
||||||
|
# Rotation data
|
||||||
|
use_rotations = psettings.use_rotations
|
||||||
|
rotation_mode = psettings.rotation_mode
|
||||||
|
rotation_factor_random = psettings.rotation_factor_random
|
||||||
|
phase_factor = psettings.phase_factor
|
||||||
|
phase_factor_random = psettings.phase_factor_random
|
||||||
|
|
||||||
|
# Texture slots data
|
||||||
|
if psettings.texture_slots and len(psettings.texture_slots.items()) != 0:
|
||||||
|
for tex_slot in psettings.texture_slots:
|
||||||
|
if not tex_slot: break
|
||||||
|
if not tex_slot.use_map_size: break # TODO: check also for other influences
|
||||||
|
if tex_slot.texture and tex_slot.texture.use_color_ramp:
|
||||||
|
if tex_slot.texture.color_ramp and tex_slot.texture.color_ramp.elements:
|
||||||
|
ramp_el_len = len(tex_slot.texture.color_ramp.elements.items())
|
||||||
|
for element in tex_slot.texture.color_ramp.elements:
|
||||||
|
ramp_positions.append(element.position)
|
||||||
|
ramp_colors_b.append(element.color[2])
|
||||||
|
size_over_time_factor = tex_slot.size_factor
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
# Outs
|
# Outs
|
||||||
out_index = True if particle_info != None and particle_info['index'] else False
|
out_index = True if particle_info != None and particle_info['index'] else False
|
||||||
out_age = True if particle_info != None and particle_info['age'] else False
|
out_age = True if particle_info != None and particle_info['age'] else False
|
||||||
@ -19,19 +62,50 @@ def write(vert, particle_info=None, shadowmap=False):
|
|||||||
out_velocity = True if particle_info != None and particle_info['velocity'] else False
|
out_velocity = True if particle_info != None and particle_info['velocity'] else False
|
||||||
out_angular_velocity = True if particle_info != None and particle_info['angular_velocity'] else False
|
out_angular_velocity = True if particle_info != None and particle_info['angular_velocity'] else False
|
||||||
|
|
||||||
|
# Force Leenkx to create a new shader per material ID
|
||||||
|
vert.write(f'#ifdef PARTICLE_ID_{vert.context.material.lnx_material_id}')
|
||||||
|
vert.write('#endif')
|
||||||
|
|
||||||
vert.add_uniform('mat4 pd', '_particleData')
|
vert.add_uniform('mat4 pd', '_particleData')
|
||||||
|
vert.add_uniform('float pd_size_random', '_particleSizeRandom')
|
||||||
|
vert.add_uniform('float pd_random', '_particleRandom')
|
||||||
|
vert.add_uniform('float pd_size', '_particleSize')
|
||||||
|
|
||||||
|
if ramp_el_len != 0:
|
||||||
|
vert.add_const('float', 'P_SIZE_OVER_TIME_FACTOR', str(size_over_time_factor))
|
||||||
|
for i in range(ramp_el_len):
|
||||||
|
vert.add_const('float', f'P_RAMP_POSITION_{i}', str(ramp_positions[i]))
|
||||||
|
vert.add_const('float', f'P_RAMP_COLOR_B_{i}', str(ramp_colors_b[i]))
|
||||||
|
|
||||||
str_tex_hash = "float fhash(float n) { return fract(sin(n) * 43758.5453); }\n"
|
str_tex_hash = "float fhash(float n) { return fract(sin(n) * 43758.5453); }\n"
|
||||||
vert.add_function(str_tex_hash)
|
vert.add_function(str_tex_hash)
|
||||||
|
|
||||||
|
|
||||||
|
if (ramp_el_len != 0):
|
||||||
|
str_ramp_scale = "float get_ramp_scale(float age) {\n"
|
||||||
|
|
||||||
|
for i in range(ramp_el_len):
|
||||||
|
if i == 0:
|
||||||
|
str_ramp_scale += f"if (age <= P_RAMP_POSITION_{i + 1})"
|
||||||
|
elif i == ramp_el_len - 1:
|
||||||
|
str_ramp_scale += f"return P_RAMP_COLOR_B_{ramp_el_len - 1};"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
str_ramp_scale += f"else if (age <= P_RAMP_POSITION_{i + 1})"
|
||||||
|
str_ramp_scale += f""" {{
|
||||||
|
float t = (age - P_RAMP_POSITION_{i}) / (P_RAMP_POSITION_{i + 1} - P_RAMP_POSITION_{i});
|
||||||
|
return mix(P_RAMP_COLOR_B_{i}, P_RAMP_COLOR_B_{i + 1}, t);
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
str_ramp_scale += "}\n"
|
||||||
|
vert.add_function(str_ramp_scale)
|
||||||
|
|
||||||
prep = 'float '
|
prep = 'float '
|
||||||
if out_age:
|
if out_age:
|
||||||
prep = ''
|
prep = ''
|
||||||
vert.add_out('float p_age')
|
vert.add_out('float p_age')
|
||||||
# var p_age = lapTime - p.i * spawnRate
|
# var p_age = lapTime - p.i * spawnRate
|
||||||
vert.write(prep + 'p_age = pd[3][3] - gl_InstanceID * pd[0][1];')
|
vert.write(prep + 'p_age = pd[3][3] - gl_InstanceID * pd[0][1];')
|
||||||
# p_age -= p_age * fhash(i) * r.lifetime_random;
|
|
||||||
vert.write('p_age -= p_age * fhash(gl_InstanceID) * pd[2][3];')
|
|
||||||
|
|
||||||
# Loop
|
# Loop
|
||||||
# pd[0][0] - animtime, loop stored in sign
|
# pd[0][0] - animtime, loop stored in sign
|
||||||
@ -43,13 +117,18 @@ def write(vert, particle_info=None, shadowmap=False):
|
|||||||
if out_lifetime:
|
if out_lifetime:
|
||||||
prep = ''
|
prep = ''
|
||||||
vert.add_out('float p_lifetime')
|
vert.add_out('float p_lifetime')
|
||||||
vert.write(prep + 'p_lifetime = pd[0][2];')
|
vert.write(prep + 'p_lifetime = pd[0][2] * (1 - (fhash(gl_InstanceID + 4 * pd[0][3] + pd_random) * pd[2][3]));')
|
||||||
# clip with nan
|
# clip with nan
|
||||||
vert.write('if (p_age < 0 || p_age > p_lifetime) {')
|
vert.write('if (p_age < 0 || p_age > p_lifetime) {')
|
||||||
vert.write(' gl_Position /= 0.0;')
|
vert.write(' gl_Position /= 0.0;')
|
||||||
vert.write(' return;')
|
vert.write(' return;')
|
||||||
vert.write('}')
|
vert.write('}')
|
||||||
|
|
||||||
|
if (ramp_el_len != 0):
|
||||||
|
vert.write('float n_age = clamp(p_age / p_lifetime, 0.0, 1.0);')
|
||||||
|
vert.write(f'spos.xyz *= 1 + (get_ramp_scale(n_age) - 1) * {size_over_time_factor};')
|
||||||
|
vert.write('spos.xyz *= 1 - (fhash(gl_InstanceID + 3 * pd[0][3] + pd_random) * pd_size_random);')
|
||||||
|
|
||||||
# vert.write('p_age /= 2;') # Match
|
# vert.write('p_age /= 2;') # Match
|
||||||
|
|
||||||
# object_align_factor / 2 + gxyz
|
# object_align_factor / 2 + gxyz
|
||||||
@ -57,20 +136,20 @@ def write(vert, particle_info=None, shadowmap=False):
|
|||||||
if out_velocity:
|
if out_velocity:
|
||||||
prep = ''
|
prep = ''
|
||||||
vert.add_out('vec3 p_velocity')
|
vert.add_out('vec3 p_velocity')
|
||||||
vert.write(prep + 'p_velocity = vec3(pd[1][0], pd[1][1], pd[1][2]);')
|
vert.write(prep + 'p_velocity = vec3(pd[1][0] * (1 / pd_size), pd[1][1] * (1 / pd_size), pd[1][2] * (1 / pd_size));')
|
||||||
|
|
||||||
vert.write('p_velocity.x += fhash(gl_InstanceID) * pd[1][3] - pd[1][3] / 2;')
|
vert.write('p_velocity.x += (fhash(gl_InstanceID + pd_random) * 2.0 / pd_size - 1.0 / pd_size) * pd[1][3];')
|
||||||
vert.write('p_velocity.y += fhash(gl_InstanceID + pd[0][3]) * pd[1][3] - pd[1][3] / 2;')
|
vert.write('p_velocity.y += (fhash(gl_InstanceID + pd_random + pd[0][3]) * 2.0 / pd_size - 1.0 / pd_size) * pd[1][3];')
|
||||||
vert.write('p_velocity.z += fhash(gl_InstanceID + 2 * pd[0][3]) * pd[1][3] - pd[1][3] / 2;')
|
vert.write('p_velocity.z += (fhash(gl_InstanceID + pd_random + 2 * pd[0][3]) * 2.0 / pd_size - 1.0 / pd_size) * pd[1][3];')
|
||||||
|
|
||||||
# factor_random = pd[1][3]
|
# factor_random = pd[1][3]
|
||||||
# p.i = gl_InstanceID
|
# p.i = gl_InstanceID
|
||||||
# particles.length = pd[0][3]
|
# particles.length = pd[0][3]
|
||||||
|
|
||||||
# gxyz
|
# gxyz
|
||||||
vert.write('p_velocity.x += (pd[2][0] * p_age) / 5;')
|
vert.write('p_velocity.x += (pd[2][0] / (2 * pd_size)) * p_age;')
|
||||||
vert.write('p_velocity.y += (pd[2][1] * p_age) / 5;')
|
vert.write('p_velocity.y += (pd[2][1] / (2 * pd_size)) * p_age;')
|
||||||
vert.write('p_velocity.z += (pd[2][2] * p_age) / 5;')
|
vert.write('p_velocity.z += (pd[2][2] / (2 * pd_size)) * p_age;')
|
||||||
|
|
||||||
prep = 'vec3 '
|
prep = 'vec3 '
|
||||||
if out_location:
|
if out_location:
|
||||||
@ -80,6 +159,96 @@ def write(vert, particle_info=None, shadowmap=False):
|
|||||||
|
|
||||||
vert.write('spos.xyz += p_location;')
|
vert.write('spos.xyz += p_location;')
|
||||||
|
|
||||||
|
# Rotation
|
||||||
|
if use_rotations:
|
||||||
|
if rotation_mode != 'NONE':
|
||||||
|
vert.write(f'float p_angle = ({phase_factor} + (fhash(gl_InstanceID + pd_random + 5 * pd[0][3])) * {phase_factor_random});')
|
||||||
|
vert.write('p_angle *= 3.141592;')
|
||||||
|
vert.write('float c = cos(p_angle);')
|
||||||
|
vert.write('float s = sin(p_angle);')
|
||||||
|
vert.write('vec3 center = spos.xyz - p_location;')
|
||||||
|
|
||||||
|
match rotation_mode:
|
||||||
|
case 'OB_X':
|
||||||
|
vert.write('vec3 rz = vec3(center.y, -center.x, center.z);')
|
||||||
|
vert.write('vec2 rotation = vec2(rz.y * c - rz.z * s, rz.y * s + rz.z * c);')
|
||||||
|
vert.write('spos.xyz = vec3(rz.x, rotation.x, rotation.y) + p_location;')
|
||||||
|
|
||||||
|
if (not shadowmap):
|
||||||
|
vert.write('wnormal = vec3(wnormal.y, -wnormal.x, wnormal.z);')
|
||||||
|
vert.write('vec2 n_rot = vec2(wnormal.y * c - wnormal.z * s, wnormal.y * s + wnormal.z * c);')
|
||||||
|
vert.write('wnormal = normalize(vec3(wnormal.x, n_rot.x, n_rot.y));')
|
||||||
|
case 'OB_Y':
|
||||||
|
vert.write('vec2 rotation = vec2(center.x * c + center.z * s, -center.x * s + center.z * c);')
|
||||||
|
vert.write('spos.xyz = vec3(rotation.x, center.y, rotation.y) + p_location;')
|
||||||
|
|
||||||
|
if (not shadowmap):
|
||||||
|
vert.write('wnormal = normalize(vec3(wnormal.x * c + wnormal.z * s, wnormal.y, -wnormal.x * s + wnormal.z * c));')
|
||||||
|
case 'OB_Z':
|
||||||
|
vert.write('vec3 rz = vec3(center.y, -center.x, center.z);')
|
||||||
|
vert.write('vec3 ry = vec3(-rz.z, rz.y, rz.x);')
|
||||||
|
vert.write('vec2 rotation = vec2(ry.x * c - ry.y * s, ry.x * s + ry.y * c);')
|
||||||
|
vert.write('spos.xyz = vec3(rotation.x, rotation.y, ry.z) + p_location;')
|
||||||
|
|
||||||
|
if (not shadowmap):
|
||||||
|
vert.write('wnormal = vec3(wnormal.y, -wnormal.x, wnormal.z);')
|
||||||
|
vert.write('wnormal = vec3(-wnormal.z, wnormal.y, wnormal.x);')
|
||||||
|
vert.write('vec2 n_rot = vec2(wnormal.x * c - wnormal.y * s, wnormal.x * s + wnormal.y * c);')
|
||||||
|
vert.write('wnormal = normalize(vec3(n_rot.x, n_rot.y, wnormal.z));')
|
||||||
|
case 'VEL':
|
||||||
|
vert.write('vec3 forward = -normalize(p_velocity);')
|
||||||
|
vert.write('if (length(forward) > 1e-5) {')
|
||||||
|
vert.write('vec3 world_up = vec3(0.0, 0.0, 1.0);')
|
||||||
|
|
||||||
|
vert.write('if (abs(dot(forward, world_up)) > 0.999) {')
|
||||||
|
vert.write('world_up = vec3(-1.0, 0.0, 0.0);')
|
||||||
|
vert.write('}')
|
||||||
|
|
||||||
|
vert.write('vec3 right = cross(world_up, forward);')
|
||||||
|
vert.write('if (length(right) < 1e-5) {')
|
||||||
|
vert.write('forward = -forward;')
|
||||||
|
vert.write('right = cross(world_up, forward);')
|
||||||
|
vert.write('}')
|
||||||
|
vert.write('right = normalize(right);')
|
||||||
|
vert.write('vec3 up = normalize(cross(forward, right));')
|
||||||
|
|
||||||
|
vert.write('mat3 rot = mat3(right, -forward, up);')
|
||||||
|
vert.write('mat3 phase = mat3(vec3(c, 0.0, -s), vec3(0.0, 1.0, 0.0), vec3(s, 0.0, c));')
|
||||||
|
vert.write('mat3 final_rot = rot * phase;')
|
||||||
|
vert.write('spos.xyz = final_rot * center + p_location;')
|
||||||
|
|
||||||
|
if (not shadowmap):
|
||||||
|
vert.write('wnormal = normalize(final_rot * wnormal);')
|
||||||
|
vert.write('}')
|
||||||
|
|
||||||
|
if rotation_factor_random != 0:
|
||||||
|
str_rotate_around = '''vec3 rotate_around(vec3 v, vec3 angle) {
|
||||||
|
// Rotate around X
|
||||||
|
float cx = cos(angle.x);
|
||||||
|
float sx = sin(angle.x);
|
||||||
|
v = vec3(v.x, v.y * cx - v.z * sx, v.y * sx + v.z * cx);
|
||||||
|
// Rotate around Y
|
||||||
|
float cy = cos(angle.y);
|
||||||
|
float sy = sin(angle.y);
|
||||||
|
v = vec3(v.x * cy + v.z * sy, v.y, -v.x * sy + v.z * cy);
|
||||||
|
// Rotate around Z
|
||||||
|
float cz = cos(angle.z);
|
||||||
|
float sz = sin(angle.z);
|
||||||
|
v = vec3(v.x * cz - v.y * sz, v.x * sz + v.y * cz, v.z);
|
||||||
|
return v;
|
||||||
|
}'''
|
||||||
|
vert.add_function(str_rotate_around)
|
||||||
|
|
||||||
|
vert.write(f'''vec3 r_angle = vec3((fhash(gl_InstanceID + pd_random + 6 * pd[0][3]) * 4 - 2) * {rotation_factor_random},
|
||||||
|
(fhash(gl_InstanceID + pd_random + 7 * pd[0][3]) * 4 - 2) * {rotation_factor_random},
|
||||||
|
(fhash(gl_InstanceID + pd_random + 8 * pd[0][3]) * 4 - 2) * {rotation_factor_random});''')
|
||||||
|
vert.write('vec3 r_center = spos.xyz - p_location;')
|
||||||
|
vert.write('r_center = rotate_around(r_center, r_angle);')
|
||||||
|
vert.write('spos.xyz = r_center + p_location;')
|
||||||
|
|
||||||
|
if not shadowmap:
|
||||||
|
vert.write('wnormal = normalize(rotate_around(wnormal, r_angle));')
|
||||||
|
|
||||||
# Particle fade
|
# Particle fade
|
||||||
if mat_state.material.lnx_particle_flag and lnx.utils.get_rp().lnx_particles == 'On' and mat_state.material.lnx_particle_fade:
|
if mat_state.material.lnx_particle_flag and lnx.utils.get_rp().lnx_particles == 'On' and mat_state.material.lnx_particle_fade:
|
||||||
vert.add_out('float p_fade')
|
vert.add_out('float p_fade')
|
||||||
|
@ -209,8 +209,7 @@ def make_instancing_and_skinning(mat: Material, mat_users: Dict[Material, List[O
|
|||||||
global_elems.append({'name': 'ipos', 'data': 'float3'})
|
global_elems.append({'name': 'ipos', 'data': 'float3'})
|
||||||
if 'Rot' in inst:
|
if 'Rot' in inst:
|
||||||
global_elems.append({'name': 'irot', 'data': 'float3'})
|
global_elems.append({'name': 'irot', 'data': 'float3'})
|
||||||
#HACK: checking `mat.arm_particle_flag` to force appending 'iscl' to the particle's vertex shader
|
if 'Scale' in inst:
|
||||||
if 'Scale' in inst or mat.arm_particle_flag:
|
|
||||||
global_elems.append({'name': 'iscl', 'data': 'float3'})
|
global_elems.append({'name': 'iscl', 'data': 'float3'})
|
||||||
|
|
||||||
elif inst == 'Off':
|
elif inst == 'Off':
|
||||||
|
@ -197,6 +197,10 @@ def init_properties():
|
|||||||
items=[('Bullet', 'Bullet', 'Bullet'),
|
items=[('Bullet', 'Bullet', 'Bullet'),
|
||||||
('Oimo', 'Oimo', 'Oimo')],
|
('Oimo', 'Oimo', 'Oimo')],
|
||||||
name="Physics Engine", default='Bullet', update=assets.invalidate_compiler_cache)
|
name="Physics Engine", default='Bullet', update=assets.invalidate_compiler_cache)
|
||||||
|
bpy.types.World.lnx_physics_fixed_step = FloatProperty(
|
||||||
|
name="Fixed Step", default=1/60, min=0, max=1,
|
||||||
|
description="Physics steps for fixed update"
|
||||||
|
)
|
||||||
bpy.types.World.lnx_physics_dbg_draw_wireframe = BoolProperty(
|
bpy.types.World.lnx_physics_dbg_draw_wireframe = BoolProperty(
|
||||||
name="Collider Wireframes", default=False,
|
name="Collider Wireframes", default=False,
|
||||||
description="Draw wireframes of the physics collider meshes and suspensions of raycast vehicle simulations"
|
description="Draw wireframes of the physics collider meshes and suspensions of raycast vehicle simulations"
|
||||||
@ -357,6 +361,7 @@ def init_properties():
|
|||||||
bpy.types.Object.lnx_rb_trigger = BoolProperty(name="Trigger", description="Disable contact response", default=False)
|
bpy.types.Object.lnx_rb_trigger = BoolProperty(name="Trigger", description="Disable contact response", default=False)
|
||||||
bpy.types.Object.lnx_rb_deactivation_time = FloatProperty(name="Deactivation Time", description="Delay putting rigid body into sleep", default=0.0)
|
bpy.types.Object.lnx_rb_deactivation_time = FloatProperty(name="Deactivation Time", description="Delay putting rigid body into sleep", default=0.0)
|
||||||
bpy.types.Object.lnx_rb_ccd = BoolProperty(name="Continuous Collision Detection", description="Improve collision for fast moving objects", default=False)
|
bpy.types.Object.lnx_rb_ccd = BoolProperty(name="Continuous Collision Detection", description="Improve collision for fast moving objects", default=False)
|
||||||
|
bpy.types.Object.lnx_rb_interpolate = BoolProperty(name="Interpolation", description="Smooths out the object's transform on physics steps", default=False)
|
||||||
bpy.types.Object.lnx_rb_collision_filter_mask = bpy.props.BoolVectorProperty(
|
bpy.types.Object.lnx_rb_collision_filter_mask = bpy.props.BoolVectorProperty(
|
||||||
name="Collision Collections Filter Mask",
|
name="Collision Collections Filter Mask",
|
||||||
description="Collision collections rigid body interacts with",
|
description="Collision collections rigid body interacts with",
|
||||||
@ -541,8 +546,10 @@ def init_properties():
|
|||||||
bpy.types.Node.lnx_watch = BoolProperty(name="Watch", description="Watch value of this node in debug console", default=False)
|
bpy.types.Node.lnx_watch = BoolProperty(name="Watch", description="Watch value of this node in debug console", default=False)
|
||||||
bpy.types.Node.lnx_version = IntProperty(name="Node Version", description="The version of an instanced node", default=0)
|
bpy.types.Node.lnx_version = IntProperty(name="Node Version", description="The version of an instanced node", default=0)
|
||||||
# Particles
|
# Particles
|
||||||
bpy.types.ParticleSettings.lnx_count_mult = FloatProperty(name="Multiply Count", description="Multiply particle count when rendering in Leenkx", default=1.0)
|
bpy.types.ParticleSettings.lnx_auto_start = BoolProperty(name="Auto Start", description="Automatically start this particle system on load", default=True)
|
||||||
|
bpy.types.ParticleSettings.lnx_is_unique = BoolProperty(name="Is Unique", description="Make this particle system look different each time it starts", default=False)
|
||||||
bpy.types.ParticleSettings.lnx_loop = BoolProperty(name="Loop", description="Loop this particle system", default=False)
|
bpy.types.ParticleSettings.lnx_loop = BoolProperty(name="Loop", description="Loop this particle system", default=False)
|
||||||
|
bpy.types.ParticleSettings.lnx_count_mult = FloatProperty(name="Multiply Count", description="Multiply particle count when rendering in Leenkx", default=1.0)
|
||||||
# Actions
|
# Actions
|
||||||
bpy.types.Action.lnx_root_motion_pos = BoolProperty(name="Root Motion Position", description="Enable position root motion", default=False)
|
bpy.types.Action.lnx_root_motion_pos = BoolProperty(name="Root Motion Position", description="Enable position root motion", default=False)
|
||||||
bpy.types.Action.lnx_root_motion_rot = BoolProperty(name="Root Motion Rotation", description="Enable rotation root motion", default=False)
|
bpy.types.Action.lnx_root_motion_rot = BoolProperty(name="Root Motion Rotation", description="Enable rotation root motion", default=False)
|
||||||
|
@ -205,6 +205,8 @@ class LNX_PT_ParticlesPropsPanel(bpy.types.Panel):
|
|||||||
if obj == None:
|
if obj == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
layout.prop(obj.settings, 'lnx_auto_start')
|
||||||
|
layout.prop(obj.settings, 'lnx_is_unique')
|
||||||
layout.prop(obj.settings, 'lnx_loop')
|
layout.prop(obj.settings, 'lnx_loop')
|
||||||
layout.prop(obj.settings, 'lnx_count_mult')
|
layout.prop(obj.settings, 'lnx_count_mult')
|
||||||
|
|
||||||
@ -240,6 +242,7 @@ class LNX_PT_PhysicsPropsPanel(bpy.types.Panel):
|
|||||||
layout.prop(obj, 'lnx_rb_angular_friction')
|
layout.prop(obj, 'lnx_rb_angular_friction')
|
||||||
layout.prop(obj, 'lnx_rb_trigger')
|
layout.prop(obj, 'lnx_rb_trigger')
|
||||||
layout.prop(obj, 'lnx_rb_ccd')
|
layout.prop(obj, 'lnx_rb_ccd')
|
||||||
|
layout.prop(obj, 'lnx_rb_interpolate')
|
||||||
|
|
||||||
if obj.soft_body is not None:
|
if obj.soft_body is not None:
|
||||||
layout.prop(obj, 'lnx_soft_body_margin')
|
layout.prop(obj, 'lnx_soft_body_margin')
|
||||||
@ -2730,8 +2733,33 @@ class LeenkxUpdateListInstalledVSButton(bpy.types.Operator):
|
|||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class LNX_PT_PhysicsProps(bpy.types.Panel):
|
||||||
|
bl_label = "Leenkx Props"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
bl_parent_id = "SCENE_PT_rigid_body_world"
|
||||||
|
|
||||||
class LNX_PT_BulletDebugDrawingPanel(bpy.types.Panel):
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.scene.rigidbody_world is not None
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = False
|
||||||
|
wrd = bpy.data.worlds['Lnx']
|
||||||
|
|
||||||
|
if wrd.lnx_physics_engine != 'Bullet' and wrd.lnx_physics_engine != 'Oimo':
|
||||||
|
row = layout.row()
|
||||||
|
row.alert = True
|
||||||
|
row.label(text="Physics debug drawing is only supported for the Bullet and Oimo physics engines")
|
||||||
|
|
||||||
|
col = layout.column(align=False)
|
||||||
|
col.prop(wrd, "lnx_physics_fixed_step")
|
||||||
|
|
||||||
|
class LNX_PT_PhysicsDebugDrawingPanel(bpy.types.Panel):
|
||||||
bl_label = "Leenkx Debug Drawing"
|
bl_label = "Leenkx Debug Drawing"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
@ -2897,7 +2925,8 @@ __REG_CLASSES = (
|
|||||||
LeenkxUpdateListAndroidEmulatorButton,
|
LeenkxUpdateListAndroidEmulatorButton,
|
||||||
LeenkxUpdateListAndroidEmulatorRunButton,
|
LeenkxUpdateListAndroidEmulatorRunButton,
|
||||||
LeenkxUpdateListInstalledVSButton,
|
LeenkxUpdateListInstalledVSButton,
|
||||||
LNX_PT_BulletDebugDrawingPanel,
|
LNX_PT_PhysicsProps,
|
||||||
|
LNX_PT_PhysicsDebugDrawingPanel,
|
||||||
LNX_OT_AddArmatureRootMotion,
|
LNX_OT_AddArmatureRootMotion,
|
||||||
scene.TLM_PT_Settings,
|
scene.TLM_PT_Settings,
|
||||||
scene.TLM_PT_Denoise,
|
scene.TLM_PT_Denoise,
|
||||||
|
Reference in New Issue
Block a user