diff --git a/leenkx/Sources/iron/object/ParticleSystem.hx b/leenkx/Sources/iron/object/ParticleSystem.hx index 5fab3a7..bd66660 100644 --- a/leenkx/Sources/iron/object/ParticleSystem.hx +++ b/leenkx/Sources/iron/object/ParticleSystem.hx @@ -71,11 +71,12 @@ class ParticleSystem { Data.getParticle(sceneName, pref.particle, function(b: ParticleData) { data = b; r = data.raw; - if (r.dynamic_emitter != null){ - dynamicEmitter = r.dynamic_emitter; - } else { - dynamicEmitter = true; + var dyn: Null = r.dynamic_emitter; + var dynValue: Bool = true; + if (dyn != null) { + dynValue = dyn; } + dynamicEmitter = dynValue; if (Scene.active.raw.gravity != null) { gx = Scene.active.raw.gravity[0] * r.weight_gravity; gy = Scene.active.raw.gravity[1] * r.weight_gravity; diff --git a/leenkx/Sources/leenkx/logicnode/AnyContactNode.hx b/leenkx/Sources/leenkx/logicnode/AnyContactNode.hx new file mode 100644 index 0000000..76d32b4 --- /dev/null +++ b/leenkx/Sources/leenkx/logicnode/AnyContactNode.hx @@ -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); + } +} diff --git a/leenkx/Sources/leenkx/logicnode/HasContactNode.hx b/leenkx/Sources/leenkx/logicnode/HasContactNode.hx index f801dd9..d04f05c 100644 --- a/leenkx/Sources/leenkx/logicnode/HasContactNode.hx +++ b/leenkx/Sources/leenkx/logicnode/HasContactNode.hx @@ -1,7 +1,10 @@ package leenkx.logicnode; - import iron.object.Object; + +#if lnx_physics +import leenkx.trait.physics.PhysicsCache; import leenkx.trait.physics.RigidBody; +#end class HasContactNode extends LogicNode { @@ -15,12 +18,15 @@ class HasContactNode extends LogicNode { if (object1 == null || object2 == null) return false; -#if lnx_physics - var physics = leenkx.trait.physics.PhysicsWorld.active; - var rb2 = object2.getTrait(RigidBody); - var rbs = physics.getContacts(object1.getTrait(RigidBody)); - if (rbs != null) for (rb in rbs) if (rb == rb2) return true; -#end + #if lnx_physics + var rb1 = PhysicsCache.getCachedRigidBody(object1); + var rb2 = PhysicsCache.getCachedRigidBody(object2); + + if (rb1 != null && rb2 != null) { + var rbs = PhysicsCache.getCachedContacts(rb1); + return PhysicsCache.hasContactWith(rbs, rb2); + } + #end return false; } } diff --git a/leenkx/Sources/leenkx/logicnode/OnContactNode.hx b/leenkx/Sources/leenkx/logicnode/OnContactNode.hx index b8935e7..263f9cf 100644 --- a/leenkx/Sources/leenkx/logicnode/OnContactNode.hx +++ b/leenkx/Sources/leenkx/logicnode/OnContactNode.hx @@ -1,7 +1,11 @@ package leenkx.logicnode; - import iron.object.Object; + +#if lnx_physics +import leenkx.trait.physics.PhysicsCache; import leenkx.trait.physics.RigidBody; +#end + class OnContactNode extends LogicNode { @@ -23,22 +27,15 @@ class OnContactNode extends LogicNode { var contact = false; -#if lnx_physics - var physics = leenkx.trait.physics.PhysicsWorld.active; - var rb1 = object1.getTrait(RigidBody); - if (rb1 != null) { - var rbs = physics.getContacts(rb1); - if (rbs != null) { - var rb2 = object2.getTrait(RigidBody); - for (rb in rbs) { - if (rb == rb2) { - contact = true; - break; - } - } + #if lnx_physics + var rb1 = PhysicsCache.getCachedRigidBody(object1); + var rb2 = PhysicsCache.getCachedRigidBody(object2); + + if (rb1 != null && rb2 != null) { + var rbs = PhysicsCache.getCachedContacts(rb1); + contact = PhysicsCache.hasContactWith(rbs, rb2); } - } -#end + #end var b = false; switch (property0) { diff --git a/leenkx/Sources/leenkx/trait/physics/PhysicsCache.hx b/leenkx/Sources/leenkx/trait/physics/PhysicsCache.hx new file mode 100644 index 0000000..1d36104 --- /dev/null +++ b/leenkx/Sources/leenkx/trait/physics/PhysicsCache.hx @@ -0,0 +1,98 @@ +package leenkx.trait.physics; + +import iron.object.Object; + +class PhysicsCache { + #if lnx_physics + static var rbCache: Map = new Map(); + static var contactsCache: Map> = 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 { + #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, 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 + } +} diff --git a/leenkx/Sources/leenkx/trait/physics/PhysicsWorld.hx b/leenkx/Sources/leenkx/trait/physics/PhysicsWorld.hx index db6f28a..84d386e 100644 --- a/leenkx/Sources/leenkx/trait/physics/PhysicsWorld.hx +++ b/leenkx/Sources/leenkx/trait/physics/PhysicsWorld.hx @@ -8,11 +8,9 @@ class PhysicsWorld extends iron.Trait { public function new() { super(); } } #else #if lnx_bullet - typedef PhysicsWorld = leenkx.trait.physics.bullet.PhysicsWorld; typedef Hit = leenkx.trait.physics.bullet.PhysicsWorld.Hit; #else - typedef PhysicsWorld = leenkx.trait.physics.oimo.PhysicsWorld; typedef Hit = leenkx.trait.physics.oimo.PhysicsWorld.Hit; #end diff --git a/leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx b/leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx index d845a29..ba076d5 100644 --- a/leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx +++ b/leenkx/Sources/leenkx/trait/physics/bullet/PhysicsWorld.hx @@ -7,6 +7,7 @@ import iron.system.Time; import iron.math.Vec4; import iron.math.Quat; import iron.math.RayCaster; +import leenkx.trait.physics.PhysicsCache; class Hit { @@ -145,6 +146,7 @@ class PhysicsWorld extends Trait { iron.Scene.active.notifyOnRemove(function() { sceneRemoved = true; + PhysicsCache.clearCache(); }); } @@ -303,6 +305,8 @@ class PhysicsWorld extends Trait { var t = Time.fixedStep * timeScale * Time.scale; if (t == 0.0) return; // Simulation paused + PhysicsCache.clearContactsCache(); + #if lnx_debug var startTime = kha.Scheduler.realTime(); #end diff --git a/leenkx/blender/lnx/logicnode/physics/LN_any_contact.py b/leenkx/blender/lnx/logicnode/physics/LN_any_contact.py new file mode 100644 index 0000000..f915979 --- /dev/null +++ b/leenkx/blender/lnx/logicnode/physics/LN_any_contact.py @@ -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')