Backlog/engine/physics/src/physicsSystem.zig

381 lines
13 KiB
Zig

const std = @import("std");
const zphysics = @import("zphysics");
const core = @import("core");
const zm = core.zm;
const physicsCollider = @import("physicsCollider.zig");
pub const PhysicsCollider = physicsCollider.PhysicsCollider;
const physicsCharacter = @import("physicsCharacter.zig");
pub const PhysicsCharacter = physicsCharacter.PhysicsCharacter;
pub const ObjectLayers = struct {
pub const non_moving: zphysics.ObjectLayer = 0;
pub const moving: zphysics.ObjectLayer = 1;
pub const len: u32 = 2;
};
pub const BroadPhaseLayers = struct {
pub const non_moving: zphysics.BroadPhaseLayer = 0;
pub const moving: zphysics.BroadPhaseLayer = 1;
pub const len: u32 = 2;
};
pub const IgnoreFixedBodiesFilter = extern struct {
__v: *const zphysics.BodyFilter.VTable = &vtable,
filtered: [16]BodyId = undefined,
len: usize = 0,
const vtable = zphysics.BodyFilter.VTable{
.shouldCollide = _shouldCollide,
.shouldCollideLocked = _shouldCollideLocked,
};
pub fn addIgnoredBody(self: *@This(), bodyId: BodyId) !void {
if (self.len == 16) {
return error.OutOfMemory;
}
self.filtered[self.len] = bodyId;
self.len += 1;
}
pub fn _shouldCollide(p: *const zphysics.BodyFilter, bodyId: *const BodyId) callconv(.c) bool {
const self: *const @This() = @ptrCast(p);
for (0..self.len) |i| {
if (self.filtered[i] == bodyId.*) {
return false;
}
}
return true;
}
pub fn _shouldCollideLocked(p: *const zphysics.BodyFilter, body: *const Body) callconv(.c) bool {
const self: *const @This() = @ptrCast(p);
_ = self;
_ = body;
return true;
}
};
const BroadPhaseLayerInterface = extern struct {
broad_phase_layer_interface: zphysics.BroadPhaseLayerInterface = .init(@This()),
object_to_broad_phase: [ObjectLayers.len]zphysics.BroadPhaseLayer = undefined,
fn init() BroadPhaseLayerInterface {
var layer_interface: BroadPhaseLayerInterface = .{};
layer_interface.object_to_broad_phase[ObjectLayers.non_moving] = BroadPhaseLayers.non_moving;
layer_interface.object_to_broad_phase[ObjectLayers.moving] = BroadPhaseLayers.moving;
return layer_interface;
}
pub fn getNumBroadPhaseLayers(_: *const zphysics.BroadPhaseLayerInterface) callconv(.c) u32 {
return BroadPhaseLayers.len;
}
pub fn getBroadPhaseLayer(
broad_phase_layer_interface: *const zphysics.BroadPhaseLayerInterface,
object_layer: zphysics.ObjectLayer,
) callconv(.c) zphysics.BroadPhaseLayer {
const self: *const BroadPhaseLayerInterface = @alignCast(@fieldParentPtr("broad_phase_layer_interface", broad_phase_layer_interface));
return self.object_to_broad_phase[object_layer];
}
};
const ObjectVsBroadPhaseLayerFilter = extern struct {
object_vs_broad_phase_layer_filter: zphysics.ObjectVsBroadPhaseLayerFilter = .init(@This()),
pub fn shouldCollide(
_: *const zphysics.ObjectVsBroadPhaseLayerFilter,
object_layer: zphysics.ObjectLayer,
broad_phase_layer: zphysics.BroadPhaseLayer,
) callconv(.c) bool {
return switch (object_layer) {
ObjectLayers.non_moving => broad_phase_layer == BroadPhaseLayers.moving,
ObjectLayers.moving => true,
else => unreachable,
};
}
};
const ObjectLayerPairFilter = extern struct {
object_layer_pair_filter: zphysics.ObjectLayerPairFilter = .init(@This()),
pub fn shouldCollide(
_: *const zphysics.ObjectLayerPairFilter,
a: zphysics.ObjectLayer,
b: zphysics.ObjectLayer,
) callconv(.c) bool {
return switch (a) {
ObjectLayers.non_moving => b == ObjectLayers.moving,
ObjectLayers.moving => true,
else => unreachable,
};
}
};
pub const PhysicsRuntime = struct {
allocator: std.mem.Allocator,
bpli: BroadPhaseLayerInterface,
ovbplf: ObjectVsBroadPhaseLayerFilter = .{},
olpf: ObjectLayerPairFilter = .{},
max_bodies: u32 = 8192,
system: *zphysics.PhysicsSystem = undefined,
shapes: std.AutoHashMapUnmanaged(u32, ShapeRef) = .{},
updatePeriod: f64 = 1.0 / 60.0,
timeSinceUpdate: f64 = 0.0,
idToEntity: std.AutoHashMapUnmanaged(zphysics.BodyId, core.Entity) = .{},
bodyIdsToDestroy: std.ArrayListUnmanaged(BodyId) = .{},
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "physics.Runtime");
pub fn registerBodyEntity(self: *@This(), bodyId: zphysics.BodyId, entity: core.Entity) !void {
try self.idToEntity.put(self.allocator, bodyId, entity);
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
try zphysics.init(std.heap.c_allocator, .{});
//try zphysics.init(allocator, .{});
self.* = .{
.allocator = allocator,
.bpli = BroadPhaseLayerInterface.init(),
};
const system = try zphysics.PhysicsSystem.create(
@as(*const zphysics.BroadPhaseLayerInterface, @ptrCast(&self.bpli)),
@as(*const zphysics.ObjectVsBroadPhaseLayerFilter, @ptrCast(&self.ovbplf)),
@as(*const zphysics.ObjectLayerPairFilter, @ptrCast(&self.olpf)),
.{
.max_bodies = self.max_bodies,
.max_body_pairs = self.max_bodies * 2,
.max_contact_constraints = self.max_bodies * 2,
.num_body_mutexes = 0,
},
);
self.system = system;
//try core.defineComponent(PhysicsCharacter, self.allocator);
// try core.defineComponent(PhysicsCollider, self.allocator);
return self;
}
pub fn tick(self: *@This(), dt: f64) void {
// self.timeSinceUpdate += dt;
// while (self.timeSinceUpdate > self.updatePeriod) {
// self.timeSinceUpdate -= self.updatePeriod;
// self.system.update(@floatCast(self.updatePeriod), .{}) catch unreachable;
// }
self.system.update(@floatCast(dt), .{}) catch unreachable;
// todo.. interpolation kinda easy here.
self.updateScenes(dt);
}
fn updateScenes(self: *@This(), dt: f64) void {
const lockInterface = self.system.getBodyLockInterface();
const interface = self.system.getBodyInterfaceMut();
for (self.bodyIdsToDestroy.items) |toDestroy| {
interface.removeAndDestroyBody(toDestroy);
}
self.bodyIdsToDestroy.clearRetainingCapacity();
// update character controllers
for (PhysicsCharacter.BaseContainer.list.items) |physChar| {
physChar.update(dt);
}
// update physics colliders
for (PhysicsCollider.BaseContainer.list.items) |collider| {
if (collider.mobile == false) {
continue;
}
if (collider.bodyId) |bodyId| {
var readLock: zphysics.BodyLockRead = .{};
readLock.lock(lockInterface, bodyId);
defer readLock.unlock();
if (readLock.body) |body| {
const scene = collider.entity.fetch(core.Scene).?;
scene.setPosition(core.Vectorf.fromArray(body.position));
scene.setRotation(.{ .quat = body.rotation });
}
}
}
}
pub fn pushDestroyBodyId(self: *@This(), bodyId: BodyId) void {
self.bodyIdsToDestroy.append(self.allocator, bodyId) catch unreachable;
}
pub fn removeShape(self: *@This(), name: *core.Name) void {
self.shapes.get(name.handle()).?.shape.release();
}
pub fn deinit(self: *@This()) void {
const allocator = self.allocator;
self.system.optimizeBroadPhase();
for (PhysicsCharacter.BaseContainer.list.items) |physChar| {
physChar.deinit();
}
self.system.optimizeBroadPhase();
// self.system.update(0.01, .{}) catch {};
{
const interface = self.system.getBodyInterfaceMut();
var iterator = self.idToEntity.keyIterator();
while (iterator.next()) |bodyId| {
interface.removeAndDestroyBody(bodyId.*);
}
}
self.system.optimizeBroadPhase();
{
var iterator = self.shapes.iterator();
while (iterator.next()) |n| {
n.value_ptr.shape.release();
}
}
self.bodyIdsToDestroy.deinit(self.allocator);
// self.system.update(0.01, .{}) catch {};
self.idToEntity.deinit(self.allocator);
// core.undefineComponent(PhysicsCharacter);
// core.undefineComponent(PhysicsCollider);
self.shapes.deinit(self.allocator);
self.system.destroy();
zphysics.deinit();
allocator.destroy(self);
}
pub fn createShape(self: *@This(), name: *core.Name, settings: ShapeSettings) !void {
const ref: ShapeRef = .{
.settings = settings,
.shape = try settings.createShape(),
};
try self.shapes.put(self.allocator, name.handle(), ref);
}
};
pub const ShapeSettings = union(enum(u8)) {
convex: *zphysics.ConvexShapeSettings,
box: *zphysics.BoxShapeSettings,
sphere: *zphysics.SphereShapeSettings,
triangle: *zphysics.TriangleShapeSettings,
capsule: *zphysics.CapsuleShapeSettings,
taperedCapsule: *zphysics.TaperedCapsuleShapeSettings,
cylinder: *zphysics.CylinderShapeSettings,
convexHull: *zphysics.ConvexHullShapeSettings,
heightField: *zphysics.HeightFieldShapeSettings,
mesh: *zphysics.MeshShapeSettings,
decorated: *zphysics.DecoratedShapeSettings,
compound: *zphysics.CompoundShapeSettings,
pub fn release(self: @This()) void {
switch (self) {
.convex => |x| {
x.asShapeSettings().release();
},
.box => |x| {
x.asShapeSettings().release();
},
.sphere => |x| {
x.asShapeSettings().release();
},
.triangle => |x| {
x.asShapeSettings().release();
},
.capsule => |x| {
x.asShapeSettings().release();
},
.taperedCapsule => |x| {
x.asShapeSettings().release();
},
.cylinder => |x| {
x.asShapeSettings().release();
},
.convexHull => |x| {
x.asShapeSettings().release();
},
.heightField => |x| {
x.asShapeSettings().release();
},
.mesh => |x| {
x.asShapeSettings().release();
},
.decorated => |x| {
x.asShapeSettings().release();
},
.compound => |x| {
x.asShapeSettings().release();
},
}
}
pub fn createShape(self: @This()) !*zphysics.Shape {
switch (self) {
.convex => |x| {
return try x.asShapeSettings().createShape();
},
.box => |x| {
return try x.asShapeSettings().createShape();
},
.sphere => |x| {
return try x.asShapeSettings().createShape();
},
.triangle => |x| {
return try x.asShapeSettings().createShape();
},
.capsule => |x| {
return try x.asShapeSettings().createShape();
},
.taperedCapsule => |x| {
return try x.asShapeSettings().createShape();
},
.cylinder => |x| {
return try x.asShapeSettings().createShape();
},
.convexHull => |x| {
return try x.asShapeSettings().createShape();
},
.heightField => |x| {
return try x.asShapeSettings().createShape();
},
.mesh => |x| {
return try x.asShapeSettings().createShape();
},
.decorated => |x| {
return try x.asShapeSettings().createShape();
},
.compound => |x| {
return try x.asShapeSettings().createShape();
},
}
}
};
pub const ShapeRef = struct {
settings: ShapeSettings = undefined,
shape: *zphysics.Shape = undefined,
};
const BodyId = zphysics.BodyId;
const Body = zphysics.Body;