Merge pull request 'moisesjpelaez and T3du - Oimo Physics, Bloom condition, array index list, camera spawn fix' (#48) from Onek8/LNXSDK:main into main

Reviewed-on: LeenkxTeam/LNXSDK#48
This commit is contained in:
LeenkxTeam 2025-05-13 16:59:59 +00:00
commit 39ab42f2da
15 changed files with 228 additions and 97 deletions

View File

@ -2,9 +2,11 @@ package leenkx.logicnode;
import iron.object.Object; import iron.object.Object;
#if lnx_physics #if lnx_bullet
import leenkx.trait.physics.PhysicsConstraint; import leenkx.trait.physics.PhysicsConstraint;
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType; import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintType;
#elseif lnx_oimo
// TODO
#end #end
class AddPhysicsConstraintNode extends LogicNode { class AddPhysicsConstraintNode extends LogicNode {
@ -25,7 +27,7 @@ class AddPhysicsConstraintNode extends LogicNode {
if (pivotObject == null || rb1 == null || rb2 == null) return; if (pivotObject == null || rb1 == null || rb2 == null) return;
#if lnx_physics #if lnx_bullet
var disableCollisions: Bool = inputs[4].get(); var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get(); var breakable: Bool = inputs[5].get();
@ -108,6 +110,8 @@ class AddPhysicsConstraintNode extends LogicNode {
} }
pivotObject.addTrait(con); pivotObject.addTrait(con);
} }
#elseif lnx_oimo
// TODO
#end #end
runOutput(0); runOutput(0);
} }

View File

@ -4,7 +4,7 @@ import iron.object.Object;
#if lnx_physics #if lnx_physics
import leenkx.trait.physics.RigidBody; import leenkx.trait.physics.RigidBody;
import leenkx.trait.physics.bullet.RigidBody.Shape; import leenkx.trait.physics.RigidBody.Shape;
#end #end

View File

@ -0,0 +1,26 @@
package leenkx.logicnode;
class ArrayIndexListNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var array: Array<Dynamic> = inputs[0].get();
array = array.map(item -> Std.string(item));
var value: Dynamic = inputs[1].get();
var from: Int = 0;
var arrayList: Array<Int> = [];
var index: Int = array.indexOf(Std.string(value), from);
while(index != -1){
arrayList.push(index);
index = array.indexOf(Std.string(value), index+1);
}
return arrayList;
}
}

View File

@ -1,7 +1,7 @@
package leenkx.logicnode; package leenkx.logicnode;
#if lnx_physics #if lnx_physics
import leenkx.trait.physics.bullet.PhysicsWorld; import leenkx.trait.physics.PhysicsWorld;
#end #end
import leenkx.trait.navigation.Navigation; import leenkx.trait.navigation.Navigation;
import iron.object.Object; import iron.object.Object;

View File

