merge upstream

This commit is contained in:
2025-10-03 22:16:29 +00:00
8 changed files with 206 additions and 29 deletions

View File

@ -71,11 +71,12 @@ class ParticleSystem {
Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
data = b; data = b;
r = data.raw; r = data.raw;
if (r.dynamic_emitter != null){ var dyn: Null<Bool> = r.dynamic_emitter;
dynamicEmitter = r.dynamic_emitter; var dynValue: Bool = true;
} else { if (dyn != null) {
dynamicEmitter = true; dynValue = dyn;
} }
dynamicEmitter = dynValue;
if (Scene.active.raw.gravity != null) { if (Scene.active.raw.gravity != null) {
gx = Scene.active.raw.gravity[0] * r.weight_gravity; gx = Scene.active.raw.gravity[0] * r.weight_gravity;
gy = Scene.active.raw.gravity[1] * r.weight_gravity; gy = Scene.active.raw.gravity[1] * r.weight_gravity;

View File

@ -0,0 +1,48 @@
package leenkx.logicnode;
import iron.object.Object;
#if lnx_physics
import leenkx.trait.physics.PhysicsCache;
import leenkx.trait.physics.RigidBody;
#end
class AnyContactNode extends LogicNode {
public var property0: String;
var lastContact = false;
public function new(tree: LogicTree) {
super(tree);
tree.notifyOnUpdate(update);
}
function update() {
var object1: Object = inputs[0].get();
if (object1 == null) object1 = tree.object;
if (object1 == null) return;
var contact = false;
#if lnx_physics
var rb1 = PhysicsCache.getCachedRigidBody(object1);
if (rb1 != null) {
var rbs = PhysicsCache.getCachedContacts(rb1);
contact = (rbs != null && rbs.length > 0);
}
#end
var shouldTrigger = false;
switch (property0) {
case "begin":
shouldTrigger = contact && !lastContact;
case "overlap":
shouldTrigger = contact;
case "end":
shouldTrigger = !contact && lastContact;
}
lastContact = contact;
if (shouldTrigger) runOutput(0);
}
}

View File

@ -1,7 +1,10 @@
package leenkx.logicnode; package leenkx.logicnode;
import iron.object.Object; import iron.object.Object;
#if lnx_physics
import leenkx.trait.physics.PhysicsCache;
import leenkx.trait.physics.RigidBody; import leenkx.trait.physics.RigidBody;
#end
class HasContactNode extends LogicNode { class HasContactNode extends LogicNode {
@ -15,12 +18,15 @@ class HasContactNode extends LogicNode {
if (object1 == null || object2 == null) return false; if (object1 == null || object2 == null) return false;
#if lnx_physics #if lnx_physics
var physics = leenkx.trait.physics.PhysicsWorld.active; var rb1 = PhysicsCache.getCachedRigidBody(object1);
var rb2 = object2.getTrait(RigidBody); var rb2 = PhysicsCache.getCachedRigidBody(object2);
var rbs = physics.getContacts(object1.getTrait(RigidBody));
if (rbs != null) for (rb in rbs) if (rb == rb2) return true; if (rb1 != null && rb2 != null) {
#end var rbs = PhysicsCache.getCachedContacts(rb1);
return PhysicsCache.hasContactWith(rbs, rb2);
}
#end
return false; return false;
} }
} }

View File

@ -1,7 +1,11 @@
package leenkx.logicnode; package leenkx.logicnode;
import iron.object.Object; import iron.object.Object;
#if lnx_physics
import leenkx.trait.physics.PhysicsCache;
import leenkx.trait.physics.RigidBody; import leenkx.trait.physics.RigidBody;
#end
class OnContactNode extends LogicNode { class OnContactNode extends LogicNode {
@ -23,22 +27,15 @@ class OnContactNode extends LogicNode {
var contact = false; var contact = false;
#if lnx_physics #if lnx_physics
var physics = leenkx.trait.physics.PhysicsWorld.active; var rb1 = PhysicsCache.getCachedRigidBody(object1);
var rb1 = object1.getTrait(RigidBody); var rb2 = PhysicsCache.getCachedRigidBody(object2);
if (rb1 != null) {
var rbs = physics.getContacts(rb1); if (rb1 != null && rb2 != null) {
if (rbs != null) { var rbs = PhysicsCache.getCachedContacts(rb1);
var rb2 = object2.getTrait(RigidBody); contact = PhysicsCache.hasContactWith(rbs, rb2);
for (rb in rbs) {
if (rb == rb2) {
contact = true;
break;
}
}
} }
} #end
#end
var b = false; var b = false;
switch (property0) { switch (property0) {

View File

@ -0,0 +1,98 @@
package leenkx.trait.physics;
import iron.object.Object;
class PhysicsCache {
#if lnx_physics
static var rbCache: Map<Int, Dynamic> = new Map();
static var contactsCache: Map<Int, Array<Dynamic>> = new Map();
static var physicsFrame: Int = 0;
static var lastQueryFrame: Int = -1;
#end
public static function getCachedRigidBody(object: Object): Dynamic {
#if (!lnx_physics)
return null;
#else
if (object == null) return null;
var cached = rbCache.get(object.uid);
if (cached != null) return cached;
#if lnx_bullet
var rb = object.getTrait(leenkx.trait.physics.bullet.RigidBody);
#else
var rb = object.getTrait(leenkx.trait.physics.oimo.RigidBody);
#end
if (rb != null) rbCache.set(object.uid, rb);
return rb;
#end
}
public static function getCachedContacts(rb: Dynamic): Array<Dynamic> {
#if (!lnx_physics)
return null;
#else
if (rb == null) return null;
var rbObjectId = (rb.object != null) ? rb.object.uid : -1;
if (rbObjectId == -1) {
#if lnx_bullet
if (leenkx.trait.physics.bullet.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.bullet.PhysicsWorld.active.getContacts(rb);
#else
if (leenkx.trait.physics.oimo.PhysicsWorld.active == null) return null;
return leenkx.trait.physics.oimo.PhysicsWorld.active.getContacts(rb);
#end
}
if (lastQueryFrame == physicsFrame) {
var cached = contactsCache.get(rbObjectId);
if (cached != null) return cached;
}
lastQueryFrame = physicsFrame;
var cached = contactsCache.get(rbObjectId);
if (cached != null) return cached;
#if lnx_bullet
if (leenkx.trait.physics.bullet.PhysicsWorld.active == null) return null;
var contacts = leenkx.trait.physics.bullet.PhysicsWorld.active.getContacts(rb);
#else
if (leenkx.trait.physics.oimo.PhysicsWorld.active == null) return null;
var contacts = leenkx.trait.physics.oimo.PhysicsWorld.active.getContacts(rb);
#end
if (contacts != null) {
contactsCache.set(rbObjectId, contacts);
}
return contacts;
#end
}
public static inline function hasContactWith(contacts: Array<Dynamic>, target: Dynamic): Bool {
#if (!lnx_physics)
return false;
#else
return contacts != null && target != null && contacts.indexOf(target) >= 0;
#end
}
public static function clearCache() {
#if lnx_physics
rbCache.clear();
contactsCache.clear();
#end
}
public static function clearContactsCache() {
#if lnx_physics
physicsFrame++;
contactsCache.clear();
#end
}
}

View File

@ -8,11 +8,9 @@ class PhysicsWorld extends iron.Trait { public function new() { super(); } }
#else #else
#if lnx_bullet #if lnx_bullet
typedef PhysicsWorld = leenkx.trait.physics.bullet.PhysicsWorld; typedef PhysicsWorld = leenkx.trait.physics.bullet.PhysicsWorld;
typedef Hit = leenkx.trait.physics.bullet.PhysicsWorld.Hit; typedef Hit = leenkx.trait.physics.bullet.PhysicsWorld.Hit;
#else #else
typedef PhysicsWorld = leenkx.trait.physics.oimo.PhysicsWorld; typedef PhysicsWorld = leenkx.trait.physics.oimo.PhysicsWorld;
typedef Hit = leenkx.trait.physics.oimo.PhysicsWorld.Hit; typedef Hit = leenkx.trait.physics.oimo.PhysicsWorld.Hit;
#end #end

View File

@ -7,6 +7,7 @@ import iron.system.Time;
import iron.math.Vec4; import iron.math.Vec4;
import iron.math.Quat; import iron.math.Quat;
import iron.math.RayCaster; import iron.math.RayCaster;
import leenkx.trait.physics.PhysicsCache;
class Hit { class Hit {
@ -145,6 +146,7 @@ class PhysicsWorld extends Trait {
iron.Scene.active.notifyOnRemove(function() { iron.Scene.active.notifyOnRemove(function() {
sceneRemoved = true; sceneRemoved = true;
PhysicsCache.clearCache();
}); });
} }
@ -303,6 +305,8 @@ class PhysicsWorld extends Trait {
var t = Time.fixedStep * timeScale * Time.scale; var t = Time.fixedStep * timeScale * Time.scale;
if (t == 0.0) return; // Simulation paused if (t == 0.0) return; // Simulation paused
PhysicsCache.clearContactsCache();
#if lnx_debug #if lnx_debug
var startTime = kha.Scheduler.realTime(); var startTime = kha.Scheduler.realTime();
#end #end

View File

@ -0,0 +1,25 @@
from lnx.logicnode.lnx_nodes import *
class AnyContactNode(LnxLogicTreeNode):
"""Activates the output when the rigid body of the connected object makes contact with
any other rigid body.
"""
bl_idname = 'LNAnyContactNode'
bl_label = 'Any Contact'
lnx_section = 'contact'
lnx_version = 1
property0: HaxeEnumProperty(
'property0',
items = [('begin', 'Begin', 'Contact with any object begins'),
('overlap', 'Overlap', 'Contact with any object is happening'),
('end', 'End', 'Contact with any object ends')],
name='', default='begin')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'RB')
self.add_output('LnxNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')