const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; const options = @import("zphysics_options"); const c = @cImport({ if (options.use_double_precision) @cDefine("JPH_DOUBLE_PRECISION", ""); if (options.enable_asserts) @cDefine("JPH_ENABLE_ASSERTS", ""); if (options.enable_cross_platform_determinism) @cDefine("JPH_CROSS_PLATFORM_DETERMINISTIC", ""); if (options.enable_debug_renderer) @cDefine("JPH_DEBUG_RENDERER", ""); @cInclude("JoltPhysicsC.h"); }); pub const Real = c.JPC_Real; comptime { assert(if (options.use_double_precision) Real == f64 else Real == f32); } pub const rvec_align = if (Real == f64) 32 else 16; pub const flt_epsilon = c.JPC_FLT_EPSILON; pub const Material = opaque {}; pub const GroupFilter = opaque {}; pub const BodyLockInterface = opaque {}; pub const SharedMutex = opaque {}; pub const BroadPhaseLayer = c.JPC_BroadPhaseLayer; pub const ObjectLayer = c.JPC_ObjectLayer; pub const BodyId = c.JPC_BodyID; pub const SubShapeId = c.JPC_SubShapeID; pub const max_physics_jobs = c.JPC_MAX_PHYSICS_JOBS; pub const max_physics_barriers = c.JPC_MAX_PHYSICS_BARRIERS; pub const body_id_invalid: BodyId = c.JPC_BODY_ID_INVALID; pub const body_id_index_bits: BodyId = c.JPC_BODY_ID_INDEX_BITS; pub const body_id_sequence_bits: BodyId = c.JPC_BODY_ID_SEQUENCE_BITS; pub const body_id_sequence_shift: BodyId = c.JPC_BODY_ID_SEQUENCE_SHIFT; pub const sub_shape_id_empty: SubShapeId = c.JPC_SUB_SHAPE_ID_EMPTY; pub const debug_renderer_enabled = options.enable_debug_renderer; comptime { assert(if (debug_renderer_enabled) c.JPC_DEBUG_RENDERER == 1 else c.JPC_DEBUG_RENDERER == 0); } const TempAllocator = opaque {}; const JobSystem = opaque {}; /// Check if this is a valid body pointer. /// When a body is freed the memory that the pointer occupies is reused to store a freelist. /// NOTE: This function is *not* protected by a lock, use with care! pub inline fn isValidBodyPointer(body: *const Body) bool { return (@intFromPtr(body) & c._JPC_IS_FREED_BODY_BIT) == 0; } /// Access a body, will return a `null` if the `body_id` is no longer valid. /// Use `PhysicsSystem.getBodies()` to get all the bodies. /// NOTE: This function is *not* protected by a lock, use with care! pub inline fn tryGetBody(all_bodies: []const *const Body, body_id: BodyId) ?*const Body { const body = all_bodies[body_id & body_id_index_bits]; return if (isValidBodyPointer(body) and body.id == body_id) body else null; } /// Access a body, will return a `null` if the `body_id` is no longer valid. /// Use `PhysicsSystem.getBodiesMut()` to get all the bodies. /// NOTE: This function is *not* protected by a lock, use with care! pub inline fn tryGetBodyMut(all_bodies: []const *Body, body_id: BodyId) ?*Body { const body = all_bodies[body_id & body_id_index_bits]; return if (isValidBodyPointer(body) and body.id == body_id) body else null; } pub const VTableHeader = switch (@import("builtin").abi) { .msvc => extern struct { __header: ?*const anyopaque = null, }, else => extern struct { __header: [2]?*const anyopaque = [_]?*const anyopaque{null} ** 2, }, }; pub const BroadPhaseLayerInterface = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn getNumBroadPhaseLayers(self: *const T) u32 { return @as(*const BroadPhaseLayerInterface.VTable, @ptrCast(self.__v)) .getNumBroadPhaseLayers(@as(*const BroadPhaseLayerInterface, @ptrCast(self))); } pub inline fn getBroadPhaseLayer(self: *const T, layer: ObjectLayer) u32 { return @as(*const BroadPhaseLayerInterface.VTable, @ptrCast(self.__v)) .getBroadPhaseLayer(@as(*const BroadPhaseLayerInterface, @ptrCast(self)), layer); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, getNumBroadPhaseLayers: *const fn (self: *const BroadPhaseLayerInterface) callconv(.C) u32, getBroadPhaseLayer: if (@import("builtin").abi == .msvc) *const fn ( self: *const BroadPhaseLayerInterface, out_layer: *BroadPhaseLayer, layer: ObjectLayer, ) callconv(.C) *const BroadPhaseLayer else *const fn ( self: *const BroadPhaseLayerInterface, layer: ObjectLayer, ) callconv(.C) BroadPhaseLayer, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_BroadPhaseLayerInterfaceVTable)); assert(@offsetOf(VTable, "getBroadPhaseLayer") == @offsetOf( c.JPC_BroadPhaseLayerInterfaceVTable, "GetBroadPhaseLayer", )); } }; pub const ObjectVsBroadPhaseLayerFilter = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn shouldCollide(self: *const T, layer1: ObjectLayer, layer2: BroadPhaseLayer) bool { return @as(*const ObjectVsBroadPhaseLayerFilter.VTable, @ptrCast(self.__v)) .shouldCollide(@as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(self)), layer1, layer2); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, shouldCollide: *const fn ( self: *const ObjectVsBroadPhaseLayerFilter, layer1: ObjectLayer, layer2: BroadPhaseLayer, ) callconv(.C) bool, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_ObjectVsBroadPhaseLayerFilterVTable)); assert(@offsetOf(VTable, "shouldCollide") == @offsetOf( c.JPC_ObjectVsBroadPhaseLayerFilterVTable, "ShouldCollide", )); } }; pub const BroadPhaseLayerFilter = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn shouldCollide(self: *const T, layer: BroadPhaseLayer) bool { return @as(*const BroadPhaseLayerFilter.VTable, @ptrCast(self.__v)) .shouldCollide(@as(*const BroadPhaseLayerFilter, @ptrCast(self)), layer); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, shouldCollide: *const fn ( self: *const BroadPhaseLayerFilter, layer: BroadPhaseLayer, ) callconv(.C) bool, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_BroadPhaseLayerFilterVTable)); assert( @offsetOf(VTable, "shouldCollide") == @offsetOf(c.JPC_BroadPhaseLayerFilterVTable, "ShouldCollide"), ); } }; pub const ObjectLayerPairFilter = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn shouldCollide(self: *const T, layer1: ObjectLayer, layer2: ObjectLayer) bool { return @as(*const ObjectLayerPairFilter.VTable, @ptrCast(self.__v)) .shouldCollide(@as(*const ObjectLayerPairFilter, @ptrCast(self)), layer1, layer2); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, shouldCollide: *const fn (self: *const ObjectLayerPairFilter, ObjectLayer, ObjectLayer) callconv(.C) bool, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_ObjectLayerPairFilterVTable)); assert( @offsetOf(VTable, "shouldCollide") == @offsetOf(c.JPC_ObjectLayerPairFilterVTable, "ShouldCollide"), ); } }; pub const ObjectLayerFilter = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn shouldCollide(self: *const T, layer: ObjectLayer) bool { return @as(*const ObjectLayerFilter.VTable, @ptrCast(self.__v)) .shouldCollide(@as(*const ObjectLayerFilter, @ptrCast(self)), layer); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, shouldCollide: *const fn (self: *const ObjectLayerFilter, ObjectLayer) callconv(.C) bool, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_ObjectLayerFilterVTable)); assert(@offsetOf(VTable, "shouldCollide") == @offsetOf(c.JPC_ObjectLayerFilterVTable, "ShouldCollide")); } }; pub const PhysicsStepListener = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn onStep(self: *const T, delta_time: f32, physics_system: *PhysicsSystem) void { return @as(*const PhysicsStepListener.VTable, @ptrCast(self.__v)) .onStep(@as(*PhysicsStepListener, @ptrCast(self)), delta_time, physics_system); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, onStep: *const fn (self: *PhysicsStepListener, f32, *PhysicsSystem) callconv(.C) void, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_PhysicsStepListenerVTable)); assert(@offsetOf(VTable, "onStep") == @offsetOf(c.JPC_PhysicsStepListenerVTable, "OnStep")); } }; pub const BodyActivationListener = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn onBodyActivated( self: *T, body_id: *const BodyId, user_data: u64, ) void { @as(*const BodyActivationListener.VTable, @ptrCast(self.__v)) .onBodyActivated(@as(*const BodyActivationListener, @ptrCast(self)), body_id, user_data); } pub inline fn onBodyDeactivated( self: *T, body_id: *const BodyId, user_data: u64, ) void { @as(*const BodyActivationListener.VTable, @ptrCast(self.__v)) .onBodyDeactivated(@as(*const BodyActivationListener, @ptrCast(self)), body_id, user_data); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, onBodyActivated: *const fn ( self: *BodyActivationListener, body_id: *const BodyId, user_data: u64, ) callconv(.C) void, onBodyDeactivated: *const fn ( self: *BodyActivationListener, body_id: *const BodyId, user_data: u64, ) callconv(.C) void, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_BodyActivationListenerVTable)); assert(@offsetOf(VTable, "onBodyDeactivated") == @offsetOf( c.JPC_BodyActivationListenerVTable, "OnBodyDeactivated", )); } }; pub const CharacterContactListener = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn OnAdjustBodyVelocity( self: *const T, character: *const CharacterVirtual, body: *const Body, io_linear_velocity: *[3]f32, io_angular_velocity: *[3]f32, ) void { return @as(*const CharacterContactListener.VTable, @ptrCast(self.__v)).OnAdjustBodyVelocity( @as(*CharacterContactListener, @ptrCast(self)), character, body, io_linear_velocity, io_angular_velocity, ); } pub inline fn OnContactValidate( self: *const T, character: *const CharacterVirtual, body: *const Body, sub_shape_id: *const SubShapeId, ) bool { return @as(*const CharacterContactListener.VTable, @ptrCast(self.__v)).OnContactValidate( @as(*CharacterContactListener, @ptrCast(self)), character, body, sub_shape_id, ); } pub inline fn OnContactAdded( self: *const T, character: *const CharacterVirtual, body: *const Body, sub_shape_id: *const SubShapeId, contact_position: *const [3]Real, contact_normal: *const [3]f32, io_settings: *CharacterContactSettings, ) void { return @as(*const CharacterContactListener.VTable, @ptrCast(self.__v)).OnContactAdded( @as(*CharacterContactListener, @ptrCast(self)), character, body, sub_shape_id, contact_position, contact_normal, io_settings, ); } pub inline fn OnContactSolve( self: *const T, character: *const CharacterVirtual, body: *const Body, sub_shape_id: *const SubShapeId, contact_position: *const [3]Real, contact_normal: *const [3]f32, contact_velocity: *const [3]f32, contact_material: *const Material, character_velocity: *const [3]f32, character_velocity_out: *[3]f32, ) void { return @as(*const CharacterContactListener.VTable, @ptrCast(self.__v)).OnContactSolve( @as(*CharacterContactListener, @ptrCast(self)), character, body, sub_shape_id, contact_position, contact_normal, contact_velocity, contact_material, character_velocity, character_velocity_out, ); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, OnAdjustBodyVelocity: *const fn ( self: *CharacterContactListener, character: *const CharacterVirtual, body: *const Body, io_linear_velocity: *[3]f32, io_angular_velocity: *[3]f32, ) callconv(.C) void, OnContactValidate: *const fn ( self: *CharacterContactListener, character: *const CharacterVirtual, body: *const Body, sub_shape_id: *const SubShapeId, ) callconv(.C) bool, OnContactAdded: *const fn ( self: *CharacterContactListener, character: *const CharacterVirtual, body: *const Body, sub_shape_id: *const SubShapeId, contact_position: *const [3]Real, contact_normal: *const [3]f32, io_settings: *CharacterContactSettings, ) callconv(.C) void, OnContactSolve: *const fn ( self: *CharacterContactListener, character: *const CharacterVirtual, body: *const Body, sub_shape_id: *const SubShapeId, contact_position: *const [3]Real, contact_normal: *const [3]f32, contact_velocity: *const [3]f32, contact_material: *const Material, character_velocity: *const [3]f32, character_velocity_out: *[3]f32, ) callconv(.C) void, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_CharacterContactListenerVTable)); assert(@offsetOf(VTable, "OnAdjustBodyVelocity") == @offsetOf(c.JPC_CharacterContactListenerVTable, "OnAdjustBodyVelocity")); assert(@offsetOf(VTable, "OnContactSolve") == @offsetOf(c.JPC_CharacterContactListenerVTable, "OnContactSolve")); } }; pub const ContactListener = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn onContactValidate( self: *T, body1: *const Body, body2: *const Body, base_offset: *const [3]Real, collision_result: *const CollideShapeResult, ) ValidateResult { return @as(*const ContactListener.VTable, @ptrCast(self.__v)) .onContactValidate( @as(*const ContactListener, @ptrCast(self)), body1, body2, base_offset, collision_result, ); } pub inline fn onContactAdded( self: *T, body1: *const Body, body2: *const Body, manifold: *const ContactManifold, settings: *ContactSettings, ) void { @as(*const ContactListener.VTable, @ptrCast(self.__v)) .onContactAdded(@as(*const ContactListener, @ptrCast(self)), body1, body2, manifold, settings); } pub inline fn onContactPersisted( self: *T, body1: *const Body, body2: *const Body, manifold: *const ContactManifold, settings: *ContactSettings, ) void { @as(*const ContactListener.VTable, @ptrCast(self.__v)) .onContactPersisted(@as(*const ContactListener, @ptrCast(self)), body1, body2, manifold, settings); } pub inline fn onContactRemoved( self: *T, sub_shape_pair: *const SubShapeIdPair, ) void { @as(*const ContactListener.VTable, @ptrCast(self.__v)) .onContactRemoved(@as(*const ContactListener, @ptrCast(self)), sub_shape_pair); } }; } pub const VTable = extern struct { onContactValidate: ?*const fn ( self: *ContactListener, body1: *const Body, body2: *const Body, base_offset: *const [3]Real, collision_result: *const CollideShapeResult, ) callconv(.C) ValidateResult = null, onContactAdded: ?*const fn ( self: *ContactListener, body1: *const Body, body2: *const Body, manifold: *const ContactManifold, settings: *ContactSettings, ) callconv(.C) void = null, onContactPersisted: ?*const fn ( self: *ContactListener, body1: *const Body, body2: *const Body, manifold: *const ContactManifold, settings: *ContactSettings, ) callconv(.C) void = null, onContactRemoved: ?*const fn ( self: *ContactListener, sub_shape_pair: *const SubShapeIdPair, ) callconv(.C) void = null, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_ContactListenerVTable)); assert(@offsetOf(VTable, "onContactAdded") == @offsetOf( c.JPC_ContactListenerVTable, "OnContactAdded", )); assert( @offsetOf(VTable, "onContactRemoved") == @offsetOf(c.JPC_ContactListenerVTable, "OnContactRemoved"), ); } }; pub const BodyFilter = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn shouldCollide(self: *const T, body_id: *const BodyId) bool { return @as(*const BodyFilter.VTable, @ptrCast(self.__v)) .shouldCollide(@as(*const BodyFilter, @ptrCast(self)), body_id); } pub inline fn shouldCollideLocked(self: *const T, body: *const Body) bool { return @as(*const BodyFilter.VTable, @ptrCast(self.__v)) .shouldCollideLocked(@as(*const BodyFilter, @ptrCast(self)), body); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, shouldCollide: *const fn (self: *const BodyFilter, body_id: *const BodyId) callconv(.C) bool, shouldCollideLocked: *const fn (self: *const BodyFilter, body: *const Body) callconv(.C) bool, }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_BodyFilterVTable)); assert(@offsetOf(VTable, "shouldCollide") == @offsetOf(c.JPC_BodyFilterVTable, "ShouldCollide")); assert( @offsetOf(VTable, "shouldCollideLocked") == @offsetOf(c.JPC_BodyFilterVTable, "ShouldCollideLocked"), ); } }; pub const ShapeFilter = extern struct { __v: *const VTable, pub usingnamespace Methods(@This()); pub fn Methods(comptime T: type) type { return extern struct { pub inline fn shouldCollide( self: *const T, receiving_body_id: u32, shape: *const Shape, sub_shape_id: *const SubShapeId, ) bool { _ = receiving_body_id; return @as(*const ShapeFilter.VTable, @ptrCast(self.__v)).shouldCollide( @as(*const ShapeFilter, @ptrCast(self)), @as(*const ShapeFilter.VTable, @ptrCast(self.__v)).receiving_body_id, shape, sub_shape_id, ); } pub inline fn pairShouldCollide( self: *const T, receiving_body_id: u32, shape1: *const Shape, sub_shape_id1: *const SubShapeId, shape2: *const Shape, sub_shape_id2: *const SubShapeId, ) bool { _ = receiving_body_id; return @as(*const ShapeFilter.VTable, @ptrCast(self.__v)).pairShouldCollide(@as(*const ShapeFilter, @ptrCast(self)), @as(*const ShapeFilter.VTable, @ptrCast(self.__v)).receiving_body_id, shape1, sub_shape_id1, shape2, sub_shape_id2); } }; } pub const VTable = extern struct { __header: VTableHeader = .{}, shouldCollide: *const fn ( self: *const ShapeFilter, shape: *const Shape, sub_shape_id: *const SubShapeId, ) callconv(.C) bool, pairShouldCollide: *const fn ( self: *const ShapeFilter, shape1: *const Shape, sub_shape_id1: *const SubShapeId, shape2: *const Shape, sub_shape_id2: *const SubShapeId, ) callconv(.C) bool, receiving_body_id: u32 = body_id_invalid, // set by jolt before each call to either of the functions above }; comptime { assert(@sizeOf(VTable) == @sizeOf(c.JPC_ShapeFilterVTable)); assert(@offsetOf(VTable, "shouldCollide") == @offsetOf(c.JPC_ShapeFilterVTable, "ShouldCollide")); assert(@offsetOf(VTable, "receiving_body_id") == @offsetOf(c.JPC_ShapeFilterVTable, "bodyId2")); } }; pub const ContactSettings = extern struct { combined_friction: f32, combined_restitution: f32, is_sensor: bool, comptime { assert(@sizeOf(ContactSettings) == @sizeOf(c.JPC_ContactSettings)); assert(@offsetOf(ContactSettings, "combined_restitution") == @offsetOf( c.JPC_ContactSettings, "combined_restitution", )); } }; pub const MassProperties = extern struct { mass: f32 = 0.0, inertia: [16]f32 align(16) = [_]f32{0} ** 16, comptime { assert(@sizeOf(MassProperties) == @sizeOf(c.JPC_MassProperties)); assert(@offsetOf(MassProperties, "inertia") == @offsetOf(c.JPC_MassProperties, "inertia")); } }; pub const SubShapeIdPair = extern struct { first: extern struct { body_id: BodyId, sub_shape_id: SubShapeId, }, second: extern struct { body_id: BodyId, sub_shape_id: SubShapeId, }, comptime { assert(@sizeOf(SubShapeIdPair) == @sizeOf(c.JPC_SubShapeIDPair)); assert(@offsetOf(SubShapeIdPair, "second") == @offsetOf(c.JPC_SubShapeIDPair, "second")); } }; pub const CollideShapeResult = extern struct { shape1_contact_point: [4]f32 align(16), // 4th element is ignored; world space shape2_contact_point: [4]f32 align(16), // 4th element is ignored; world space penetration_axis: [4]f32 align(16), // 4th element is ignored; world space penetration_depth: f32, shape1_sub_shape_id: SubShapeId, shape2_sub_shape_id: SubShapeId, body2_id: BodyId, shape1_face: extern struct { num_points: u32 align(16), points: [32][4]f32 align(16), // 4th element is ignored; world space }, shape2_face: extern struct { num_points: u32 align(16), points: [32][4]f32 align(16), // 4th element is ignored; world space }, comptime { assert(@sizeOf(CollideShapeResult) == @sizeOf(c.JPC_CollideShapeResult)); assert(@offsetOf(CollideShapeResult, "shape2_face") == @offsetOf(c.JPC_CollideShapeResult, "shape2_face")); } }; pub const ContactManifold = extern struct { base_offset: [4]Real align(rvec_align), // 4th element is ignored; world space normal: [4]f32 align(16), // 4th element is ignored; world space penetration_depth: f32, shape1_sub_shape_id: SubShapeId, shape2_sub_shape_id: SubShapeId, shape1_relative_contact: extern struct { num_points: u32 align(16), points: [64][4]f32 align(16), // 4th element is ignored; world space }, shape2_relative_contact: extern struct { num_points: u32 align(16), points: [64][4]f32 align(16), // 4th element is ignored; world space }, comptime { assert(@sizeOf(ContactManifold) == @sizeOf(c.JPC_ContactManifold)); assert(@offsetOf(ContactManifold, "shape2_relative_contact") == @offsetOf(c.JPC_ContactManifold, "shape2_relative_contact")); } }; pub const CollisionGroup = extern struct { filter: ?*GroupFilter = null, group_id: GroupId = invalid_group, sub_group_id: SubGroupId = invalid_sub_group, pub const GroupId = c.JPC_CollisionGroupID; pub const SubGroupId = c.JPC_CollisionSubGroupID; const invalid_group = @as(GroupId, c.JPC_COLLISION_GROUP_INVALID_GROUP); const invalid_sub_group = @as(SubGroupId, c.JPC_COLLISION_GROUP_INVALID_SUB_GROUP); comptime { assert(@sizeOf(CollisionGroup) == @sizeOf(c.JPC_CollisionGroup)); } }; pub const Activation = enum(c.JPC_Activation) { activate = c.JPC_ACTIVATION_ACTIVATE, dont_activate = c.JPC_ACTIVATION_DONT_ACTIVATE, }; pub const ValidateResult = enum(c.JPC_ValidateResult) { accept_all_contacts = c.JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS, accept_contact = c.JPC_VALIDATE_RESULT_ACCEPT_CONTACT, reject_contact = c.JPC_VALIDATE_RESULT_REJECT_CONTACT, reject_all_contacts = c.JPC_VALIDATE_RESULT_REJECT_ALL_CONTACTS, }; pub const MotionType = enum(c.JPC_MotionType) { static = c.JPC_MOTION_TYPE_STATIC, kinematic = c.JPC_MOTION_TYPE_KINEMATIC, dynamic = c.JPC_MOTION_TYPE_DYNAMIC, }; pub const MotionQuality = enum(c.JPC_MotionQuality) { discrete = c.JPC_MOTION_QUALITY_DISCRETE, linear_cast = c.JPC_MOTION_QUALITY_LINEAR_CAST, }; pub const OverrideMassProperties = enum(c.JPC_OverrideMassProperties) { calc_mass_inertia = c.JPC_OVERRIDE_MASS_PROPS_CALC_MASS_INERTIA, calc_inertia = c.JPC_OVERRIDE_MASS_PROPS_CALC_INERTIA, mass_inertia_provided = c.JPC_OVERRIDE_MASS_PROPS_MASS_INERTIA_PROVIDED, }; pub const CharacterGroundState = enum(c.JPC_CharacterGroundState) { on_ground = c.JPC_CHARACTER_GROUND_STATE_ON_GROUND, on_steep_ground = c.JPC_CHARACTER_GROUND_STATE_ON_STEEP_GROUND, not_supported = c.JPC_CHARACTER_GROUND_STATE_NOT_SUPPORTED, in_air = c.JPC_CHARACTER_GROUND_STATE_IN_AIR, }; pub const BodyCreationSettings = extern struct { position: [4]Real align(rvec_align) = .{ 0, 0, 0, 0 }, // 4th element is ignored, also ignored in setupByShape rotation: [4]f32 align(16) = .{ 0, 0, 0, 1 }, // ignored in setupByShape linear_velocity: [4]f32 align(16) = .{ 0, 0, 0, 0 }, // 4th element is ignored // ignored in SetupByShape angular_velocity: [4]f32 align(16) = .{ 0, 0, 0, 0 }, // 4th element is ignored // ignored in SetupByShape user_data: u64 = 0, object_layer: ObjectLayer = 0, collision_group: CollisionGroup = .{}, motion_type: MotionType = .dynamic, allow_dynamic_or_kinematic: bool = false, is_sensor: bool = false, use_manifold_reduction: bool = true, motion_quality: MotionQuality = .discrete, allow_sleeping: bool = true, friction: f32 = 0.2, restitution: f32 = 0.0, linear_damping: f32 = 0.05, angular_damping: f32 = 0.05, max_linear_velocity: f32 = 500.0, max_angular_velocity: f32 = 0.25 * c.JPC_PI * 60.0, gravity_factor: f32 = 1.0, override_mass_properties: OverrideMassProperties = .calc_mass_inertia, inertia_multiplier: f32 = 1.0, mass_properties_override: MassProperties = .{}, reserved: ?*const anyopaque = null, shape: ?*const Shape = null, // ignored in SetupByShape comptime { assert(@sizeOf(BodyCreationSettings) == @sizeOf(c.JPC_BodyCreationSettings)); assert(@offsetOf(BodyCreationSettings, "is_sensor") == @offsetOf(c.JPC_BodyCreationSettings, "is_sensor")); assert(@offsetOf(BodyCreationSettings, "shape") == @offsetOf(c.JPC_BodyCreationSettings, "shape")); assert(@offsetOf(BodyCreationSettings, "user_data") == @offsetOf(c.JPC_BodyCreationSettings, "user_data")); assert(@offsetOf(BodyCreationSettings, "motion_quality") == @offsetOf(c.JPC_BodyCreationSettings, "motion_quality")); } }; pub const CharacterContactSettings = extern struct { can_push_character: bool = true, can_receive_impulses: bool = true, }; pub const CharacterBaseSettings = extern struct { __header: VTableHeader = .{}, up: [4]f32 align(16), // 4th element is ignored supporting_volume: [4]f32 align(16), // JPH::Plane - 4th element is used max_slope_angle: f32, shape: *Shape, // must provide valid shape (such as the typical capsule) comptime { assert(@sizeOf(CharacterBaseSettings) == @sizeOf(c.JPC_CharacterBaseSettings)); assert(@offsetOf(CharacterBaseSettings, "up") == @offsetOf(c.JPC_CharacterBaseSettings, "up")); assert(@offsetOf(CharacterBaseSettings, "shape") == @offsetOf(c.JPC_CharacterBaseSettings, "shape")); } }; pub const CharacterSettings = extern struct { pub fn create() !*CharacterSettings { const settings = c.JPC_CharacterSettings_Create(); if (settings == null) return error.FailedToCreateCharacterSettings; return @as(*CharacterSettings, @ptrCast(settings)); } pub fn release(settings: *CharacterSettings) void { c.JPC_CharacterSettings_Release(@as(*c.JPC_CharacterSettings, @ptrCast(settings))); } pub fn addRef(settings: *CharacterSettings) void { c.JPC_CharacterSettings_AddRef(@as(*c.JPC_CharacterSettings, @ptrCast(settings))); } base: CharacterBaseSettings, layer: ObjectLayer, mass: f32, friction: f32, gravity_factor: f32, comptime { assert(@sizeOf(CharacterSettings) == @sizeOf(c.JPC_CharacterSettings)); assert(@offsetOf(CharacterSettings, "base") == @offsetOf(c.JPC_CharacterSettings, "base")); assert(@offsetOf(CharacterSettings, "layer") == @offsetOf(c.JPC_CharacterSettings, "layer")); assert(@offsetOf(CharacterSettings, "friction") == @offsetOf(c.JPC_CharacterSettings, "friction")); } }; pub const CharacterVirtualSettings = extern struct { pub fn create() !*CharacterVirtualSettings { const settings = c.JPC_CharacterVirtualSettings_Create(); if (settings == null) return error.FailedToCreateCharacterVirtualSettings; return @as(*CharacterVirtualSettings, @ptrCast(settings)); } pub fn release(settings: *CharacterVirtualSettings) void { c.JPC_CharacterVirtualSettings_Release(@as(*c.JPC_CharacterVirtualSettings, @ptrCast(settings))); } base: CharacterBaseSettings, mass: f32, max_strength: f32, shape_offset: [4]f32 align(16), // 4th element is ignored back_face_mode: BackFaceMode, predictive_contact_distance: f32, max_collision_iterations: u32, max_constraint_iterations: u32, min_time_remaining: f32, collision_tolerance: f32, character_padding: f32, max_num_hits: u32, hit_reduction_cos_max_angle: f32, penetration_recovery_speed: f32, comptime { assert(@sizeOf(CharacterVirtualSettings) == @sizeOf(c.JPC_CharacterVirtualSettings)); assert(@offsetOf(CharacterVirtualSettings, "base") == @offsetOf(c.JPC_CharacterVirtualSettings, "base")); assert(@offsetOf(CharacterVirtualSettings, "mass") == @offsetOf(c.JPC_CharacterVirtualSettings, "mass")); assert(@offsetOf(CharacterVirtualSettings, "max_num_hits") == @offsetOf(c.JPC_CharacterVirtualSettings, "max_num_hits")); assert(@offsetOf(CharacterVirtualSettings, "penetration_recovery_speed") == @offsetOf(c.JPC_CharacterVirtualSettings, "penetration_recovery_speed")); } }; pub const RRayCast = extern struct { origin: [4]Real align(rvec_align), // 4th element is ignored direction: [4]f32 align(16), // 4th element is ignored pub fn getPointOnRay(self: RRayCast, fraction: Real) [3]Real { return .{ self.origin[0] + self.direction[0] * fraction, self.origin[1] + self.direction[1] * fraction, self.origin[2] + self.direction[2] * fraction, }; } comptime { assert(@sizeOf(RRayCast) == @sizeOf(c.JPC_RRayCast)); assert(@offsetOf(RRayCast, "origin") == @offsetOf(c.JPC_RRayCast, "origin")); assert(@offsetOf(RRayCast, "direction") == @offsetOf(c.JPC_RRayCast, "direction")); } }; pub const RayCastResult = extern struct { body_id: BodyId = body_id_invalid, fraction: f32 = 1.0 + flt_epsilon, sub_shape_id: SubShapeId = undefined, comptime { assert(@sizeOf(RayCastResult) == @sizeOf(c.JPC_RayCastResult)); assert(@offsetOf(RayCastResult, "body_id") == @offsetOf(c.JPC_RayCastResult, "body_id")); assert(@offsetOf(RayCastResult, "fraction") == @offsetOf(c.JPC_RayCastResult, "fraction")); assert(@offsetOf(RayCastResult, "sub_shape_id") == @offsetOf(c.JPC_RayCastResult, "sub_shape_id")); } }; pub const BackFaceMode = enum(c.JPC_BackFaceMode) { ignore_back_faces = c.JPC_BACK_FACE_IGNORE, collide_with_back_faces = c.JPC_BACK_FACE_COLLIDE, }; pub const RayCastSettings = extern struct { back_face_mode: BackFaceMode, treat_convex_as_solid: bool, comptime { assert(@sizeOf(RayCastSettings) == @sizeOf(c.JPC_RayCastSettings)); assert( @offsetOf(RayCastSettings, "back_face_mode") == @offsetOf(c.JPC_RayCastSettings, "back_face_mode"), ); assert(@offsetOf(RayCastSettings, "treat_convex_as_solid") == @offsetOf(c.JPC_RayCastSettings, "treat_convex_as_solid")); } }; pub const DebugRenderer = if (!debug_renderer_enabled) extern struct {} else extern struct { pub fn createSingleton(debug_renderer_impl: *anyopaque) !void { switch (@as(DebugRendererResult, @enumFromInt(c.JPC_CreateDebugRendererSingleton(debug_renderer_impl)))) { .success => { return; }, .duplicate_singleton => { return error.DebugRendererDuplicateSingleton; }, .missing_singleton => { return error.DebugRendererMissingSingleton; }, .incomplete_impl => { return error.DebugRendererIncompleteImplementation; }, } } pub fn destroySingleton() void { _ = c.JPC_DestroyDebugRendererSingleton(); // For Zig API, don't care if one actually existed, discard error. } pub fn createTriangleBatch(primitive_in: *const anyopaque) *TriangleBatch { return @ptrCast(c.JPC_DebugRenderer_TriangleBatch_Create(primitive_in)); } pub fn getPrimitiveFromBatch(batch_in: *const TriangleBatch) *const Primitive { return @ptrCast(c.JPC_DebugRenderer_TriangleBatch_GetPrimitive(@ptrCast(batch_in))); } pub fn createBodyDrawFilter(filter_func: BodyDrawFilterFunc) *BodyDrawFilter { return @ptrCast(c.JPC_BodyDrawFilter_Create(@ptrCast(filter_func))); } pub fn destroyBodyDrawFilter(filter: *BodyDrawFilter) void { c.JPC_BodyDrawFilter_Destroy(@ptrCast(filter)); } pub fn Methods(comptime T: type) type { return extern struct { pub inline fn drawLine(self: *T, from: *const [3]Real, to: *const [3]Real, color: *const Color) void { return @as(*const DebugRenderer.VTable(T), @ptrCast(self.__v)) .drawLine( @as(*const T, @ptrCast(self)), from, to, color, ); } pub inline fn drawTriangle( self: *T, v1: *const [3]Real, v2: *const [3]Real, v3: *const [3]Real, color: *const Color, ) void { return @as(*const DebugRenderer.VTable(T), @ptrCast(self.__v)) .drawTriangle( @as(*const T, @ptrCast(self)), v1, v2, v3, color, ); } pub inline fn createTriangleBatch( self: *T, triangles: []Triangle, triangle_count: u32, ) T.Batch { return @as(*const DebugRenderer.VTable(T), @ptrCast(self.__v)) .createTriangleBatch( @as(*const T, @ptrCast(self)), triangles, triangle_count, ); } pub inline fn createTriangleBatchIndexed( self: *T, vertices: []Vertex, vertex_count: u32, indices: []u32, index_count: u32, ) T.Batch { return @as(*const DebugRenderer.VTable(T), @ptrCast(self.__v)) .createTriangleBatchIndexed( @as(*const T, @ptrCast(self)), vertices, vertex_count, indices, index_count, ); } pub inline fn drawGeometry( self: *T, model_matrix: *const [16]Real, world_space_bound: *const AABox, lod_scale_sq: f32, color: Color, geometry: *const Geometry, cull_mode: CullMode, cast_shadow: CastShadow, draw_mode: DrawMode, ) void { return @as(*const DebugRenderer.VTable(T), @ptrCast(self.__v)) .drawGeometry( @as(*const T, @ptrCast(self)), model_matrix, world_space_bound, lod_scale_sq, color, geometry, cull_mode, cast_shadow, draw_mode, ); } pub inline fn drawText3D( self: *T, positions: *const [3]Real, string: [*:0]const u8, color: Color, height: f32, ) void { return @as(*const DebugRenderer.VTable(T), @ptrCast(self.__v)) .drawText3D( @as(*const T, @ptrCast(self)), positions, string, color, height, ); } }; } pub fn VTable(comptime T: type) type { return extern struct { drawLine: ?*const fn ( self: *T, from: *const [3]Real, to: *const [3]Real, color: *const Color, ) callconv(.C) void = null, drawTriangle: ?*const fn ( self: *T, v1: *const [3]Real, v2: *const [3]Real, v3: *const [3]Real, color: *const Color, ) callconv(.C) void = null, createTriangleBatch: ?*const fn ( self: *T, triangles: [*]Triangle, triangle_count: u32, ) callconv(.C) *anyopaque = null, createTriangleBatchIndexed: ?*const fn ( self: *T, vertices: [*]Vertex, vertex_count: u32, indices: [*]u32, index_count: u32, ) callconv(.C) *anyopaque = null, drawGeometry: ?*const fn ( self: *T, model_matrix: *const [16]Real, world_space_bound: *const AABox, lod_scale_sq: f32, color: Color, geometry: *const Geometry, cull_mode: CullMode, cast_shadow: CastShadow, draw_mode: DrawMode, ) callconv(.C) void = null, drawText3D: ?*const fn ( self: *T, positions: *const [3]Real, string: [*:0]const u8, color: Color, height: f32, ) callconv(.C) void = null, }; } pub const Color = extern union { uint: u32, comp: extern struct { r: u8, g: u8, b: u8, a: u8, } }; pub const Triangle = extern struct { v: [3]Vertex, }; pub const Vertex = extern struct { position: [3]f32, normal: [3]f32, uv: [2]f32, color: Color, }; pub const AABox = extern struct { min: [3]f32, max: [3]f32, }; pub const LOD = extern struct { batch: *TriangleBatch, distance: f32, }; pub const Geometry = extern struct { LODs: [*]LOD, num_LODs: u64, bounds: *AABox, }; // zig fmt: off pub const BodyDrawSettings = extern struct { get_support_func: bool = false, // Draw the GetSupport() function, used for convex collision detection get_support_dir: bool = false, // If above true, also draw direction mapped to a specific support point get_supporting_face: bool = false, // Draw the faces that were found colliding during collision detection shape: bool = true, // Draw the shapes of all bodies shape_wireframe: bool = false, // If 'shape' true, the shapes will be drawn in wireframe instead of solid. shape_color: ShapeColor = .motion_type_color, // Coloring scheme to use for shapes bounding_box: bool = false, // Draw a bounding box per body center_of_mass_transform: bool = false, // Draw the center of mass for each body world_transform: bool = false, // Draw the world transform (which can be different than CoM) for each body velocity: bool = false, // Draw the velocity vector for each body mass_and_inertia: bool = false, // Draw the mass and inertia (as the box equivalent) for each body sleep_stats: bool = false, // Draw stats regarding the sleeping algorithm of each body }; // zig fmt: on pub const BodyDrawFilterFunc = *const fn (*const Body) callconv(.C) bool; pub const BodyDrawFilter = opaque {}; pub const TriangleBatch = opaque {}; pub const Primitive = opaque {}; pub const DebugRendererResult = enum(c.JPC_DebugRendererResult) { success = c.JPC_DEBUGRENDERER_SUCCESS, duplicate_singleton = c.JPC_DEBUGRENDERER_DUPLICATE_SINGLETON, missing_singleton = c.JPC_DEBUGRENDERER_MISSING_SINGLETON, incomplete_impl = c.JPC_DEBUGRENDERER_INCOMPLETE_IMPL, }; // zig fmt: off pub const ShapeColor = enum(c.JPC_ShapeColor) { instance_color = c.JPC_INSTANCE_COLOR, // Random color per instance shape_type_color = c.JPC_SHAPE_TYPE_COLOR, // Convex = green, scaled = yellow, compound = orange, mesh = red motion_type_color = c.JPC_MOTION_TYPE_COLOR, // Static = grey, keyframed = green, dynamic = random sleep_color = c.JPC_SLEEP_COLOR, // Static = grey, keyframed = green, dynamic = yellow, asleep= red island_color = c.JPC_ISLAND_COLOR, // Static = grey, active = random per island, sleeping = light grey material_color = c.JPC_MATERIAL_COLOR, // Color as defined by the PhysicsMaterial of the shape }; // zig fmt: on pub const CullMode = enum(c.JPC_CullMode) { cull_back_face = c.JPC_CULL_BACK_FACE, cull_front_face = c.JPC_CULL_FRONT_FACE, culling_off = c.JPC_CULLING_OFF, }; pub const CastShadow = enum(c.JPC_CastShadow) { cast_shadow_on = c.JPC_CAST_SHADOW_ON, cast_shadow_off = c.JPC_CAST_SHADOW_OFF, }; pub const DrawMode = enum(c.JPC_DrawMode) { draw_mode_solid = c.JPC_DRAW_MODE_SOLID, draw_mode_wireframe = c.JPC_DRAW_MODE_WIREFRAME, }; comptime { if (debug_renderer_enabled) { assert(@sizeOf(VTable(@This())) == @sizeOf(c.JPC_DebugRendererVTable)); assert(@offsetOf(VTable(@This()), "drawTriangle") == @offsetOf(c.JPC_DebugRendererVTable, "DrawTriangle")); assert(@offsetOf(VTable(@This()), "drawText3D") == @offsetOf(c.JPC_DebugRendererVTable, "DrawText3D")); } } }; //-------------------------------------------------------------------------------------------------- // // Init/deinit and global state // //-------------------------------------------------------------------------------------------------- const SizeAndAlignment = packed struct(u64) { size: u48, alignment: u16, }; var mem_allocator: ?std.mem.Allocator = null; var mem_allocations: ?std.AutoHashMap(usize, SizeAndAlignment) = null; var mem_mutex: std.Thread.Mutex = .{}; const mem_alignment = 16; var temp_allocator: ?*TempAllocator = null; var job_system: ?*JobSystem = null; pub fn init(allocator: std.mem.Allocator, args: struct { temp_allocator_size: u32 = 16 * 1024 * 1024, max_jobs: u32 = max_physics_jobs, max_barriers: u32 = max_physics_barriers, num_threads: i32 = -1, }) !void { std.debug.assert(mem_allocator == null and mem_allocations == null); mem_allocator = allocator; mem_allocations = std.AutoHashMap(usize, SizeAndAlignment).init(allocator); mem_allocations.?.ensureTotalCapacity(32) catch unreachable; c.JPC_RegisterCustomAllocator(zphysicsAlloc, zphysicsFree, zphysicsAlignedAlloc, zphysicsFree); c.JPC_CreateFactory(); c.JPC_RegisterTypes(); assert(temp_allocator == null and job_system == null); temp_allocator = @as(*TempAllocator, @ptrCast(c.JPC_TempAllocator_Create(args.temp_allocator_size))); job_system = @as(*JobSystem, @ptrCast(c.JPC_JobSystem_Create(args.max_jobs, args.max_barriers, args.num_threads))); } pub fn deinit() void { c.JPC_JobSystem_Destroy(@as(*c.JPC_JobSystem, @ptrCast(job_system))); job_system = null; c.JPC_TempAllocator_Destroy(@as(*c.JPC_TempAllocator, @ptrCast(temp_allocator))); temp_allocator = null; c.JPC_DestroyFactory(); mem_allocations.?.deinit(); mem_allocations = null; mem_allocator = null; } //-------------------------------------------------------------------------------------------------- // // PhysicsSystem // //-------------------------------------------------------------------------------------------------- pub const PhysicsSystem = opaque { pub fn create( broad_phase_layer_interface: *const BroadPhaseLayerInterface, object_vs_broad_phase_layer_filter: *const ObjectVsBroadPhaseLayerFilter, object_layer_pair_filter: *const ObjectLayerPairFilter, args: struct { max_bodies: u32 = 1024, num_body_mutexes: u32 = 0, max_body_pairs: u32 = 1024, max_contact_constraints: u32 = 1024, }, ) !*PhysicsSystem { return @as(*PhysicsSystem, @ptrCast(c.JPC_PhysicsSystem_Create( args.max_bodies, args.num_body_mutexes, args.max_body_pairs, args.max_contact_constraints, broad_phase_layer_interface, object_vs_broad_phase_layer_filter, object_layer_pair_filter, ))); } pub fn destroy(physics_system: *PhysicsSystem) void { c.JPC_PhysicsSystem_Destroy(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn getNumBodies(physics_system: *const PhysicsSystem) u32 { return c.JPC_PhysicsSystem_GetNumBodies(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn getNumActiveBodies(physics_system: *const PhysicsSystem) u32 { return c.JPC_PhysicsSystem_GetNumActiveBodies(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn getMaxBodies(physics_system: *const PhysicsSystem) u32 { return c.JPC_PhysicsSystem_GetMaxBodies(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn getGravity(physics_system: *const PhysicsSystem) [3]f32 { var gravity: [3]f32 = undefined; c.JPC_PhysicsSystem_GetGravity(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)), &gravity); return gravity; } pub fn setGravity(physics_system: *PhysicsSystem, gravity: [3]f32) void { c.JPC_PhysicsSystem_SetGravity(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), &gravity); } pub fn getBodyInterface(physics_system: *const PhysicsSystem) *const BodyInterface { return @as( *const BodyInterface, @ptrCast(c.JPC_PhysicsSystem_GetBodyInterface(@as(*c.JPC_PhysicsSystem, @ptrFromInt(@intFromPtr(physics_system))))), ); } pub fn getBodyInterfaceNoLock(physics_system: *const PhysicsSystem) *const BodyInterface { return @as( *const BodyInterface, @ptrCast(c.JPC_PhysicsSystem_GetBodyInterfaceNoLock(@as(*c.JPC_PhysicsSystem, @ptrFromInt(@intFromPtr(physics_system))))), ); } pub fn getBodyInterfaceMut(physics_system: *PhysicsSystem) *BodyInterface { return @as( *BodyInterface, @ptrCast(c.JPC_PhysicsSystem_GetBodyInterface(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)))), ); } pub fn getBodyInterfaceMutNoLock(physics_system: *PhysicsSystem) *BodyInterface { return @as( *BodyInterface, @ptrCast(c.JPC_PhysicsSystem_GetBodyInterfaceNoLock(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)))), ); } pub fn getNarrowPhaseQuery(physics_system: *const PhysicsSystem) *const NarrowPhaseQuery { return @as( *const NarrowPhaseQuery, @ptrCast(c.JPC_PhysicsSystem_GetNarrowPhaseQuery(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)))), ); } pub fn getNarrowPhaseQueryNoLock(physics_system: *const PhysicsSystem) *const NarrowPhaseQuery { return @as( *const NarrowPhaseQuery, @ptrCast(c.JPC_PhysicsSystem_GetNarrowPhaseQueryNoLock(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)))), ); } pub fn getBodyLockInterface(physics_system: *const PhysicsSystem) *const BodyLockInterface { return @as( *const BodyLockInterface, @ptrCast(c.JPC_PhysicsSystem_GetBodyLockInterface(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)))), ); } pub fn getBodyLockInterfaceNoLock(physics_system: *const PhysicsSystem) *const BodyLockInterface { return @as( *const BodyLockInterface, @ptrCast(c.JPC_PhysicsSystem_GetBodyLockInterfaceNoLock(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)))), ); } pub fn setBodyActivationListener(physics_system: *PhysicsSystem, listener: ?*anyopaque) void { c.JPC_PhysicsSystem_SetBodyActivationListener(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), listener); } pub fn getBodyActivationListener(physics_system: *const PhysicsSystem) ?*anyopaque { return c.JPC_PhysicsSystem_GetBodyActivationListener(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn setContactListener(physics_system: *PhysicsSystem, listener: ?*anyopaque) void { c.JPC_PhysicsSystem_SetContactListener(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), listener); } pub fn getContactListener(physics_system: *const PhysicsSystem) ?*anyopaque { return c.JPC_PhysicsSystem_GetContactListener(@as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn optimizeBroadPhase(physics_system: *PhysicsSystem) void { c.JPC_PhysicsSystem_OptimizeBroadPhase(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn addStepListener(physics_system: *PhysicsSystem, listener: ?*anyopaque) void { c.JPC_PhysicsSystem_AddStepListener(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), listener); } pub fn removeStepListener(physics_system: *PhysicsSystem, listener: ?*anyopaque) void { c.JPC_PhysicsSystem_RemoveStepListener(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), listener); } pub fn addConstraint(physics_system: *PhysicsSystem, two_body_constraint: ?*anyopaque) void { c.JPC_PhysicsSystem_AddConstraint(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), two_body_constraint); } pub fn removeConstraint(physics_system: *PhysicsSystem, two_body_constraint: ?*anyopaque) void { c.JPC_PhysicsSystem_RemoveConstraint(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), two_body_constraint); } pub fn update( physics_system: *PhysicsSystem, delta_time: f32, args: struct { collision_steps: i32 = 1, integration_sub_steps: i32 = 1, }, ) !void { const res = c.JPC_PhysicsSystem_Update( @as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), delta_time, args.collision_steps, args.integration_sub_steps, @as(*c.JPC_TempAllocator, @ptrCast(temp_allocator)), @as(*c.JPC_JobSystem, @ptrCast(job_system)), ); switch (res) { c.JPC_PHYSICS_UPDATE_NO_ERROR => {}, c.JPC_PHYSICS_UPDATE_MANIFOLD_CACHE_FULL => return error.ManifoldCacheFull, c.JPC_PHYSICS_UPDATE_BODY_PAIR_CACHE_FULL => return error.BodyPairCacheFull, c.JPC_PHYSICS_UPDATE_CONTACT_CONSTRAINTS_FULL => return error.ContactConstraintsFull, else => return error.Unknown, } } pub usingnamespace if (!debug_renderer_enabled) struct {} else struct { pub fn drawBodies( physics_system: *PhysicsSystem, in_draw_settings: *const DebugRenderer.BodyDrawSettings, in_draw_filter: ?*const DebugRenderer.BodyDrawFilter, ) void { c.JPC_PhysicsSystem_DrawBodies( @as(*c.JPC_PhysicsSystem, @ptrCast(physics_system)), @as(*const c.JPC_BodyManager_DrawSettings, @ptrCast(in_draw_settings)), @as(?*const c.JPC_BodyDrawFilter, @ptrCast(in_draw_filter)), ); } pub fn drawConstraints(physics_system: *PhysicsSystem) void { c.JPC_PhysicsSystem_DrawConstraints(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn drawConstraintLimits(physics_system: *PhysicsSystem) void { c.JPC_PhysicsSystem_DrawConstraintLimits(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system))); } pub fn drawConstraintReferenceFrame(physics_system: *PhysicsSystem) void { c.JPC_PhysicsSystem_DrawConstraintReferenceFrame(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system))); } }; pub fn getBodyIds(physics_system: *const PhysicsSystem, body_ids: *std.ArrayList(BodyId)) !void { try body_ids.ensureTotalCapacityPrecise(physics_system.getMaxBodies()); var num_body_ids: u32 = 0; c.JPC_PhysicsSystem_GetBodyIDs( @as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)), @as(u32, @intCast(body_ids.capacity)), &num_body_ids, body_ids.items.ptr, ); body_ids.items.len = num_body_ids; } pub fn getActiveBodyIds(physics_system: *const PhysicsSystem, body_ids: *std.ArrayList(BodyId)) !void { try body_ids.ensureTotalCapacityPrecise(physics_system.getMaxBodies()); var num_body_ids: u32 = 0; c.JPC_PhysicsSystem_GetActiveBodyIDs( @as(*const c.JPC_PhysicsSystem, @ptrCast(physics_system)), @as(u32, @intCast(body_ids.capacity)), &num_body_ids, body_ids.items.ptr, ); body_ids.items.len = num_body_ids; } /// NOTE: Advanced. This function is *not* protected by a lock, use with care! pub fn getBodiesUnsafe(physics_system: *const PhysicsSystem) []const *const Body { const ptr = c.JPC_PhysicsSystem_GetBodiesUnsafe( @as(*c.JPC_PhysicsSystem, @ptrFromInt(@intFromPtr(physics_system))), ); return @as([*]const *const Body, @ptrCast(ptr))[0..physics_system.getNumBodies()]; } /// NOTE: Advanced. This function is *not* protected by a lock, use with care! pub fn getBodiesMutUnsafe(physics_system: *PhysicsSystem) []const *Body { const ptr = c.JPC_PhysicsSystem_GetBodiesUnsafe(@as(*c.JPC_PhysicsSystem, @ptrCast(physics_system))); return @as([*]const *Body, @ptrCast(ptr))[0..physics_system.getNumBodies()]; } }; //-------------------------------------------------------------------------------------------------- // // BodyLock* // //-------------------------------------------------------------------------------------------------- pub const BodyLockRead = extern struct { lock_interface: *const BodyLockInterface = undefined, mutex: ?*SharedMutex = null, body: ?*const Body = null, pub fn lock( read_lock: *BodyLockRead, lock_interface: *const BodyLockInterface, body_id: BodyId, ) void { c.JPC_BodyLockInterface_LockRead( @as(*const c.JPC_BodyLockInterface, @ptrCast(lock_interface)), body_id, @as(*c.JPC_BodyLockRead, @ptrCast(read_lock)), ); } pub fn unlock(read_lock: *BodyLockRead) void { c.JPC_BodyLockInterface_UnlockRead( @as(*const c.JPC_BodyLockInterface, @ptrCast(read_lock.lock_interface)), @as(*c.JPC_BodyLockRead, @ptrCast(read_lock)), ); } comptime { assert(@sizeOf(BodyLockRead) == @sizeOf(c.JPC_BodyLockRead)); assert(@offsetOf(BodyLockRead, "mutex") == @offsetOf(c.JPC_BodyLockRead, "mutex")); assert(@offsetOf(BodyLockRead, "body") == @offsetOf(c.JPC_BodyLockRead, "body")); } }; pub const BodyLockWrite = extern struct { lock_interface: *const BodyLockInterface = undefined, mutex: ?*SharedMutex = null, body: ?*Body = null, pub fn lock( write_lock: *BodyLockWrite, lock_interface: *const BodyLockInterface, body_id: BodyId, ) void { c.JPC_BodyLockInterface_LockWrite( @as(*const c.JPC_BodyLockInterface, @ptrCast(lock_interface)), body_id, @as(*c.JPC_BodyLockWrite, @ptrCast(write_lock)), ); } pub fn unlock(write_lock: *BodyLockWrite) void { c.JPC_BodyLockInterface_UnlockWrite( @as(*const c.JPC_BodyLockInterface, @ptrCast(write_lock.lock_interface)), @as(*c.JPC_BodyLockWrite, @ptrCast(write_lock)), ); } comptime { assert(@sizeOf(BodyLockWrite) == @sizeOf(c.JPC_BodyLockWrite)); assert(@offsetOf(BodyLockWrite, "mutex") == @offsetOf(c.JPC_BodyLockWrite, "mutex")); assert(@offsetOf(BodyLockWrite, "body") == @offsetOf(c.JPC_BodyLockWrite, "body")); } }; //-------------------------------------------------------------------------------------------------- // // BodyInterface // //-------------------------------------------------------------------------------------------------- pub const BodyInterface = opaque { pub fn createBody(body_iface: *BodyInterface, settings: BodyCreationSettings) !*Body { const body = c.JPC_BodyInterface_CreateBody( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), @as(*const c.JPC_BodyCreationSettings, @ptrCast(&settings)), ); if (body == null) return error.FailedToCreateBody; return @as(*Body, @ptrCast(body)); } pub fn createBodyWithId(body_iface: *BodyInterface, body_id: BodyId, settings: BodyCreationSettings) !*Body { const body = c.JPC_BodyInterface_CreateBodyWithID( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, @as(*const c.JPC_BodyCreationSettings, @ptrCast(&settings)), ); if (body == null) return error.FailedToCreateBody; return @as(*Body, @ptrCast(body)); } pub fn destroyBody(body_iface: *BodyInterface, body_id: BodyId) void { c.JPC_BodyInterface_DestroyBody(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn addBody(body_iface: *BodyInterface, body_id: BodyId, mode: Activation) void { c.JPC_BodyInterface_AddBody(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, @intFromEnum(mode)); } pub fn removeBody(body_iface: *BodyInterface, body_id: BodyId) void { c.JPC_BodyInterface_RemoveBody(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn createAndAddBody(body_iface: *BodyInterface, settings: BodyCreationSettings, mode: Activation) !BodyId { const body_id = c.JPC_BodyInterface_CreateAndAddBody( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), @as(*const c.JPC_BodyCreationSettings, @ptrCast(&settings)), @intFromEnum(mode), ); if (body_id == body_id_invalid) return error.FailedToCreateBody; return body_id; } pub fn removeAndDestroyBody(body_iface: *BodyInterface, body_id: BodyId) void { body_iface.removeBody(body_id); body_iface.destroyBody(body_id); } pub fn isAdded(body_iface: *const BodyInterface, body_id: BodyId) bool { return c.JPC_BodyInterface_IsAdded(@as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn activate(body_iface: *BodyInterface, body_id: BodyId) void { return c.JPC_BodyInterface_ActivateBody(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn deactivate(body_iface: *BodyInterface, body_id: BodyId) void { return c.JPC_BodyInterface_DeactivateBody(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn isActive(body_iface: *const BodyInterface, body_id: BodyId) bool { return c.JPC_BodyInterface_IsActive(@as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn setLinearAndAngularVelocity( body_iface: *BodyInterface, body_id: BodyId, linear_velocity: [3]f32, angular_velocity: [3]f32, ) void { return c.JPC_BodyInterface_SetLinearAndAngularVelocity( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &linear_velocity, &angular_velocity, ); } pub fn getLinearAndAngularVelocity( body_iface: *const BodyInterface, body_id: BodyId, ) struct { linear: [3]f32, angular: [3]f32 } { var linear: [3]f32 = undefined; var angular: [3]f32 = undefined; c.JPC_BodyInterface_GetLinearAndAngularVelocity( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &linear, &angular, ); return .{ .linear = linear, .angular = angular }; } pub fn setLinearVelocity(body_iface: *BodyInterface, body_id: BodyId, velocity: [3]f32) void { return c.JPC_BodyInterface_SetLinearVelocity( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &velocity, ); } pub fn getLinearVelocity(body_iface: *const BodyInterface, body_id: BodyId) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_BodyInterface_GetLinearVelocity( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &velocity, ); return velocity; } pub fn addLinearVelocity(body_iface: *BodyInterface, body_id: BodyId, velocity: [3]f32) void { return c.JPC_BodyInterface_AddLinearVelocity( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &velocity, ); } pub fn addLinearAndAngularVelocity( body_iface: *BodyInterface, body_id: BodyId, linear_velocity: [3]f32, angular_velocity: [3]f32, ) void { return c.JPC_BodyInterface_AddLinearAndAngularVelocity( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &linear_velocity, &angular_velocity, ); } pub fn setAngularVelocity(body_iface: *BodyInterface, body_id: BodyId, velocity: [3]f32) void { return c.JPC_BodyInterface_SetAngularVelocity( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &velocity, ); } pub fn getAngularVelocity(body_iface: *const BodyInterface, body_id: BodyId) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_BodyInterface_GetAngularVelocity( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &velocity, ); return velocity; } pub fn getPointVelocity(body_iface: *const BodyInterface, body_id: BodyId, point: [3]Real) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_BodyInterface_GetPointVelocity( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &point, &velocity, ); return velocity; } pub fn getPosition(body_iface: *const BodyInterface, body_id: BodyId) [3]Real { var position: [3]Real = undefined; c.JPC_BodyInterface_GetPosition( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &position, ); return position; } pub fn setPosition(body_iface: *BodyInterface, body_id: BodyId, in_position: [3]Real, in_activation_type: Activation) void { c.JPC_BodyInterface_SetPosition(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &in_position, @intFromEnum(in_activation_type)); } pub fn getCenterOfMassPosition(body_iface: *const BodyInterface, body_id: BodyId) [3]Real { var position: [3]Real = undefined; c.JPC_BodyInterface_GetCenterOfMassPosition( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &position, ); return position; } pub fn getRotation(body_iface: *const BodyInterface, body_id: BodyId) [4]f32 { var rotation: [4]f32 = undefined; c.JPC_BodyInterface_GetRotation( @as(*const c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &rotation, ); return rotation; } pub fn setRotation(body_iface: *BodyInterface, body_id: BodyId, in_rotation: [4]Real, in_activation_type: Activation) void { c.JPC_BodyInterface_SetRotation(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &in_rotation, @intFromEnum(in_activation_type)); } pub fn setPositionRotationAndVelocity( body_iface: *BodyInterface, body_id: BodyId, position: [3]Real, rotation: [4]f32, linear_velocity: [3]f32, angular_velocity: [3]f32, ) void { return c.JPC_BodyInterface_SetPositionRotationAndVelocity( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &position, &rotation, &linear_velocity, &angular_velocity, ); } pub fn addForce(body_iface: *BodyInterface, body_id: BodyId, force: [3]f32) void { return c.JPC_BodyInterface_AddForce( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &force, ); } pub fn addForceAtPosition(body_iface: *BodyInterface, body_id: BodyId, force: [3]f32, position: [3]Real) void { return c.JPC_BodyInterface_AddForceAtPosition( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &force, &position, ); } pub fn addTorque(body_iface: *BodyInterface, body_id: BodyId, torque: [3]f32) void { return c.JPC_BodyInterface_AddTorque( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &torque, ); } pub fn addForceAndTorque(body_iface: *BodyInterface, body_id: BodyId, force: [3]f32, torque: [3]f32) void { return c.JPC_BodyInterface_AddForceAndTorque( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &force, &torque, ); } pub fn addImpulse(body_iface: *BodyInterface, body_id: BodyId, impulse: [3]f32) void { return c.JPC_BodyInterface_AddImpulse( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &impulse, ); } pub fn addImpulseAtPosition( body_iface: *BodyInterface, body_id: BodyId, impulse: [3]f32, position: [3]Real, ) void { return c.JPC_BodyInterface_AddImpulseAtPosition( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &impulse, &position, ); } pub fn addAngularImpulse(body_iface: *BodyInterface, body_id: BodyId, impulse: [3]f32) void { return c.JPC_BodyInterface_AddAngularImpulse( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, &impulse, ); } pub fn getMotionType(body_iface: *const BodyInterface, body_id: BodyId) MotionType { return @as(MotionType, @enumFromInt( c.JPC_BodyInterface_GetMotionType( @ptrCast(body_iface), body_id, ), )); } pub fn setMotionType(body_iface: *BodyInterface, body_id: BodyId, in_motion_type: MotionType, in_activation_type: Activation) void { return c.JPC_BodyInterface_SetMotionType( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, @intFromEnum(in_motion_type), @intFromEnum(in_activation_type), ); } pub fn getObjectLayer(body_iface: *BodyInterface, body_id: BodyId) ObjectLayer { return c.JPC_BodyInterface_GetObjectLayer(@as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id); } pub fn setObjectLayer(body_iface: *BodyInterface, body_id: BodyId, in_layer: ObjectLayer) void { c.JPC_BodyInterface_SetObjectLayer( @as(*c.JPC_BodyInterface, @ptrCast(body_iface)), body_id, in_layer, ); } }; //-------------------------------------------------------------------------------------------------- // // NarrowPhaseQuery // //-------------------------------------------------------------------------------------------------- pub const NarrowPhaseQuery = opaque { pub fn castRay( query: *const NarrowPhaseQuery, ray: RRayCast, args: struct { broad_phase_layer_filter: ?*const BroadPhaseLayerFilter = null, object_layer_filter: ?*const ObjectLayerFilter = null, body_filter: ?*const BodyFilter = null, }, ) struct { has_hit: bool, hit: RayCastResult } { var hit: RayCastResult = .{}; const has_hit = c.JPC_NarrowPhaseQuery_CastRay( @as(*const c.JPC_NarrowPhaseQuery, @ptrCast(query)), @as(*const c.JPC_RRayCast, @ptrCast(&ray)), @as(*c.JPC_RayCastResult, @ptrCast(&hit)), args.broad_phase_layer_filter, args.object_layer_filter, args.body_filter, ); return .{ .has_hit = has_hit, .hit = hit }; } }; //-------------------------------------------------------------------------------------------------- // // Body // //-------------------------------------------------------------------------------------------------- pub const Body = extern struct { position: [4]Real align(rvec_align), // 4th element is ignored rotation: [4]f32 align(16), bounds_min: [4]f32 align(16), // 4th element is ignored bounds_max: [4]f32 align(16), // 4th element is ignored shape: *const Shape, motion_properties: ?*MotionProperties, // Will be null for static objects user_data: u64, collision_group: CollisionGroup, friction: f32, restitution: f32, id: BodyId, object_layer: ObjectLayer, broad_phase_layer: BroadPhaseLayer, motion_type: MotionType, flags: u8, pub fn getId(body: *const Body) BodyId { return c.JPC_Body_GetID(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn isActive(body: *const Body) bool { return c.JPC_Body_IsActive(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn isStatic(body: *const Body) bool { return c.JPC_Body_IsStatic(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn isKinematic(body: *const Body) bool { return c.JPC_Body_IsKinematic(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn isDynamic(body: *const Body) bool { return c.JPC_Body_IsDynamic(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn canBeKinematicOrDynamic(body: *const Body) bool { return c.JPC_Body_CanBeKinematicOrDynamic(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn isSensor(body: *const Body) bool { return c.JPC_Body_IsSensor(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn setIsSensor(body: *Body, is_sensor: bool) void { c.JPC_Body_SetIsSensor(@as(*c.JPC_Body, @ptrCast(body)), is_sensor); } pub fn getMotionType(body: *const Body) MotionType { return @as(MotionType, @enumFromInt(c.JPC_Body_GetMotionType(@as(*const c.JPC_Body, @ptrCast(body))))); } pub fn setMotionType(body: *Body, motion_type: MotionType) void { return c.JPC_Body_SetMotionType(@as(*c.JPC_Body, @ptrCast(body)), @intFromEnum(motion_type)); } pub fn getBroadPhaseLayer(body: *const Body) BroadPhaseLayer { return c.JPC_Body_GetBroadPhaseLayer(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn getObjectLayer(body: *const Body) ObjectLayer { return c.JPC_Body_GetObjectLayer(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn getCollisionGroup(body: *const Body) *const CollisionGroup { return @as( *const CollisionGroup, @ptrCast(c.JPC_Body_GetCollisionGroup(@as(*c.JPC_Body, @ptrFromInt(@intFromPtr(body))))), ); } pub fn getCollisionGroupMut(body: *Body) *CollisionGroup { return @as( *CollisionGroup, @ptrCast(c.JPC_Body_GetCollisionGroup(@as(*c.JPC_Body, @ptrCast(body)))), ); } pub fn setCollisionGroup(body: *Body, group: CollisionGroup) void { c.JPC_Body_SetCollisionGroup( @as(*c.JPC_Body, @ptrCast(body)), @as(*const c.JPC_CollisionGroup, @ptrCast(&group)), ); } pub fn getAllowSleeping(body: *const Body) bool { return c.JPC_Body_GetAllowSleeping(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn setAllowSleeping(body: *Body, allow: bool) void { c.JPC_Body_SetAllowSleeping(@as(*c.JPC_Body, @ptrCast(body)), allow); } pub fn getFriction(body: *const Body) f32 { return c.JPC_Body_GetFriction(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn setFriction(body: *Body, friction: f32) void { c.JPC_Body_SetFriction(@as(*c.JPC_Body, @ptrCast(body)), friction); } pub fn getRestitution(body: *const Body) f32 { return c.JPC_Body_GetRestitution(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn setRestitution(body: *Body, restitution: f32) void { c.JPC_Body_SetRestitution(@as(*c.JPC_Body, @ptrCast(body)), restitution); } pub fn getLinearVelocity(body: *const Body) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_Body_GetLinearVelocity(@as(*const c.JPC_Body, @ptrCast(body)), &velocity); return velocity; } pub fn setLinearVelocity(body: *Body, velocity: [3]f32) void { c.JPC_Body_SetLinearVelocity(@as(*c.JPC_Body, @ptrCast(body)), &velocity); } pub fn setLinearVelocityClamped(body: *Body, velocity: [3]f32) void { c.JPC_Body_SetLinearVelocityClamped(@as(*c.JPC_Body, @ptrCast(body)), &velocity); } pub fn getAngularVelocity(body: *const Body) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_Body_GetAngularVelocity(@as(*const c.JPC_Body, @ptrCast(body)), &velocity); return velocity; } pub fn setAngularVelocity(body: *Body, velocity: [3]f32) void { c.JPC_Body_SetAngularVelocity(@as(*c.JPC_Body, @ptrCast(body)), &velocity); } pub fn setAngularVelocityClamped(body: *Body, velocity: [3]f32) void { c.JPC_Body_SetAngularVelocityClamped(@as(*c.JPC_Body, @ptrCast(body)), &velocity); } /// `point` is relative to the center of mass (com) pub fn getPointVelocityCom(body: *const Body, point: [3]f32) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_Body_GetPointVelocityCOM(@as(*const c.JPC_Body, @ptrCast(body)), &point, &velocity); return velocity; } /// `point` is in the world space pub fn getPointVelocity(body: *const Body, point: [3]Real) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_Body_GetPointVelocity(@as(*const c.JPC_Body, @ptrCast(body)), &point, &velocity); return velocity; } pub fn addForce(body: *Body, force: [3]f32) void { c.JPC_Body_AddForce(@as(*c.JPC_Body, @ptrCast(body)), &force); } pub fn addForceAtPosition(body: *Body, force: [3]f32, position: [3]Real) void { c.JPC_Body_AddForceAtPosition(@as(*c.JPC_Body, @ptrCast(body)), &force, &position); } pub fn addTorque(body: *Body, torque: [3]f32) void { c.JPC_Body_AddTorque(@as(*c.JPC_Body, @ptrCast(body)), &torque); } pub fn getInverseInertia(body: *const Body) [16]f32 { var inverse_inertia: [16]f32 = undefined; c.JPC_Body_GetInverseInertia(@as(*const c.JPC_Body, @ptrCast(body)), &inverse_inertia); return inverse_inertia; } pub fn addImpulse(body: *Body, impulse: [3]f32) void { c.JPC_Body_AddImpulse(@as(*c.JPC_Body, @ptrCast(body)), &impulse); } pub fn addImpulseAtPosition(body: *Body, impulse: [3]f32, position: [3]Real) void { c.JPC_Body_AddImpulseAtPosition(@as(*c.JPC_Body, @ptrCast(body)), &impulse, &position); } pub fn addAngularImpulse(body: *Body, impulse: [3]f32) void { c.JPC_Body_AddAngularImpulse(@as(*c.JPC_Body, @ptrCast(body)), &impulse); } pub fn moveKinematic( body: *Body, target_position: [3]Real, target_rotation: [4]f32, delta_time: f32, ) void { c.JPC_Body_MoveKinematic( @as(*c.JPC_Body, @ptrCast(body)), &target_position, &target_rotation, delta_time, ); } pub fn applyBuoyancyImpulse( body: *Body, surface_position: [3]Real, surface_normal: [3]f32, buoyancy: f32, linear_drag: f32, angular_drag: f32, fluid_velocity: [3]f32, gravity: [3]f32, delta_time: f32, ) void { c.JPC_Body_ApplyBuoyancyImpulse( @as(*c.JPC_Body, @ptrCast(body)), &surface_position, &surface_normal, buoyancy, linear_drag, angular_drag, &fluid_velocity, &gravity, delta_time, ); } pub fn isInBroadPhase(body: *const Body) bool { return c.JPC_Body_IsInBroadPhase(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn isCollisionCacheInvalid(body: *const Body) bool { return c.JPC_Body_IsCollisionCacheInvalid(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn getShape(body: *const Body) *const Shape { return @as(*const Shape, @ptrCast(c.JPC_Body_GetShape(@as(*const c.JPC_Body, @ptrCast(body))))); } pub fn getPosition(body: *const Body) [3]Real { var position: [3]Real = undefined; c.JPC_Body_GetPosition(@as(*const c.JPC_Body, @ptrCast(body)), &position); return position; } pub fn getRotation(body: *const Body) [4]f32 { var rotation: [4]f32 = undefined; c.JPC_Body_GetRotation(@as(*const c.JPC_Body, @ptrCast(body)), &rotation); return rotation; } pub fn getWorldTransform(body: *const Body) struct { rotation: [9]f32, position: [3]Real, } { var rotation: [9]f32 = undefined; var position: [3]Real = undefined; c.JPC_Body_GetWorldTransform(@as(*const c.JPC_Body, @ptrCast(body)), &rotation, &position); return .{ .rotation = rotation, .position = position }; } pub fn getCenterOfMassPosition(body: *const Body) [3]Real { var position: [3]Real = undefined; c.JPC_Body_GetCenterOfMassPosition(@as(*const c.JPC_Body, @ptrCast(body)), &position); return position; } pub fn getCenterOfMassTransform(body: *const Body) struct { rotation: [9]f32, position: [3]Real, } { var rotation: [9]f32 = undefined; var position: [3]Real = undefined; c.JPC_Body_GetCenterOfMassTransform(@as(*const c.JPC_Body, @ptrCast(body)), &rotation, &position); return .{ .rotation = rotation, .position = position }; } pub fn getInverseCenterOfMassTransform(body: *const Body) struct { rotation: [9]f32, position: [3]Real, } { var rotation: [9]f32 = undefined; var position: [3]Real = undefined; c.JPC_Body_GetInverseCenterOfMassTransform(@as(*const c.JPC_Body, @ptrCast(body)), &rotation, &position); return .{ .rotation = rotation, .position = position }; } pub fn getWorldSpaceBounds(body: *const Body) struct { min: [3]f32, max: [3]f32, } { var min: [3]f32 = undefined; var max: [3]f32 = undefined; c.JPC_Body_GetWorldSpaceBounds(@as(*const c.JPC_Body, @ptrCast(body)), &min, &max); return .{ .min = min, .max = max }; } pub fn getMotionProperties(body: *const Body) *const MotionProperties { return @as( *const MotionProperties, @ptrCast(c.JPC_Body_GetMotionProperties(@as(*c.JPC_Body, @ptrFromInt(@intFromPtr(body))))), ); } pub fn getMotionPropertiesMut(body: *Body) *MotionProperties { return @as( *MotionProperties, @ptrCast(c.JPC_Body_GetMotionProperties(@as(*c.JPC_Body, @ptrCast(body)))), ); } pub fn getUserData(body: *const Body) u64 { return c.JPC_Body_GetUserData(@as(*const c.JPC_Body, @ptrCast(body))); } pub fn setUserData(body: *Body, user_data: u64) void { return c.JPC_Body_SetUserData(@as(*c.JPC_Body, @ptrCast(body)), user_data); } pub fn getWorldSpaceSurfaceNormal( body: *const Body, sub_shape_id: SubShapeId, position: [3]Real, // world space ) [3]f32 { var normal: [3]f32 = undefined; c.JPC_Body_GetWorldSpaceSurfaceNormal( @as(*const c.JPC_Body, @ptrCast(body)), sub_shape_id, &position, &normal, ); return normal; } comptime { assert(@sizeOf(Body) == @sizeOf(c.JPC_Body)); assert(@offsetOf(Body, "flags") == @offsetOf(c.JPC_Body, "flags")); assert(@offsetOf(Body, "motion_properties") == @offsetOf(c.JPC_Body, "motion_properties")); assert(@offsetOf(Body, "object_layer") == @offsetOf(c.JPC_Body, "object_layer")); assert(@offsetOf(Body, "rotation") == @offsetOf(c.JPC_Body, "rotation")); } }; //-------------------------------------------------------------------------------------------------- // // Character // //-------------------------------------------------------------------------------------------------- pub const Character = opaque { pub fn create( in_settings: *const CharacterSettings, in_position: [3]Real, in_rotation: [4]f32, in_user_data: u64, in_physics_system: *PhysicsSystem, ) !*Character { return @as(*Character, @ptrCast(c.JPC_Character_Create( @as(*const c.JPC_CharacterSettings, @ptrCast(in_settings)), &in_position, &in_rotation, in_user_data, @as(*c.JPC_PhysicsSystem, @ptrCast(in_physics_system)), ))); } pub fn destroy(character: *Character) void { c.JPC_Character_Destroy(@as(*c.JPC_Character, @ptrCast(character))); } pub fn addToPhysicsSystem(character: *Character, args: struct { activation: Activation = .activate, lock_bodies: bool = true }) void { c.JPC_Character_AddToPhysicsSystem( @as(*c.JPC_Character, @ptrCast(character)), @intFromEnum(args.activation), args.lock_bodies, ); } pub fn removeFromPhysicsSystem(character: *Character, args: struct { lock_bodies: bool = true }) void { c.JPC_Character_RemoveFromPhysicsSystem(@as(*c.JPC_Character, @ptrCast(character)), args.lock_bodies); } pub fn getBodyId(character: *const Character) BodyId { return @bitCast(c.JPC_Character_GetBodyId(@as(*const c.JPC_Character, @ptrCast(character)))); } pub fn getPosition(character: *const Character) [3]Real { var position: [3]Real = undefined; c.JPC_Character_GetPosition(@as(*const c.JPC_Character, @ptrCast(character)), &position); return position; } pub fn setPosition(character: *Character, position: [3]Real) void { c.JPC_Character_SetPosition(@as(*c.JPC_Character, @ptrCast(character)), &position); } pub fn getLinearVelocity(character: *const Character) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_Character_GetLinearVelocity(@as(*const c.JPC_Character, @ptrCast(character)), &velocity); return velocity; } pub fn setLinearVelocity(character: *Character, velocity: [3]f32) void { c.JPC_Character_SetLinearVelocity(@as(*c.JPC_Character, @ptrCast(character)), &velocity); } }; //-------------------------------------------------------------------------------------------------- // // CharacterVirtual // //-------------------------------------------------------------------------------------------------- pub const CharacterVirtual = opaque { pub fn create( in_settings: *const CharacterVirtualSettings, in_position: [3]Real, in_rotation: [4]f32, in_physics_system: *PhysicsSystem, ) !*CharacterVirtual { return @as(*CharacterVirtual, @ptrCast(c.JPC_CharacterVirtual_Create( @as(*const c.JPC_CharacterVirtualSettings, @ptrCast(in_settings)), &in_position, &in_rotation, @as(*c.JPC_PhysicsSystem, @ptrCast(in_physics_system)), ))); } pub fn destroy(character: *CharacterVirtual) void { c.JPC_CharacterVirtual_Destroy(@as(*c.JPC_CharacterVirtual, @ptrCast(character))); } pub fn update( character: *CharacterVirtual, delta_time: f32, gravity: [3]f32, args: struct { broad_phase_layer_filter: ?*const BroadPhaseLayerFilter = null, object_layer_filter: ?*const ObjectLayerFilter = null, body_filter: ?*const BodyFilter = null, shape_filter: ?*const ShapeFilter = null, }, ) void { c.JPC_CharacterVirtual_Update( @as(*c.JPC_CharacterVirtual, @ptrCast(character)), delta_time, &gravity, args.broad_phase_layer_filter, args.object_layer_filter, args.body_filter, args.shape_filter, @as(*c.JPC_TempAllocator, @ptrCast(temp_allocator)), ); } pub fn setListener(character: *CharacterVirtual, listener: ?*anyopaque) void { c.JPC_CharacterVirtual_SetListener(@as(*c.JPC_CharacterVirtual, @ptrCast(character)), listener); } pub fn updateGroundVelocity(character: *CharacterVirtual) void { c.JPC_CharacterVirtual_UpdateGroundVelocity(@as(*c.JPC_CharacterVirtual, @ptrCast(character))); } pub fn getGroundVelocity(character: *const CharacterVirtual) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_CharacterVirtual_GetGroundVelocity(@as(*const c.JPC_CharacterVirtual, @ptrCast(character)), &velocity); return velocity; } pub fn getGroundState(character: *CharacterVirtual) CharacterGroundState { return @enumFromInt(c.JPC_CharacterVirtual_GetGroundState(@as(*c.JPC_CharacterVirtual, @ptrCast(character)))); } pub fn getPosition(character: *const CharacterVirtual) [3]Real { var position: [3]Real = undefined; c.JPC_CharacterVirtual_GetPosition(@as(*const c.JPC_CharacterVirtual, @ptrCast(character)), &position); return position; } pub fn setPosition(character: *CharacterVirtual, position: [3]Real) void { c.JPC_CharacterVirtual_SetPosition(@as(*c.JPC_CharacterVirtual, @ptrCast(character)), &position); } pub fn getRotation(character: *const CharacterVirtual) [4]f32 { var rotation: [4]f32 = undefined; c.JPC_CharacterVirtual_GetRotation(@as(*const c.JPC_CharacterVirtual, @ptrCast(character)), &rotation); return rotation; } pub fn setRotation(character: *CharacterVirtual, rotation: [4]f32) void { c.JPC_CharacterVirtual_SetRotation(@as(*c.JPC_CharacterVirtual, @ptrCast(character)), &rotation); } pub fn getLinearVelocity(character: *const CharacterVirtual) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_CharacterVirtual_GetLinearVelocity(@as(*const c.JPC_CharacterVirtual, @ptrCast(character)), &velocity); return velocity; } pub fn setLinearVelocity(character: *CharacterVirtual, velocity: [3]f32) void { c.JPC_CharacterVirtual_SetLinearVelocity(@as(*c.JPC_CharacterVirtual, @ptrCast(character)), &velocity); } }; //-------------------------------------------------------------------------------------------------- // // MotionProperties // //-------------------------------------------------------------------------------------------------- pub const MotionProperties = extern struct { linear_velocity: [4]f32 align(16), // 4th element is ignored angular_velocity: [4]f32 align(16), // 4th element is ignored inv_inertia_diagonal: [4]f32 align(16), inertia_rotation: [4]f32 align(16), force: [3]f32, torque: [3]f32, inv_mass: f32, linear_damping: f32, angular_damping: f32, max_linear_velocity: f32, max_angular_velocity: f32, gravity_factor: f32, index_in_active_bodies: u32, island_index: u32, motion_quality: MotionQuality, allow_sleeping: bool, reserved: [52 + c.JPC_ENABLE_ASSERTS * 3 + c.JPC_DOUBLE_PRECISION * 24]u8 align(4 + 4 * c.JPC_DOUBLE_PRECISION), pub fn getMotionQuality(motion: *const MotionProperties) MotionQuality { return @as(MotionQuality, @enumFromInt(c.JPC_MotionProperties_GetMotionQuality( @as(*const c.JPC_MotionProperties, @ptrCast(motion)), ))); } pub fn getLinearVelocity(motion: *const MotionProperties) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_MotionProperties_GetLinearVelocity(@as(*const c.JPC_MotionProperties, @ptrCast(motion)), &velocity); return velocity; } pub fn setLinearVelocity(motion: *MotionProperties, velocity: [3]f32) void { c.JPC_MotionProperties_SetLinearVelocity(@as(*c.JPC_MotionProperties, @ptrCast(motion)), &velocity); } pub fn setLinearVelocityClamped(motion: *MotionProperties, velocity: [3]f32) void { c.JPC_MotionProperties_SetLinearVelocityClamped(@as(*c.JPC_MotionProperties, @ptrCast(motion)), &velocity); } pub fn getAngularVelocity(motion: *const MotionProperties) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_MotionProperties_GetAngularVelocity(@as(*const c.JPC_MotionProperties, @ptrCast(motion)), &velocity); return velocity; } pub fn setAngularVelocity(motion: *MotionProperties, velocity: [3]f32) void { c.JPC_MotionProperties_SetAngularVelocity(@as(*c.JPC_MotionProperties, @ptrCast(motion)), &velocity); } pub fn setAngularVelocityClamped(motion: *MotionProperties, velocity: [3]f32) void { c.JPC_MotionProperties_SetAngularVelocityClamped(@as(*c.JPC_MotionProperties, @ptrCast(motion)), &velocity); } /// `point` is relative to the center of mass (com) pub fn getPointVelocityCom(motion: *const MotionProperties, point: [3]f32) [3]f32 { var velocity: [3]f32 = undefined; c.JPC_MotionProperties_GetPointVelocityCOM( @as(*const c.JPC_MotionProperties, @ptrCast(motion)), &point, &velocity, ); return velocity; } pub fn getMaxLinearVelocity(motion: *const MotionProperties) f32 { return c.JPC_MotionProperties_GetMaxLinearVelocity(@as(*const c.JPC_MotionProperties, @ptrCast(motion))); } pub fn setMaxLinearVelocity(motion: *MotionProperties, velocity: f32) void { c.JPC_MotionProperties_SetMaxLinearVelocity(@as(*c.JPC_MotionProperties, @ptrCast(motion)), velocity); } pub fn getMaxAngularVelocity(motion: *const MotionProperties) f32 { return c.JPC_MotionProperties_GetMaxAngularVelocity(@as(*const c.JPC_MotionProperties, @ptrCast(motion))); } pub fn setMaxAngularVelocity(motion: *MotionProperties, velocity: f32) void { c.JPC_MotionProperties_SetMaxAngularVelocity(@as(*c.JPC_MotionProperties, @ptrCast(motion)), velocity); } pub fn moveKinematic( motion: *MotionProperties, delta_position: [3]f32, delta_rotation: [4]f32, delta_time: f32, ) void { c.JPC_MotionProperties_MoveKinematic( @as(*c.JPC_MotionProperties, @ptrCast(motion)), &delta_position, &delta_rotation, delta_time, ); } pub fn clampLinearVelocity(motion: *MotionProperties) void { c.JPC_MotionProperties_ClampLinearVelocity(@as(*c.JPC_MotionProperties, @ptrCast(motion))); } pub fn clampAngularVelocity(motion: *MotionProperties) void { c.JPC_MotionProperties_ClampAngularVelocity(@as(*c.JPC_MotionProperties, @ptrCast(motion))); } pub fn getLinearDamping(motion: *const MotionProperties) f32 { return c.JPC_MotionProperties_GetLinearDamping(@as(*const c.JPC_MotionProperties, @ptrCast(motion))); } pub fn setLinearDamping(motion: *MotionProperties, damping: f32) void { c.JPC_MotionProperties_SetLinearDamping(@as(*c.JPC_MotionProperties, @ptrCast(motion)), damping); } pub fn getAngularDamping(motion: *const MotionProperties) f32 { return c.JPC_MotionProperties_GetAngularDamping(@as(*const c.JPC_MotionProperties, @ptrCast(motion))); } pub fn setAngularDamping(motion: *MotionProperties, damping: f32) void { c.JPC_MotionProperties_SetAngularDamping(@as(*c.JPC_MotionProperties, @ptrCast(motion)), damping); } pub fn getGravityFactor(motion: *const MotionProperties) f32 { return c.JPC_MotionProperties_GetGravityFactor(@as(*const c.JPC_MotionProperties, @ptrCast(motion))); } pub fn setGravityFactor(motion: *MotionProperties, factor: f32) void { c.JPC_MotionProperties_SetGravityFactor(@as(*c.JPC_MotionProperties, @ptrCast(motion)), factor); } pub fn setMassProperties(motion: *MotionProperties, mass_properties: MassProperties) void { c.JPC_MotionProperties_SetMassProperties( @as(*c.JPC_MotionProperties, @ptrCast(motion)), @as(*const c.JPC_MassProperties, @ptrCast(&mass_properties)), ); } pub fn getInverseMass(motion: *const MotionProperties) f32 { return c.JPC_MotionProperties_GetInverseMass(@as(*const c.JPC_MotionProperties, @ptrCast(motion))); } pub fn setInverseMass(motion: *MotionProperties, inverse_mass: f32) void { c.JPC_MotionProperties_SetInverseMass(@as(*c.JPC_MotionProperties, @ptrCast(motion)), inverse_mass); } pub fn getInverseInertiaDiagonal(motion: *const MotionProperties) [3]f32 { var diagonal: [3]f32 = undefined; c.JPC_MotionProperties_GetInverseInertiaDiagonal( @as(*const c.JPC_MotionProperties, @ptrCast(motion)), &diagonal, ); return diagonal; } pub fn getInertiaRotation(motion: *const MotionProperties) [4]f32 { var rotation: [4]f32 = undefined; c.JPC_MotionProperties_GetInertiaRotation(@as(*const c.JPC_MotionProperties, @ptrCast(motion)), &rotation); return rotation; } pub fn setInverseInertia(motion: *MotionProperties, diagonal: [3]f32, rotation: [4]f32) void { c.JPC_MotionProperties_SetInverseInertia(@as(*c.JPC_MotionProperties, @ptrCast(motion)), &diagonal, &rotation); } pub fn getLocalSpaceInverseInertia(motion: *const MotionProperties) [16]f32 { var inertia: [16]f32 = undefined; c.JPC_MotionProperties_GetLocalSpaceInverseInertia( @as(*const c.JPC_MotionProperties, @ptrCast(motion)), &inertia, ); return inertia; } pub fn getInverseInertiaForRotation(motion: *const MotionProperties, rotation_matrix: [16]f32) [16]f32 { var inertia: [16]f32 = undefined; c.JPC_MotionProperties_GetInverseInertiaForRotation( @as(*const c.JPC_MotionProperties, @ptrCast(motion)), &rotation_matrix, &inertia, ); return inertia; } pub fn multiplyWorldSpaceInverseInertiaByVector( motion: *const MotionProperties, rotation: [4]f32, vector: [3]f32, ) [3]f32 { var out: [3]f32 = undefined; c.JPC_MotionProperties_MultiplyWorldSpaceInverseInertiaByVector( @as(*const c.JPC_MotionProperties, @ptrCast(motion)), &rotation, &vector, &out, ); return out; } comptime { assert(@sizeOf(MotionProperties) == @sizeOf(c.JPC_MotionProperties)); assert(@offsetOf(MotionProperties, "force") == @offsetOf(c.JPC_MotionProperties, "force")); assert(@offsetOf(MotionProperties, "motion_quality") == @offsetOf(c.JPC_MotionProperties, "motion_quality")); assert(@offsetOf(MotionProperties, "gravity_factor") == @offsetOf(c.JPC_MotionProperties, "gravity_factor")); } }; //-------------------------------------------------------------------------------------------------- // // ShapeSettings // //-------------------------------------------------------------------------------------------------- pub const ShapeSettings = opaque { pub usingnamespace Methods(@This()); fn Methods(comptime T: type) type { return struct { pub fn asShapeSettings(shape_settings: *const T) *const ShapeSettings { return @as(*const ShapeSettings, @ptrCast(shape_settings)); } pub fn asShapeSettingsMut(shape_settings: *T) *ShapeSettings { return @as(*ShapeSettings, @ptrCast(shape_settings)); } pub fn addRef(shape_settings: *T) void { c.JPC_ShapeSettings_AddRef(@as(*c.JPC_ShapeSettings, @ptrCast(shape_settings))); } pub fn release(shape_settings: *T) void { c.JPC_ShapeSettings_Release(@as(*c.JPC_ShapeSettings, @ptrCast(shape_settings))); } pub fn getRefCount(shape_settings: *const T) u32 { return c.JPC_ShapeSettings_GetRefCount(@as(*const c.JPC_ShapeSettings, @ptrCast(shape_settings))); } pub fn createShape(shape_settings: *const T) !*Shape { const shape = c.JPC_ShapeSettings_CreateShape(@as(*const c.JPC_ShapeSettings, @ptrCast(shape_settings))); if (shape == null) return error.FailedToCreateShape; return @as(*Shape, @ptrCast(shape)); } pub fn getUserData(shape_settings: *const T) u64 { return c.JPC_ShapeSettings_GetUserData(@as(*const c.JPC_ShapeSettings, @ptrCast(shape_settings))); } pub fn setUserData(shape_settings: *T, user_data: u64) void { return c.JPC_ShapeSettings_SetUserData(@as(*c.JPC_ShapeSettings, @ptrCast(shape_settings)), user_data); } }; } }; //-------------------------------------------------------------------------------------------------- // // ConvexShapeSettings (-> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const ConvexShapeSettings = opaque { pub usingnamespace Methods(@This()); fn Methods(comptime T: type) type { return struct { pub usingnamespace ShapeSettings.Methods(T); pub fn asConvexShapeSettings(convex_shape_settings: *T) *ConvexShapeSettings { return @as(*ConvexShapeSettings, @ptrCast(convex_shape_settings)); } pub fn getMaterial(convex_shape_settings: *const T) ?*const Material { return @as(?*const Material, @ptrCast(c.JPC_ConvexShapeSettings_GetMaterial( @as(*const c.JPC_ConvexShapeSettings, @ptrCast(convex_shape_settings)), ))); } pub fn setMaterial(convex_shape_settings: *T, material: ?*Material) void { c.JPC_ConvexShapeSettings_SetMaterial( @as(*c.JPC_ConvexShapeSettings, @ptrCast(convex_shape_settings)), @as(?*c.JPC_PhysicsMaterial, @ptrCast(material)), ); } pub fn getDensity(convex_shape_settings: *const T) f32 { return c.JPC_ConvexShapeSettings_GetDensity( @as(*const c.JPC_ConvexShapeSettings, @ptrCast(convex_shape_settings)), ); } pub fn setDensity(shape_settings: *T, density: f32) void { c.JPC_ConvexShapeSettings_SetDensity( @as(*c.JPC_ConvexShapeSettings, @ptrCast(shape_settings)), density, ); } }; } }; //-------------------------------------------------------------------------------------------------- // // BoxShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const BoxShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(half_extent: [3]f32) !*BoxShapeSettings { const box_shape_settings = c.JPC_BoxShapeSettings_Create(&half_extent); if (box_shape_settings == null) return error.FailedToCreateBoxShapeSettings; return @as(*BoxShapeSettings, @ptrCast(box_shape_settings)); } pub fn getHalfExtent(box_shape_settings: *const BoxShapeSettings) [3]f32 { var half_extent: [3]f32 = undefined; c.JPC_BoxShapeSettings_GetHalfExtent( @as(*const c.JPC_BoxShapeSettings, @ptrCast(box_shape_settings)), &half_extent, ); return half_extent; } pub fn setHalfExtent(box_shape_settings: *BoxShapeSettings, half_extent: [3]f32) void { c.JPC_BoxShapeSettings_SetHalfExtent(@as(*c.JPC_BoxShapeSettings, @ptrCast(box_shape_settings)), &half_extent); } pub fn getConvexRadius(box_shape_settings: *const BoxShapeSettings) f32 { return c.JPC_BoxShapeSettings_GetConvexRadius(@as(*const c.JPC_BoxShapeSettings, @ptrCast(box_shape_settings))); } pub fn setConvexRadius(box_shape_settings: *BoxShapeSettings, convex_radius: f32) void { c.JPC_BoxShapeSettings_SetConvexRadius( @as(*c.JPC_BoxShapeSettings, @ptrCast(box_shape_settings)), convex_radius, ); } }; //-------------------------------------------------------------------------------------------------- // // SphereShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const SphereShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(radius: f32) !*SphereShapeSettings { const sphere_shape_settings = c.JPC_SphereShapeSettings_Create(radius); if (sphere_shape_settings == null) return error.FailedToCreateSphereShapeSettings; return @as(*SphereShapeSettings, @ptrCast(sphere_shape_settings)); } pub fn getRadius(sphere_shape_settings: *const SphereShapeSettings) f32 { return c.JPC_SphereShapeSettings_GetRadius( @as(*const c.JPC_SphereShapeSettings, @ptrCast(sphere_shape_settings)), ); } pub fn setRadius(sphere_shape_settings: *SphereShapeSettings, radius: f32) void { c.JPC_SphereShapeSettings_SetRadius( @as(*c.JPC_SphereShapeSettings, @ptrCast(sphere_shape_settings)), radius, ); } }; //-------------------------------------------------------------------------------------------------- // // TriangleShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const TriangleShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(v1: [3]f32, v2: [3]f32, v3: [3]f32) !*TriangleShapeSettings { const triangle_shape_settings = c.JPC_TriangleShapeSettings_Create(&v1, &v2, &v3); if (triangle_shape_settings == null) return error.FailedToCreateTriangleShapeSettings; return @as(*TriangleShapeSettings, @ptrCast(triangle_shape_settings)); } pub fn getConvexRadius(triangle_shape_settings: *const TriangleShapeSettings) f32 { return c.JPC_TriangleShapeSettings_GetConvexRadius( @as(*const c.JPC_TriangleShapeSettings, @ptrCast(triangle_shape_settings)), ); } pub fn setConvexRadius(triangle_shape_settings: *TriangleShapeSettings, convex_radius: f32) void { c.JPC_TriangleShapeSettings_SetConvexRadius( @as(*c.JPC_TriangleShapeSettings, @ptrCast(triangle_shape_settings)), convex_radius, ); } }; //-------------------------------------------------------------------------------------------------- // // CapsuleShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const CapsuleShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(half_height: f32, radius: f32) !*CapsuleShapeSettings { const capsule_shape_settings = c.JPC_CapsuleShapeSettings_Create(half_height, radius); if (capsule_shape_settings == null) return error.FailedToCreateCapsuleShapeSettings; return @as(*CapsuleShapeSettings, @ptrCast(capsule_shape_settings)); } pub fn getHalfHeight(capsule_shape_settings: *const CapsuleShapeSettings) f32 { return c.JPC_CapsuleShapeSettings_GetHalfHeight( @as(*const c.JPC_CapsuleShapeSettings, @ptrCast(capsule_shape_settings)), ); } pub fn setHalfHeight(capsule_shape_settings: *CapsuleShapeSettings, half_height: f32) void { c.JPC_CapsuleShapeSettings_SetHalfHeight( @as(*c.JPC_CapsuleShapeSettings, @ptrCast(capsule_shape_settings)), half_height, ); } pub fn getRadius(capsule_shape_settings: *const CapsuleShapeSettings) f32 { return c.JPC_CapsuleShapeSettings_GetRadius( @as(*const c.JPC_CapsuleShapeSettings, @ptrCast(capsule_shape_settings)), ); } pub fn setRadius(capsule_shape_settings: *CapsuleShapeSettings, radius: f32) void { c.JPC_CapsuleShapeSettings_SetRadius( @as(*c.JPC_CapsuleShapeSettings, @ptrCast(capsule_shape_settings)), radius, ); } }; //-------------------------------------------------------------------------------------------------- // // TaperedCapsuleShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const TaperedCapsuleShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(half_height: f32, top_radius: f32, bottom_radius: f32) !*TaperedCapsuleShapeSettings { const capsule_shape_settings = c.JPC_TaperedCapsuleShapeSettings_Create( half_height, top_radius, bottom_radius, ); if (capsule_shape_settings == null) return error.FailedToCreateTaperedCapsuleShapeSettings; return @as(*TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)); } pub fn getHalfHeight(capsule_shape_settings: *const TaperedCapsuleShapeSettings) f32 { return c.JPC_TaperedCapsuleShapeSettings_GetHalfHeight( @as(*const c.JPC_TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)), ); } pub fn setHalfHeight(capsule_shape_settings: *TaperedCapsuleShapeSettings, half_height: f32) void { c.JPC_TaperedCapsuleShapeSettings_SetHalfHeight( @as(*c.JPC_TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)), half_height, ); } pub fn getTopRadius(capsule_shape_settings: *const TaperedCapsuleShapeSettings) f32 { return c.JPC_TaperedCapsuleShapeSettings_GetTopRadius( @as(*const c.JPC_TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)), ); } pub fn setTopRadius(capsule_shape_settings: *TaperedCapsuleShapeSettings, radius: f32) void { c.JPC_TaperedCapsuleShapeSettings_SetTopRadius( @as(*c.JPC_TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)), radius, ); } pub fn getBottomRadius(capsule_shape_settings: *const TaperedCapsuleShapeSettings) f32 { return c.JPC_TaperedCapsuleShapeSettings_GetBottomRadius( @as(*const c.JPC_TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)), ); } pub fn setBottomRadius(capsule_shape_settings: *TaperedCapsuleShapeSettings, radius: f32) void { c.JPC_TaperedCapsuleShapeSettings_SetBottomRadius( @as(*c.JPC_TaperedCapsuleShapeSettings, @ptrCast(capsule_shape_settings)), radius, ); } }; //-------------------------------------------------------------------------------------------------- // // CylinderShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const CylinderShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(half_height: f32, radius: f32) !*CylinderShapeSettings { const cylinder_shape_settings = c.JPC_CylinderShapeSettings_Create(half_height, radius); if (cylinder_shape_settings == null) return error.FailedToCreateCylinderShapeSettings; return @as(*CylinderShapeSettings, @ptrCast(cylinder_shape_settings)); } pub fn getConvexRadius(cylinder_shape_settings: *const CylinderShapeSettings) f32 { return c.JPC_CylinderShapeSettings_GetConvexRadius( @as(*const c.JPC_CylinderShapeSettings, @ptrCast(cylinder_shape_settings)), ); } pub fn setConvexRadius(cylinder_shape_settings: *CylinderShapeSettings, convex_radius: f32) void { c.JPC_CylinderShapeSettings_SetConvexRadius( @as(*c.JPC_CylinderShapeSettings, @ptrCast(cylinder_shape_settings)), convex_radius, ); } pub fn getHalfHeight(cylinder_shape_settings: *const CylinderShapeSettings) f32 { return c.JPC_CylinderShapeSettings_GetHalfHeight( @as(*const c.JPC_CylinderShapeSettings, @ptrCast(cylinder_shape_settings)), ); } pub fn setHalfHeight(cylinder_shape_settings: *CylinderShapeSettings, half_height: f32) void { c.JPC_CylinderShapeSettings_SetHalfHeight( @as(*c.JPC_CylinderShapeSettings, @ptrCast(cylinder_shape_settings)), half_height, ); } pub fn getRadius(cylinder_shape_settings: *const CylinderShapeSettings) f32 { return c.JPC_CylinderShapeSettings_GetRadius( @as(*const c.JPC_CylinderShapeSettings, @ptrCast(cylinder_shape_settings)), ); } pub fn setRadius(cylinder_shape_settings: *CylinderShapeSettings, radius: f32) void { c.JPC_CylinderShapeSettings_SetRadius( @as(*c.JPC_CylinderShapeSettings, @ptrCast(cylinder_shape_settings)), radius, ); } }; //-------------------------------------------------------------------------------------------------- // // ConvexHullShapeSettings (-> ConvexShapeSettings -> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const ConvexHullShapeSettings = opaque { pub usingnamespace ConvexShapeSettings.Methods(@This()); pub fn create(vertices: *const anyopaque, num_vertices: u32, vertex_size: u32) !*ConvexHullShapeSettings { const settings = c.JPC_ConvexHullShapeSettings_Create(vertices, num_vertices, vertex_size); if (settings == null) return error.FailedToCreateConvexHullShapeSettings; return @as(*ConvexHullShapeSettings, @ptrCast(settings)); } pub fn getMaxConvexRadius(settings: *const ConvexHullShapeSettings) f32 { return c.JPC_ConvexHullShapeSettings_GetMaxConvexRadius( @as(*const c.JPC_ConvexHullShapeSettings, @ptrCast(settings)), ); } pub fn setMaxConvexRadius(settings: *ConvexHullShapeSettings, radius: f32) void { c.JPC_ConvexHullShapeSettings_SetMaxConvexRadius( @as(*c.JPC_ConvexHullShapeSettings, @ptrCast(settings)), radius, ); } }; //-------------------------------------------------------------------------------------------------- // // HeightFieldShapeSettings (-> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const HeightFieldShapeSettings = opaque { pub usingnamespace ShapeSettings.Methods(@This()); pub fn create( samples: [*]const f32, // height_field_size^2 samples height_field_size: u32, // height_field_size / block_size must be a power of 2 and minimally 2 ) !*HeightFieldShapeSettings { const settings = c.JPC_HeightFieldShapeSettings_Create(samples, height_field_size); if (settings == null) return error.FailedToCreateHeightFieldShapeSettings; return @as(*HeightFieldShapeSettings, @ptrCast(settings)); } pub fn getBlockSize(settings: *const HeightFieldShapeSettings) u32 { return c.JPC_HeightFieldShapeSettings_GetBlockSize( @as(*const c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), ); } pub fn setBlockSize(settings: *HeightFieldShapeSettings, block_size: u32) void { c.JPC_HeightFieldShapeSettings_SetBlockSize( @as(*c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), block_size, ); } pub fn getBitsPerSample(settings: *const HeightFieldShapeSettings) u32 { return c.JPC_HeightFieldShapeSettings_GetBitsPerSample( @as(*const c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), ); } pub fn setBitsPerSample(settings: *HeightFieldShapeSettings, num_bits: u32) void { c.JPC_HeightFieldShapeSettings_SetBitsPerSample( @as(*c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), num_bits, ); } pub fn getOffset(settings: *const HeightFieldShapeSettings) [3]f32 { var offset: [3]f32 = undefined; c.JPC_HeightFieldShapeSettings_GetOffset( @as(*const c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), &offset, ); return offset; } pub fn setOffset(settings: *HeightFieldShapeSettings, offset: [3]f32) void { c.JPC_HeightFieldShapeSettings_SetOffset( @as(*c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), &offset, ); } pub fn getScale(settings: *const HeightFieldShapeSettings) [3]f32 { var scale: [3]f32 = undefined; c.JPC_HeightFieldShapeSettings_GetScale( @as(*const c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), &scale, ); return scale; } pub fn setScale(settings: *HeightFieldShapeSettings, scale: [3]f32) void { c.JPC_HeightFieldShapeSettings_SetScale( @as(*c.JPC_HeightFieldShapeSettings, @ptrCast(settings)), &scale, ); } }; //-------------------------------------------------------------------------------------------------- // // MeshShapeSettings (-> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const MeshShapeSettings = opaque { pub usingnamespace ShapeSettings.Methods(@This()); pub fn create( vertices: *const anyopaque, num_vertices: u32, vertex_size: u32, indices: []const u32, ) !*MeshShapeSettings { const settings = c.JPC_MeshShapeSettings_Create( vertices, num_vertices, vertex_size, indices.ptr, @as(u32, @intCast(indices.len)), ); if (settings == null) return error.FailedToCreateMeshShapeSettings; return @as(*MeshShapeSettings, @ptrCast(settings)); } pub fn getMaxTrianglesPerLeaf(settings: *const MeshShapeSettings) u32 { return c.JPC_MeshShapeSettings_GetMaxTrianglesPerLeaf( @as(*const c.JPC_MeshShapeSettings, @ptrCast(settings)), ); } pub fn setMaxTrianglesPerLeaf(settings: *MeshShapeSettings, max_triangles: u32) void { c.JPC_MeshShapeSettings_SetMaxTrianglesPerLeaf( @as(*c.JPC_MeshShapeSettings, @ptrCast(settings)), max_triangles, ); } pub fn sanitize(settings: *MeshShapeSettings) void { c.JPC_MeshShapeSettings_Sanitize(@as(*c.JPC_MeshShapeSettings, @ptrCast(settings))); } }; //-------------------------------------------------------------------------------------------------- // // DecoratedShapeSettings (-> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const DecoratedShapeSettings = opaque { pub usingnamespace ShapeSettings.Methods(@This()); pub fn createRotatedTranslated( inner_shape: *const ShapeSettings, rotation: [4]Real, translation: [3]Real, ) !*DecoratedShapeSettings { const settings = c.JPC_RotatedTranslatedShapeSettings_Create( @as(*const c.JPC_ShapeSettings, @ptrCast(inner_shape)), &rotation, &translation, ); if (settings == null) return error.FailedToCreateDecoratedShapeSettings; return @as(*DecoratedShapeSettings, @ptrCast(settings)); } pub fn createScaled(inner_shape: *const ShapeSettings, scale: [3]Real) !*DecoratedShapeSettings { const settings = c.JPC_ScaledShapeSettings_Create( @as(*const c.JPC_ShapeSettings, @ptrCast(inner_shape)), &scale, ); if (settings == null) return error.FailedToCreateDecoratedShapeSettings; return @as(*DecoratedShapeSettings, @ptrCast(settings)); } pub fn createOffsetCenterOfMass(inner_shape: *const ShapeSettings, offset: [3]Real) !*DecoratedShapeSettings { const settings = c.JPC_OffsetCenterOfMassShapeSettings_Create( @as(*const c.JPC_ShapeSettings, @ptrCast(inner_shape)), &offset, ); if (settings == null) return error.FailedToCreateDecoratedShapeSettings; return @as(*DecoratedShapeSettings, @ptrCast(settings)); } }; //-------------------------------------------------------------------------------------------------- // // CompoundShapeSettings (-> ShapeSettings) // //-------------------------------------------------------------------------------------------------- pub const CompoundShapeSettings = opaque { pub usingnamespace ShapeSettings.Methods(@This()); pub fn createStatic() !*CompoundShapeSettings { const settings = c.JPC_StaticCompoundShapeSettings_Create(); if (settings == null) return error.FailedToCreateCompoundShapeSettings; return @as(*CompoundShapeSettings, @ptrCast(settings)); } pub fn createMutable() !*CompoundShapeSettings { const settings = c.JPC_MutableCompoundShapeSettings_Create(); if (settings == null) return error.FailedToCreateCompoundShapeSettings; return @as(*CompoundShapeSettings, @ptrCast(settings)); } pub fn addShape(settings: *CompoundShapeSettings, position: [3]Real, rotation: [4]Real, shape: *const ShapeSettings, user_data: u32) void { c.JPC_CompoundShapeSettings_AddShape( @as(*c.JPC_CompoundShapeSettings, @ptrCast(settings)), &position, &rotation, @as(*const c.JPC_ShapeSettings, @ptrCast(shape)), user_data, ); } }; //-------------------------------------------------------------------------------------------------- // // Shape // //-------------------------------------------------------------------------------------------------- pub const Shape = opaque { pub usingnamespace Methods(@This()); pub const Type = enum(c.JPC_ShapeType) { convex = c.JPC_SHAPE_TYPE_CONVEX, compound = c.JPC_SHAPE_TYPE_COMPOUND, decorated = c.JPC_SHAPE_TYPE_DECORATED, mesh = c.JPC_SHAPE_TYPE_MESH, height_field = c.JPC_SHAPE_TYPE_HEIGHT_FIELD, user1 = c.JPC_SHAPE_TYPE_USER1, user2 = c.JPC_SHAPE_TYPE_USER2, user3 = c.JPC_SHAPE_TYPE_USER3, user4 = c.JPC_SHAPE_TYPE_USER4, }; pub const SubType = enum(c.JPC_ShapeSubType) { sphere = c.JPC_SHAPE_SUB_TYPE_SPHERE, box = c.JPC_SHAPE_SUB_TYPE_BOX, triangle = c.JPC_SHAPE_SUB_TYPE_TRIANGLE, capsule = c.JPC_SHAPE_SUB_TYPE_CAPSULE, tapered_capsule = c.JPC_SHAPE_SUB_TYPE_TAPERED_CAPSULE, cylinder = c.JPC_SHAPE_SUB_TYPE_CYLINDER, convex_hull = c.JPC_SHAPE_SUB_TYPE_CONVEX_HULL, static_compound = c.JPC_SHAPE_SUB_TYPE_STATIC_COMPOUND, mutable_compound = c.JPC_SHAPE_SUB_TYPE_MUTABLE_COMPOUND, rotated_translated = c.JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED, scaled = c.JPC_SHAPE_SUB_TYPE_SCALED, offset_center_of_mass = c.JPC_SHAPE_SUB_TYPE_OFFSET_CENTER_OF_MASS, mesh = c.JPC_SHAPE_SUB_TYPE_MESH, height_field = c.JPC_SHAPE_SUB_TYPE_HEIGHT_FIELD, user1 = c.JPC_SHAPE_SUB_TYPE_USER1, user2 = c.JPC_SHAPE_SUB_TYPE_USER2, user3 = c.JPC_SHAPE_SUB_TYPE_USER3, user4 = c.JPC_SHAPE_SUB_TYPE_USER4, user5 = c.JPC_SHAPE_SUB_TYPE_USER5, user6 = c.JPC_SHAPE_SUB_TYPE_USER6, user7 = c.JPC_SHAPE_SUB_TYPE_USER7, user8 = c.JPC_SHAPE_SUB_TYPE_USER8, user_convex1 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX1, user_convex2 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX2, user_convex3 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX3, user_convex4 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX4, user_convex5 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX5, user_convex6 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX6, user_convex7 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX7, user_convex8 = c.JPC_SHAPE_SUB_TYPE_USER_CONVEX8, }; fn Methods(comptime T: type) type { return struct { pub fn asShape(shape: *const T) *const Shape { return @as(*const Shape, @ptrCast(shape)); } pub fn asShapeMut(shape: *T) *Shape { return @as(*Shape, @ptrCast(shape)); } pub fn addRef(shape: *T) void { c.JPC_Shape_AddRef(@as(*c.JPC_Shape, @ptrCast(shape))); } pub fn release(shape: *T) void { c.JPC_Shape_Release(@as(*c.JPC_Shape, @ptrCast(shape))); } pub fn getRefCount(shape: *const T) u32 { return c.JPC_Shape_GetRefCount(@as(*const c.JPC_Shape, @ptrCast(shape))); } pub fn getType(shape: *const T) Type { return @as( Type, @enumFromInt(c.JPC_Shape_GetType(@as(*const c.JPC_Shape, @ptrCast(shape)))), ); } pub fn getSubType(shape: *const T) SubType { return @as( SubType, @enumFromInt(c.JPC_Shape_GetSubType(@as(*const c.JPC_Shape, @ptrCast(shape)))), ); } pub fn getUserData(shape: *const T) u64 { return c.JPC_Shape_GetUserData(@as(*const c.JPC_Shape, @ptrCast(shape))); } pub fn setUserData(shape: *T, user_data: u64) void { return c.JPC_Shape_SetUserData(@as(*c.JPC_Shape, @ptrCast(shape)), user_data); } pub fn getCenterOfMass(shape: *const T) [3]Real { var center: [3]Real = undefined; c.JPC_Shape_GetCenterOfMass(@as(*const c.JPC_Shape, @ptrCast(shape)), ¢er); return center; } }; } }; //-------------------------------------------------------------------------------------------------- // // ConvexHullShape (-> Shape) // //-------------------------------------------------------------------------------------------------- pub const ConvexHullShape = opaque { pub usingnamespace Shape.Methods(@This()); pub fn asConvexHullShape(shape: *const Shape) *const ConvexHullShape { assert(shape.getSubType() == .convex_hull); return @as(*const ConvexHullShape, @ptrCast(shape)); } pub fn asConvexHullShapeMut(shape: *Shape) *ConvexHullShape { assert(shape.getSubType() == .convex_hull); return @as(*ConvexHullShape, @ptrCast(shape)); } pub fn getNumPoints(shape: *const ConvexHullShape) u32 { return c.JPC_ConvexHullShape_GetNumPoints(@as(*const c.JPC_ConvexHullShape, @ptrCast(shape))); } pub fn getPoint(shape: *const ConvexHullShape, in_point_index: u32) [3]f32 { var point: [3]f32 = undefined; c.JPC_ConvexHullShape_GetPoint(@as(*const c.JPC_ConvexHullShape, @ptrCast(shape)), in_point_index, &point); return point; } pub fn getNumFaces(shape: *const ConvexHullShape) u32 { return c.JPC_ConvexHullShape_GetNumFaces(@as(*const c.JPC_ConvexHullShape, @ptrCast(shape))); } pub fn getNumVerticesInFace(shape: *const ConvexHullShape, in_face_index: u32) u32 { return c.JPC_ConvexHullShape_GetNumVerticesInFace( @as(*const c.JPC_ConvexHullShape, @ptrCast(shape)), in_face_index, ); } /// out_vertex_buffer points to memory owned by the caller. /// If out_vertex_buffer.len is less than getNumVerticesInFace(in_face_index), not all vertices are returned. /// The return value gives the number of vertices in the face, identical to getNumVerticesInFace(in_face_index). pub fn getFaceVertices(shape: *const ConvexHullShape, in_face_index: u32, out_vertex_buffer: []u32) u32 { return c.JPC_ConvexHullShape_GetFaceVertices( @as(*const c.JPC_ConvexHullShape, @ptrCast(shape)), in_face_index, @as(u32, @intCast(out_vertex_buffer.len)), out_vertex_buffer.ptr, ); } }; //-------------------------------------------------------------------------------------------------- // // ConstraintSettings // //-------------------------------------------------------------------------------------------------- pub const ConstraintSettings = opaque { pub usingnamespace Methods(@This()); fn Methods(comptime T: type) type { return struct { pub fn asConstraintSettings(constraint_settings: *const T) *const ConstraintSettings { return @as(*const ConstraintSettings, @ptrCast(constraint_settings)); } pub fn asConstraintSettingsMut(constraint_settings: *T) *ConstraintSettings { return @as(*ConstraintSettings, @ptrCast(constraint_settings)); } pub fn addRef(constraint_settings: *T) void { c.JPC_ConstraintSettings_AddRef(@as(*c.JPC_ConstraintSettings, @ptrCast(constraint_settings))); } pub fn release(constraint_settings: *T) void { c.JPC_ConstraintSettings_Release(@as(*c.JPC_ConstraintSettings, @ptrCast(constraint_settings))); } pub fn getRefCount(constraint_settings: *const T) u32 { return c.JPC_ConstraintSettings_GetRefCount(@as(*const c.JPC_ConstraintSettings, @ptrCast(constraint_settings))); } pub fn getUserData(constraint_settings: *const T) u64 { return c.JPC_ConstraintSettings_GetUserData(@as(*const c.JPC_ConstraintSettings, @ptrCast(constraint_settings))); } pub fn setUserData(constraint_settings: *T, user_data: u64) void { return c.JPC_ConstraintSettings_SetUserData( @as(*c.JPC_ConstraintSettings, @ptrCast(constraint_settings)), user_data, ); } }; } }; //-------------------------------------------------------------------------------------------------- // // TwoBodyConstraintSettings (-> ConstraintSettings) // //-------------------------------------------------------------------------------------------------- pub const TwoBodyConstraintSettings = opaque { pub usingnamespace Methods(@This()); fn Methods(comptime T: type) type { return struct { pub usingnamespace ConstraintSettings.Methods(T); pub fn asTwoBodyConstraintSettings(two_body_constraint_settings: *T) *TwoBodyConstraintSettings { return @as(*TwoBodyConstraintSettings, @ptrCast(two_body_constraint_settings)); } pub fn createConstraint(two_body_constraint_settings: *const T, body1: *Body, body2: *Body) !*Constraint { const constraint = c.JPC_TwoBodyConstraintSettings_CreateConstraint( @as(*const c.JPC_TwoBodyConstraintSettings, @ptrCast(two_body_constraint_settings)), @as(*c.JPC_Body, @ptrCast(body1)), @as(*c.JPC_Body, @ptrCast(body2)), ); if (constraint == null) return error.FailedToCreateConstraint; return @as(*Constraint, @ptrCast(constraint)); } }; } }; //-------------------------------------------------------------------------------------------------- // // FixedConstraintSettings (-> TwoBodyConstraintSettings -> ConstraintSettings) // //-------------------------------------------------------------------------------------------------- pub const FixedConstraintSettings = opaque { pub usingnamespace TwoBodyConstraintSettings.Methods(@This()); pub fn create() !*FixedConstraintSettings { const fixed_constraint_settings = c.JPC_FixedConstraintSettings_Create(); if (fixed_constraint_settings == null) return error.FailedToCreateFixedConstraintSettings; return @as(*FixedConstraintSettings, @ptrCast(fixed_constraint_settings)); } pub fn setSpace(settings: *FixedConstraintSettings, space: Constraint.Space) void { c.JPC_FixedConstraintSettings_SetSpace( @as(*c.JPC_FixedConstraintSettings, @ptrCast(settings)), @intFromEnum(space), ); } pub fn setAutoDetectPoint(settings: *FixedConstraintSettings, enabled: bool) void { c.JPC_FixedConstraintSettings_SetAutoDetectPoint( @as(*c.JPC_FixedConstraintSettings, @ptrCast(settings)), enabled, ); } }; //-------------------------------------------------------------------------------------------------- // // Constraint // //-------------------------------------------------------------------------------------------------- pub const Constraint = opaque { pub usingnamespace Methods(@This()); pub const Type = enum(c.JPC_ConstraintType) { constraint = c.JPC_CONSTRAINT_TYPE_CONSTRAINT, two_body_constraint = c.JPC_CONSTRAINT_TYPE_TWO_BODY_CONSTRAINT, }; pub const SubType = enum(c.JPC_ConstraintSubType) { fixed = c.JPC_CONSTRAINT_SUB_TYPE_FIXED, point = c.JPC_CONSTRAINT_SUB_TYPE_POINT, hinge = c.JPC_CONSTRAINT_SUB_TYPE_HINGE, slider = c.JPC_CONSTRAINT_SUB_TYPE_SLIDER, distance = c.JPC_CONSTRAINT_SUB_TYPE_DISTANCE, cone = c.JPC_CONSTRAINT_SUB_TYPE_CONE, swing_twist = c.JPC_CONSTRAINT_SUB_TYPE_SWING_TWIST, six_dof = c.JPC_CONSTRAINT_SUB_TYPE_SIX_DOF, path = c.JPC_CONSTRAINT_SUB_TYPE_PATH, vehicle = c.JPC_CONSTRAINT_SUB_TYPE_VEHICLE, rack_and_pinion = c.JPC_CONSTRAINT_SUB_TYPE_RACK_AND_PINION, gear = c.JPC_CONSTRAINT_SUB_TYPE_GEAR, pulley = c.JPC_CONSTRAINT_SUB_TYPE_PULLEY, user1 = c.JPC_CONSTRAINT_SUB_TYPE_USER1, user2 = c.JPC_CONSTRAINT_SUB_TYPE_USER2, user3 = c.JPC_CONSTRAINT_SUB_TYPE_USER3, user4 = c.JPC_CONSTRAINT_SUB_TYPE_USER4, }; pub const Space = enum(c.JPC_ConstraintSpace) { local_to_body_com = c.JPC_CONSTRAINT_SPACE_LOCAL_TO_BODY_COM, world_space = c.JPC_CONSTRAINT_SPACE_WORLD_SPACE, }; fn Methods(comptime T: type) type { return struct { pub fn asConstraint(constraint: *const T) *const Constraint { return @as(*const Constraint, @ptrCast(constraint)); } pub fn asConstraintMut(constraint: *T) *Constraint { return @as(*Constraint, @ptrCast(constraint)); } pub fn addRef(constraint: *T) void { c.JPC_Constraint_AddRef(@as(*c.JPC_Constraint, @ptrCast(constraint))); } pub fn release(constraint: *T) void { c.JPC_Constraint_Release(@as(*c.JPC_Constraint, @ptrCast(constraint))); } pub fn getRefCount(constraint: *const T) u32 { return c.JPC_Constraint_GetRefCount(@as(*const c.JPC_Constraint, @ptrCast(constraint))); } pub fn getType(constraint: *const T) Type { return @as( Type, @enumFromInt(c.JPC_Constraint_GetType(@as(*const c.JPC_Constraint, @ptrCast(constraint)))), ); } pub fn getSubType(constraint: *const T) SubType { return @as( SubType, @enumFromInt(c.JPC_Constraint_GetSubType(@as(*const c.JPC_Constraint, @ptrCast(constraint)))), ); } pub fn getUserData(constraint: *const T) u64 { return c.JPC_Constraint_GetUserData(@as(*const c.JPC_Constraint, @ptrCast(constraint))); } pub fn setUserData(constraint: *T, user_data: u64) void { return c.JPC_Constraint_SetUserData(@as(*c.JPC_Constraint, @ptrCast(constraint)), user_data); } }; } }; //-------------------------------------------------------------------------------------------------- // // Memory allocation // //-------------------------------------------------------------------------------------------------- fn zphysicsAlloc(size: usize) callconv(.C) ?*anyopaque { mem_mutex.lock(); defer mem_mutex.unlock(); const ptr = mem_allocator.?.rawAlloc( size, std.mem.Alignment.@"16", @returnAddress(), ); if (ptr == null) @panic("zphysics: out of memory"); mem_allocations.?.put( @intFromPtr(ptr), .{ .size = @as(u48, @intCast(size)), .alignment = mem_alignment }, ) catch @panic("zphysics: out of memory"); return ptr; } fn zphysicsAlignedAlloc(size: usize, alignment: usize) callconv(.C) ?*anyopaque { mem_mutex.lock(); defer mem_mutex.unlock(); const ptr = mem_allocator.?.rawAlloc( size, @enumFromInt(std.math.log2_int(u29, @as(u29, @intCast(alignment)))), @returnAddress(), ); if (ptr == null) @panic("zphysics: out of memory"); mem_allocations.?.put( @intFromPtr(ptr), .{ .size = @as(u32, @intCast(size)), .alignment = @as(u16, @intCast(alignment)) }, ) catch @panic("zphysics: out of memory"); return ptr; } fn zphysicsFree(maybe_ptr: ?*anyopaque) callconv(.C) void { if (maybe_ptr) |ptr| { mem_mutex.lock(); defer mem_mutex.unlock(); const info = mem_allocations.?.fetchRemove(@intFromPtr(ptr)).?.value; const mem = @as([*]u8, @ptrCast(ptr))[0..info.size]; mem_allocator.?.rawFree( mem, @enumFromInt(std.math.log2_int(u29, @as(u29, @intCast(info.alignment)))), @returnAddress(), ); } } //-------------------------------------------------------------------------------------------------- // // Tests // //-------------------------------------------------------------------------------------------------- const expect = std.testing.expect; test { std.testing.refAllDeclsRecursive(@This()); } extern fn JoltCTest_Basic1() u32; test "jolt_c.basic1" { const ret = JoltCTest_Basic1(); try expect(ret != 0); } extern fn JoltCTest_Basic2() u32; test "jolt_c.basic2" { const ret = JoltCTest_Basic2(); try expect(ret != 0); } extern fn JoltCTest_HelloWorld() u32; test "jolt_c.helloworld" { const ret = JoltCTest_HelloWorld(); try expect(ret != 0); } test "zphysics.BodyCreationSettings" { try init(std.testing.allocator, .{}); defer deinit(); const approxEql = std.math.approxEqAbs; const bcs0 = BodyCreationSettings{}; const bcs1 = blk: { var settings: c.JPC_BodyCreationSettings = undefined; c.JPC_BodyCreationSettings_SetDefault(&settings); break :blk @as(*const BodyCreationSettings, @ptrCast(&settings)).*; }; try expect(approxEql(Real, bcs0.position[0], bcs1.position[0], 0.0001)); try expect(approxEql(Real, bcs0.position[1], bcs1.position[1], 0.0001)); try expect(approxEql(Real, bcs0.position[2], bcs1.position[2], 0.0001)); try expect(approxEql(f32, bcs0.rotation[0], bcs1.rotation[0], 0.0001)); try expect(approxEql(f32, bcs0.rotation[1], bcs1.rotation[1], 0.0001)); try expect(approxEql(f32, bcs0.rotation[2], bcs1.rotation[2], 0.0001)); try expect(approxEql(f32, bcs0.rotation[3], bcs1.rotation[3], 0.0001)); try expect(approxEql(f32, bcs0.linear_velocity[0], bcs1.linear_velocity[0], 0.0001)); try expect(approxEql(f32, bcs0.linear_velocity[1], bcs1.linear_velocity[1], 0.0001)); try expect(approxEql(f32, bcs0.linear_velocity[2], bcs1.linear_velocity[2], 0.0001)); try expect(approxEql(f32, bcs0.angular_velocity[0], bcs1.angular_velocity[0], 0.0001)); try expect(approxEql(f32, bcs0.angular_velocity[1], bcs1.angular_velocity[1], 0.0001)); try expect(approxEql(f32, bcs0.angular_velocity[2], bcs1.angular_velocity[2], 0.0001)); try expect(bcs0.user_data == bcs1.user_data); try expect(bcs0.object_layer == bcs1.object_layer); //try expect(eql(u8, asBytes(&bcs0.collision_group), asBytes(&bcs1.collision_group))); try expect(bcs0.motion_type == bcs1.motion_type); try expect(bcs0.allow_dynamic_or_kinematic == bcs1.allow_dynamic_or_kinematic); try expect(bcs0.is_sensor == bcs1.is_sensor); try expect(bcs0.use_manifold_reduction == bcs1.use_manifold_reduction); try expect(bcs0.motion_quality == bcs1.motion_quality); try expect(bcs0.allow_sleeping == bcs1.allow_sleeping); try expect(approxEql(f32, bcs0.friction, bcs1.friction, 0.0001)); try expect(approxEql(f32, bcs0.restitution, bcs1.restitution, 0.0001)); try expect(approxEql(f32, bcs0.linear_damping, bcs1.linear_damping, 0.0001)); try expect(approxEql(f32, bcs0.angular_damping, bcs1.angular_damping, 0.0001)); try expect(approxEql(f32, bcs0.max_linear_velocity, bcs1.max_linear_velocity, 0.0001)); try expect(approxEql(f32, bcs0.max_angular_velocity, bcs1.max_angular_velocity, 0.0001)); try expect(approxEql(f32, bcs0.gravity_factor, bcs1.gravity_factor, 0.0001)); try expect(bcs0.override_mass_properties == bcs1.override_mass_properties); try expect(approxEql(f32, bcs0.inertia_multiplier, bcs1.inertia_multiplier, 0.0001)); try expect(approxEql(f32, bcs0.mass_properties_override.mass, bcs1.mass_properties_override.mass, 0.0001)); //try expect(eql( // u8, // asBytes(&bcs0.mass_properties_override.inertia), // asBytes(&bcs1.mass_properties_override.inertia), //)); try expect(bcs0.reserved == bcs1.reserved); try expect(bcs0.shape == bcs1.shape); } test "zphysics.basic" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{ .max_bodies = 1024, .num_body_mutexes = 0, .max_body_pairs = 1024, .max_contact_constraints = 1024, }, ); defer physics_system.destroy(); try expect(physics_system.getNumBodies() == 0); try expect(physics_system.getNumActiveBodies() == 0); try expect(physics_system.getMaxBodies() == 1024); { physics_system.setGravity(.{ 0, -10.0, 0 }); const gravity = physics_system.getGravity(); try expect(gravity[0] == 0 and gravity[1] == -10.0 and gravity[2] == 0); } try expect(physics_system.getBodyActivationListener() == null); physics_system.setBodyActivationListener(null); try expect(physics_system.getBodyActivationListener() == null); try expect(physics_system.getContactListener() == null); physics_system.setContactListener(null); try expect(physics_system.getContactListener() == null); _ = physics_system.getBodyInterface(); _ = physics_system.getBodyInterfaceNoLock(); _ = physics_system.getBodyInterfaceMut(); _ = physics_system.getBodyInterfaceMutNoLock(); _ = physics_system.getBodyLockInterface(); _ = physics_system.getBodyLockInterfaceNoLock(); _ = physics_system.getNarrowPhaseQuery(); _ = physics_system.getNarrowPhaseQueryNoLock(); var my_step_listener = test_cb1.MyPhysicsStepListener{}; physics_system.addStepListener(@ptrCast(@alignCast(&my_step_listener))); physics_system.optimizeBroadPhase(); try physics_system.update(1.0 / 60.0, .{ .collision_steps = 1, .integration_sub_steps = 1 }); try physics_system.update(1.0 / 60.0, .{}); physics_system.removeStepListener(@ptrCast(@alignCast(&my_step_listener))); var box_shape_settings: ?*BoxShapeSettings = null; box_shape_settings = try BoxShapeSettings.create(.{ 1.0, 2.0, 3.0 }); defer { if (box_shape_settings) |bss| bss.release(); } box_shape_settings.?.setDensity(2.0); try expect(box_shape_settings.?.getDensity() == 2.0); box_shape_settings.?.setUserData(123); try expect(box_shape_settings.?.getUserData() == 123); box_shape_settings.?.setConvexRadius(0.5); try expect(box_shape_settings.?.getConvexRadius() == 0.5); try expect(box_shape_settings.?.getRefCount() == 1); box_shape_settings.?.addRef(); try expect(box_shape_settings.?.getRefCount() == 2); box_shape_settings.?.release(); try expect(box_shape_settings.?.getRefCount() == 1); { var he = box_shape_settings.?.getHalfExtent(); try expect(he[0] == 1.0 and he[1] == 2.0 and he[2] == 3.0); box_shape_settings.?.setHalfExtent(.{ 4.0, 5.0, 6.0 }); he = box_shape_settings.?.getHalfExtent(); try expect(he[0] == 4.0 and he[1] == 5.0 and he[2] == 6.0); } try expect(box_shape_settings.?.asConvexShapeSettings().getDensity() == 2.0); try expect(box_shape_settings.?.asShapeSettings().getRefCount() == 1); const box_shape = try box_shape_settings.?.createShape(); defer box_shape.release(); { const bs = try box_shape_settings.?.createShape(); defer bs.release(); try expect(bs == box_shape); try expect(bs.getRefCount() == 3); } try expect(box_shape.getRefCount() == 2); box_shape_settings.?.release(); box_shape_settings = null; try expect(box_shape.getRefCount() == 1); try expect(box_shape.getType() == .convex); try expect(box_shape.getSubType() == .box); box_shape.setUserData(456); try expect(box_shape.getUserData() == 456); } test "zphysics.shape.sphere" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const sphere_shape_settings = try SphereShapeSettings.create(10.0); defer sphere_shape_settings.release(); try expect(sphere_shape_settings.getRadius() == 10.0); sphere_shape_settings.setRadius(2.0); try expect(sphere_shape_settings.getRadius() == 2.0); sphere_shape_settings.setDensity(2.0); try expect(sphere_shape_settings.getDensity() == 2.0); sphere_shape_settings.setMaterial(null); try expect(sphere_shape_settings.getMaterial() == null); const sphere_shape = try sphere_shape_settings.createShape(); defer sphere_shape.release(); try expect(sphere_shape.getRefCount() == 2); try expect(sphere_shape.getType() == .convex); try expect(sphere_shape.getSubType() == .sphere); sphere_shape.setUserData(1456); try expect(sphere_shape.getUserData() == 1456); } test "zphysics.shape.capsule" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const capsule_shape_settings = try CapsuleShapeSettings.create(10.0, 2.0); defer capsule_shape_settings.release(); try expect(capsule_shape_settings.getRadius() == 2.0); try expect(capsule_shape_settings.getHalfHeight() == 10.0); capsule_shape_settings.setRadius(4.0); try expect(capsule_shape_settings.getRadius() == 4.0); capsule_shape_settings.setHalfHeight(1.0); try expect(capsule_shape_settings.getHalfHeight() == 1.0); const capsule_shape = try capsule_shape_settings.createShape(); defer capsule_shape.release(); try expect(capsule_shape.getRefCount() == 2); try expect(capsule_shape.getType() == .convex); try expect(capsule_shape.getSubType() == .capsule); capsule_shape.setUserData(146); try expect(capsule_shape.getUserData() == 146); } test "zphysics.shape.taperedcapsule" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const capsule_shape_settings = try TaperedCapsuleShapeSettings.create(10.0, 2.0, 3.0); defer capsule_shape_settings.release(); try expect(capsule_shape_settings.getTopRadius() == 2.0); try expect(capsule_shape_settings.getBottomRadius() == 3.0); try expect(capsule_shape_settings.getHalfHeight() == 10.0); capsule_shape_settings.setTopRadius(4.0); try expect(capsule_shape_settings.getTopRadius() == 4.0); capsule_shape_settings.setBottomRadius(1.0); try expect(capsule_shape_settings.getBottomRadius() == 1.0); const capsule_shape = try capsule_shape_settings.createShape(); defer capsule_shape.release(); try expect(capsule_shape.getRefCount() == 2); try expect(capsule_shape.getType() == .convex); try expect(capsule_shape.getSubType() == .tapered_capsule); capsule_shape.setUserData(1146); try expect(capsule_shape.getUserData() == 1146); } test "zphysics.shape.cylinder" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const cylinder_shape_settings = try CylinderShapeSettings.create(10.0, 2.0); defer cylinder_shape_settings.release(); try expect(cylinder_shape_settings.getRadius() == 2.0); try expect(cylinder_shape_settings.getHalfHeight() == 10.0); cylinder_shape_settings.setRadius(4.0); try expect(cylinder_shape_settings.getRadius() == 4.0); cylinder_shape_settings.setHalfHeight(1.0); try expect(cylinder_shape_settings.getHalfHeight() == 1.0); cylinder_shape_settings.setConvexRadius(0.5); try expect(cylinder_shape_settings.getConvexRadius() == 0.5); const cylinder_shape = try cylinder_shape_settings.createShape(); defer cylinder_shape.release(); try expect(cylinder_shape.getRefCount() == 2); try expect(cylinder_shape.getType() == .convex); try expect(cylinder_shape.getSubType() == .cylinder); cylinder_shape.setUserData(146); try expect(cylinder_shape.getUserData() == 146); } test "zphysics.shape.convexhull" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const points = [_]f32{ 0, 0, 0, 1, 1, 1, 1, 1, 0 }; const settings = try ConvexHullShapeSettings.create(&points, 3, 12); defer settings.release(); settings.setMaxConvexRadius(0.1); try expect(settings.getMaxConvexRadius() == 0.1); const shape = try settings.createShape(); defer shape.release(); try expect(shape.getRefCount() == 2); try expect(shape.getType() == .convex); try expect(shape.getSubType() == .convex_hull); shape.setUserData(111); try expect(shape.getUserData() == 111); } test "zphysics.shape.heightfield" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const points = [16]f32{ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 }; // Height field size is 4x4 const settings = try HeightFieldShapeSettings.create(&points, 4); defer settings.release(); settings.setBlockSize(2); settings.setBitsPerSample(6); settings.setOffset(.{ 1, 2, 3 }); settings.setScale(.{ 4, 5, 6 }); try expect(settings.getBlockSize() == 2); try expect(settings.getBitsPerSample() == 6); try expect(settings.getOffset()[0] == 1); try expect(settings.getOffset()[1] == 2); try expect(settings.getOffset()[2] == 3); try expect(settings.getScale()[0] == 4); try expect(settings.getScale()[1] == 5); try expect(settings.getScale()[2] == 6); const shape = try settings.createShape(); defer shape.release(); try expect(shape.getRefCount() == 2); try expect(shape.getType() == .height_field); try expect(shape.getSubType() == .height_field); shape.setUserData(1112); try expect(shape.getUserData() == 1112); } test "zphysics.shape.meshshape" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const vertices = [9]f32{ 0, 0, 0, 1, 1, 1, 1, -1, 1 }; const indices = [3]u32{ 0, 1, 2 }; const settings = try MeshShapeSettings.create(&vertices, 3, @sizeOf([3]f32), &indices); defer settings.release(); settings.setMaxTrianglesPerLeaf(4); settings.sanitize(); try expect(settings.getMaxTrianglesPerLeaf() == 4); const shape = try settings.createShape(); defer shape.release(); try expect(shape.getRefCount() == 2); try expect(shape.getType() == .mesh); try expect(shape.getSubType() == .mesh); shape.setUserData(1112); try expect(shape.getUserData() == 1112); } test "zphysics.body.basic" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{ .max_bodies = 1024, .num_body_mutexes = 0, .max_body_pairs = 1024, .max_contact_constraints = 1024, }, ); defer physics_system.destroy(); const body_interface_mut = physics_system.getBodyInterfaceMut(); const body_interface = physics_system.getBodyInterface(); const floor_shape_settings = try BoxShapeSettings.create(.{ 100.0, 1.0, 100.0 }); defer floor_shape_settings.release(); const floor_shape = try floor_shape_settings.createShape(); defer floor_shape.release(); const floor_settings = BodyCreationSettings{ .position = .{ 0.0, -1.0, 0.0, 1.0 }, .rotation = .{ 0.0, 0.0, 0.0, 1.0 }, .shape = floor_shape, .motion_type = .static, .object_layer = test_cb1.object_layers.non_moving, }; const body_id = try body_interface_mut.createAndAddBody(floor_settings, .activate); defer { body_interface_mut.removeBody(body_id); body_interface_mut.destroyBody(body_id); } physics_system.optimizeBroadPhase(); { const query = physics_system.getNarrowPhaseQuery(); var result = query.castRay(.{ .origin = .{ 0, 10, 0, 1 }, .direction = .{ 0, -20, 0, 0 } }, .{}); try expect(result.has_hit == true); try expect(result.hit.body_id == body_id); try expect(result.hit.sub_shape_id == sub_shape_id_empty); try expect(std.math.approxEqAbs(f32, result.hit.fraction, 0.5, 0.001) == true); result = query.castRay(.{ .origin = .{ 0, 10, 0, 1 }, .direction = .{ 0, 20, 0, 0 } }, .{}); try expect(result.has_hit == false); try expect(result.hit.body_id == body_id_invalid); result = query.castRay(.{ .origin = .{ 0, 10, 0, 1 }, .direction = .{ 0, -5, 0, 0 } }, .{}); try expect(result.has_hit == false); try expect(result.hit.body_id == body_id_invalid); const ray = c.JPC_RRayCast{ .origin = .{ 0, 10, 0, 0 }, .direction = .{ 0, -20, 0, 0 }, }; var hit: c.JPC_RayCastResult = .{ .body_id = body_id_invalid, .fraction = 1.0 + flt_epsilon, .sub_shape_id = undefined, }; const has_hit = c.JPC_NarrowPhaseQuery_CastRay( @as(*const c.JPC_NarrowPhaseQuery, @ptrCast(query)), &ray, &hit, null, // broad_phase_layer_filter null, // object_layer_filter null, // body_filter ); try expect(has_hit == true); try expect(std.math.approxEqAbs(f32, hit.fraction, 0.5, 0.001) == true); } { var body_ids = std.ArrayList(BodyId).init(std.testing.allocator); defer body_ids.deinit(); try physics_system.getBodyIds(&body_ids); try expect(body_ids.items.len == 1); try expect(body_ids.capacity >= physics_system.getMaxBodies()); try expect(body_ids.items[0] == body_id); } { var body_ids = std.ArrayList(BodyId).init(std.testing.allocator); defer body_ids.deinit(); try physics_system.getActiveBodyIds(&body_ids); try expect(body_ids.items.len == 0); try expect(body_ids.capacity >= physics_system.getMaxBodies()); } { const lock_interface = physics_system.getBodyLockInterfaceNoLock(); var read_lock: BodyLockRead = .{}; read_lock.lock(lock_interface, body_id); defer read_lock.unlock(); if (read_lock.body) |locked_body| { const all_bodies: []const *const Body = physics_system.getBodiesUnsafe(); try expect(isValidBodyPointer(all_bodies[body_id & body_id_index_bits])); try expect(locked_body == all_bodies[body_id & body_id_index_bits]); try expect(locked_body.id == body_id); try expect(locked_body.id == all_bodies[body_id & body_id_index_bits].id); } } { const lock_interface = physics_system.getBodyLockInterface(); var write_lock: BodyLockWrite = .{}; write_lock.lock(lock_interface, body_id); defer write_lock.unlock(); if (write_lock.body) |locked_body| { const all_bodies_mut: []const *Body = physics_system.getBodiesMutUnsafe(); try expect(isValidBodyPointer(all_bodies_mut[body_id & body_id_index_bits])); try expect(locked_body == all_bodies_mut[body_id & body_id_index_bits]); try expect(locked_body.id == body_id); try expect(locked_body.id == all_bodies_mut[body_id & body_id_index_bits].id); all_bodies_mut[body_id & body_id_index_bits].user_data = 12345; try expect(all_bodies_mut[body_id & body_id_index_bits].user_data == 12345); } } try expect(physics_system.getNumBodies() == 1); try expect(physics_system.getNumActiveBodies() == 0); { const body1 = try body_interface_mut.createBody(floor_settings); defer body_interface_mut.destroyBody(body1.id); try expect(body_interface.isAdded(body1.getId()) == false); const xform = body1.getWorldTransform(); try expect(xform.rotation[0] == 1.0); try expect(xform.position[1] == -1.0); body1.setUserData(12345); try expect(body1.getUserData() == 12345); body1.setMotionType(.static); try expect(body1.getMotionType() == .static); body1.setCollisionGroup(.{ .group_id = 123 }); try expect(body1.getCollisionGroup().group_id == 123); body1.getCollisionGroupMut().group_id += 1; try expect(body1.getCollisionGroup().group_id == 124); body_interface_mut.addBody(body1.getId(), .activate); try expect(body_interface_mut.isAdded(body1.getId()) == true); try expect(body_interface.isActive(body1.id) == false); body_interface_mut.removeBody(body1.getId()); try expect(body_interface.isAdded(body1.id) == false); try expect(physics_system.getNumBodies() == 2); try expect(physics_system.getNumActiveBodies() == 0); } try expect(physics_system.getNumBodies() == 1); try expect(physics_system.getNumActiveBodies() == 0); } test "zphysics.body.motion" { try init(std.testing.allocator, .{}); defer deinit(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const body_interface = physics_system.getBodyInterfaceMut(); const lock_interface = physics_system.getBodyLockInterface(); const shape_settings = try BoxShapeSettings.create(.{ 1.0, 2.0, 3.0 }); defer shape_settings.release(); const shape = try shape_settings.createShape(); defer shape.release(); const body_settings = BodyCreationSettings{ .position = .{ 0.0, 10.0, 0.0, 1.0 }, .rotation = .{ 0.0, 0.0, 0.0, 1.0 }, .shape = shape, .motion_type = .dynamic, .object_layer = test_cb1.object_layers.moving, }; const body_id = try body_interface.createAndAddBody(body_settings, .activate); defer body_interface.removeAndDestroyBody(body_id); physics_system.optimizeBroadPhase(); var write_lock: BodyLockWrite = .{}; write_lock.lock(lock_interface, body_id); defer write_lock.unlock(); const body = write_lock.body.?; body.setRestitution(0.5); body.setFriction(0.25); body.setUserData(0xC0DE_C0DE_C0DE_C0DE); body.setAllowSleeping(false); try expect(body.getFriction() == 0.25); try expect(body.friction == 0.25); try expect(body.getRestitution() == 0.5); try expect(body.restitution == 0.5); try expect(body.isInBroadPhase() == true); try expect(body.isDynamic() == true); try expect(body.isStatic() == false); try expect(body.isSensor() == false); try expect(body.getShape() == shape); try expect(body.shape == shape); try expect(body.getUserData() == 0xC0DE_C0DE_C0DE_C0DE); try expect(body.user_data == 0xC0DE_C0DE_C0DE_C0DE); try expect(body.getAllowSleeping() == false); const normal0 = body.getWorldSpaceSurfaceNormal(sub_shape_id_empty, .{ 0, 12, 0 }); const normal1 = body.getWorldSpaceSurfaceNormal(sub_shape_id_empty, .{ -1, 10, 0 }); try expect(std.math.approxEqAbs(f32, normal0[0], 0.0, 0.001) == true); try expect(std.math.approxEqAbs(f32, normal0[1], 1.0, 0.001) == true); try expect(std.math.approxEqAbs(f32, normal0[2], 0.0, 0.001) == true); try expect(std.math.approxEqAbs(f32, normal1[0], -1.0, 0.001) == true); try expect(std.math.approxEqAbs(f32, normal1[1], 0.0, 0.001) == true); try expect(std.math.approxEqAbs(f32, normal1[2], 0.0, 0.001) == true); const motion = body.getMotionPropertiesMut(); try expect(body.motion_properties.? == motion); motion.setLinearDamping(0.5); motion.setAngularDamping(0.25); motion.setGravityFactor(0.5); try expect(motion.allow_sleeping == false); try expect(motion.getLinearDamping() == 0.5); try expect(motion.linear_damping == 0.5); try expect(motion.getAngularDamping() == 0.25); try expect(motion.angular_damping == 0.25); try expect(motion.getGravityFactor() == 0.5); try expect(motion.gravity_factor == 0.5); } test "zphysics.debugrenderer" { if (!debug_renderer_enabled) return; try init(std.testing.allocator, .{}); defer deinit(); var my_debug_renderer = test_cb1.MyDebugRenderer{}; try DebugRenderer.createSingleton(&my_debug_renderer); defer DebugRenderer.destroySingleton(); const my_broad_phase_layer_interface = test_cb1.MyBroadphaseLayerInterface.init(); const my_broad_phase_should_collide = test_cb1.MyObjectVsBroadPhaseLayerFilter{}; const my_object_should_collide = test_cb1.MyObjectLayerPairFilter{}; const physics_system = try PhysicsSystem.create( @as(*const BroadPhaseLayerInterface, @ptrCast(&my_broad_phase_layer_interface)), @as(*const ObjectVsBroadPhaseLayerFilter, @ptrCast(&my_broad_phase_should_collide)), @as(*const ObjectLayerPairFilter, @ptrCast(&my_object_should_collide)), .{}, ); defer physics_system.destroy(); const shape_settings = try BoxShapeSettings.create(.{ 1.0, 2.0, 3.0 }); defer shape_settings.release(); const shape = try shape_settings.createShape(); defer shape.release(); const body_settings = BodyCreationSettings{ .position = .{ 0.0, 10.0, 0.0, 1.0 }, .rotation = .{ 0.0, 0.0, 0.0, 1.0 }, .shape = shape, .motion_type = .dynamic, .object_layer = test_cb1.object_layers.moving, }; const body_interface = physics_system.getBodyInterfaceMut(); const body_id = try body_interface.createAndAddBody(body_settings, .activate); defer body_interface.removeAndDestroyBody(body_id); physics_system.optimizeBroadPhase(); try physics_system.update(0.1, .{}); const draw_settings: DebugRenderer.BodyDrawSettings = .{}; const draw_filter = DebugRenderer.createBodyDrawFilter(test_cb1.MyDebugRenderer.shouldBodyDraw); defer DebugRenderer.destroyBodyDrawFilter(draw_filter); physics_system.drawBodies(&draw_settings, draw_filter); } test { std.testing.refAllDecls(@This()); } const test_cb1 = struct { const object_layers = struct { const non_moving: ObjectLayer = 0; const moving: ObjectLayer = 1; const len: u32 = 2; }; const broad_phase_layers = struct { const non_moving: BroadPhaseLayer = 0; const moving: BroadPhaseLayer = 1; const len: u32 = 2; }; const MyBroadphaseLayerInterface = extern struct { usingnamespace BroadPhaseLayerInterface.Methods(@This()); __v: *const BroadPhaseLayerInterface.VTable = &vtable, object_to_broad_phase: [object_layers.len]BroadPhaseLayer = undefined, const vtable = BroadPhaseLayerInterface.VTable{ .getNumBroadPhaseLayers = _getNumBroadPhaseLayers, .getBroadPhaseLayer = if (@import("builtin").abi == .msvc) _getBroadPhaseLayerMsvc else _getBroadPhaseLayer, }; fn init() MyBroadphaseLayerInterface { var layer_interface: MyBroadphaseLayerInterface = .{}; layer_interface.object_to_broad_phase[object_layers.non_moving] = broad_phase_layers.non_moving; layer_interface.object_to_broad_phase[object_layers.moving] = broad_phase_layers.moving; return layer_interface; } fn _getNumBroadPhaseLayers(iself: *const BroadPhaseLayerInterface) callconv(.C) u32 { const self = @as(*const MyBroadphaseLayerInterface, @ptrCast(iself)); return @as(u32, @intCast(self.object_to_broad_phase.len)); } fn _getBroadPhaseLayer( iself: *const BroadPhaseLayerInterface, layer: ObjectLayer, ) callconv(.C) BroadPhaseLayer { const self = @as(*const MyBroadphaseLayerInterface, @ptrCast(iself)); return self.object_to_broad_phase[@as(usize, @intCast(layer))]; } fn _getBroadPhaseLayerMsvc( iself: *const BroadPhaseLayerInterface, out_layer: *BroadPhaseLayer, layer: ObjectLayer, ) callconv(.C) *const BroadPhaseLayer { const self = @as(*const MyBroadphaseLayerInterface, @ptrCast(iself)); out_layer.* = self.object_to_broad_phase[@as(usize, @intCast(layer))]; return out_layer; } }; const MyObjectVsBroadPhaseLayerFilter = extern struct { usingnamespace ObjectVsBroadPhaseLayerFilter.Methods(@This()); __v: *const ObjectVsBroadPhaseLayerFilter.VTable = &vtable, const vtable = ObjectVsBroadPhaseLayerFilter.VTable{ .shouldCollide = _shouldCollide }; fn _shouldCollide( _: *const ObjectVsBroadPhaseLayerFilter, layer1: ObjectLayer, layer2: BroadPhaseLayer, ) callconv(.C) bool { return switch (layer1) { object_layers.non_moving => layer2 == broad_phase_layers.moving, object_layers.moving => true, else => unreachable, }; } }; const MyObjectLayerPairFilter = extern struct { usingnamespace ObjectLayerPairFilter.Methods(@This()); __v: *const ObjectLayerPairFilter.VTable = &vtable, const vtable = ObjectLayerPairFilter.VTable{ .shouldCollide = _shouldCollide }; fn _shouldCollide( _: *const ObjectLayerPairFilter, object1: ObjectLayer, object2: ObjectLayer, ) callconv(.C) bool { return switch (object1) { object_layers.non_moving => object2 == object_layers.moving, object_layers.moving => true, else => unreachable, }; } }; const MyPhysicsStepListener = extern struct { usingnamespace PhysicsStepListener.Methods(@This()); __v: *const PhysicsStepListener.VTable = &vtable, steps_heard: u32 = 0, const vtable = PhysicsStepListener.VTable{ .onStep = _onStep }; fn _onStep(psl: *PhysicsStepListener, delta_time: f32, physics_system: *PhysicsSystem) callconv(.C) void { _ = delta_time; _ = physics_system; const self = @as(*MyPhysicsStepListener, @ptrCast(psl)); self.steps_heard += 1; } }; const MyDebugRenderer = if (!debug_renderer_enabled) void else extern struct { const MyRenderPrimitive = extern struct { // Actual render data goes here foobar: i32 = 0, }; usingnamespace DebugRenderer.Methods(@This()); __v: *const DebugRenderer.VTable(@This()) = &vtable, primitives: [32]MyRenderPrimitive = [_]MyRenderPrimitive{.{}} ** 32, prim_head: i32 = -1, const vtable = DebugRenderer.VTable(@This()){ .drawLine = drawLine, .drawTriangle = drawTriangle, .createTriangleBatch = createTriangleBatch, .createTriangleBatchIndexed = createTriangleBatchIndexed, .drawGeometry = drawGeometry, .drawText3D = drawText3D, }; pub fn shouldBodyDraw(_: *const Body) align(DebugRenderer.BodyDrawFilterFuncAlignment) callconv(.C) bool { return true; } fn drawLine( self: *MyDebugRenderer, from: *const [3]Real, to: *const [3]Real, color: *const DebugRenderer.Color, ) callconv(.C) void { _ = self; _ = from; _ = to; _ = color; } fn drawTriangle( self: *MyDebugRenderer, v1: *const [3]Real, v2: *const [3]Real, v3: *const [3]Real, color: *const DebugRenderer.Color, ) callconv(.C) void { _ = self; _ = v1; _ = v2; _ = v3; _ = color; } fn createTriangleBatch( self: *MyDebugRenderer, triangles: [*]DebugRenderer.Triangle, triangle_count: u32, ) callconv(.C) *anyopaque { _ = triangles; _ = triangle_count; self.prim_head += 1; const prim = &self.primitives[@as(usize, @intCast(self.prim_head))]; return DebugRenderer.createTriangleBatch(prim); } fn createTriangleBatchIndexed( self: *MyDebugRenderer, vertices: [*]DebugRenderer.Vertex, vertex_count: u32, indices: [*]u32, index_count: u32, ) callconv(.C) *anyopaque { _ = vertices; _ = vertex_count; _ = indices; _ = index_count; self.prim_head += 1; const prim = &self.primitives[@as(usize, @intCast(self.prim_head))]; return DebugRenderer.createTriangleBatch(prim); } fn drawGeometry( self: *MyDebugRenderer, model_matrix: *const [16]Real, world_space_bound: *const DebugRenderer.AABox, lod_scale_sq: f32, color: DebugRenderer.Color, geometry: *const DebugRenderer.Geometry, cull_mode: DebugRenderer.CullMode, cast_shadow: DebugRenderer.CastShadow, draw_mode: DebugRenderer.DrawMode, ) callconv(.C) void { _ = self; _ = model_matrix; _ = world_space_bound; _ = lod_scale_sq; _ = color; _ = geometry; _ = cull_mode; _ = cast_shadow; _ = draw_mode; } fn drawText3D( self: *MyDebugRenderer, positions: *const [3]Real, string: [*:0]const u8, color: DebugRenderer.Color, height: f32, ) callconv(.C) void { _ = self; _ = positions; _ = string; _ = color; _ = height; } }; }; //--------------------------------------------------------------------------------------------------