Refactoring Editor Script To Approximate Primitive Colliders Instead of Meshed Ones

Hello Everyone,

I have posted on these forums before asking for your help in writing an editor script that does the following: (https://forum.playcanvas.com/t/editor-script-setting-a-render-asset-as-mesh-collider/32977)

  • Loops through the children of the selected object in the hierarchy.
  • If the entity has a renderer, the system adds a mesh collider and extracts the render asset from the renderer component, it also adds a static rigidbody.

This is crucial for me setting up the environment, since i am creating a large scene that can change later on.

However, I have found that this is affecting performance on mobiles. Therefore, I want to scrap the idea of adding a mesh collider and focus on adding primitive colliders. Is there a way I can change this script to do that?

I tried working with AABB and pulling the metadata from the file, but I am unable to get anywhere.

Here is the script for the refactored code:

(function () {
    async function listChildrenAndAddComponents(entityId) {
        const entity = editor.entities.get(entityId);

        if (!entity) {
            console.warn("Entity not found.");
            return;
        }

        if (entity.has('components.render')) {
            const renderAssetId = entity.get('components.render.asset');
            const renderAsset = editor.assets.get(renderAssetId);

            console.log("Entity has renderer component");

            let aabb = null;

            if (renderAsset && renderAsset.has('file')) {
                const assetFile = renderAsset.get('file');
                console.log("entity gas render asset file");
                if (assetFile && assetFile.aabb) {
                    aabb = assetFile.aabb;
                    console.log("Entity has aabb");
                }
            }

            if (aabb) {
                const extents = new pc.Vec3(
                    (aabb.max[0] - aabb.min[0]) / 2,
                    (aabb.max[1] - aabb.min[1]) / 2,
                    (aabb.max[2] - aabb.min[2]) / 2
                );

                // Decide the type of collider based on the extents
                if (!entity.has('components.collision')) {
                    if (Math.abs(extents.x - extents.y) < 0.1 && Math.abs(extents.y - extents.z) < 0.1) {
                        entity.addComponent('collision', { type: 'sphere', radius: extents.length() / 3 });
                    } else {
                        entity.addComponent('collision', { type: 'box', halfExtents: extents });
                    }
                }

                if (!entity.has('components.rigidbody')) {
                    entity.addComponent('rigidbody', { type: 'static' });
                }
            } else {
                console.log("AABB not found for the render asset.");
            }
        }

        const children = entity.get('children');
        if (children && children.length > 0) {
            for (const childId of children) {
                await listChildrenAndAddComponents(childId);
            }
        }
    }

    function createButton() {
        const btn = new pcui.Button({ text: 'Add Rigidbody and Approximated Colliders' });
        btn.style.position = 'absolute';
        btn.style.bottom = '10px';
        btn.style.right = '10px';
        editor.call('layout.viewport').append(btn);

        btn.on('click', async () => {
            const selectedEntities = editor.call('selector:items');
            if (selectedEntities.length === 0) {
                editor.call('status:text', 'No entity selected.');
                return;
            }

            console.log("button clicked");

            for (const entity of selectedEntities) {
                await listChildrenAndAddComponents(entity.get('resource_id'));
            }
        });
    }

    createButton();
})();

And previously this was the code that just worked with mesh colliders:

/*
An internal editor dev tool that is used to iterate over the selected item in the editor and add rigidbody static and collision mesh.
It fetches the render asset in the render component and adds it to the render asset field on the collider.
*/

(function () {
    async function listChildrenAndAddComponents(entityId, indentation = '') {
        const entity = editor.entities.get(entityId);

        if (!entity) {
            console.warn("Entity not found.");
            return;
        }

        // If the entity iterated over has a render component add rigidbody and collider of type mesh
        if (entity.has('components.render')) {
            // Fetch the render asset id from the renderer
            const renderAssetId = entity.get('components.render.asset');
            const renderAsset = editor.assets.get(renderAssetId);

            if (!entity.has('components.collision')) {
                entity.addComponent('collision', { type: 'mesh' });
            }

            if (!entity.has('components.rigidbody')) {
                entity.addComponent('rigidbody', { type: 'static' });
            }

            if (renderAsset && renderAssetId) {
                // Set the collision asset to the render asset
                entity.set('components.collision.renderAsset', renderAssetId);
            } else {
                console.log("Render asset not found.");
            }
        }

        // Recursion
        const children = entity.get('children');
        if (children && children.length > 0) {
            for (const childId of children) {
                await listChildrenAndAddComponents(childId, indentation + '  ');
            }
        }
    }

    // GUI
    function createButton() {
        const btn = new pcui.Button({ text: 'Add Rigidbody and mesh colliders to selected' });
        btn.style.position = 'absolute';
        btn.style.bottom = '10px';
        btn.style.right = '10px';
        editor.call('layout.viewport').append(btn);

        btn.on('click', async () => {
            const selectedEntities = editor.call('selector:items');
            if (selectedEntities.length === 0) {
                editor.call('status:text', 'No entity selected.');
                return;
            }

            for (const entity of selectedEntities) {
                await listChildrenAndAddComponents(entity.get('resource_id'));
            }
        });
    }

    createButton();
})();

Any help would be immensely helpful!

Hi @Firas_Al_Jerdy_Brigh,

I haven’t tested your code, but can you share what error/issue are you getting?

For example here is how to set the halfExtents property on a box collider:

item.set('components.collision.halfExtents', [halfExtents.x, halfExtents.y, halfExtents.z]);

Similarly you can set the linearOffset and angularOffset.