@ -1,7 +1,7 @@
package leenkx.logicnode; package leenkx.logicnode;
#if lnx_physics #if lnx_physics
import leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintAxis; import leenkx.trait.physics.PhysicsConstraint.ConstraintAxis;
#end #end
class PhysicsConstraintNode extends LogicNode { class PhysicsConstraintNode extends LogicNode {

View File

@ -10,6 +10,10 @@ class KinematicCharacterController extends iron.Trait { public function new() {
typedef KinematicCharacterController = leenkx.trait.physics.bullet.KinematicCharacterController; typedef KinematicCharacterController = leenkx.trait.physics.bullet.KinematicCharacterController;
#else
typedef KinematicCharacterController = leenkx.trait.physics.oimo.KinematicCharacterController;
#end #end
#end #end

View File

@ -3,17 +3,16 @@ package leenkx.trait.physics;
#if (!lnx_physics) #if (!lnx_physics)
class PhysicsConstraint extends iron.Trait { public function new() { super(); } } class PhysicsConstraint extends iron.Trait { public function new() { super(); } }
@:enum abstract ConstraintAxis(Int) from Int to Int { }
#else #else
#if lnx_bullet #if lnx_bullet
typedef PhysicsConstraint = leenkx.trait.physics.bullet.PhysicsConstraint; typedef PhysicsConstraint = leenkx.trait.physics.bullet.PhysicsConstraint;
typedef ConstraintAxis = leenkx.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#else #else
typedef PhysicsConstraint = leenkx.trait.physics.oimo.PhysicsConstraint; typedef PhysicsConstraint = leenkx.trait.physics.oimo.PhysicsConstraint;
typedef ConstraintAxis = leenkx.trait.physics.oimo.PhysicsConstraint.ConstraintAxis;
#end #end
#end #end

View File

@ -2,6 +2,7 @@ package leenkx.trait.physics;
#if (!lnx_physics) #if (!lnx_physics)
class Hit { }
class PhysicsWorld extends iron.Trait { public function new() { super(); } } class PhysicsWorld extends iron.Trait { public function new() { super(); } }
#else #else
@ -9,11 +10,11 @@ class PhysicsWorld extends iron.Trait { public function new() { super(); } }
#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;
#else #else
typedef PhysicsWorld = leenkx.trait.physics.oimo.PhysicsWorld; typedef PhysicsWorld = leenkx.trait.physics.oimo.PhysicsWorld;
typedef Hit = leenkx.trait.physics.oimo.PhysicsWorld.Hit;
#end #end
#end #end

View File

@ -3,17 +3,20 @@ package leenkx.trait.physics;
#if (!lnx_physics) #if (!lnx_physics)
class RigidBody extends iron.Trait { public function new() { super(); } } class RigidBody extends iron.Trait { public function new() { super(); } }
@:enum abstract Shape(Int) from Int to Int { }
#else #else
#if lnx_bullet #if lnx_bullet
typedef RigidBody = leenkx.trait.physics.bullet.RigidBody; typedef RigidBody = leenkx.trait.physics.bullet.RigidBody;
typedef Shape = leenkx.trait.physics.bullet.RigidBody.Shape;
#else #else
typedef RigidBody = leenkx.trait.physics.oimo.RigidBody; typedef RigidBody = leenkx.trait.physics.oimo.RigidBody;
typedef Shape = leenkx.trait.physics.oimo.RigidBody.Shape;
#end #end
#end #end

View File

@ -1,5 +1,8 @@
package leenkx.trait.physics.bullet; package leenkx.trait.physics.bullet;
#if lnx_bullet
import leenkx.trait.physics.bullet.PhysicsWorld.DebugDrawMode;
import bullet.Bt.Vector3; import bullet.Bt.Vector3;
import kha.FastFloat; import kha.FastFloat;
@ -18,15 +21,21 @@ class DebugDrawHelper {
static inline var contactPointNormalColor = 0xffffffff; static inline var contactPointNormalColor = 0xffffffff;
static inline var contactPointDrawLifetime = true; static inline var contactPointDrawLifetime = true;
final rayCastColor: Vec4 = new Vec4(0.0, 1.0, 0.0);
final rayCastHitColor: Vec4 = new Vec4(1.0, 0.0, 0.0);
final rayCastHitPointColor: Vec4 = new Vec4(1.0, 1.0, 0.0);
final physicsWorld: PhysicsWorld; final physicsWorld: PhysicsWorld;
final lines: Array<LineData> = []; final lines: Array<LineData> = [];
final texts: Array<TextData> = []; final texts: Array<TextData> = [];
var font: kha.Font = null; var font: kha.Font = null;
var debugMode: PhysicsWorld.DebugDrawMode = NoDebug; var rayCasts:Array<TRayCastData> = [];
var debugDrawMode: DebugDrawMode = NoDebug;
public function new(physicsWorld: PhysicsWorld) { public function new(physicsWorld: PhysicsWorld, debugDrawMode: DebugDrawMode) {
this.physicsWorld = physicsWorld; this.physicsWorld = physicsWorld;
this.debugDrawMode = debugDrawMode;
#if lnx_ui #if lnx_ui
iron.data.Data.getFont(Canvas.defaultFontName, function(defaultFont: kha.Font) { iron.data.Data.getFont(Canvas.defaultFontName, function(defaultFont: kha.Font) {
@ -35,6 +44,11 @@ class DebugDrawHelper {
#end #end
iron.App.notifyOnRender2D(onRender); iron.App.notifyOnRender2D(onRender);
if (debugDrawMode & DrawRayCast != 0) {
iron.App.notifyOnUpdate(function () {
rayCasts.resize(0);
});
}
} }
public function drawLine(from: bullet.Bt.Vector3, to: bullet.Bt.Vector3, color: bullet.Bt.Vector3) { public function drawLine(from: bullet.Bt.Vector3, to: bullet.Bt.Vector3, color: bullet.Bt.Vector3) {
@ -63,25 +77,6 @@ class DebugDrawHelper {
} }
} }
// Draws raycast in its own function because
// something is conflicting with the btVector3 and JS pointer wrapping
public function drawRayCast(fx:FastFloat, fy:FastFloat, fz:FastFloat, tx:FastFloat, ty:FastFloat, tz:FastFloat, r:FastFloat, g:FastFloat, b:FastFloat) {
final fromScreenSpace = worldToScreenFast(new Vec4(fx, fy, fz, 1.0));
final toScreenSpace = worldToScreenFast(new Vec4(tx, ty, tz, 1.0));
// TO DO: May still go off screen sides.
if (fromScreenSpace.w == 1 || toScreenSpace.w == 1) {
final color = kha.Color.fromFloats(r, g, b, 1.0);
lines.push({
fromX: fromScreenSpace.x,
fromY: fromScreenSpace.y,
toX: toScreenSpace.x,
toY: toScreenSpace.y,
color: color
});
}
}
public function drawContactPoint(pointOnB: Vector3, normalOnB: Vector3, distance: kha.FastFloat, lifeTime: Int, color: Vector3) { public function drawContactPoint(pointOnB: Vector3, normalOnB: Vector3, distance: kha.FastFloat, lifeTime: Int, color: Vector3) {
#if js #if js
pointOnB = js.Syntax.code("Ammo.wrapPointer({0}, Ammo.btVector3)", pointOnB); pointOnB = js.Syntax.code("Ammo.wrapPointer({0}, Ammo.btVector3)", pointOnB);
@ -126,7 +121,62 @@ class DebugDrawHelper {
x: contactPointScreenSpace.x, x: contactPointScreenSpace.x,
y: contactPointScreenSpace.y, y: contactPointScreenSpace.y,
color: color, color: color,
text: Std.string(lifeTime), text: Std.string(lifeTime), // lifeTime: number of frames the contact point existed
});
}
}
}
public function rayCast(rayCastData:TRayCastData) {
rayCasts.push(rayCastData);
}
function drawRayCast(f: Vec4, t: Vec4, hit: Bool) {
final from = worldToScreenFast(f.clone());
final to = worldToScreenFast(t.clone());
var c: kha.Color;
if (from.w == 1 && to.w == 1) {
if (hit) c = kha.Color.fromFloats(rayCastHitColor.x, rayCastHitColor.y, rayCastHitColor.z);
else c = kha.Color.fromFloats(rayCastColor.x, rayCastColor.y, rayCastColor.z);
lines.push({
fromX: from.x,
fromY: from.y,
toX: to.x,
toY: to.y,
color: c
});
}
}
function drawHitPoint(hp: Vec4) {
final hitPoint = worldToScreenFast(hp.clone());
final c = kha.Color.fromFloats(rayCastHitPointColor.x, rayCastHitPointColor.y, rayCastHitPointColor.z);
if (hitPoint.w == 1) {
lines.push({
fromX: hitPoint.x - contactPointSizePx,
fromY: hitPoint.y - contactPointSizePx,
toX: hitPoint.x + contactPointSizePx,
toY: hitPoint.y + contactPointSizePx,
color: c
});
lines.push({
fromX: hitPoint.x - contactPointSizePx,
fromY: hitPoint.y + contactPointSizePx,
toX: hitPoint.x + contactPointSizePx,
toY: hitPoint.y - contactPointSizePx,
color: c
});
if (font != null) {
texts.push({
x: hitPoint.x,
y: hitPoint.y,
color: c,
text: 'RAYCAST HIT'
}); });
} }
} }
@ -155,13 +205,13 @@ class DebugDrawHelper {
}); });
} }
public function setDebugMode(debugMode: PhysicsWorld.DebugDrawMode) { public function setDebugMode(debugDrawMode: DebugDrawMode) {
this.debugMode = debugMode; this.debugDrawMode = debugDrawMode;
} }
public function getDebugMode(): PhysicsWorld.DebugDrawMode { public function getDebugMode(): DebugDrawMode {
#if js #if js
return debugMode; return debugDrawMode;
#elseif hl #elseif hl
return physicsWorld.getDebugDrawMode(); return physicsWorld.getDebugDrawMode();
#else #else
@ -170,7 +220,7 @@ class DebugDrawHelper {
} }
function onRender(g: kha.graphics2.Graphics) { function onRender(g: kha.graphics2.Graphics) {
if (getDebugMode() == NoDebug && !physicsWorld.drawRaycasts) { if (getDebugMode() == NoDebug) {
return; return;
} }
@ -179,7 +229,9 @@ class DebugDrawHelper {
// will cause Bullet to call the btIDebugDraw callbacks), but this way // will cause Bullet to call the btIDebugDraw callbacks), but this way
// we can ensure that--within a frame--the function will not be called // we can ensure that--within a frame--the function will not be called
// before some user-specific physics update, which would result in a // before some user-specific physics update, which would result in a
// one-frame drawing delay... // one-frame drawing delay... Ideally we would ensure that debugDrawWorld()
// is called when all other (late) update callbacks are already executed...
physicsWorld.world.debugDrawWorld(); physicsWorld.world.debugDrawWorld();
g.opacity = 1.0; g.opacity = 1.0;
@ -199,6 +251,17 @@ class DebugDrawHelper {
} }
texts.resize(0); texts.resize(0);
} }
if (debugDrawMode & DrawRayCast != 0) {
for (rayCastData in rayCasts) {
if (rayCastData.hasHit) {
drawRayCast(rayCastData.from, rayCastData.hitPoint, true);
drawHitPoint(rayCastData.hitPoint);
} else {
drawRayCast(rayCastData.from, rayCastData.to, false);
}
}
}
} }
/** /**
@ -241,3 +304,14 @@ class TextData {
public var color: kha.Color; public var color: kha.Color;
public var text: String; public var text: String;
} }
@:structInit
typedef TRayCastData = {
var from: Vec4;
var to: Vec4;
var hasHit: Bool;
@:optional var hitPoint: Vec4;
@:optional var hitNormal: Vec4;
}
#end

View File

@ -71,7 +71,6 @@ class PhysicsWorld extends Trait {
public var convexHitPointWorld = new Vec4(); public var convexHitPointWorld = new Vec4();
public var convexHitNormalWorld = new Vec4(); public var convexHitNormalWorld = new Vec4();
var pairCache: Bool = false; var pairCache: Bool = false;
public var drawRaycasts: Bool = false;
static var nullvec = true; static var nullvec = true;
static var vec1: bullet.Bt.Vector3 = null; static var vec1: bullet.Bt.Vector3 = null;
@ -102,7 +101,7 @@ class PhysicsWorld extends Trait {
public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10, debugDrawMode: DebugDrawMode = NoDebug, drawRaycasts: Bool = false) { public function new(timeScale = 1.0, maxSteps = 10, solverIterations = 10, debugDrawMode: DebugDrawMode = NoDebug) {
super(); super();
if (nullvec) { if (nullvec) {
@ -121,7 +120,6 @@ class PhysicsWorld extends Trait {
this.timeScale = timeScale; this.timeScale = timeScale;
this.maxSteps = maxSteps; this.maxSteps = maxSteps;
this.solverIterations = solverIterations; this.solverIterations = solverIterations;
this.drawRaycasts = drawRaycasts;
// First scene // First scene
if (active == null) { if (active == null) {
@ -408,14 +406,6 @@ class PhysicsWorld extends Trait {
var worldDyn: bullet.Bt.DynamicsWorld = world; var worldDyn: bullet.Bt.DynamicsWorld = world;
var worldCol: bullet.Bt.CollisionWorld = worldDyn; var worldCol: bullet.Bt.CollisionWorld = worldDyn;
if (this.drawRaycasts && this.debugDrawHelper != null) {
this.debugDrawHelper.drawRayCast(
rayFrom.x(), rayFrom.y(), rayFrom.z(),
rayTo.x(), rayTo.y(), rayTo.z(),
0.73, 0.341, 1.0
);
}
worldCol.rayTest(rayFrom, rayTo, rayCallback); worldCol.rayTest(rayFrom, rayTo, rayCallback);
var rb: RigidBody = null; var rb: RigidBody = null;
var hitInfo: Hit = null; var hitInfo: Hit = null;
@ -441,6 +431,16 @@ class PhysicsWorld extends Trait {
#end #end
} }
if (getDebugDrawMode() & DrawRayCast != 0) {
debugDrawHelper.rayCast({
from: from,
to: to,
hasHit: rc.hasHit(),
hitPoint: hitPointWorld,
hitNormal: hitNormalWorld
});
}
#if js #if js
bullet.Bt.Ammo.destroy(rayCallback); bullet.Bt.Ammo.destroy(rayCallback);
#else #else
@ -519,22 +519,14 @@ class PhysicsWorld extends Trait {
public function setDebugDrawMode(debugDrawMode: DebugDrawMode) { public function setDebugDrawMode(debugDrawMode: DebugDrawMode) {
if (debugDrawHelper == null) { if (debugDrawHelper == null) {
// Initialize if helper is null AND (standard debug mode is requested OR our custom raycast drawing is requested) if (debugDrawMode == NoDebug) {
if (debugDrawMode != NoDebug || this.drawRaycasts) {
initDebugDrawing();
}
else {
// Helper is null and no debug drawing needed, so exit
return; return;
} }
initDebugDrawing(debugDrawMode);
} }
// If we reached here, the helper is initialized (or was already)
// Now set the standard Bullet debug mode on the actual drawer
#if js #if js
// Ensure drawer exists before setting mode (might have just been initialized) world.getDebugDrawer().setDebugMode(debugDrawMode);
var drawer = world.getDebugDrawer();
if (drawer != null) drawer.setDebugMode(debugDrawMode);
#elseif hl #elseif hl
hlDebugDrawer_setDebugMode(debugDrawMode); hlDebugDrawer_setDebugMode(debugDrawMode);
#end #end
@ -554,8 +546,8 @@ class PhysicsWorld extends Trait {
#end #end
} }
function initDebugDrawing() { function initDebugDrawing(debugDrawMode: DebugDrawMode) {
debugDrawHelper = new DebugDrawHelper(this); debugDrawHelper = new DebugDrawHelper(this, debugDrawMode);
#if js #if js
final drawer = new bullet.Bt.DebugDrawer(); final drawer = new bullet.Bt.DebugDrawer();
@ -691,6 +683,8 @@ enum abstract DebugDrawMode(Int) from Int to Int {
**/ **/
var DrawFrames = 1 << 15; var DrawFrames = 1 << 15;
var DrawRayCast = 1 << 16;
@:op(~A) public inline function bitwiseNegate(): DebugDrawMode { @:op(~A) public inline function bitwiseNegate(): DebugDrawMode {
return ~this; return ~this;
} }

