diff options
Diffstat (limited to 'src/geom.zig')
-rw-r--r-- | src/geom.zig | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/geom.zig b/src/geom.zig new file mode 100644 index 0000000..910f355 --- /dev/null +++ b/src/geom.zig @@ -0,0 +1,247 @@ +// Geometry functions +// Copyright (C) 2002 David Rosen +// Copyright (C) 2023 Nguyễn Gia Phong +// +// This file is part of Black Shades. +// +// Black Shades is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Black Shades is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Black Shades. If not, see <https://www.gnu.org/licenses/>. + +const Child = std.meta.Child; +const std = @import("std"); + +const c = @import("cimport.zig"); + +fn sqr(x: anytype) @TypeOf(x) { + return x * x; +} + +fn dot(u: anytype, v: @TypeOf(u)) Child(@TypeOf(u)) { + return @reduce(.Add, u * v); +} + +export fn sqrlen(v: XYZ) f32 { + const u = @bitCast(@Vector(3, f32), v); + return dot(u, u); +} + +fn norm(v: anytype) Child(@TypeOf(v)) { + return @sqrt(dot(v, v)); +} + +const XYZ = extern struct { x: f32, y: f32, z: f32 }; + +export fn crossProduct(u: XYZ, v: XYZ) XYZ { + return .{ + .x = u.y * v.z - u.z * v.y, + .y = u.z * v.x - u.x * v.z, + .z = u.x * v.y - u.y * v.x, + }; +} + +export fn normalize(v: XYZ) XYZ { + const u = @bitCast(@Vector(3, f32), v); + const d = norm(u); + return if (d == 0) v else @bitCast(XYZ, u / @splat(3, d)); +} + +export fn reflect(v: XYZ, n: XYZ) XYZ { + const u = @bitCast(@Vector(3, f32), v); + const m = @bitCast(@Vector(3, f32), n); + return @bitCast(XYZ, u - m * @splat(3, dot(u, m) * 2)); +} + +export fn segmentIntersectsSphere(a: XYZ, b: XYZ, j: XYZ, r: f32) bool { + // FIXME: call directly with vectors + const p = @bitCast(@Vector(3, f32), a); + const q = @bitCast(@Vector(3, f32), b); + const i = @bitCast(@Vector(3, f32), j); + + if (@reduce(.Or, @max(p, q) < i - @splat(3, r))) return false; + if (@reduce(.Or, @min(p, q) > i + @splat(3, r))) return false; + // https://en.wikipedia.org/wiki/Line–sphere_intersection + const d = q - p; // line's direction + const u = d / @splat(3, norm(d)); // unit vector + return sqr(dot(u, (p - i))) >= @reduce(.Add, sqr(p - i)) - sqr(r); +} + +export fn getFrustum(frustum: [*][4]f32) void { + var proj: [16]f32 = undefined; + c.glGetFloatv(c.GL_PROJECTION_MATRIX, &proj); + + var mv: [16]f32 = undefined; + c.glGetFloatv(c.GL_MODELVIEW_MATRIX, &mv); + + const clip = [16]f32{ + mv[0] * proj[0] + mv[1] * proj[4] + mv[2] * proj[8] + mv[3] * proj[12], + mv[0] * proj[1] + mv[1] * proj[5] + mv[2] * proj[9] + mv[3] * proj[13], + mv[0] * proj[2] + mv[1] * proj[6] + mv[2] * proj[10] + mv[3] * proj[14], + mv[0] * proj[3] + mv[1] * proj[7] + mv[2] * proj[11] + mv[3] * proj[15], + mv[4] * proj[0] + mv[5] * proj[4] + mv[6] * proj[8] + mv[7] * proj[12], + mv[4] * proj[1] + mv[5] * proj[5] + mv[6] * proj[9] + mv[7] * proj[13], + mv[4] * proj[2] + mv[5] * proj[6] + mv[6] * proj[10] + mv[7] * proj[14], + mv[4] * proj[3] + mv[5] * proj[7] + mv[6] * proj[11] + mv[7] * proj[15], + mv[8] * proj[0] + mv[9] * proj[4] + mv[10] * proj[8] + mv[11] * proj[12], + mv[8] * proj[1] + mv[9] * proj[5] + mv[10] * proj[9] + mv[11] * proj[13], + mv[8] * proj[2] + mv[9] * proj[6] + mv[10] * proj[10] + mv[11] * proj[14], + mv[8] * proj[3] + mv[9] * proj[7] + mv[10] * proj[11] + mv[11] * proj[15], + mv[12] * proj[0] + mv[13] * proj[4] + mv[14] * proj[8] + mv[15] * proj[12], + mv[12] * proj[1] + mv[13] * proj[5] + mv[14] * proj[9] + mv[15] * proj[13], + mv[12] * proj[2] + mv[13] * proj[6] + mv[14] * proj[10] + mv[15] * proj[14], + mv[12] * proj[3] + mv[13] * proj[7] + mv[14] * proj[11] + mv[15] * proj[15], + }; + + // Right plane + frustum[0][0] = clip[3] - clip[0]; + frustum[0][1] = clip[7] - clip[4]; + frustum[0][2] = clip[11] - clip[8]; + frustum[0][3] = clip[15] - clip[12]; + + // Left plane + frustum[1][0] = clip[3] + clip[0]; + frustum[1][1] = clip[7] + clip[4]; + frustum[1][2] = clip[11] + clip[8]; + frustum[1][3] = clip[15] + clip[12]; + + // Bottom plane + frustum[2][0] = clip[3] + clip[1]; + frustum[2][1] = clip[7] + clip[5]; + frustum[2][2] = clip[11] + clip[9]; + frustum[2][3] = clip[15] + clip[13]; + + // Top plane + frustum[3][0] = clip[3] - clip[1]; + frustum[3][1] = clip[7] - clip[5]; + frustum[3][2] = clip[11] - clip[9]; + frustum[3][3] = clip[15] - clip[13]; + + // Far plane + frustum[4][0] = clip[3] - clip[2]; + frustum[4][1] = clip[7] - clip[6]; + frustum[4][2] = clip[11] - clip[10]; + frustum[4][3] = clip[15] - clip[14]; + + // Near plane + frustum[5][0] = clip[3] + clip[2]; + frustum[5][1] = clip[7] + clip[6]; + frustum[5][2] = clip[11] + clip[10]; + frustum[5][3] = clip[15] + clip[14]; + + // normalize the right plane + var t: f32 = undefined; + t = @sqrt(frustum[0][0]*frustum[0][0] + + frustum[0][1]*frustum[0][1] + + frustum[0][2]*frustum[0][2]); + frustum[0][0] /= t; + frustum[0][1] /= t; + frustum[0][2] /= t; + frustum[0][3] /= t; + + // calculate left plane + frustum[1][0] = clip[3] + clip[0]; + frustum[1][1] = clip[7] + clip[4]; + frustum[1][2] = clip[11] + clip[ 8]; + frustum[1][3] = clip[15] + clip[12]; + + // normalize the left plane + t = @sqrt(frustum[1][0]*frustum[1][0] + + frustum[1][1]*frustum[1][1] + + frustum[1][2]*frustum[1][2]); + frustum[1][0] /= t; + frustum[1][1] /= t; + frustum[1][2] /= t; + frustum[1][3] /= t; + + // calculate the bottom plane + frustum[2][0] = clip[3] + clip[1]; + frustum[2][1] = clip[7] + clip[5]; + frustum[2][2] = clip[11] + clip[9]; + frustum[2][3] = clip[15] + clip[13]; + + // normalize the bottom plane + t = @sqrt(frustum[2][0]*frustum[2][0] + + frustum[2][1]*frustum[2][1] + + frustum[2][2]*frustum[2][2]); + frustum[2][0] /= t; + frustum[2][1] /= t; + frustum[2][2] /= t; + frustum[2][3] /= t; + + // calculate the top plane + frustum[3][0] = clip[3] - clip[1]; + frustum[3][1] = clip[7] - clip[5]; + frustum[3][2] = clip[11] - clip[9]; + frustum[3][3] = clip[15] - clip[13]; + + // normalize the top plane + t = @sqrt(frustum[3][0]*frustum[3][0] + + frustum[3][1]*frustum[3][1] + + frustum[3][2]*frustum[3][2]); + frustum[3][0] /= t; + frustum[3][1] /= t; + frustum[3][2] /= t; + frustum[3][3] /= t; + + // calculate the far plane + frustum[4][0] = clip[3] - clip[2]; + frustum[4][1] = clip[7] - clip[6]; + frustum[4][2] = clip[11] - clip[10]; + frustum[4][3] = clip[15] - clip[14]; + + // normalize the far plane + t = @sqrt(frustum[4][0]*frustum[4][0] + + frustum[4][1]*frustum[4][1] + + frustum[4][2]*frustum[4][2]); + frustum[4][0] /= t; + frustum[4][1] /= t; + frustum[4][2] /= t; + frustum[4][3] /= t; + + // calculate the near plane + frustum[5][0] = clip[3] + clip[2]; + frustum[5][1] = clip[7] + clip[6]; + frustum[5][2] = clip[11] + clip[10]; + frustum[5][3] = clip[15] + clip[14]; + + // normalize the near plane + t = @sqrt(frustum[5][0]*frustum[5][0] + + frustum[5][1]*frustum[5][1] + + frustum[5][2]*frustum[5][2]); + frustum[5][0] /= t; + frustum[5][1] /= t; + frustum[5][2] /= t; + frustum[5][3] /= t; +} + +export fn cubeInFrustum(frustum: [*][4]f32, + x: f32, y: f32, z: f32, size: f32) bool { + const delta = [_]f32{ -size, size }; + var i = @as(u8, 0); + loop: while (i < 6) : (i += 1) { + for (delta) |dx| for (delta) |dy| for (delta) |dz| + if (frustum[i][0] * (x + dx) + frustum[i][1] * (y + dy) + + frustum[i][2] * (z + dz) + frustum[i][3] > 0) continue :loop; + return false; + } + return true; +} + +export fn sphereInFrustum(frustum: [*][4]f32, + x: f32, y: f32, z: f32, r: f32) bool { + var i = @as(u8, 0); + while (i < 6) : (i += 1) + if (frustum[i][0] * x + frustum[i][1] * y + + frustum[i][2] * z + frustum[i][3] <= -r) + return false; + return true; +} |