fabmax / physx-js-webidl

Javascript WASM bindings for Nvidia PhysX
MIT License
119 stars 28 forks source link

Number of Contacts is Always 0 #32

Closed ghost closed 7 months ago

ghost commented 7 months ago

Greetings, fabmax.

I have read through #7 and was able to setup the onContact callback and get the involved shapes, but wasn't able to get any contacts from the callback, it means that pair.extractContacts always returns 0. Again here is a minimal reproducible example that is 1 to 1 from what i read in the PhysX docs.

As always, thank you so much for your attention.

<!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 onContact CALLBACK ///////////
            //////////////////////////////////////////////
            let simulationEventCallback = new PhysX.PxSimulationEventCallbackImpl();
            simulationEventCallback.onContact = (
                pairHeader,
                pairs,
                nbPairs
            ) => {
                const contacts = new PhysX.Vector_PxContactPairPoint(64);
                for (let i = 0; i < nbPairs; i++) {
                    let pair = PhysX.NativeArrayHelpers.prototype.getContactPairAt(pairs, i);
                    let numberOfContacts = pair.extractContacts(contacts.data(), contacts.size())
                    console.log(numberOfContacts, "<- is always 0")
                    for (let j = 0; j < numberOfContacts; j++) {
                        let point = contacts.at(j)
                        console.log(
                            "position:",
                            point.position.x,
                            point.position.y,
                            point.position.z,
                            "impulse:",
                            point.impulse.x,
                            point.impulse.y,
                            point.impulse.z)
                    }
                }
                PhysX.destroy(contacts)
            };
            scene.setSimulationEventCallback(simulationEventCallback);
            //////////////////////////////////////////////
            /////////// SET onContact CALLBACK ///////////
            //////////////////////////////////////////////

            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);

            /////////////////////////////////////////////////////
            /////////// SET eNOTIFY_TOUCH_FOUND on w2 ///////////
            /////////////////////////////////////////////////////
            var tmpFilterData = new PhysX.PxFilterData(1, 1, PhysX.PxPairFlagEnum.eNOTIFY_TOUCH_FOUND, 0);
            /////////////////////////////////////////////////////
            /////////// SET eNOTIFY_TOUCH_FOUND on w2  ///////////
            /////////////////////////////////////////////////////

            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 < 1; 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);
            }

            function simulationLoop() {
                let lastFrame = 0;
                requestAnimationFrame(function loop(hrTime) {
                    var timeStep = Math.min(0.03, (hrTime - lastFrame) / 1000);
                    scene.simulate(timeStep);
                    scene.fetchResults(true);
                    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 7 months ago

Took me a moment to figure it out :smile:

In order to get the contact points you need to set a second flag eNOTIFY_CONTACT_POINTS:

var tmpFilterData = new PhysX.PxFilterData(1, 1, PhysX.PxPairFlagEnum.eNOTIFY_TOUCH_FOUND | PhysX.PxPairFlagEnum.eNOTIFY_CONTACT_POINTS, 0);

Don't know why this isn't described more clearly in the PhysX docs...

ghost commented 7 months ago

You had to dig deep, no way i could ever find that. The best part is that together with NOTIFY_THRESHOLD_FORCE_FOUND you can bring the number of calls really low. Man, you are the best, thanks for your patience and giving me a hand in all this.