// Decal construction and drawing // Copyright (C) 2002 David Rosen // Copyright (C) 2003 Steven Fuller // 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 . //const f32Eps = std.math.floatEps(f32); const XYZ = @import("geom.zig").XYZ; const c = @import("cimport.zig"); const size = 120; const Kind = enum(c_int) { bullet_hole, crater, blood_pool }; const Decals = extern struct { // GLuint is always 32-bit. hole_textures: [2]u32, blood_textures: [11]u32, len: u32, kind: [size]Kind, points: [size * 8]XYZ, numpoints: [size]u32, texcoordsx: [size * 8]f32, texcoordsy: [size * 8]f32, alive: [size]f32, }; export fn drawDecals(d: *const Decals) void { c.glAlphaFunc(c.GL_GREATER, 0.01); c.glDepthFunc(c.GL_LEQUAL); c.glEnable(c.GL_BLEND); c.glEnable(c.GL_CULL_FACE); c.glEnable(c.GL_TEXTURE_2D); c.glEnable(c.GL_LIGHTING); c.glDepthMask(0); c.glAlphaFunc(c.GL_GREATER, 0.01); c.glTexParameterf(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, c.GL_CLAMP); c.glTexParameterf(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, c.GL_CLAMP); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); c.glEnable(c.GL_POLYGON_OFFSET_FILL); for (0..d.len) |i| { switch (d.kind[i]) { .bullet_hole, .crater => |k| { c.glColor4f(1.0, 1.0, 1.0, 1.0 - d.alive[i] / 10.0); c.glBindTexture(c.GL_TEXTURE_2D, d.hole_textures[switch (k) { .bullet_hole => 0, .crater => 1, // https://github.com/ziglang/zig/issues/12863 else => unreachable, // TODO: remove }]); }, .blood_pool => { const alpha = if (d.alive[i] < 2.0) @mod(d.alive[i], 0.2) + 0.8 else (20.0 - d.alive[i]) / 18.0; c.glColor4f(1.0, 1.0, 1.0, alpha); const j: usize = @intFromFloat(d.alive[i] * 5.0); c.glBindTexture(c.GL_TEXTURE_2D, d.blood_textures[@min(j, 10)]); }, } c.glPushMatrix(); c.glBegin(c.GL_TRIANGLE_FAN); for (0..d.numpoints[i]) |j| { c.glTexCoord2f(d.texcoordsx[i * 8 + j], d.texcoordsy[i * 8 + j]); c.glVertex3f(d.points[i * 8 + j].x, d.points[i * 8 + j].y, d.points[i * 8 + j].z); } c.glEnd(); c.glPopMatrix(); } c.glDepthMask(1); c.glDisable(c.GL_TEXTURE_2D); c.glEnable(c.GL_CULL_FACE); c.glDisable(c.GL_POLYGON_OFFSET_FILL); c.glDepthFunc(c.GL_LEQUAL); } export fn updateDecals(d: *Decals) void { for (0..d.len) |i| { d.alive[i] += c.multiplier; if (d.alive[i] < @as(f32, switch (d.kind[i]) { .bullet_hole, .crater => 10.0, .blood_pool => 20.0, })) continue; d.len -= 1; const last = d.len; d.numpoints[i] = d.numpoints[last]; d.alive[i] = d.alive[last]; d.kind[i] = d.kind[last]; for (0..d.numpoints[i]) |j| { d.points[i * 8 + j] = d.points[last * 8 + j]; d.texcoordsx[i * 8 + j] = d.texcoordsx[last * 8 + j]; d.texcoordsy[i * 8 + j] = d.texcoordsy[last * 8 + j]; } } } export fn destroyDecals(d: *const Decals) void { c.glDeleteTextures(2, &d.hole_textures); c.glDeleteTextures(11, &d.blood_textures); }