Try to optimize for many draw calls
This commit is contained in:
parent
f21bc2245a
commit
b0163b1f6b
@ -9,6 +9,7 @@
|
||||
|
||||
struct DrawCmdData {
|
||||
mat4 transform;
|
||||
int materialIdx;
|
||||
};
|
||||
|
||||
// UBOs
|
||||
@ -18,7 +19,6 @@ layout(std140, binding = 0) uniform Matrices {
|
||||
};
|
||||
|
||||
layout(std430, binding = 3) readonly buffer DrawCmdDatas {
|
||||
uint draws_count;
|
||||
// Access by gl_DrawID
|
||||
DrawCmdData draw_data[];
|
||||
};
|
||||
@ -149,13 +149,13 @@ int getCSMSplit(int lightIdx, float depth) {
|
||||
}
|
||||
|
||||
EvalMaterial evalMaterial() {
|
||||
Material mat = materials[DrawID];
|
||||
EvalMaterial result;
|
||||
result.albedo = textureSize(mat.albedo_map, 0) == ivec2(0) ? vec4(pow(mat.albedo.rgb, vec3(2.2)), mat.albedo.a) : texture(mat.albedo_map, VertexOut.uv * mat.albedo_map_uv_scale);
|
||||
float fMetallic = textureSize(mat.metallic_map, 0) == ivec2(0) ? mat.metallic : texture(mat.metallic_map, VertexOut.uv * mat.metallic_map_uv_scale).b;
|
||||
int materialIdx = draw_data[DrawID].materialIdx;
|
||||
result.albedo = textureSize(materials[materialIdx].albedo_map, 0) == ivec2(0) ? vec4(pow(materials[materialIdx].albedo.rgb, vec3(2.2)), materials[materialIdx].albedo.a) : texture(materials[materialIdx].albedo_map, VertexOut.uv * materials[materialIdx].albedo_map_uv_scale);
|
||||
float fMetallic = textureSize(materials[materialIdx].metallic_map, 0) == ivec2(0) ? materials[materialIdx].metallic : texture(materials[materialIdx].metallic_map, VertexOut.uv * materials[materialIdx].metallic_map_uv_scale).b;
|
||||
result.metallic = fMetallic > 0.1;
|
||||
result.roughness = max(0.01, textureSize(mat.roughness_map, 0) == ivec2(0) ? mat.roughness : texture(mat.roughness_map, VertexOut.uv * mat.roughness_map_uv_scale).g);
|
||||
result.emission = textureSize(mat.emission_map, 0) == ivec2(0) ? mat.emission : texture(mat.emission_map, VertexOut.uv * mat.emission_map_uv_scale).rgb;
|
||||
result.roughness = max(0.01, textureSize(materials[materialIdx].roughness_map, 0) == ivec2(0) ? materials[materialIdx].roughness : texture(materials[materialIdx].roughness_map, VertexOut.uv * materials[materialIdx].roughness_map_uv_scale).g);
|
||||
result.emission = textureSize(materials[materialIdx].emission_map, 0) == ivec2(0) ? materials[materialIdx].emission : texture(materials[materialIdx].emission_map, VertexOut.uv * materials[materialIdx].emission_map_uv_scale).rgb;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -223,21 +223,7 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) {
|
||||
// 0 - means directional, 1 - means point light
|
||||
bool point = subgroupAll(lights[light_idx].vPos.w == 1);
|
||||
vec3 lightI = lights[light_idx].color.rgb;
|
||||
float lightRadius = max(lights[light_idx].color.a, eps);
|
||||
vec3 L = mix(-lights[light_idx].vPos.xyz, lights[light_idx].vPos.xyz - P, lights[light_idx].vPos.w);
|
||||
float dist = max(length(L), eps);
|
||||
L /= dist;
|
||||
|
||||
// TODO: I think this is uniform control flow
|
||||
// so makes sense to use `if` there for directional/point
|
||||
// and don't calculate attenuation for directional at all
|
||||
float att = lightAttenuation(
|
||||
point,
|
||||
dist,
|
||||
lightRadius
|
||||
);
|
||||
lightI *= att;
|
||||
|
||||
vec3 V = normalize(-P);
|
||||
vec3 H = normalize(V + L);
|
||||
|
||||
@ -255,6 +241,16 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) {
|
||||
float shadow_mult = 1;
|
||||
vec4 shadow_offset = vec4(VertexOut.wNormal * normal_offset_scale, 0);
|
||||
if (point) {
|
||||
float dist = max(length(L), eps);
|
||||
L /= dist;
|
||||
float lightRadius = max(lights[light_idx].color.a, eps);
|
||||
float att = lightAttenuation(
|
||||
point,
|
||||
dist,
|
||||
lightRadius
|
||||
);
|
||||
lightI *= att;
|
||||
|
||||
vec2 shadow_map_texel_size = 1.0 / vec2(textureSize(cube_shadow_maps, 0));
|
||||
shadow_offset *= shadow_map_texel_size.x;
|
||||
vec3 shadow_dir = (lights[light_idx].view_mat * vec4(VertexOut.wPos, 1.0)).xyz;
|
||||
@ -295,7 +291,7 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) {
|
||||
texcoord.z = shadow_map_idx + csm_split_idx;
|
||||
|
||||
float sum = 0;
|
||||
sum = texture(shadow_maps, vec4(texcoord.xy, texcoord.zw));
|
||||
sum = 1.0; // texture(shadow_maps, vec4(texcoord.xy, texcoord.zw));
|
||||
// for (float y = -0.5; y <= 0.5; y += 1) {
|
||||
// for (float x = -0.5; x <= 0.5; x += 1) {
|
||||
// sum += ;
|
||||
@ -308,14 +304,19 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) {
|
||||
|
||||
vec3 specBrdf = geomSmith(mat, NDotL) * geomSmith(mat, NDotV) * 0.25 * ggxDistribution(mat, NDotH) * schlickFresnel(mat, LDotH);
|
||||
|
||||
return (PI * specBrdf + diffuseBrdf) * NDotL * shadow_mult * lightI + mat.emission;
|
||||
vec3 vecTerm = PI * specBrdf + diffuseBrdf;
|
||||
float scalarTerm = NDotL * shadow_mult;
|
||||
|
||||
return scalarTerm * lightI + mat.emission;
|
||||
}
|
||||
|
||||
void main() {
|
||||
Material mat = materials[DrawID];
|
||||
int materialIdx = draw_data[DrawID].materialIdx;
|
||||
sampler2D normal_map = materials[materialIdx].normal_map;
|
||||
vec2 normal_map_uv_scale = materials[materialIdx].normal_map_uv_scale;
|
||||
EvalMaterial material = evalMaterial();
|
||||
|
||||
vec3 N = textureSize(mat.normal_map, 0) == ivec2(0) ? vec3(0.5) : vec3(texture(mat.normal_map, VertexOut.uv * mat.normal_map_uv_scale).xy, 0);
|
||||
vec3 N = textureSize(normal_map, 0) == ivec2(0) ? vec3(0.5) : vec3(texture(normal_map, VertexOut.uv * normal_map_uv_scale).xy, 0);
|
||||
N = N * 2.0 - 1.0;
|
||||
N.z = sqrt(clamp(1 - N.x * N.x - N.y * N.y, 0, 1));
|
||||
N = normalize(N);
|
||||
@ -324,8 +325,8 @@ void main() {
|
||||
vec3 finalColor = vec3(0);
|
||||
|
||||
// int n_lights = clamp(int(lights_count), 0, MAX_POINT_LIGHTS);
|
||||
for (int i = 0; i < lights_count; i++) {
|
||||
// if (i >= lights_count) break;
|
||||
for (int i = 0; i < MAX_POINT_LIGHTS; i++) {
|
||||
if (i >= lights_count) break;
|
||||
finalColor += microfacetModel(material, i, VertexOut.vPos, N);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
struct DrawCmdData {
|
||||
mat4 transform;
|
||||
int materialIdx;
|
||||
};
|
||||
|
||||
// UBOs
|
||||
@ -10,7 +11,6 @@ layout(std140, binding = 0) uniform Matrices {
|
||||
};
|
||||
|
||||
layout(std430, binding = 3) readonly buffer DrawCmdDatas {
|
||||
uint draws_count;
|
||||
// Access by gl_DrawID
|
||||
DrawCmdData draw_data[];
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ const a = @import("asset_manifest");
|
||||
const globals = @import("globals.zig");
|
||||
pub const Material = @import("formats.zig").Material;
|
||||
const math = @import("math.zig");
|
||||
const formats = @import("formats.zig");
|
||||
|
||||
const za = @import("zalgebra");
|
||||
const Vec2 = za.Vec2;
|
||||
@ -17,7 +18,7 @@ const Vec2_i32 = za.Vec2_i32;
|
||||
|
||||
pub const MAX_FRAMES_QUEUED = 3;
|
||||
pub const MAX_LIGHTS = 8;
|
||||
pub const MAX_DRAW_COMMANDS = 4096;
|
||||
pub const MAX_DRAW_COMMANDS = 1024 * 16;
|
||||
pub const MAX_LIGHT_COMMANDS = 2048;
|
||||
pub const MAX_MATERIALS = MAX_DRAW_COMMANDS;
|
||||
pub const CSM_SPLITS = 4;
|
||||
@ -722,20 +723,25 @@ pub fn finish(self: *Render) void {
|
||||
const switched_to_alpha_blend = false;
|
||||
gl.disable(gl.BLEND);
|
||||
|
||||
const draw_indirect_cmds_c: [*]u8 = @ptrCast(gl.mapNamedBuffer(
|
||||
self.draw_indirect_buffer,
|
||||
gl.WRITE_ONLY,
|
||||
) orelse {
|
||||
var draw_indirect_cmds = self.frame_arena.alloc(DrawIndirectCmd, MAX_DRAW_COMMANDS) catch @panic("OOM");
|
||||
var draw_cmd_data = self.frame_arena.alloc(DrawCommandData, MAX_DRAW_COMMANDS) catch @panic("OOM");
|
||||
|
||||
var draw_indirect_buf: gl.GLuint = 0;
|
||||
gl.createBuffers(1, &draw_indirect_buf);
|
||||
checkGLError();
|
||||
@panic("map draw indirect buffer");
|
||||
});
|
||||
var draw_indirect_cmds = std.mem.bytesAsSlice(DrawIndirectCmd, draw_indirect_cmds_c[0 .. @sizeOf(DrawIndirectCmd) * MAX_DRAW_COMMANDS]);
|
||||
defer gl.deleteBuffers(1, &draw_indirect_buf);
|
||||
|
||||
var draw_cmd_data_buf: gl.GLuint = 0;
|
||||
gl.createBuffers(1, &draw_cmd_data_buf);
|
||||
checkGLError();
|
||||
defer gl.deleteBuffers(1, &draw_cmd_data_buf);
|
||||
|
||||
const materials = self.materials_pbr_ssbo.getInstance(self.tripple_buffer_index);
|
||||
materials.count.* = 0;
|
||||
|
||||
const draw_cmd_data = self.draw_cmd_data_ssbo.getInstance(self.tripple_buffer_index);
|
||||
var material_map = std.StringHashMap(i32).init(self.frame_arena);
|
||||
|
||||
var materials_count: usize = 0;
|
||||
var rendered_count: usize = 0;
|
||||
cmds: for (self.command_buffer[0..self.command_count]) |*cmd| {
|
||||
const mesh = self.assetman.resolveMesh(cmd.mesh);
|
||||
@ -755,12 +761,20 @@ pub fn finish(self: *Render) void {
|
||||
// gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
draw_cmd_data.data[rendered_count] = DrawCommandData{
|
||||
.transform = cmd.transform,
|
||||
};
|
||||
const material_bytes = std.mem.asBytes(&material);
|
||||
const material_copy = self.frame_arena.alloc(u8, material_bytes.len) catch @panic("OOM");
|
||||
@memcpy(material_copy, material_bytes);
|
||||
const gop = material_map.getOrPut(material_copy) catch @panic("OOM");
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = @intCast(materials_count);
|
||||
materials.data[materials_count] = MaterialPBR.fromMaterial(self.assetman, &material);
|
||||
materials_count += 1;
|
||||
}
|
||||
|
||||
materials.data[rendered_count] = MaterialPBR.fromMaterial(self.assetman, &material);
|
||||
materials.count.* += 1;
|
||||
draw_cmd_data[rendered_count] = DrawCommandData{
|
||||
.transform = cmd.transform,
|
||||
.material_index = gop.value_ptr.*,
|
||||
};
|
||||
|
||||
draw_indirect_cmds[rendered_count] = DrawIndirectCmd{
|
||||
.count = mesh.indices.count,
|
||||
@ -774,10 +788,6 @@ pub fn finish(self: *Render) void {
|
||||
rendered_count += 1;
|
||||
}
|
||||
|
||||
_ = gl.unmapNamedBuffer(self.draw_indirect_buffer);
|
||||
|
||||
gl.bindBuffer(gl.DRAW_INDIRECT_BUFFER, self.draw_indirect_buffer);
|
||||
|
||||
{
|
||||
const camera_matrix: *CameraMatrices = @alignCast(@ptrCast(self.camera_matrices[self.tripple_buffer_index * self.uboAlignedSizeOf(CameraMatrices) ..].ptr));
|
||||
camera_matrix.* = .{
|
||||
@ -796,15 +806,21 @@ pub fn finish(self: *Render) void {
|
||||
checkGLError();
|
||||
}
|
||||
|
||||
gl.namedBufferStorage(draw_indirect_buf, @intCast(@sizeOf(DrawIndirectCmd) * rendered_count), draw_indirect_cmds.ptr, 0);
|
||||
gl.bindBuffer(gl.DRAW_INDIRECT_BUFFER, draw_indirect_buf);
|
||||
|
||||
gl.namedBufferStorage(draw_cmd_data_buf, @intCast(@sizeOf(DrawCommandData) * rendered_count), draw_cmd_data.ptr, 0);
|
||||
gl.bindBufferBase(gl.SHADER_STORAGE_BUFFER, SSBO.DrawCommandData.value(), draw_cmd_data_buf);
|
||||
|
||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.z_prepass).program);
|
||||
gl.bindVertexArray(self.shadow_vao);
|
||||
|
||||
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value());
|
||||
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value(), 0);
|
||||
checkGLError();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
|
||||
checkGLError();
|
||||
|
||||
gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, null, @intCast(rendered_count), @sizeOf(DrawIndirectCmd));
|
||||
// gl.multiDrawElementsIndirect(gl.TRIANGLES, gl.UNSIGNED_INT, null, @intCast(rendered_count), @sizeOf(DrawIndirectCmd));
|
||||
|
||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.mesh).program);
|
||||
gl.bindVertexArray(self.mesh_vao);
|
||||
@ -1314,13 +1330,13 @@ pub fn BufferSSBOAlign(comptime T: type, comptime alignment: usize) type {
|
||||
_start: [0]T align(alignment),
|
||||
|
||||
pub fn calculateBufSize(max_count: usize, ssbo_align: usize) usize {
|
||||
return std.mem.alignForward(usize, @sizeOf(BufferInstance) + @sizeOf(T) * max_count, ssbo_align);
|
||||
return std.mem.alignForward(usize, @sizeOf(BufferLayout) + std.mem.alignForward(usize, @sizeOf(T), alignment) * max_count, ssbo_align);
|
||||
}
|
||||
|
||||
pub fn getData(self: *BufferLayout, len: usize) []T {
|
||||
var data_c: [*]T = @ptrFromInt(@intFromPtr(self) + @offsetOf(BufferLayout, "_start"));
|
||||
pub fn getData(self: *BufferLayout, len: usize) ([]align(alignment) T) {
|
||||
var data_c: [*]align(alignment) T = @ptrFromInt(@intFromPtr(self) + @offsetOf(BufferLayout, "_start"));
|
||||
|
||||
return data_c[0..len];
|
||||
return @alignCast(data_c[0..len]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1408,9 +1424,11 @@ const MaterialPBRSSBO = BufferSSBO(MaterialPBR);
|
||||
|
||||
const DrawCommandData = extern struct {
|
||||
transform: Mat4,
|
||||
material_index: c_int,
|
||||
_pad: [0]void align(16) = std.mem.zeroes([0]void),
|
||||
};
|
||||
|
||||
const DrawCommandDataSSBO = BufferSSBOAlign(DrawCommandData, 16);
|
||||
const DrawCommandDataSSBO = BufferSSBO(DrawCommandData);
|
||||
|
||||
const DrawIndirectCmd = extern struct {
|
||||
count: gl.GLuint,
|
||||
|
60
src/game.zig
60
src/game.zig
@ -193,35 +193,35 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
||||
.rotate = .{ .axis = Vec3.up(), .rate = -10 },
|
||||
});
|
||||
|
||||
const light_root = globals.g_mem.world.addEntity(.{
|
||||
.flags = .{ .rotate = true },
|
||||
.transform = .{ .pos = Vec3.new(0, 0.1, 0) },
|
||||
.rotate = .{ .axis = Vec3.up(), .rate = 60 },
|
||||
});
|
||||
// const light_root = globals.g_mem.world.addEntity(.{
|
||||
// .flags = .{ .rotate = true },
|
||||
// .transform = .{ .pos = Vec3.new(0, 0.1, 0) },
|
||||
// .rotate = .{ .axis = Vec3.up(), .rate = 60 },
|
||||
// });
|
||||
|
||||
const light1 = globals.g_mem.world.addEntity(.{
|
||||
.transform = .{ .pos = Vec3.new(1.8, 1, 0) },
|
||||
.flags = .{ .point_light = true, .rotate = true },
|
||||
.light = .{ .color_intensity = Vec4.new(1.0, 0.3, 0.1, 100.0) },
|
||||
.point_light = .{ .radius = 0.1 },
|
||||
.rotate = .{ .axis = Vec3.up(), .rate = -40 },
|
||||
});
|
||||
light1.ptr.setParent(light_root.handle);
|
||||
// const light1 = globals.g_mem.world.addEntity(.{
|
||||
// .transform = .{ .pos = Vec3.new(1.8, 1, 0) },
|
||||
// .flags = .{ .point_light = true, .rotate = true },
|
||||
// .light = .{ .color_intensity = Vec4.new(1.0, 0.3, 0.1, 100.0) },
|
||||
// .point_light = .{ .radius = 0.1 },
|
||||
// .rotate = .{ .axis = Vec3.up(), .rate = -40 },
|
||||
// });
|
||||
// light1.ptr.setParent(light_root.handle);
|
||||
|
||||
const light2 = globals.g_mem.world.addEntity(.{
|
||||
.transform = .{ .pos = Vec3.new(-2, 0, 0) },
|
||||
.flags = .{ .point_light = true, .rotate = true },
|
||||
.light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0) },
|
||||
.point_light = .{ .radius = 0.1 },
|
||||
});
|
||||
light2.ptr.setParent(light1.handle);
|
||||
// const light2 = globals.g_mem.world.addEntity(.{
|
||||
// .transform = .{ .pos = Vec3.new(-2, 0, 0) },
|
||||
// .flags = .{ .point_light = true, .rotate = true },
|
||||
// .light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 100.0) },
|
||||
// .point_light = .{ .radius = 0.1 },
|
||||
// });
|
||||
// light2.ptr.setParent(light1.handle);
|
||||
|
||||
_ = globals.g_mem.world.addEntity(.{
|
||||
.transform = .{ .pos = Vec3.new(1, 0.5, 4) },
|
||||
.flags = .{ .point_light = true },
|
||||
.light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 10.0) },
|
||||
.point_light = .{ .radius = 1 },
|
||||
});
|
||||
// _ = globals.g_mem.world.addEntity(.{
|
||||
// .transform = .{ .pos = Vec3.new(1, 0.5, 4) },
|
||||
// .flags = .{ .point_light = true },
|
||||
// .light = .{ .color_intensity = Vec4.new(0.2, 0.5, 1.0, 10.0) },
|
||||
// .point_light = .{ .radius = 1 },
|
||||
// });
|
||||
|
||||
// Plane
|
||||
_ = globals.g_mem.world.addEntity(.{
|
||||
@ -239,9 +239,10 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
||||
|
||||
// 10 dielectric bunnies
|
||||
{
|
||||
for (0..10) |i| {
|
||||
for (0..100) |y| {
|
||||
for (0..10) |x| {
|
||||
_ = globals.g_mem.world.addEntity(.{
|
||||
.transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(i)) * 0.3 - 0.3 * 4.5, 0, 0) },
|
||||
.transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(x)) * 0.3 - 0.3 * 4.5, 0, @as(f32, @floatFromInt(y)) * 0.3 - 0.3 * 4.5) },
|
||||
|
||||
.flags = .{ .mesh = true },
|
||||
.mesh = .{
|
||||
@ -249,13 +250,14 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
|
||||
.material = .{
|
||||
.albedo_map = a.Textures.bunny_tex1,
|
||||
// .normal_map = a.Textures.@"tile.norm",
|
||||
.roughness = @as(f32, @floatFromInt(i)) / 10.0,
|
||||
.roughness = @as(f32, @floatFromInt(y)) / 100.0,
|
||||
},
|
||||
.override_material = true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 10 metallic bunnies
|
||||
{
|
||||
for (0..10) |i| {
|
||||
|
117
src/math.zig
117
src/math.zig
@ -86,14 +86,14 @@ pub const AABB = struct {
|
||||
|
||||
// TODO: optimize
|
||||
pub fn transform(self: *const AABB, matrix: Mat4) AABB {
|
||||
var min = Vec3.zero();
|
||||
var max = Vec3.zero();
|
||||
var min = Vec3.new(std.math.floatMax(f32), std.math.floatMax(f32), std.math.floatMax(f32));
|
||||
var max = Vec3.new(std.math.floatMin(f32), std.math.floatMin(f32), std.math.floatMin(f32));
|
||||
|
||||
inline for (box_corners) |corner| {
|
||||
const corner_pos = matrix.mulByVec4(self.origin.add(self.extents.mul(corner)).toVec4(1));
|
||||
const corner_pos3 = corner_pos.scale(1 / corner_pos.w()).toVec3();
|
||||
min = Vec3.new(@min(corner_pos3.x(), min.x()), @min(corner_pos3.y(), min.y()), @min(corner_pos3.z(), min.z()));
|
||||
max = Vec3.new(@max(corner_pos3.x(), max.x()), @max(corner_pos3.y(), max.y()), @max(corner_pos3.z(), max.z()));
|
||||
const corner_pos3 = corner_pos.toVec3();
|
||||
min = corner_pos3.min(min);
|
||||
max = corner_pos3.max(max);
|
||||
}
|
||||
|
||||
return AABB.fromMinMax(min, max);
|
||||
@ -118,12 +118,22 @@ pub const BoundingSphere = struct {
|
||||
|
||||
pub const Frustum = struct {
|
||||
// Plane normals
|
||||
top: Plane = .{},
|
||||
right: Plane = .{},
|
||||
bottom: Plane = .{},
|
||||
left: Plane = .{},
|
||||
near: Plane = .{},
|
||||
far: Plane = .{},
|
||||
// top
|
||||
// right
|
||||
// bottom
|
||||
// left
|
||||
// near
|
||||
// far
|
||||
planes: [6]Plane = std.mem.zeroes([6]Plane),
|
||||
|
||||
pub const PlaneSide = enum {
|
||||
Top,
|
||||
Right,
|
||||
Bottom,
|
||||
Left,
|
||||
Near,
|
||||
Far,
|
||||
};
|
||||
|
||||
/// Extracts frustum planes from matrices using Gribb-Hartmann method
|
||||
/// If you pass in a projection matrix planes will be in view space.
|
||||
@ -143,31 +153,36 @@ pub const Frustum = struct {
|
||||
const far = row4.sub(row3);
|
||||
|
||||
return .{
|
||||
.top = Plane.new(top),
|
||||
.right = Plane.new(right),
|
||||
.bottom = Plane.new(bottom),
|
||||
.left = Plane.new(left),
|
||||
.near = Plane.new(near),
|
||||
.far = Plane.new(far),
|
||||
.planes = .{
|
||||
Plane.new(top),
|
||||
Plane.new(right),
|
||||
Plane.new(bottom),
|
||||
Plane.new(left),
|
||||
Plane.new(near),
|
||||
Plane.new(far),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getPlane(self: *const Frustum, side: PlaneSide) *const Plane {
|
||||
return &self.planes[@intFromEnum(side)];
|
||||
}
|
||||
|
||||
pub fn getNearDist(self: *const Frustum) f32 {
|
||||
return self.near.distance();
|
||||
return self.getPlane(.Near).distance();
|
||||
}
|
||||
|
||||
pub fn rangeZ(self: *const Frustum) f32 {
|
||||
return self.far.point().sub(self.near.point()).dot(self.near.normal());
|
||||
return self.getPLane(.Far).point().sub(self.getPlane(.Near).point()).dot(self.getPlane(.Near).normal());
|
||||
}
|
||||
|
||||
pub fn transform(self: *const Frustum, matrix: *const Mat4) Frustum {
|
||||
const new_planes = self.planes;
|
||||
for (new_planes) |*plane| {
|
||||
plane.* = plane.transform(matrix);
|
||||
}
|
||||
return Frustum{
|
||||
.top = self.top.transform(matrix),
|
||||
.right = self.right.transform(matrix),
|
||||
.bottom = self.bottom.transform(matrix),
|
||||
.left = self.left.transform(matrix),
|
||||
.near = self.near.transform(matrix),
|
||||
.far = self.far.transform(matrix),
|
||||
.planes = new_planes,
|
||||
};
|
||||
}
|
||||
|
||||
@ -176,26 +191,44 @@ pub const Frustum = struct {
|
||||
}
|
||||
|
||||
fn intersectAABBInternal(self: *const Frustum, aabb: AABB, comptime skip_near: bool) bool {
|
||||
var outside_top_plane = true;
|
||||
var outside_bottom_plane = true;
|
||||
var outside_left_plane = true;
|
||||
var outside_right_plane = true;
|
||||
var outside_near_plane = !skip_near;
|
||||
var outside_far_plane = true;
|
||||
inline for (box_corners) |corner| {
|
||||
const p = aabb.origin.add(aabb.extents.mul(corner));
|
||||
for (0..6) |i| {
|
||||
if (skip_near and i == @intFromEnum(PlaneSide.Near)) continue;
|
||||
|
||||
outside_top_plane = outside_top_plane and self.top.isUnder(p);
|
||||
outside_bottom_plane = outside_bottom_plane and self.bottom.isUnder(p);
|
||||
outside_left_plane = outside_left_plane and self.left.isUnder(p);
|
||||
outside_right_plane = outside_right_plane and self.right.isUnder(p);
|
||||
if (!skip_near) {
|
||||
outside_near_plane = outside_near_plane and self.near.isUnder(p);
|
||||
}
|
||||
outside_far_plane = outside_far_plane and self.far.isUnder(p);
|
||||
const plane = self.planes[i];
|
||||
const nx = plane.normal().x() > 0;
|
||||
const ny = plane.normal().y() > 0;
|
||||
const nz = plane.normal().z() > 0;
|
||||
|
||||
const min = aabb.origin.sub(aabb.extents);
|
||||
const max = aabb.origin.add(aabb.extents);
|
||||
|
||||
// TODO: vectorize
|
||||
const dot = (plane.normal().x() * if (nx) max.x() else min.x()) + (plane.normal().y() * if (ny) max.y() else min.y()) + (plane.normal().z() * if (nz) max.z() else min.z());
|
||||
|
||||
if (dot < plane.distance()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(outside_left_plane or outside_right_plane or outside_bottom_plane or outside_top_plane or outside_near_plane or outside_far_plane);
|
||||
// const dot2 = (plane.normal().x() * if (nx) min.x else max.x) + (plane.normal().y() * if (ny) min.y else max.y) + (plane.normal().z() * if (nz) min.z else max.z);
|
||||
// planes have unit-length normal, offset = -dot(normal, point on plane)
|
||||
// const Plane3& plane = planes[i];
|
||||
// Index nx = plane.normal.x > Real(0);
|
||||
// Index ny = plane.normal.y > Real(0);
|
||||
// Index nz = plane.normal.z > Real(0);
|
||||
//
|
||||
// // getMinMax(): 0 = return min coordinate. 1 = return max.
|
||||
// Real dot = (plane.normal.x*box.getMinMax(nx).x) + (plane.normal.y*box.getMinMax(ny).y) + (plane.normal.z*box.getMinMax(nz).z);
|
||||
//
|
||||
// if ( dot < -plane.offset )
|
||||
// return OUTSIDE;
|
||||
//
|
||||
// Real dot2 = (plane.normal.x*box.getMinMax(1-nx).x) + (plane.normal.y*box.getMinMax(1-ny).y) + (plane.normal.z*box.getMinMax(1-nz).z);
|
||||
//
|
||||
// if ( dot2 <= -plane.offset )
|
||||
// result = INTERSECTS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn intersectAABB(self: *const Frustum, aabb: AABB) bool {
|
||||
|
Loading…
x
Reference in New Issue
Block a user