381 lines
13 KiB
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(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
|
if (!first) {
|
|
return;
|
|
}
|
|
|
|
try zphysics.init(std.heap.smp_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;
|
|
}
|
|
|
|
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;
|
|
// }
|
|
var z = core.tracy.ZoneN(@src(), "physics system update ");
|
|
self.system.update(@floatCast(dt), .{}) catch unreachable;
|
|
defer z.End();
|
|
|
|
// 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);
|
|
}
|
|
var z = core.tracy.ZoneN(@src(), "Updating physics transform");
|
|
defer z.End();
|
|
|
|
// 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 {
|
|
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();
|
|
_ = interface;
|
|
var iterator = self.idToEntity.keyIterator();
|
|
while (iterator.next()) |bodyId| {
|
|
_ = 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();
|
|
}
|
|
|
|
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;
|