joltc-sys 0.3.1+Jolt-5.0.0

Unsafe bindings to Jolt Physics using JoltC
Documentation
mod framework;

use std::f32::consts::{PI, TAU};
use std::mem;

use joltc_sys::*;
use rand::Rng;

use crate::framework::*;

struct SetupTeardown;

impl SmokeTest for SetupTeardown {
    unsafe fn setup(_system: *mut JPC_PhysicsSystem) -> Self {
        Self
    }
}

#[test]
fn setup_teardown() {
    run_test::<SetupTeardown>();
}

struct HelloShapes {
    floor: JPC_BodyID,
    sphere: JPC_BodyID,
}

impl SmokeTest for HelloShapes {
    unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);

        let floor_shape = create_box(&JPC_BoxShapeSettings {
            HalfExtent: vec3(100.0, 1.0, 100.0),
            ..Default::default()
        })
        .unwrap();

        let floor_settings = JPC_BodyCreationSettings {
            Position: rvec3(0.0, -1.0, 0.0),
            MotionType: JPC_MOTION_TYPE_STATIC,
            ObjectLayer: OL_NON_MOVING,
            Shape: floor_shape,
            ..Default::default()
        };

        let floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings);
        let floor_id = JPC_Body_GetID(floor);
        JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE);

        let sphere_shape = create_sphere(&JPC_SphereShapeSettings {
            Radius: 0.5,
            ..Default::default()
        })
        .unwrap();

        let sphere_settings = JPC_BodyCreationSettings {
            Position: rvec3(0.0, 2.0, 0.0),
            MotionType: JPC_MOTION_TYPE_DYNAMIC,
            ObjectLayer: OL_MOVING,
            Shape: sphere_shape,
            ..Default::default()
        };

        let sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings);
        let sphere_id = JPC_Body_GetID(sphere);
        JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE);

        JPC_BodyInterface_SetLinearVelocity(body_interface, sphere_id, vec3(0.0, -5.0, 0.0));

        Self {
            sphere: sphere_id,
            floor: floor_id,
        }
    }

    unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);
        JPC_BodyInterface_IsActive(body_interface, self.sphere)
    }

    unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);

        JPC_BodyInterface_RemoveBody(body_interface, self.floor);
        JPC_BodyInterface_DestroyBody(body_interface, self.floor);

        JPC_BodyInterface_RemoveBody(body_interface, self.sphere);
        JPC_BodyInterface_DestroyBody(body_interface, self.sphere);
    }
}

#[test]
fn hello_shapes() {
    run_test::<HelloShapes>();
}

struct HelloConvexHull {
    floor: JPC_BodyID,
    hull: JPC_BodyID,
}

impl SmokeTest for HelloConvexHull {
    unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);

        let floor_shape = create_box(&JPC_BoxShapeSettings {
            HalfExtent: vec3(100.0, 1.0, 100.0),
            ..Default::default()
        })
        .unwrap();

        let floor_settings = JPC_BodyCreationSettings {
            Position: rvec3(0.0, -1.0, 0.0),
            MotionType: JPC_MOTION_TYPE_STATIC,
            ObjectLayer: OL_NON_MOVING,
            Shape: floor_shape,
            ..Default::default()
        };

        let floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings);
        let floor_id = JPC_Body_GetID(floor);
        JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE);

        let mut rng = rand::thread_rng();
        let mut points = Vec::with_capacity(200);
        for _ in 0..200 {
            let theta = rng.gen_range(0.0..PI);
            let phi = rng.gen_range(0.0..TAU);
            points.push(vec3(
                theta.sin() * phi.cos(),
                theta.sin() * phi.sin(),
                theta.cos(),
            ));
        }

        let hull_shape = create_convex_hull(&JPC_ConvexHullShapeSettings {
            Points: points.as_ptr(),
            PointsLen: points.len(),
            ..Default::default()
        })
        .unwrap();

        let hull_settings = JPC_BodyCreationSettings {
            Position: rvec3(0.0, 2.0, 0.0),
            MotionType: JPC_MOTION_TYPE_DYNAMIC,
            ObjectLayer: OL_MOVING,
            Shape: hull_shape,
            ..Default::default()
        };

        let hull = JPC_BodyInterface_CreateBody(body_interface, &hull_settings);
        let hull_id = JPC_Body_GetID(hull);
        JPC_BodyInterface_AddBody(body_interface, hull_id, JPC_ACTIVATION_ACTIVATE);

        JPC_BodyInterface_SetLinearVelocity(body_interface, hull_id, vec3(0.0, -5.0, 0.0));

        Self {
            hull: hull_id,
            floor: floor_id,
        }
    }

    unsafe fn post_update(&mut self, system: *mut JPC_PhysicsSystem) -> bool {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);
        JPC_BodyInterface_IsActive(body_interface, self.hull)
    }

    unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);

        JPC_BodyInterface_RemoveBody(body_interface, self.floor);
        JPC_BodyInterface_DestroyBody(body_interface, self.floor);

        JPC_BodyInterface_RemoveBody(body_interface, self.hull);
        JPC_BodyInterface_DestroyBody(body_interface, self.hull);
    }
}

#[test]
fn hello_convex_hull() {
    run_test::<HelloConvexHull>();
}

struct NarrowPhaseRayCast {
    sphere: JPC_BodyID,
}

impl SmokeTest for NarrowPhaseRayCast {
    unsafe fn setup(system: *mut JPC_PhysicsSystem) -> Self {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);

        let sphere_shape = create_sphere(&JPC_SphereShapeSettings {
            Radius: 0.5,
            ..Default::default()
        })
        .unwrap();

        let sphere_settings = JPC_BodyCreationSettings {
            Position: rvec3(0.0, 2.0, 0.0),
            MotionType: JPC_MOTION_TYPE_DYNAMIC,
            ObjectLayer: OL_MOVING,
            Shape: sphere_shape,
            ..Default::default()
        };

        let sphere = JPC_BodyInterface_CreateBody(body_interface, &sphere_settings);
        let sphere_id = JPC_Body_GetID(sphere);
        JPC_BodyInterface_AddBody(body_interface, sphere_id, JPC_ACTIVATION_ACTIVATE);

        let query = JPC_PhysicsSystem_GetNarrowPhaseQuery(system);

        let mut args = JPC_NarrowPhaseQuery_CastRayArgs {
            Ray: JPC_RRayCast {
                Origin: rvec3(1.0, 2.0, 0.0),
                Direction: vec3(-2.0, 0.0, 0.0),
            },
            ..mem::zeroed()
        };
        let hit = JPC_NarrowPhaseQuery_CastRay(query, &mut args);

        assert!(hit, "ray should hit the sphere");
        assert!(
            (args.Result.Fraction - 0.25).abs() < 0.01,
            "ray should hit at around 0.25 fraction"
        );

        Self { sphere: sphere_id }
    }

    unsafe fn post_update(&mut self, _system: *mut JPC_PhysicsSystem) -> bool {
        false
    }

    unsafe fn teardown(&mut self, system: *mut JPC_PhysicsSystem) {
        let body_interface = JPC_PhysicsSystem_GetBodyInterface(system);

        JPC_BodyInterface_RemoveBody(body_interface, self.sphere);
        JPC_BodyInterface_DestroyBody(body_interface, self.sphere);
    }
}

#[test]
fn narrow_phase_ray_cast() {
    run_test::<NarrowPhaseRayCast>();
}