Creating bounding box for dynamically spawned nested GLB correctly

Hi i feel the resources and forum links are scattered everywhere to get to correct answer fast. So here is how you make sure the transform of your spawned object aligns with your root entity.

loadObjectFromArrayBuffer(arrayBuffer, callback) {
        const blob = new Blob([arrayBuffer], { type: 'model/gltf-binary' });
        const url = URL.createObjectURL(blob);
        let spawnedAsset = new Asset('object', 'container', { url: url });

        spawnedAsset.on('load', () => {
            URL.revokeObjectURL(url);
            const spawnedEntity = new Entity('SpawnedObject');

            debugLog('Spawned Asset:', spawnedAsset);
            debugLog('Spawned Asset Resource:', spawnedAsset.resource);

            if (spawnedAsset.resource && spawnedAsset.resource.model) {
                spawnedEntity.addComponent('model', {
                    asset: spawnedAsset.resource.model
                });
                spawnedEntity.model.enabled = true;
                const parentWorldTransform = spawnedEntity.getWorldTransform();
                let minX = Infinity, minY = Infinity, minZ = Infinity;
                let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
                spawnedEntity.model.meshInstances.forEach(function (meshInstance) {
                    if (meshInstance && meshInstance.aabb) {
                        const aabb = meshInstance.aabb;
                        const worldMin = new pc.Vec3();
                        const worldMax = new pc.Vec3();

                        parentWorldTransform.transformPoint(aabb.getMin(), worldMin);
                        parentWorldTransform.transformPoint(aabb.getMax(), worldMax);

                        minX = Math.min(minX, worldMin.x);
                        minY = Math.min(minY, worldMin.y);
                        minZ = Math.min(minZ, worldMin.z);
                        maxX = Math.max(maxX, worldMax.x);
                        maxY = Math.max(maxY, worldMax.y);
                        maxZ = Math.max(maxZ, worldMax.z);
                    }
                });

                const center = new pc.Vec3(
                    (minX + maxX) / 2,
                    (minY + maxY) / 2,
                    (minZ + maxZ) / 2
                );

                const halfExtents = new pc.Vec3(
                    (maxX - minX) / 2,
                    (maxY - minY) / 2,
                    (maxZ - minZ) / 2
                );

                const offset = new pc.Vec3();
                offset.sub2(center, spawnedEntity.getLocalPosition());
                spawnedEntity.addComponent('collision', {
                    type: 'box',
                    halfExtents: halfExtents,
                    linearOffset: offset 
                });
                spawnedEntity.addComponent('rigidbody', {
                    type: 'dynamic',
                    mass: 10,
                    restitution: 0,
                    friction: 0.3,
                });


                if (callback) {
                    callback(spawnedEntity);
                }
            } else {
                debugLog('Error: Model resource not found in loaded asset');
                if (callback) {
                    callback(null);
                }
            }
        });

        spawnedAsset.on('error', (err) => {
            URL.revokeObjectURL(url);
            debugLog('Error loading object:', err);
            if (callback) {
                callback(null);
            }
        });

        this.app.assets.add(spawnedAsset);
        this.app.assets.load(spawnedAsset);
    }
1 Like