View File

@ -324,6 +324,20 @@ class LeenkxExporter:
def export_object_transform(self, bobject: bpy.types.Object, o): def export_object_transform(self, bobject: bpy.types.Object, o):
wrd = bpy.data.worlds['Lnx'] wrd = bpy.data.worlds['Lnx']
# HACK: In Blender 4.2.x, each camera must be selected to ensure its matrix is correctly assigned
if bpy.app.version >= (4, 2, 0) and bobject.type == 'CAMERA' and bobject.users_scene:
current_scene = bpy.context.window.scene
bpy.context.window.scene = bobject.users_scene[0]
bpy.context.view_layer.update()
bobject.select_set(True)
bpy.context.view_layer.update()
bobject.select_set(False)
bpy.context.window.scene = current_scene
bpy.context.view_layer.update()
# Static transform # Static transform
o['transform'] = {'values': LeenkxExporter.write_matrix(bobject.matrix_local)} o['transform'] = {'values': LeenkxExporter.write_matrix(bobject.matrix_local)}
@ -1552,8 +1566,7 @@ class LeenkxExporter:
log.error(e.message) log.error(e.message)
else: else:
# Assume it was caused because of encountering n-gons # Assume it was caused because of encountering n-gons
log.error(f"""object {bobject.name} contains n-gons in its mesh, so it's impossible to compute tanget space for normal mapping. log.error(f"""object {bobject.name} contains n-gons in its mesh, so it's impossible to compute tanget space for normal mapping. Make sure the mesh only has tris/quads.""")
Make sure the mesh only has tris/quads.""")
tangdata = np.empty(num_verts * 3, dtype='<f4') tangdata = np.empty(num_verts * 3, dtype='<f4')
if has_col: if has_col:
@ -3026,16 +3039,16 @@ Make sure the mesh only has tris/quads.""")
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)]
if phys_pkg == 'bullet': if phys_pkg == 'bullet' or phys_pkg == 'oimo':
debug_draw_mode = 1 if wrd.lnx_bullet_dbg_draw_wireframe else 0 debug_draw_mode = 1 if wrd.lnx_physics_dbg_draw_wireframe else 0
debug_draw_mode |= 2 if wrd.lnx_bullet_dbg_draw_aabb else 0 debug_draw_mode |= 2 if wrd.lnx_physics_dbg_draw_aabb else 0
debug_draw_mode |= 8 if wrd.lnx_bullet_dbg_draw_contact_points else 0 debug_draw_mode |= 8 if wrd.lnx_physics_dbg_draw_contact_points else 0
debug_draw_mode |= 2048 if wrd.lnx_bullet_dbg_draw_constraints else 0 debug_draw_mode |= 2048 if wrd.lnx_physics_dbg_draw_constraints else 0
debug_draw_mode |= 4096 if wrd.lnx_bullet_dbg_draw_constraint_limits else 0 debug_draw_mode |= 4096 if wrd.lnx_physics_dbg_draw_constraint_limits else 0
debug_draw_mode |= 16384 if wrd.lnx_bullet_dbg_draw_normals else 0 debug_draw_mode |= 16384 if wrd.lnx_physics_dbg_draw_normals else 0
debug_draw_mode |= 32768 if wrd.lnx_bullet_dbg_draw_axis_gizmo else 0 debug_draw_mode |= 32768 if wrd.lnx_physics_dbg_draw_axis_gizmo else 0
debug_draw_mode |= 65536 if wrd.lnx_physics_dbg_draw_raycast else 0
out_trait['parameters'].append(str(debug_draw_mode)) out_trait['parameters'].append(str(debug_draw_mode))
out_trait['parameters'].append(str(wrd.lnx_bullet_dbg_draw_raycast).lower())
self.output['traits'].append(out_trait) self.output['traits'].append(out_trait)

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class ArrayIndexNode(LnxLogicTreeNode):
"""Returns the array index list of the given value as an array."""
bl_idname = 'LNArrayIndexListNode'
bl_label = 'Array Index List'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxDynamicSocket', 'Value')
self.add_output('LnxNodeSocketArray', 'Array')

