330 lines
11 KiB
Zig
330 lines
11 KiB
Zig
pub const Settings = struct {
|
|
mapName: []const u8 = "map_",
|
|
vertexScale: f32 = 1.0 / 32.0,
|
|
rotation: core.Rotation = .{},
|
|
};
|
|
|
|
pub const ColliderSpec = struct {
|
|
name: core.Name,
|
|
};
|
|
|
|
pub const TBMap = struct {
|
|
root: core.Entity,
|
|
shapes: std.ArrayListUnmanaged(core.Name) = .{},
|
|
meshes: std.ArrayListUnmanaged(core.Entity) = .{}, // each material has its own entity
|
|
colliders: std.ArrayListUnmanaged(core.Entity) = .{}, // each solid in worldspawn has it's own convex collider
|
|
colliderSpecs: std.ArrayListUnmanaged(ColliderSpec) = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|
const self = try allocator.create(@This());
|
|
|
|
core.engine_logs("creating map root");
|
|
self.* = .{
|
|
.allocator = allocator,
|
|
.root = try core.createEntity(),
|
|
};
|
|
|
|
_ = self.root.addComponent(core.Scene).?;
|
|
|
|
return self;
|
|
}
|
|
|
|
var fmtBuf: [256]u8 = undefined;
|
|
var fmtBuf2: [256]u8 = undefined;
|
|
pub fn addMeshScene(self: *@This(), builder: *MeshBuilder) !void {
|
|
const newEntity = try core.createEntity();
|
|
const scene = newEntity.addComponent(core.Scene).?;
|
|
scene.setParent(self.root);
|
|
|
|
const mesh = newEntity.addComponent(rend.MeshComponent).?;
|
|
mesh.setMesh(builder.meshName.utf8());
|
|
|
|
const textureName = try std.fmt.bufPrint(&fmtBuf, "t_map/{s}", .{builder.materialName.utf8()});
|
|
mesh.setTexture(textureName);
|
|
|
|
if (!rend.textureExists(textureName)) {
|
|
var textureRef = builder.materialName.utf8();
|
|
if (std.mem.eql(u8, textureRef, "__TB_empty")) {
|
|
textureRef = "texture_sample";
|
|
}
|
|
|
|
try assets.load(
|
|
assets.MakeImportRefOptions(
|
|
"Texture",
|
|
textureName,
|
|
// TODO don't hardcode the root path
|
|
.{ .path = try std.fmt.bufPrint(&fmtBuf2, "textures/{s}.png", .{textureRef}), .textureUseBlockySampler = false },
|
|
),
|
|
);
|
|
}
|
|
// if the texture Does not exist issue an asset load request
|
|
|
|
try self.meshes.append(self.allocator, newEntity);
|
|
}
|
|
|
|
pub fn addPhysicsShape(self: *@This(), solid: QuakeMap.Solid, buildSettings: Settings) !void {
|
|
var floatList: std.ArrayListUnmanaged(f32) = .{};
|
|
defer floatList.deinit(self.allocator);
|
|
var vertexCount: u32 = 0;
|
|
for (solid.faces.items) |face| {
|
|
for (face.vertices) |vert| {
|
|
// core.engine_log("adding vertex {any}", .{vert});
|
|
const position =
|
|
core.Vectorf{
|
|
.x = @floatCast(-vert.data[0]),
|
|
.y = @floatCast(vert.data[1]),
|
|
.z = @floatCast(vert.data[2]),
|
|
};
|
|
const final = buildSettings.rotation.rotateVector(
|
|
position.fmul(buildSettings.vertexScale),
|
|
);
|
|
|
|
try floatList.append(self.allocator, @floatCast(final.x));
|
|
try floatList.append(self.allocator, @floatCast(final.y));
|
|
try floatList.append(self.allocator, @floatCast(final.z));
|
|
vertexCount += 1;
|
|
}
|
|
}
|
|
|
|
const settings = try physics.ConvexHullShapeSettings.create(
|
|
@ptrCast(floatList.items.ptr),
|
|
vertexCount,
|
|
12,
|
|
);
|
|
defer settings.asShapeSettings().release();
|
|
|
|
// settings.setMaxConvexRadius(0.1);
|
|
var nameBuffer: [256]u8 = undefined;
|
|
|
|
const shapeName = try std.fmt.bufPrint(&nameBuffer, "t_map/worldspawn_shape_{d}", .{self.colliderSpecs.items.len});
|
|
|
|
try physics.addShape(shapeName, .{ .convexHull = settings });
|
|
try self.shapes.append(self.allocator, core.MakeName(shapeName));
|
|
|
|
try self.colliderSpecs.append(self.allocator, .{
|
|
.name = core.MakeName(shapeName),
|
|
});
|
|
|
|
{
|
|
const entity = try core.createEntity();
|
|
const scene = entity.addComponent(core.Scene).?;
|
|
scene.setPosition(.{});
|
|
// scene.setParent(self.root);
|
|
const collider = entity.addComponent(physics.PhysicsCollider).?;
|
|
try collider.setupByShapeName(core.MakeName(shapeName), .{
|
|
.motion_type = .static,
|
|
.object_layer = physics.ObjectLayers.non_moving,
|
|
.friction = 0.8,
|
|
});
|
|
try self.colliders.append(self.allocator, entity);
|
|
}
|
|
}
|
|
|
|
pub fn destroy(self: *@This()) void {
|
|
for (self.meshes.items) |*m| {
|
|
m.destroy();
|
|
}
|
|
|
|
self.meshes.deinit(self.allocator);
|
|
|
|
for (self.colliders.items) |*c| {
|
|
c.destroy();
|
|
}
|
|
|
|
self.colliders.deinit(self.allocator);
|
|
for (self.colliderSpecs.items) |spec| {
|
|
var name = spec.name;
|
|
physics.context().shapes.get(name.handle()).?.shape.release();
|
|
}
|
|
self.colliderSpecs.deinit(self.allocator);
|
|
|
|
self.shapes.deinit(self.allocator);
|
|
self.root.destroy();
|
|
|
|
self.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
// assumed to be consumed by submitMesh(). no deinit provided
|
|
pub const MeshBuilder = struct {
|
|
materialName: core.Name,
|
|
meshName: core.Name,
|
|
vertexOffset: u32 = 0,
|
|
|
|
// these three use MeshAlloc
|
|
meshAlloc: std.mem.Allocator,
|
|
vertexList: std.ArrayListUnmanaged(rend.MeshVertex) = .{},
|
|
indexList: std.ArrayListUnmanaged(u32) = .{},
|
|
jointNames: std.ArrayListUnmanaged(JointNameEntry) = .{},
|
|
// jointNames intentionally left empty, needed for the vk_meshpool interface.
|
|
// if i refactor it, this doesnt need to exist anymore, but that takes work...
|
|
|
|
pub fn submitMesh(self: *@This()) !void {
|
|
const newMesh: MeshUpdate = .{
|
|
.new = .{
|
|
.vertices = try self.vertexList.toOwnedSlice(self.meshAlloc),
|
|
.indices = try self.indexList.toOwnedSlice(self.meshAlloc),
|
|
.name = self.meshName,
|
|
.jointNames = try self.jointNames.toOwnedSlice(self.meshAlloc),
|
|
.skeletonName = null,
|
|
},
|
|
};
|
|
|
|
try rend.renderer.pushMeshUpdate(newMesh);
|
|
}
|
|
|
|
pub fn addFace(self: *@This(), face: QuakeMap.Face, settings: Settings) !void {
|
|
const indexStart = self.vertexOffset;
|
|
|
|
const count = face.vertices.len;
|
|
const uAxis: Vec3 = face.u_axis;
|
|
const vAxis: Vec3 = face.v_axis;
|
|
|
|
const texSizeX: f32 = 64;
|
|
const texSizeY: f32 = 64;
|
|
|
|
const d10 = face.vertices[1].sub(face.vertices[0]);
|
|
const d21 = face.vertices[2].sub(face.vertices[1]);
|
|
|
|
const n = d10.cross(d21);
|
|
|
|
var normal =
|
|
core.Vectorf{
|
|
.x = @floatCast(-n.data[0]),
|
|
.y = @floatCast(n.data[1]),
|
|
.z = @floatCast(n.data[2]),
|
|
};
|
|
|
|
normal = normal.normalize();
|
|
|
|
// normal = .{ .y = 1.0 };
|
|
normal = settings.rotation.rotateVector(normal);
|
|
if (normal.dot(.{ .y = 1.0 }) < 0.5) {
|
|
// return;
|
|
}
|
|
|
|
// append all vertices to the vertex list
|
|
for (face.vertices) |vert| {
|
|
// uv_up axis
|
|
|
|
const position =
|
|
core.Vectorf{
|
|
.x = @floatCast(-vert.data[0]),
|
|
.y = @floatCast(vert.data[1]),
|
|
.z = @floatCast(vert.data[2]),
|
|
};
|
|
|
|
const uAxisV = core.Vectorf.fromArray(uAxis.data);
|
|
const vAxisV = core.Vectorf.fromArray(vAxis.data);
|
|
|
|
try self.vertexList.append(self.meshAlloc, .{
|
|
.position = settings.rotation.rotateVector(
|
|
position.fmul(settings.vertexScale),
|
|
),
|
|
// .normal: Vectorf = .{},
|
|
.normal = normal,
|
|
.uv = .{
|
|
.x = (uAxisV.dot(position) + face.shift_x) / texSizeX,
|
|
.y = (vAxisV.dot(position) + face.shift_y) / texSizeY,
|
|
},
|
|
});
|
|
|
|
self.vertexOffset += 1;
|
|
}
|
|
|
|
// generate ngons
|
|
try self.indexList.append(self.meshAlloc, indexStart + 2);
|
|
try self.indexList.append(self.meshAlloc, indexStart + 1);
|
|
try self.indexList.append(self.meshAlloc, indexStart);
|
|
|
|
for (3..count) |j| {
|
|
const i: u32 = @intCast(j);
|
|
try self.indexList.append(self.meshAlloc, indexStart + i);
|
|
try self.indexList.append(self.meshAlloc, indexStart + i - 1);
|
|
try self.indexList.append(self.meshAlloc, indexStart);
|
|
}
|
|
}
|
|
};
|
|
|
|
var tbMapDefaultName: core.Name = core.DefineName("__TB_empty");
|
|
|
|
pub fn LoadTrenchbroomMap(baseAllocator: std.mem.Allocator, settings: Settings) !*TBMap {
|
|
var err: QuakeMap.ErrorInfo = undefined;
|
|
const fileMapping = try core.fs().loadFile(settings.mapName);
|
|
defer core.fs().unmap(fileMapping);
|
|
|
|
core.engine_log("loading map :{s}", .{settings.mapName});
|
|
|
|
var arena = std.heap.ArenaAllocator.init(baseAllocator);
|
|
defer arena.deinit();
|
|
const alloc = arena.allocator();
|
|
|
|
const map = try QuakeMap.read(arena.allocator(), fileMapping.bytesNoEnd(), &err);
|
|
|
|
var builders: std.AutoHashMapUnmanaged(u32, MeshBuilder) = .{};
|
|
|
|
core.engine_log("worldspawn solids count: {d}", .{map.worldspawn.solids.items.len});
|
|
|
|
const meshAlloc = rend.getAllocator();
|
|
|
|
for (map.worldspawn.solids.items) |solid| {
|
|
for (solid.faces.items) |face| {
|
|
var name = core.MakeName(face.texture_name);
|
|
|
|
if (!builders.contains(name.handle())) {
|
|
var meshName: []u8 = undefined;
|
|
meshName = try std.fmt.allocPrint(alloc, "map/m_{s}", .{face.texture_name});
|
|
|
|
try builders.put(alloc, name.handle(), .{
|
|
.materialName = name,
|
|
.meshName = core.MakeName(meshName),
|
|
.meshAlloc = meshAlloc,
|
|
});
|
|
core.engine_log("new material found, {s}", .{face.texture_name});
|
|
}
|
|
}
|
|
}
|
|
|
|
// loop over all solids and list all materials.
|
|
// creating a unique mesh for each one
|
|
for (map.worldspawn.solids.items) |solid| {
|
|
for (solid.faces.items) |face| {
|
|
var name = core.MakeName(face.texture_name);
|
|
const builder = builders.getPtr(name.handle()).?;
|
|
try builder.addFace(face, settings);
|
|
}
|
|
}
|
|
// loop over every vertex in quakeMap and push vertices and indices.
|
|
const tbMap = try TBMap.create(baseAllocator);
|
|
|
|
var iterator = builders.valueIterator();
|
|
while (iterator.next()) |builder| {
|
|
try tbMap.addMeshScene(builder);
|
|
try builder.submitMesh();
|
|
}
|
|
|
|
//
|
|
for (map.worldspawn.solids.items) |solid| {
|
|
try tbMap.addPhysicsShape(solid, settings);
|
|
}
|
|
|
|
// physics.context().system.update(0.001, .{}) catch {};
|
|
// loop over all point entities look for info_player_start
|
|
return tbMap;
|
|
}
|
|
|
|
const MeshUpdate = rend.MeshUpdate;
|
|
const JointNameEntry = rend.JointNameEntry;
|
|
|
|
const za = QuakeMap.za;
|
|
const Vec3 = za.Vec3;
|
|
const Vec3d = za.Vec3_f64;
|
|
const QuakeMap = @import("QuakeMap.zig");
|
|
const std = @import("std");
|
|
const backlog = @import("Backlog");
|
|
const rend = backlog.rend;
|
|
const physics = backlog.physics;
|
|
const core = backlog.core;
|
|
const assets = backlog.assets;
|