fabmax / physx-js-webidl

Javascript WASM bindings for Nvidia PhysX
MIT License
114 stars 27 forks source link

Active Actors API bug #30

Open ghost opened 5 months ago

ghost commented 5 months ago

Hey, i hope you are doing well

introduction

I was looking the physx official documentation and stumbled upon the active actors API, it creates a list of rigid bodies that moved in the last iteration, it is pretty handy and provides a slight performance improvement.

https://nvidia-omniverse.github.io/PhysX/physx/5.3.1/docs/RigidBodyDynamics.html?highlight=active%20actor#active-actors

problem

When consuming the api like this.

let activeActors = PhysX.SupportFunctions.prototype.PxScene_getActiveActors(scene);

And checking if there are any active actors like this.

console.log(activeActors.size())

It always returns 0 no matter what.

reproducible example

Here is the modified example scene to reflect this bug. I added some comments indicating changes and added a console.log for logging activeActors.size()

<!DOCTYPE html>

<head>
    <title>PhysX Test</title>
    <style>
        html,
        body,
        canvas {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }

        canvas {
            display: block;
        }
    </style>
</head>

<body>
    <canvas></canvas>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js"
        integrity="sha512-eV9ExyTa3b+YHr99IBTYpwk4wbgDMDlfW8uTxhywO8dWb810fGUSKDgHhEv1fAqmJT4jyYnt1iWWMW4FRxeQOQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="physx-js-webidl.js"></script>
    <script>
        PhysX().then(function (PhysX) {
            var version = PhysX.PHYSICS_VERSION;

            var allocator = new PhysX.PxDefaultAllocator();
            var errorCb = new PhysX.PxDefaultErrorCallback();
            var foundation = PhysX.CreateFoundation(version, allocator, errorCb);

            var tolerances = new PhysX.PxTolerancesScale();
            var physics = PhysX.CreatePhysics(version, foundation, tolerances);

            var tmpVec = new PhysX.PxVec3(0, -9.81, 0);
            var sceneDesc = new PhysX.PxSceneDesc(tolerances);
            sceneDesc.set_gravity(tmpVec);
            sceneDesc.set_cpuDispatcher(PhysX.DefaultCpuDispatcherCreate(0));
            sceneDesc.set_filterShader(PhysX.DefaultFilterShader());
            var scene = physics.createScene(sceneDesc);

            ///////////////////////// set flag ///////////////////////////////
            scene.setFlag(PhysX.PxSceneFlagEnum.ENABLE_ACTIVE_ACTORS, true)
            ///////////////////////// set flag ///////////////////////////////

            var material = physics.createMaterial(0.5, 0.5, 0.5);
            var shapeFlags = new PhysX.PxShapeFlags(PhysX.PxShapeFlagEnum.eSCENE_QUERY_SHAPE | PhysX.PxShapeFlagEnum.eSIMULATION_SHAPE | PhysX.PxShapeFlagEnum.eVISUALIZATION);

            var tmpPose = new PhysX.PxTransform(PhysX.PxIDENTITYEnum.PxIdentity);
            var tmpFilterData = new PhysX.PxFilterData(1, 1, 0, 0);

            var groundGeometry = new PhysX.PxBoxGeometry(10, 0.5, 10);
            var groundShape = physics.createShape(groundGeometry, material, true, shapeFlags);
            var ground = physics.createRigidStatic(tmpPose);
            groundShape.setSimulationFilterData(tmpFilterData);
            ground.attachShape(groundShape);
            scene.addActor(ground);

            var boxGeometry = new PhysX.PxBoxGeometry(0.5, 0.5, 0.5);
            var lastBox = null;
            for (var y = 0; y < 10; y++) {
                tmpVec.set_x(0); tmpVec.set_y(y * 2 + 5); tmpVec.set_z(0);
                tmpPose.set_p(tmpVec);
                var boxShape = physics.createShape(boxGeometry, material, true, shapeFlags);
                var box = physics.createRigidDynamic(tmpPose);
                boxShape.setSimulationFilterData(tmpFilterData);
                box.attachShape(boxShape);
                scene.addActor(box);
                lastBox = box;
            }

            PhysX.destroy(groundGeometry);
            PhysX.destroy(boxGeometry);
            PhysX.destroy(tmpFilterData);
            PhysX.destroy(tmpPose);
            PhysX.destroy(tmpVec);
            PhysX.destroy(shapeFlags);
            PhysX.destroy(sceneDesc);
            PhysX.destroy(tolerances);

            const {mat4, vec4, vec3} = glMatrix;
            const viewMatrix = mat4.create();
            const projectionMatrix = mat4.create();
            const viewProjectionMatrix = mat4.create();
            const tmpVec4 = vec4.create();

            const canvas = document.querySelector('canvas');
            const context = canvas.getContext('2d');
            setupDebugDrawer(PhysX, scene);

            simulationLoop();

            function setupDebugDrawer() {
                canvas.width = canvas.clientWidth;
                canvas.height = canvas.clientHeight;

                mat4.lookAt(viewMatrix, [12, 15, 20], [0, 0, 0], [0, 1, 0])
                mat4.perspective(projectionMatrix, 45 * (Math.PI / 180), canvas.width / canvas.height, 0.01, 75);
                mat4.multiply(viewProjectionMatrix, projectionMatrix, viewMatrix);

                const context = canvas.getContext('2d');
                scene.setVisualizationParameter(PhysX.eSCALE, 1);
                scene.setVisualizationParameter(PhysX.eWORLD_AXES, 1);
                scene.setVisualizationParameter(PhysX.eACTOR_AXES, 1);
                scene.setVisualizationParameter(PhysX.eCOLLISION_SHAPES, 1);
            }

            ///////////// check if there are active actors ///////////////
            function logActiveActors() {
                let activeActors = PhysX.SupportFunctions.prototype.PxScene_getActiveActors(scene);
                console.log(activeActors.size())
            }
            ///////////// check if there are active actors ///////////////

            function simulationLoop() {
                let lastFrame = 0;
                requestAnimationFrame(function loop(hrTime) {
                    var timeStep = Math.min(0.03, (hrTime - lastFrame) / 1000);
                    scene.simulate(timeStep);
                    scene.fetchResults(true);

                    /////////// log active actors /////////////
                    logActiveActors()
                    /////////// log active actors /////////////

                    debugDraw(scene);

                    var lastBoxPos = lastBox.getGlobalPose().get_p();

                    lastFrame = hrTime;
                    requestAnimationFrame(loop);
                });
            }

            function project(x, y, z) {
                const result = vec4.transformMat4(tmpVec4, [x, y, z, 1], viewProjectionMatrix);
                const clipX = (result[0] / result[3]);
                const clipY = (result[1] / result[3]);
                return [(canvas.width / 2) * (1 + clipX), (canvas.height / 2) * (1 - clipY)];
            }

            function drawLine(from, to, color) {
                const [r, g, b] = color;

                context.beginPath();
                context.strokeStyle = `rgb(${255 * r}, ${255 * g}, ${255 * b})`;
                context.moveTo(...from);
                context.lineTo(...to);
                context.stroke();
            }

            function debugDraw() {
                canvas.width = canvas.width;
                const rb = scene.getRenderBuffer();
                for (let i = 0; i < rb.getNbLines(); i++) {
                    const line = PhysX.NativeArrayHelpers.prototype.getDebugLineAt(rb.getLines(), i);
                    const from = project(line.pos0.get_x(), line.pos0.get_y(), line.pos0.get_z());
                    const to = project(line.pos1.get_x(), line.pos1.get_y(), line.pos1.get_z());
                    drawLine(from, to, colors[line.get_color0()]);
                }
            }

            const colors = {
                [PhysX.PxDebugColorEnum.eARGB_BLACK]: [0, 0, 0],
                [PhysX.PxDebugColorEnum.eARGB_RED]: [1, 0, 0],
                [PhysX.PxDebugColorEnum.eARGB_GREEN]: [0, 1, 0],
                [PhysX.PxDebugColorEnum.eARGB_BLUE]: [0, 0, 1],
                [PhysX.PxDebugColorEnum.eARGB_YELLOW]: [1, 1, 0],
                [PhysX.PxDebugColorEnum.eARGB_MAGENTA]: [1, 0, 1],
                [PhysX.PxDebugColorEnum.eARGB_CYAN]: [0, 1, 1],
                [PhysX.PxDebugColorEnum.eARGB_WHITE]: [1, 1, 1],
                [PhysX.PxDebugColorEnum.eARGB_GREY]: [0.5, 0.5, 0.5],
                [PhysX.PxDebugColorEnum.eARGB_DARKRED]: [0.5, 0, 0],
                [PhysX.PxDebugColorEnum.eARGB_DARKGREEN]: [0, 0.5, 0],
                [PhysX.PxDebugColorEnum.eARGB_DARKBLUE]: [0, 0, 0.5],
            };
        });
    </script>
</body>

</html>
fabmax commented 5 months ago

You made a typo :smile:

PhysX enum values are prefixed with a lower case 'e'. It has to be eENABLE_ACTIVE_ACTORS instead of ENABLE_ACTIVE_ACTORS:

scene.setFlag(PhysX.PxSceneFlagEnum.eENABLE_ACTIVE_ACTORS, true)

It's a bit odd that this is not an error. I assume the wasm runtime simply treats the undefined as a 0 and sets some other flag instead.

ghost commented 5 months ago

we have some faulty typescript types, here is the declaration in the physx-js-webidl.wasm.d.ts file.

enum PxSceneFlagEnum {
        'ENABLE_ACTIVE_ACTORS',
        [...]
}

and faulty me, should have researched a little bit more.

Thanks again for responding to my question, have a great day!

fabmax commented 5 months ago

Did you generate the typescript bindings with the method given in the README.md?

The script isn't mine and I don't have any typescript experience at all, so unfortunately I won't be able to fix this :smiley: