dkl9 commited on 2025-184 01:10:44
Showing 9 changed files, with 61 additions and 70 deletions.
| ... | ... |
@@ -33,18 +33,13 @@ void main() {{
|
| 33 | 33 |
vec3 c = colF.xyz; |
| 34 | 34 |
{"\n".join([
|
| 35 | 35 |
f"if (abs(j - {i}.0) < 1e-3)" +
|
| 36 |
- f"c = texture2D(tex[{i}], texcF.xy).xyz;" for i in range(TEX_COUNT)
|
|
| 36 |
+ f"c *= texture2D(tex[{i}], texcF.xy).xyz;" for i in range(TEX_COUNT)
|
|
| 37 | 37 |
])} |
| 38 | 38 |
gl_FragColor = vec4((ambient + diffuse + specular) * c, colF.w); |
| 39 | 39 |
}} |
| 40 | 40 |
""" |
| 41 | 41 |
|
| 42 | 42 |
def load_texture(i, img_data): |
| 43 |
- if isinstance(img_data, str): |
|
| 44 |
- pli = pygame.image.load(img_data).convert() |
|
| 45 |
- (tw, th) = pli.get_size() |
|
| 46 |
- img_data = np.array(list(pygame.image.tobytes(pli, "RGB")), np.uint8) |
|
| 47 |
- elif isinstance(img_data, np.ndarray): |
|
| 48 | 43 |
(tw, th) = img_data.shape[:2] |
| 49 | 44 |
tex = glGenTextures(1) |
| 50 | 45 |
glActiveTexture(GL_TEXTURE0 + i) |
| ... | ... |
@@ -57,10 +52,9 @@ def load_texture(i, img_data): |
| 57 | 52 |
|
| 58 | 53 |
def init_display(): |
| 59 | 54 |
pygame.init() |
| 60 |
- full = os.environ.get("FULLSCREEN")
|
|
| 61 | 55 |
pygame.display.set_mode((640, 480), |
| 62 |
- pygame.DOUBLEBUF | pygame.OPENGL | |
|
| 63 |
- (pygame.FULLSCREEN | pygame.SCALED if full else pygame.RESIZABLE), |
|
| 56 |
+ pygame.DOUBLEBUF | pygame.OPENGL | (pygame.FULLSCREEN | pygame.SCALED |
|
| 57 |
+ if conf["maximised"] else pygame.RESIZABLE), |
|
| 64 | 58 |
vsync=1) |
| 65 | 59 |
pygame.event.set_allowed([ |
| 66 | 60 |
pygame.KEYDOWN, pygame.KEYUP, |
| ... | ... |
@@ -68,8 +62,6 @@ def init_display(): |
| 68 | 62 |
pygame.QUIT, pygame.WINDOWRESIZED, pygame.WINDOWSIZECHANGED |
| 69 | 63 |
]) |
| 70 | 64 |
pygame.display.set_caption("Voxels")
|
| 71 |
- load_texture(0, "favicon.png") |
|
| 72 |
- load_texture(1, "container.jpg") |
|
| 73 | 65 |
sp = shaders.compileProgram( |
| 74 | 66 |
shaders.compileShader(VS, GL_VERTEX_SHADER), |
| 75 | 67 |
shaders.compileShader(FS, GL_FRAGMENT_SHADER) |
| ... | ... |
@@ -34,12 +34,12 @@ def selection(): |
| 34 | 34 |
|
| 35 | 35 |
def skybox(): |
| 36 | 36 |
sb = mesh.SquareMesh() |
| 37 |
- offset = [0, 0, VIEW_DIST] |
|
| 37 |
+ offset = [0, 0, conf["view-dist"]] |
|
| 38 | 38 |
for d in range(3): |
| 39 | 39 |
roll_vec3(offset, 1) |
| 40 | 40 |
for s in range(2): |
| 41 | 41 |
sf = (-1) ** s |
| 42 |
- sb.add_sq((d + 1) * sf, 2 * VIEW_DIST, sf * vec(offset), 2) |
|
| 42 |
+ sb.add_sq((d + 1) * sf, 2 * conf["view-dist"], sf * vec(offset), 2) |
|
| 43 | 43 |
i = 6 * (2 * d + s) + 1 |
| 44 | 44 |
for t in range(2): |
| 45 | 45 |
vp = np.s_[i + 3 * t:i + 3 * t + 2] |
| ... | ... |
@@ -48,7 +48,7 @@ def skybox(): |
| 48 | 48 |
|
| 49 | 49 |
def sun(): |
| 50 | 50 |
s = attributes.AttributedEntity( |
| 51 |
- [[0.0, 0.0, 0.0]], normalise(LIGHT_POS) * VIEW_DIST, |
|
| 51 |
+ [[0.0, 0.0, 0.0]], normalise(LIGHT_POS) * conf["view-dist"], |
|
| 52 | 52 |
[1.0, 1.0, 0.9, 1.0], texi=-1, mode=GL_TRIANGLE_FAN |
| 53 | 53 |
) |
| 54 | 54 |
cv = [] |
| ... | ... |
@@ -4,12 +4,7 @@ import numpy as np |
| 4 | 4 |
import pygame |
| 5 | 5 |
|
| 6 | 6 |
TEX_COUNT = 7 |
| 7 |
-TEX_RES = 64 |
|
| 8 | 7 |
LIGHT_POS = [5, 30, 10] |
| 9 |
-REGION_SIZE = 8 |
|
| 10 |
-GRAVITY = 1 |
|
| 11 |
-VIEW_DIST = 30 |
|
| 12 |
-FPS = 60 |
|
| 13 | 8 |
ARROWS = [ |
| 14 | 9 |
["RIGHT", "e", "d"], ["LEFT", "a"], |
| 15 | 10 |
["SPACE"], ["LSHIFT"], |
| ... | ... |
@@ -17,8 +12,16 @@ ARROWS = [ |
| 17 | 12 |
] |
| 18 | 13 |
ARROWS = [[getattr(pygame, f"K_{n}") for n in opts] for opts in ARROWS]
|
| 19 | 14 |
|
| 15 |
+conf = {
|
|
| 16 |
+ "fps": 60, |
|
| 17 |
+ "gravity": 1.0, |
|
| 18 |
+ "maximised": False, |
|
| 19 |
+ "region": 8, |
|
| 20 |
+ "tex-res": 32, |
|
| 21 |
+ "view-dist": 30.0, |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 20 | 24 |
FLOAT = np.float32 |
| 21 |
-VOXEL_TYPE = np.uint8 |
|
| 22 | 25 |
VERTEX_ATTRS = [ |
| 23 | 26 |
"vec3 position", "vec4 col", "vec3 normal", "vec4 refls", "vec3 texc" |
| 24 | 27 |
] |
| ... | ... |
@@ -60,7 +63,6 @@ def smerp(corners, weights): |
| 60 | 63 |
return corners[0] |
| 61 | 64 |
|
| 62 | 65 |
def norm_tri(verts): |
| 63 |
- assert verts.shape[0] == 3 |
|
| 64 | 66 |
return normalise(np.cross(verts[2] - verts[0], verts[1] - verts[0])) |
| 65 | 67 |
|
| 66 | 68 |
def update_attr(ent, n, l=None): |
| ... | ... |
@@ -1,35 +1,47 @@ |
| 1 | 1 |
from libs import * |
| 2 |
+import argparse |
|
| 2 | 3 |
import display |
| 3 | 4 |
from mapgen import MapGen |
| 4 | 5 |
from player import Player |
| 5 | 6 |
|
| 7 |
+TEX_PARAMS = [ |
|
| 8 |
+ ("debug", 0.1, (0.5, 0.5)), ("sky", 0.15, (0.15, 0.1)),
|
|
| 9 |
+ ("stone", 0.1, (0.2, 0.3)), ("grass", 0.15, (0.15, 0.12)),
|
|
| 10 |
+ ("bark", 0.2, (0.04, 0.25)), ("leaves", 0.4, (0.2, 0.3)),
|
|
| 11 |
+] |
|
| 12 |
+ |
|
| 6 | 13 |
def debug_mode(mg, cam): |
| 7 | 14 |
pass |
| 8 | 15 |
|
| 9 |
-def build_texture(i, mg, name, mid, offset, period): |
|
| 10 |
- display.load_texture(i, mg.make_texture( |
|
| 11 |
- name, (TEX_RES, TEX_RES), mid, offset, period |
|
| 12 |
- )) |
|
| 13 |
- |
|
| 14 | 16 |
def in_view(wr, cam): |
| 15 | 17 |
rwrc = wr.pos + wr.r / 2 - cam.pos |
| 16 |
- close = np.linalg.norm(rwrc) < VIEW_DIST |
|
| 18 |
+ close = np.linalg.norm(rwrc) < conf["view-dist"] |
|
| 17 | 19 |
ld = cam.looking() |
| 18 | 20 |
cv = rwrc + 8 * ld |
| 19 | 21 |
frust = np.dot(cv, ld) / np.linalg.norm(cv) > 0.5 |
| 20 | 22 |
return close and frust |
| 21 | 23 |
|
| 22 | 24 |
def main(): |
| 25 |
+ parser = argparse.ArgumentParser() |
|
| 26 |
+ for (k, v) in conf.items(): |
|
| 27 |
+ parser.add_argument(f"-{k[0]}", f"--{k}", type=type(v))
|
|
| 28 |
+ args = parser.parse_args() |
|
| 29 |
+ for k in conf: |
|
| 30 |
+ v = getattr(args, k.replace("-", "_"))
|
|
| 31 |
+ if v is not None: |
|
| 32 |
+ conf[k] = v |
|
| 23 | 33 |
display.init_display() |
| 24 | 34 |
glUniform4fv(UNIFORMS["lightPos"][2], 1, vec([*LIGHT_POS, 100])) |
| 25 | 35 |
mg = MapGen() |
| 26 |
- build_texture(2, mg, "sky", [0.5, 0.5, 0.8], [0.4, 0.4, 0.1], (20, 8)) |
|
| 27 |
- build_texture(3, mg, "stone", [0.7, 0.7, 0.8], [0.1, 0.1, 0.1], (12, 15)) |
|
| 28 |
- build_texture(4, mg, "grass", [0.3, 0.6, 0.3], [0.2, 0.1, 0.2], (3, 5)) |
|
| 29 |
- build_texture(5, mg, "bark", [0.2, 0.1, 0.0], [0.1, 0.1, 0.0], (2, 8)) |
|
| 30 |
- build_texture(6, mg, "leaves", [0.2, 0.5, 0.2], [0.2, 0.4, 0.2], (10, 14)) |
|
| 36 |
+ tr = conf["tex-res"] |
|
| 37 |
+ for (i, p) in enumerate(TEX_PARAMS): |
|
| 38 |
+ v = [0.2, 0.2, 0.0] if i == 0 else [p[1]] * 3 |
|
| 39 |
+ m = [1 - 1.5 * p[1]] * 3 |
|
| 40 |
+ display.load_texture(i + 1, mg.make_texture( |
|
| 41 |
+ p[0], (tr, tr), m, v, np.multiply(tr, p[2]) |
|
| 42 |
+ )) |
|
| 31 | 43 |
clock = pygame.time.Clock() |
| 32 |
- cam = Player(speed=6/FPS) |
|
| 44 |
+ cam = Player(speed=6/conf["fps"]) |
|
| 33 | 45 |
cam.pos = [4, 8, 4] |
| 34 | 46 |
cam.set_focus(True) |
| 35 | 47 |
while True: |
| ... | ... |
@@ -57,9 +69,9 @@ def main(): |
| 57 | 69 |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) |
| 58 | 70 |
for ent in scene: |
| 59 | 71 |
cam.draw(ent) |
| 60 |
- perf = clock.get_fps() / FPS |
|
| 72 |
+ perf = clock.get_fps() / conf["fps"] |
|
| 61 | 73 |
cam.draw_hud(scene, perf) |
| 62 | 74 |
pygame.display.flip() |
| 63 |
- clock.tick(FPS) |
|
| 75 |
+ clock.tick(conf["fps"]) |
|
| 64 | 76 |
|
| 65 | 77 |
main() |
| ... | ... |
@@ -49,7 +49,8 @@ class MapGen(world.WorldHolder): |
| 49 | 49 |
wr.grid[i, :min(h, wr.r), j] = 3 |
| 50 | 50 |
if h < wr.r and h >= 0: |
| 51 | 51 |
wr.grid[i, h, j] = 4 |
| 52 |
- if 2 <= i < wr.r - 2 and 2 <= j < wr.r - 2 and self.poisson( |
|
| 52 |
+ inner = 2 <= i < wr.r - 2 and 2 <= j < wr.r - 2 |
|
| 53 |
+ if inner and self.poisson( |
|
| 53 | 54 |
"trees", wr.pos[[0, 2]], wr.r, (6, 6) |
| 54 | 55 |
)[i, j] >= 1: |
| 55 | 56 |
for u in range(1, 6): |
| ... | ... |
@@ -58,32 +59,15 @@ class MapGen(world.WorldHolder): |
| 58 | 59 |
for v in range(-7 + u, 8 - u): |
| 59 | 60 |
b = 7 - u - abs(v) |
| 60 | 61 |
for w in range(-b, b + 1): |
| 61 |
- self.voxel_at(wr.pos + [i + v, h + u, j + w], 6) |
|
| 62 |
- return n |
|
| 63 |
- |
|
| 64 |
- def world_min(self, wr): |
|
| 65 |
- wr.grid[0, 0, 0] = 1 |
|
| 66 |
- return 1 |
|
| 67 |
- |
|
| 68 |
- def world_grid(self, wr): |
|
| 69 |
- n = 0 |
|
| 70 |
- for i in range(0, wr.r, 2): |
|
| 71 |
- for j in range(0, wr.r, 2): |
|
| 72 |
- for k in range(0, wr.r, 2): |
|
| 73 |
- wr.grid[i, j, k] = 1 |
|
| 74 |
- n += 1 |
|
| 75 |
- return n |
|
| 76 |
- |
|
| 77 |
- def world_checker(self, wr): |
|
| 78 |
- n = 0 |
|
| 79 |
- for i in range(wr.r): |
|
| 80 |
- for j in range(wr.r): |
|
| 81 |
- for k in range(wr.r): |
|
| 82 |
- if not (i + j + k) % 2: |
|
| 83 |
- wr.grid[i, j, k] = 1 |
|
| 84 |
- n += 1 |
|
| 62 |
+ self.voxel_at( |
|
| 63 |
+ wr.pos + [i + v, h + u, j + w], 6 |
|
| 64 |
+ ) |
|
| 85 | 65 |
return n |
| 86 | 66 |
|
| 87 | 67 |
def make_texture(self, name, dims, mid, offset, period): |
| 88 |
- mid, offset = vec(mid), vec(offset) |
|
| 89 |
- return np.multiply(256, [[mid + offset * self.perlin(name, vec((i, j)), period) for j in range(dims[1])] for i in range(dims[0])], dtype=FLOAT) |
|
| 68 |
+ m, o = vec(mid), vec(offset) |
|
| 69 |
+ ta = np.empty(dims + (3,)) |
|
| 70 |
+ for i in range(dims[1]): |
|
| 71 |
+ for j in range(dims[0]): |
|
| 72 |
+ ta[i, j] = m + o * self.perlin(name, vec((i, j)), period) |
|
| 73 |
+ return np.multiply(255, ta, dtype=FLOAT) |
| ... | ... |
@@ -38,11 +38,11 @@ class SquareMesh(attributes.AttributedEntity): |
| 38 | 38 |
return (vec([0.0, 0.0, 0.0, 0.0]), vec([1.0, 0.0, 0.0, 1.0])) |
| 39 | 39 |
case 1: |
| 40 | 40 |
return ( |
| 41 |
- vec([*np.add(0.5, axis2offset(axis, 0.5)), 1.0]), |
|
| 41 |
+ vec([*np.add(0.5, axis2offset(axis, 0.4)), 1.0]), |
|
| 42 | 42 |
vec([1.0, 0.0, 0.1, 128.0]) |
| 43 | 43 |
) |
| 44 | 44 |
case 2: |
| 45 |
- top = (vec([0.2, 0.2, 1.0, 1.0]), vec([1.0, 0.0, 0.0, 1.0])) |
|
| 45 |
+ top = (vec([0.8, 0.8, 1.5, 1.0]), vec([1.0, 0.0, 0.0, 1.0])) |
|
| 46 | 46 |
bottom = (vec([0.0, 0.0, 0.2, 1.0]), vec([1.0, 0.0, 0.0, 1.0])) |
| 47 | 47 |
match axis: |
| 48 | 48 |
case 2: |
| ... | ... |
@@ -52,7 +52,7 @@ class SquareMesh(attributes.AttributedEntity): |
| 52 | 52 |
case a: |
| 53 | 53 |
return bottom if corner & (2 >> face_orient) else top |
| 54 | 54 |
case 3: |
| 55 |
- return (vec([0.7, 0.7, 0.8, 1.0]), vec([0.5, 0.5, 0.01, 2.0])) |
|
| 55 |
+ return (vec([0.5, 0.5, 0.6, 1.0]), vec([0.5, 0.5, 0.01, 16.0])) |
|
| 56 | 56 |
case 4: |
| 57 | 57 |
top = (vec([0.2, 0.6, 0.2, 1.0]), vec([0.6, 0.4, 0.0, 1.0])) |
| 58 | 58 |
bottom = (vec([0.5, 0.2, 0.0, 1.0]), vec([0.8, 0.2, 0.0, 1.0])) |
| ... | ... |
@@ -73,7 +73,7 @@ class SquareMesh(attributes.AttributedEntity): |
| 73 | 73 |
case _: |
| 74 | 74 |
return bark |
| 75 | 75 |
case 6: |
| 76 |
- return (vec([0.0, 0.4, 0.0, 0.5]), vec([0.6, 0.4, 0.01, 4.0])) |
|
| 76 |
+ return (vec([0.2, 0.6, 0.2, 0.5]), vec([0.6, 0.4, 0.01, 4.0])) |
|
| 77 | 77 |
|
| 78 | 78 |
def mat_list(self): |
| 79 | 79 |
cl = np.empty((2, 3 * len(self.faces), 4), dtype=FLOAT) |
| ... | ... |
@@ -22,7 +22,7 @@ class Player(interactive.InteractiveCamera): |
| 22 | 22 |
cd = 0.3 * sf * self.vaa_mat()[i] |
| 23 | 23 |
hit = self.collides and mg.voxel_at(self.pos + cd) |
| 24 | 24 |
if i == 1 and sf == -1 and self.falls and not hit: |
| 25 |
- netv[i] = self.vaa_vel[i] - GRAVITY / FPS |
|
| 25 |
+ netv[i] = self.vaa_vel[i] - conf["gravity"] / conf["fps"] |
|
| 26 | 26 |
if i != 1 or not self.falls or mg.voxel_at(self.pos - cd): |
| 27 | 27 |
netv[i] += sf * any(pressed[j] for j in ARROWS[2 * i + s]) |
| 28 | 28 |
if hit: |
| ... | ... |
@@ -4,9 +4,10 @@ import heapq |
| 4 | 4 |
import mesh |
| 5 | 5 |
|
| 6 | 6 |
class WorldRegion(mesh.SquareMesh): |
| 7 |
- def __init__(self, size=REGION_SIZE): |
|
| 7 |
+ def __init__(self, size=0): |
|
| 8 | 8 |
super().__init__(refls=[0.5, 0.5, 0.5, 32.0]) |
| 9 |
- self.grid = np.zeros((size, size, size), dtype=VOXEL_TYPE) |
|
| 9 |
+ size = size or conf["region"] |
|
| 10 |
+ self.grid = np.zeros((size, size, size), dtype=np.uint8) |
|
| 10 | 11 |
self.r = size |
| 11 | 12 |
self.dirty = False |
| 12 | 13 |
|
| ... | ... |
@@ -22,7 +22,7 @@ class WorldHolder(): |
| 22 | 22 |
try: |
| 23 | 23 |
r = next(iter(self.regions.values())).r |
| 24 | 24 |
except StopIteration: |
| 25 |
- r = REGION_SIZE |
|
| 25 |
+ r = conf["region"] |
|
| 26 | 26 |
pr, pf = np.divmod(pos + 0.5, r) |
| 27 | 27 |
wr = self.fetch(tuple(int(r * c) for c in pr)) |
| 28 | 28 |
it = tuple(int(c) for c in pf) |
| 29 | 29 |