Skip to content
82 changes: 82 additions & 0 deletions armory/Sources/armory/trait/physics/bullet/RigidBody.hx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class RigidBody extends iron.Trait {
public var onReady: Void->Void = null;
public var onContact: Array<RigidBody->Void> = null;
public var heightData: haxe.io.Bytes = null;

// Compound shape children (baked from exporter)
var compoundChildren: Array<CompoundChild> = null;

#if js
static var ammoArray: Int = -1;
#end
Expand Down Expand Up @@ -144,6 +148,9 @@ class RigidBody extends iron.Trait {
this.staticObj = flags.staticObj;
this.useDeactivation = flags.useDeactivation;

// Store compound children data if provided
this.compoundChildren = params.compoundChildren;

notifyOnAdd(init);
}

Expand Down Expand Up @@ -245,6 +252,25 @@ class RigidBody extends iron.Trait {
btshape.setLocalScaling(vec1);
#end
}
else if (shape == Shape.Compound) {
// Create compound shape and add all child shapes
var compound = new bullet.Bt.CompoundShape(true);
if (compoundChildren != null) {
for (child in compoundChildren) {
var childShape = createChildShape(child);
if (childShape != null) {
// Set child local transform
trans2.setIdentity();
vec1.setValue(child.posX, child.posY, child.posZ);
trans2.setOrigin(vec1);
quat1.setValue(child.rotX, child.rotY, child.rotZ, child.rotW);
trans2.setRotation(quat1);
compound.addChildShape(trans2, childShape);
}
}
}
btshape = compound;
}

trans1.setIdentity();
vec1.setX(transform.worldx());
Expand Down Expand Up @@ -332,6 +358,46 @@ class RigidBody extends iron.Trait {
#end
}

/**
* Creates a child collision shape for compound rigidbodies from baked export data.
* @param child The compound child data containing shape type and dimensions
* @return The created Bullet collision shape, or null if shape type is unsupported
*/
function createChildShape(child: CompoundChild): bullet.Bt.CollisionShape {
var childShapeType: Int = child.shape;

if (childShapeType == Shape.Box) {
vec1.setValue(withMargin(child.dimX / 2), withMargin(child.dimY / 2), withMargin(child.dimZ / 2));
return new bullet.Bt.BoxShape(vec1);
}
else if (childShapeType == Shape.Sphere) {
return new bullet.Bt.SphereShape(withMargin(child.dimX / 2));
}
else if (childShapeType == Shape.Cone) {
var coneZ = new bullet.Bt.ConeShapeZ(
withMargin(child.dimX / 2), // Radius
withMargin(child.dimZ)); // Height
return coneZ;
}
else if (childShapeType == Shape.Cylinder) {
vec1.setValue(withMargin(child.dimX / 2), withMargin(child.dimY / 2), withMargin(child.dimZ / 2));
var cylZ = new bullet.Bt.CylinderShapeZ(vec1);
return cylZ;
}
else if (childShapeType == Shape.Capsule) {
var r = child.dimX / 2;
var capsZ = new bullet.Bt.CapsuleShapeZ(
withMargin(r), // Radius
withMargin(child.dimZ - r * 2)); // Height between 2 sphere centers
return capsZ;
}
else {
// Unsupported shape type for compound children (ConvexHull, Mesh, Terrain)
trace("Warning: Unsupported compound child shape type: " + childShapeType);
return null;
}
}

function update() {
if (interpolate) {
var t: Float = Time.fixedStepInterpolation;
Expand Down Expand Up @@ -732,6 +798,7 @@ class RigidBody extends iron.Trait {
var Cylinder = 5;
var Capsule = 6;
var Terrain = 7;
var Compound = 8;
}

typedef RigidBodyParams = {
Expand All @@ -748,6 +815,21 @@ typedef RigidBodyParams = {
var linearDeactivationThreshold: Float;
var angularDeactivationThreshold: Float;
var deactivationTime: Float;
@:optional var compoundChildren: Array<CompoundChild>;
}

typedef CompoundChild = {
var shape: Int; // 0=Box, 1=Sphere, 2=ConvexHull, 3=Mesh, 4=Cone, 5=Cylinder, 6=Capsule
var posX: Float; // Local position relative to parent
var posY: Float;
var posZ: Float;
var rotX: Float; // Local rotation quaternion
var rotY: Float;
var rotZ: Float;
var rotW: Float;
var dimX: Float; // Dimensions for shape creation
var dimY: Float;
var dimZ: Float;
}

typedef RigidBodyFlags = {
Expand Down
23 changes: 18 additions & 5 deletions armory/Sources/iron/Scene.hx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class Scene {
public var traitRemoves: Array<Void->Void> = [];

var initializing: Bool; // Is the scene in its initialization phase?
var spawnDepth: Int = 0; // Nested spawn counter (defer trait creation while > 0)
var spawning(get, never): Bool;
inline function get_spawning(): Bool return spawnDepth > 0;

public function new() {
uid = uidCounter++;
Expand Down Expand Up @@ -437,7 +440,7 @@ class Scene {
var result = objects.length;
for (o in objects) {
if (discardNoSpawn && o.spawn != null && o.spawn == false) continue; // Do not count children of non-spawned objects
if (o.children != null) result += getObjectsCount(o.children);
if (o.children != null) result += getObjectsCount(o.children, discardNoSpawn);
}
return result;
}
Expand All @@ -451,11 +454,16 @@ class Scene {
@param srcRaw If not `null`, spawn the object from the given scene data instead of using the scene this function is called on. Useful to spawn objects from other scenes.
**/
public function spawnObject(name: String, parent: Null<Object>, done: Null<Object->Void>, spawnChildren = true, srcRaw: Null<TSceneFormat> = null) {
spawnObjectInternal(name, parent, done, spawnChildren, srcRaw, true);
}

function spawnObjectInternal(name: String, parent: Null<Object>, done: Null<Object->Void>, spawnChildren: Bool, srcRaw: Null<TSceneFormat>, createTraits: Bool) {
if (srcRaw == null) srcRaw = raw;
var objectsTraversed = 0;
var obj = getRawObjectByName(srcRaw, name);
var objectsCount = spawnChildren ? getObjectsCount([obj], false) : 1;
var rootId = -1;
spawnDepth++; // Defer trait creation until all objects are ready
function spawnObjectTree(obj: TObj, parent: Object, parentObject: TObj, done: Object->Void) {
createObject(obj, srcRaw, parent, parentObject, function(object: Object) {
if (rootId == -1) {
Expand All @@ -471,6 +479,11 @@ class Scene {
while (object.uid != rootId) {
object = object.parent;
}
// Create traits bottom-up after all objects are ready
spawnDepth--;
if (createTraits) {
createTraitsBottomUp(object);
}
// Then call user callback
if (done != null) done(object);
}
Expand Down Expand Up @@ -604,7 +617,7 @@ class Scene {
else {
for (object_ref in object_refs) {
// Spawn top-level collection objects and their children
spawnObject(object_ref, groupOwner, function(spawnedObject: Object) {
spawnObjectInternal(object_ref, groupOwner, function(spawnedObject: Object) {
// Apply collection/group instance offset to all
// top-level parents of that group
if (!isObjectInGroup(groupRef, spawnedObject.parent, format)) {
Expand All @@ -624,7 +637,7 @@ class Scene {
groupOwner.transform.reset();
done();
}
}, true, format);
}, true, format, false);
}
}
}
Expand Down Expand Up @@ -874,9 +887,9 @@ class Scene {
}
}

// If the scene is still initializing, traits will be created later
// If the scene is still initializing or spawning, traits will be created later
// to ensure that object references for trait properties are valid
if (!active.initializing) createTraits(o.traits, object);
if (!active.initializing && !active.spawning) createTraits(o.traits, object);
}
done(object);
}
Expand Down
2 changes: 1 addition & 1 deletion armory/Sources/iron/object/Object.hx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class Object {
public function getTraitFromChildren<T: Trait>(c: Class<T>): T {
var t: T = getTrait(c);
if (t != null) return t;
for (child in getChildren()) {
for (child in getChildren(true)) {
t = child.getTraitFromChildren(c);
if (t != null) return t;
}
Expand Down
10 changes: 10 additions & 0 deletions armory/blender/arm/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ def reset():
shader_cons['voxel_frag'] = []
shader_cons['voxel_geom'] = []

def reset_shader_cons():
# Reset shader comparison arrays to prevent cross-scene shader merging
global shader_cons
shader_cons['mesh_vert'] = []
shader_cons['depth_vert'] = []
shader_cons['depth_frag'] = []
shader_cons['voxel_vert'] = []
shader_cons['voxel_frag'] = []
shader_cons['voxel_geom'] = []

def add(asset_file):
global assets

Expand Down
Loading