Index texture by vertex attribute texc.z
dkl9

dkl9 commited on 2025-183 00:58:09
Showing 8 changed files, with 128 additions and 79 deletions.

... ...
@@ -0,0 +1,73 @@
1
+from libs import *
2
+import entity
3
+import types
4
+
5
+draw_modes = types.SimpleNamespace()
6
+draw_modes.TRI_STRIP = GL_TRIANGLE_STRIP
7
+draw_modes.TRI_FAN = GL_TRIANGLE_FAN
8
+draw_modes.TRIS = GL_TRIANGLES
9
+draw_modes.LINE_STRIP = GL_LINE_STRIP
10
+draw_modes.LINES = GL_LINES
11
+
12
+class AttributedEntity(entity.Entity):
13
+    def __init__(self, verts, pos, col, **kwargs):
14
+        super().__init__(verts, pos, **kwargs)
15
+        self.col = vec(col)
16
+        refl = [1.0, 0.0, 0.0, 1.0] if self.hud else [0.5, 0.5, 0.0, 32.0]
17
+        self.refls = vec(kwargs.get("refls", refl))
18
+        self.texi = kwargs.get("texi", 0)
19
+        self.draw_mode = kwargs.get("mode", GL_TRIANGLE_STRIP)
20
+        self.vao = None
21
+        self.vbos = [[None, 0] for _ in VERTEX_ATTRS]
22
+
23
+    def mat_list(self):
24
+        return np.repeat(vec([[self.col], [self.refls]]), len(self.verts), 1)
25
+
26
+    def norm_list(self):
27
+        l = []
28
+        match self.draw_mode:
29
+            case draw_modes.TRI_STRIP | draw_modes.TRI_FAN:
30
+                l.append(norm_tri(self.verts[:3]))
31
+                for i in range(1, len(self.verts) - 1):
32
+                    l.append(norm_tri(self.verts[i - 1:i + 2]))
33
+                l.append(norm_tri(self.verts[-3:]))
34
+            case draw_modes.TRIS:
35
+                for i in range(0, len(self.verts), 3):
36
+                    for _ in range(3):
37
+                        l.append(norm_tri(self.verts[i:i + 3]))
38
+            case draw_modes.LINE_STRIP | draw_modes.LINES:
39
+                for _ in range(len(self.verts)):
40
+                    l.append([1, 0, 0])
41
+            case _:
42
+                raise NotImplementedError
43
+        return (self.orient[:3, :3] @ vec(l).T).T
44
+
45
+    def texc_list(self):
46
+        if self.texi < 0:
47
+            return np.repeat(vec([[0, 0, self.texi]]), len(self.verts), 0)
48
+        match self.draw_mode:
49
+            case draw_modes.TRI_FAN:
50
+                return np.concatenate((
51
+                    vec([[0, 0, self.texi]]), np.tile(
52
+                        [[0, 1, self.texi], [1, 0, self.texi]],
53
+                        ((len(self.verts) - 1) // 2, 1)
54
+                    )
55
+                ))
56
+            case draw_modes.TRIS | draw_modes.TRI_STRIP:
57
+                return np.astype(np.tile(
58
+                    [[0, 0, self.texi], [0, 1, self.texi], [1, 0, self.texi]],
59
+                    (int(np.ceil(len(self.verts) / 3)), 1)
60
+                )[:len(self.verts)], FLOAT)
61
+            case draw_modes.LINE_STRIP | draw_modes.LINES:
62
+                return np.repeat(vec([[0, 0, 0]]), len(self.verts), 0)
63
+            case _:
64
+                raise NotImplementedError
65
+
66
+    def build_vao(self):
67
+        self.vao = glGenVertexArrays(1)
68
+        glBindVertexArray(self.vao)
69
+        for i in range(len(VERTEX_ATTRS)):
70
+            self.vbos[i][0] = glGenBuffers(1)
71
+            l = update_attr(self, i)
72
+            glEnableVertexAttribArray(i)
73
+            glVertexAttribPointer(i, l.shape[1], GL_FLOAT, GL_FALSE, 0, None)
... ...
@@ -29,11 +29,29 @@ void main() {{
29 29
     float specular = reflsF.z * lightPos.w * pow(max(dot(
30 30
         normalize(camPos - positionF), reflect(-lightDir, normalF)
31 31
     ), 0.0), reflsF.w);
32
-    gl_FragColor = vec4((ambient + diffuse + specular) * colF.xyz, colF.w);
33
-    gl_FragColor = texture2D(tex, texcF);
32
+    float j = texcF.z;
33
+    vec3 c = colF.xyz;
34
+    if (abs(j) < 1e-3)
35
+        c = texture2D(tex[0], texcF.xy).xyz;
36
+    if (abs(j - 1.0) < 1e-3)
37
+        c = texture2D(tex[1], texcF.xy).xyz;
38
+    gl_FragColor = vec4((ambient + diffuse + specular) * c, colF.w);
34 39
 }}
35 40
 """
36 41
 
42
+def load_texture(i, fname):
43
+    pli = pygame.image.load(fname).convert()
44
+    (tw, th) = pli.get_size()
45
+    img_data = np.array(list(pygame.image.tobytes(pli, "RGB")), np.uint8)
46
+    tex = glGenTextures(1)
47
+    glActiveTexture(GL_TEXTURE0 + i)
48
+    glBindTexture(GL_TEXTURE_2D, tex)
49
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
50
+    glTexImage2D(
51
+        GL_TEXTURE_2D, 0, GL_RGB, tw, th,
52
+        0, GL_RGB, GL_UNSIGNED_BYTE, img_data
53
+    )
54
+
37 55
 def init_display():
38 56
     pygame.init()
39 57
     full = os.environ.get("FULLSCREEN")
... ...
@@ -47,16 +65,8 @@ def init_display():
47 65
         pygame.QUIT, pygame.WINDOWRESIZED, pygame.WINDOWSIZECHANGED
48 66
     ])
49 67
     pygame.display.set_caption("Voxels")
50
-    pli = pygame.image.load("favicon.png").convert()
51
-    (tw, th) = pli.get_size()
52
-    img_data = np.array(list(pygame.image.tobytes(pli, "RGB")), np.uint8)
53
-    tex = glGenTextures(1)
54
-    glBindTexture(GL_TEXTURE_2D, tex)
55
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
56
-    glTexImage2D(
57
-        GL_TEXTURE_2D, 0, GL_RGB, tw, th,
58
-        0, GL_RGB, GL_UNSIGNED_BYTE, img_data
59
-    )
68
+    load_texture(0, "favicon.png")
69
+    load_texture(1, "container.jpg")
60 70
     sp = shaders.compileProgram(
61 71
         shaders.compileShader(VS, GL_VERTEX_SHADER),
62 72
         shaders.compileShader(FS, GL_FRAGMENT_SHADER)
... ...
@@ -64,7 +74,7 @@ def init_display():
64 74
     for (k, v) in UNIFORMS.items():
65 75
         v[2] = glGetUniformLocation(sp, k)
66 76
     glUseProgram(sp)
67
-    glUniform1i(UNIFORMS["tex"][2], 0)
77
+    glUniform1iv(UNIFORMS["tex"][2], 2, np.array([0, 1]))
68 78
     glEnable(GL_DEPTH_TEST)
69 79
     glEnable(GL_BLEND)
70 80
     glEnable(GL_CULL_FACE)
... ...
@@ -1,58 +1,13 @@
1 1
 from libs import *
2 2
 from raycast import intersect_tri
3
-import types
4
-
5
-draw_modes = types.SimpleNamespace()
6
-draw_modes.TRI_STRIP = GL_TRIANGLE_STRIP
7
-draw_modes.TRI_FAN = GL_TRIANGLE_FAN
8
-draw_modes.TRIS = GL_TRIANGLES
9
-draw_modes.LINE_STRIP = GL_LINE_STRIP
10
-draw_modes.LINES = GL_LINES
11 3
 
12 4
 class Entity:
13
-    def __init__(self, verts, col, pos, **kwargs):
5
+    def __init__(self, verts, pos, **kwargs):
14 6
         self.verts = vec(verts)
15 7
         self.hud = kwargs.get("hud", False)
16
-        self.col = vec(col)
17
-        refl = [1.0, 0.0, 0.0, 1.0] if self.hud else [0.5, 0.5, 0.0, 32.0]
18
-        self.refls = vec(kwargs.get("refls", refl))
19 8
         self.pos = vec(pos)
20
-        self.draw_mode = kwargs.get("mode", GL_TRIANGLE_STRIP)
21 9
         self.orient = np.identity(4, dtype=FLOAT)
22 10
         self.onclick = lambda s, b, n, h: print(f"{h}:{b} on {s}:{n}")
23
-        self.vao = None
24
-        self.vbos = [[None, 0] for _ in VERTEX_ATTRS]
25
-
26
-    def mat_list(self):
27
-        return np.repeat(vec([[self.col], [self.refls]]), len(self.verts), 1)
28
-
29
-    def norm_list(self):
30
-        l = []
31
-        match self.draw_mode:
32
-            case draw_modes.TRI_STRIP | draw_modes.TRI_FAN:
33
-                l.append(norm_tri(self.verts[:3]))
34
-                for i in range(1, len(self.verts) - 1):
35
-                    l.append(norm_tri(self.verts[i - 1:i + 2]))
36
-                l.append(norm_tri(self.verts[-3:]))
37
-            case draw_modes.TRIS:
38
-                for i in range(0, len(self.verts), 3):
39
-                    for _ in range(3):
40
-                        l.append(norm_tri(self.verts[i:i + 3]))
41
-            case draw_modes.LINE_STRIP | draw_modes.LINES:
42
-                for _ in range(len(self.verts)):
43
-                    l.append([1, 0, 0])
44
-            case _:
45
-                raise NotImplementedError
46
-        return (self.orient[:3, :3] @ vec(l).T).T
47
-
48
-    def build_vao(self):
49
-        self.vao = glGenVertexArrays(1)
50
-        glBindVertexArray(self.vao)
51
-        for i in range(len(VERTEX_ATTRS)):
52
-            self.vbos[i][0] = glGenBuffers(1)
53
-            l = update_attr(self, i)
54
-            glEnableVertexAttribArray(i)
55
-            glVertexAttribPointer(i, l.shape[1], GL_FLOAT, GL_FALSE, 0, None)
56 11
 
57 12
     def translate(self):
58 13
         m = np.identity(4, dtype=FLOAT)
... ...
@@ -1,21 +1,21 @@
1 1
 from libs import *
2
-import entity
2
+import attributes
3 3
 import mesh
4 4
 
5 5
 def crosshair():
6
-    return entity.Entity(
6
+    return attributes.AttributedEntity(
7 7
         [[-0.03, 0, 0], [0.03, 0, 0], [0, -0.03, 0], [0, 0.03, 0]],
8
-        [1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0], mode=GL_LINES, hud=True
8
+        [0, 0, 0], [1, 1, 1, 1], mode=GL_LINES, hud=True
9 9
     )
10 10
 
11 11
 def fps():
12
-    return entity.Entity(
12
+    return attributes.AttributedEntity(
13 13
         [[0, 0, 0], [0, -0.1, 0], [0.1, 0, 0], [0.1, -0.1, 0]],
14
-        YELLOW, [-1.0, 1.0, 0.0], mode=GL_TRIANGLE_STRIP, hud=True
14
+        [-1.0, 1.0, 0.0], YELLOW, texi=-1, mode=GL_TRIANGLE_STRIP, hud=True
15 15
     )
16 16
 
17 17
 def hand():
18
-    h = mesh.SquareMesh(hud=True)
18
+    h = mesh.SquareMesh(hud=True, texi=1)
19 19
     h.add_sq(1, 1, [0.5, 0, 0], 1)
20 20
     h.add_sq(2, 1, [0, 0.5, 0], 1)
21 21
     h.add_sq(3, 1, [0, 0, 0.5], 1)
... ...
@@ -33,7 +33,7 @@ def selection():
33 33
     return s
34 34
 
35 35
 def skybox():
36
-    sb = mesh.SquareMesh()
36
+    sb = mesh.SquareMesh(texi=-1)
37 37
     offset = [0, 0, VIEW_DIST]
38 38
     for d in range(3):
39 39
         roll_vec3(offset, 1)
... ...
@@ -47,9 +47,9 @@ def skybox():
47 47
     return sb
48 48
 
49 49
 def sun():
50
-    s = entity.Entity(
51
-        [[0.0, 0.0, 0.0]], [1.0, 1.0, 0.9, 1.0],
52
-        normalise(LIGHT_POS) * VIEW_DIST, mode=GL_TRIANGLE_FAN
50
+    s = attributes.AttributedEntity(
51
+        [[0.0, 0.0, 0.0]], normalise(LIGHT_POS) * VIEW_DIST,
52
+        [1.0, 1.0, 0.9, 1.0], texi=-1, mode=GL_TRIANGLE_FAN
53 53
     )
54 54
     cv = []
55 55
     for k in np.arange(0, 1.01, 0.05):
... ...
@@ -6,13 +6,13 @@ import pygame
6 6
 FLOAT = np.float32
7 7
 VOXEL_TYPE = np.uint8
8 8
 VERTEX_ATTRS = [
9
-    "vec3 position", "vec4 col", "vec3 normal", "vec4 refls", "vec2 texc"
9
+    "vec3 position", "vec4 col", "vec3 normal", "vec4 refls", "vec3 texc"
10 10
 ]
11 11
 UNIFORMS = {
12 12
     "MVP": ["vert", "mat4", None],
13 13
     "lightPos": ["frag", "vec4", None],
14 14
     "camPos": ["frag", "vec3", None],
15
-    "tex": ["frag", "sampler2D", None]
15
+    "tex": ["frag", "sampler2D[2]", None]
16 16
 }
17 17
 
18 18
 LIGHT_POS = [5, 30, 10]
... ...
@@ -78,10 +78,9 @@ def update_attr(ent, n, l=None):
78 78
             case "refls":
79 79
                 l = ent.mat_list()[1]
80 80
             case "texc":
81
-                l = np.astype(np.tile(
82
-                    [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]],
83
-                    (int(np.ceil(len(ent.verts) / 3)), 1)
84
-                )[:len(ent.verts)], FLOAT)
81
+                l = ent.texc_list()
82
+            case "texi":
83
+                l = vec(np.repeat(ent.texi, len(ent.verts)))
85 84
     if ent.vao:
86 85
         glBindBuffer(GL_ARRAY_BUFFER, ent.vbos[i][0])
87 86
         if l.shape[0] != ent.vbos[i][1]:
... ...
@@ -1,7 +1,7 @@
1 1
 from libs import *
2
-import entity
2
+import attributes
3 3
 
4
-class SquareMesh(entity.Entity):
4
+class SquareMesh(attributes.AttributedEntity):
5 5
     def __init__(self, **kwargs):
6 6
         super().__init__(
7 7
             np.empty((0, 3), dtype=FLOAT),
... ...
@@ -83,3 +83,15 @@ class SquareMesh(entity.Entity):
83 83
                 i = 6 * s + t
84 84
                 cl[0][i], cl[1][i] = self.cube_mat(fd, axis, c)
85 85
         return cl
86
+
87
+    def texc_list(self):
88
+        if self.texi < 0:
89
+            return np.repeat(vec([[0, 0, self.texi]]), len(self.verts), 0)
90
+        sq = [(0, 0), (0, 1), (1, 0), (1, 1), (1, 0), (0, 1)]
91
+        l = []
92
+        hg = hasattr(self, "grid")
93
+        for i in range(len(self.faces) // 2):
94
+            for (x, y) in sq:
95
+                fd = self.faces[2 * i][1]
96
+                l.append([x, y, self.grid[*fd] if hg else fd])
97
+        return vec(l)
... ...
@@ -32,7 +32,7 @@ class Player(interactive.InteractiveCamera):
32 32
         self.hand = int(new_hand)
33 33
         hg = self.hud["hand"]
34 34
         hg.faces = [(a, self.hand) for (a, _) in hg.faces]
35
-        for i in range(1, 4, 2):
35
+        for i in [1, 3, 4]:
36 36
             update_attr(hg, i)
37 37
 
38 38
     def handle_event(self, ev, mg, scene):
... ...
@@ -21,7 +21,7 @@ class WorldRegion(mesh.SquareMesh):
21 21
             for d in range(3):
22 22
                 for s in range(2):
23 23
                     oob = pos[d] == self.r - 1 and s == 0
24
-                    oob |= s == 0 or pos[d] == 0 and s == 1
24
+                    oob |= pos[d] == 0 and s == 1
25 25
                     if oob:
26 26
                         continue
27 27
                     a = (d + 1) * (-1) ** s
28 28