View File

@ -197,38 +197,38 @@ 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_bullet_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"
) )
bpy.types.World.lnx_bullet_dbg_draw_raycast = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_raycast = BoolProperty(
name="Trace Raycast", default=False, name="Raycasts", default=False,
description="Draw raycasts to trace the results" description="Draw raycasts to trace the results"
) )
bpy.types.World.lnx_bullet_dbg_draw_aabb = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_aabb = BoolProperty(
name="Axis-aligned Minimum Bounding Boxes", default=False, name="Axis-aligned Minimum Bounding Boxes", default=False,
description="Draw axis-aligned minimum bounding boxes (AABBs) of the physics collider meshes" description="Draw axis-aligned minimum bounding boxes (AABBs) of the physics collider meshes"
) )
bpy.types.World.lnx_bullet_dbg_draw_contact_points = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_contact_points = BoolProperty(
name="Contact Points", default=False, name="Contact Points", default=False,
description="Visualize contact points of multiple colliders" description="Visualize contact points of multiple colliders"
) )
bpy.types.World.lnx_bullet_dbg_draw_constraints = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_constraints = BoolProperty(
name="Constraints", default=False, name="Constraints", default=False,
description="Draw axis gizmos for important constraint points" description="Draw axis gizmos for important constraint points"
) )
bpy.types.World.lnx_bullet_dbg_draw_constraint_limits = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_constraint_limits = BoolProperty(
name="Constraint Limits", default=False, name="Constraint Limits", default=False,
description="Draw additional constraint information such as distance or angle limits" description="Draw additional constraint information such as distance or angle limits"
) )
bpy.types.World.lnx_bullet_dbg_draw_normals = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_normals = BoolProperty(
name="Face Normals", default=False, name="Face Normals", default=False,
description=( description=(
"Draw the normal vectors of the triangles of the physics collider meshes." "Draw the normal vectors of the triangles of the physics collider meshes."
" This only works for mesh collision shapes" " This only works with Bullet physics, for mesh collision shapes"
) )
) )
bpy.types.World.lnx_bullet_dbg_draw_axis_gizmo = BoolProperty( bpy.types.World.lnx_physics_dbg_draw_axis_gizmo = BoolProperty(
name="Axis Gizmos", default=False, name="Axis Gizmos", default=False,
description=( description=(
"Draw a small axis gizmo at the origin of the collision shape." "Draw a small axis gizmo at the origin of the collision shape."

View File

@ -1907,7 +1907,7 @@ class LNX_PT_RenderPathPostProcessPanel(bpy.types.Panel):
col.prop(rpdat, "rp_bloom") col.prop(rpdat, "rp_bloom")
_col = col.column() _col = col.column()
_col.enabled = rpdat.rp_bloom _col.enabled = rpdat.rp_bloom
if bpy.app.version <= (4, 2, 4): if bpy.app.version < (4, 3, 0):
_col.prop(rpdat, 'lnx_bloom_follow_blender') _col.prop(rpdat, 'lnx_bloom_follow_blender')
if not rpdat.lnx_bloom_follow_blender: if not rpdat.lnx_bloom_follow_blender:
_col.prop(rpdat, 'lnx_bloom_threshold') _col.prop(rpdat, 'lnx_bloom_threshold')
@ -2749,20 +2749,20 @@ class LNX_PT_BulletDebugDrawingPanel(bpy.types.Panel):
layout.use_property_decorate = False layout.use_property_decorate = False
wrd = bpy.data.worlds['Lnx'] wrd = bpy.data.worlds['Lnx']
if wrd.lnx_physics_engine != 'Bullet': if wrd.lnx_physics_engine != 'Bullet' and wrd.lnx_physics_engine != 'Oimo':
row = layout.row() row = layout.row()
row.alert = True row.alert = True
row.label(text="Physics debug drawing is only supported for the Bullet physics engine") row.label(text="Physics debug drawing is only supported for the Bullet and Oimo physics engines")
col = layout.column(align=False) col = layout.column(align=False)
col.prop(wrd, "lnx_bullet_dbg_draw_wireframe") col.prop(wrd, "lnx_physics_dbg_draw_wireframe")
col.prop(wrd, "lnx_bullet_dbg_draw_raycast") col.prop(wrd, "lnx_physics_dbg_draw_raycast")
col.prop(wrd, "lnx_bullet_dbg_draw_aabb") col.prop(wrd, "lnx_physics_dbg_draw_aabb")
col.prop(wrd, "lnx_bullet_dbg_draw_contact_points") col.prop(wrd, "lnx_physics_dbg_draw_contact_points")
col.prop(wrd, "lnx_bullet_dbg_draw_constraints") col.prop(wrd, "lnx_physics_dbg_draw_constraints")
col.prop(wrd, "lnx_bullet_dbg_draw_constraint_limits") col.prop(wrd, "lnx_physics_dbg_draw_constraint_limits")
col.prop(wrd, "lnx_bullet_dbg_draw_normals") col.prop(wrd, "lnx_physics_dbg_draw_normals")
col.prop(wrd, "lnx_bullet_dbg_draw_axis_gizmo") col.prop(wrd, "lnx_physics_dbg_draw_axis_gizmo")
def draw_custom_node_menu(self, context): def draw_custom_node_menu(self, context):
"""Extension of the node context menu. """Extension of the node context menu.