summary refs log tree commit diff
path: root/src/decal.zig
blob: de4d9e8b9741d818a560ca5993ee9f4c44f147de (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// 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 <https://www.gnu.org/licenses/>.

//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);
}