diff --git a/assets/shaders/mesh.glsl b/assets/shaders/mesh.glsl index 29a4c2f..80b2d42 100644 --- a/assets/shaders/mesh.glsl +++ b/assets/shaders/mesh.glsl @@ -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); } diff --git a/assets/shaders/z_prepass.glsl b/assets/shaders/z_prepass.glsl index f1ac2b1..363923e 100644 --- a/assets/shaders/z_prepass.glsl +++ b/assets/shaders/z_prepass.glsl @@ -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[]; }; diff --git a/src/Render.zig b/src/Render.zig index 5a979a0..cc0d6b8 100644 --- a/src/Render.zig +++ b/src/Render.zig @@ -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 { - checkGLError(); - @panic("map draw indirect buffer"); - }); - var draw_indirect_cmds = std.mem.bytesAsSlice(DrawIndirectCmd, draw_indirect_cmds_c[0 .. @sizeOf(DrawIndirectCmd) * MAX_DRAW_COMMANDS]); + 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(); + 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, diff --git a/src/game.zig b/src/game.zig index 475d68a..6ee878e 100644 --- a/src/game.zig +++ b/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,21 +239,23 @@ export fn game_init(global_allocator: *std.mem.Allocator) void { // 10 dielectric bunnies { - for (0..10) |i| { - _ = globals.g_mem.world.addEntity(.{ - .transform = .{ .pos = Vec3.new(@as(f32, @floatFromInt(i)) * 0.3 - 0.3 * 4.5, 0, 0) }, + for (0..100) |y| { + for (0..10) |x| { + _ = globals.g_mem.world.addEntity(.{ + .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 = .{ - .handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1, - .material = .{ - .albedo_map = a.Textures.bunny_tex1, - // .normal_map = a.Textures.@"tile.norm", - .roughness = @as(f32, @floatFromInt(i)) / 10.0, + .flags = .{ .mesh = true }, + .mesh = .{ + .handle = a.Meshes.bunny.BunnyStanfordUVUnwrapped_res1_bun_zipper_res1, + .material = .{ + .albedo_map = a.Textures.bunny_tex1, + // .normal_map = a.Textures.@"tile.norm", + .roughness = @as(f32, @floatFromInt(y)) / 100.0, + }, + .override_material = true, }, - .override_material = true, - }, - }); + }); + } } } // 10 metallic bunnies diff --git a/src/math.zig b/src/math.zig index 4d93c02..4cf7a6a 100644 --- a/src/math.zig +++ b/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); + 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; } - outside_far_plane = outside_far_plane and self.far.isUnder(p); + + // 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 !(outside_left_plane or outside_right_plane or outside_bottom_plane or outside_top_plane or outside_near_plane or outside_far_plane); + return true; } pub fn intersectAABB(self: *const Frustum, aabb: AABB) bool {