Working, but...

This commit is contained in:
The Wobbler 2023-10-19 12:59:58 +02:00
parent 0c042c79d7
commit b1edaf6fc1
20 changed files with 807 additions and 0 deletions

3
.gitignore vendored
View file

@ -160,3 +160,6 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
/__pycache__/
/mesh/__pycache__/
/world_objects/__pycache__/

BIN
assets/frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

BIN
assets/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

59
camera.py Normal file
View file

@ -0,0 +1,59 @@
#!/usr/bin/python3
import glm
from settings import *
class Camera:
def __init__(self, position, yaw, pitch):
self.position = glm.vec3(position)
self.yaw = glm.radians(yaw)
self.pitch = glm.radians(pitch)
self.up = glm.vec3(0, 1, 0)
self.right = glm.vec3(1, 0, 0)
self.forward = glm.vec3(0, 0, -1)
self.m_proj = glm.perspective(V_FOV, ASPECT_RATIO, NEAR, FAR)
self.m_view = glm.mat4()
def update(self):
self.update_vectors()
self.update_view_matrix()
def update_view_matrix(self):
self.m_view = glm.lookAt(self.position, self.position + self.forward, self.up)
def update_vectors(self):
self.forward.x = glm.cos(self.yaw) * glm.cos(self.pitch)
self.forward.y = glm.sin(self.pitch)
self.forward.z = glm.sin(self.yaw) * glm.cos(self.pitch)
self.forward = glm.normalize(self.forward)
self.right = glm.normalize(glm.cross(self.forward, glm.vec3(0, 1, 0)))
self.up = glm.normalize(glm.cross(self.right, self.forward))
def rotate_pitch(self, delta_y):
self.pitch -= delta_y
self.pitch = glm.clamp(self.pitch, -PITCH_MAX, PITCH_MAX)
def rotate_yaw(self, delta_x):
self.yaw += delta_x
def move_left(self, velocity):
self.position -= self.right * velocity
def move_right(self, velocity):
self.position += self.right * velocity
def move_up(self, velocity):
self.position += self.up * velocity
def move_down(self, velocity):
self.position -= self.up * velocity
def move_forward(self, velocity):
self.position += self.forward * velocity
def move_back(self, velocity):
self.position -= self.forward * velocity

75
engine.py Normal file
View file

@ -0,0 +1,75 @@
#!/usr/bin/python3
from settings import *
import moderngl as mgl
import pygame as pg
import sys
from shader_program import ShaderProgram
from scene import Scene
from player import Player
from textures import Textures
class VoxelEngine:
def __init__(self):
pg.init()
pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)
pg.display.gl_set_attribute(pg.GL_DEPTH_SIZE, 24)
pg.display.set_mode(WIN_RES, flags=pg.OPENGL | pg.DOUBLEBUF)
self.ctx = mgl.create_context()
self.ctx.enable(flags=mgl.DEPTH_TEST | mgl.CULL_FACE | mgl.BLEND)
self.ctx.gc_mode = "auto"
self.clock = pg.time.Clock()
self.delta_time = 0
self.time = 0
pg.event.set_grab(True)
pg.mouse.set_visible(False)
self.on_init()
self.running = True
def on_init(self):
self.textures = Textures(self)
self.player = Player(self)
self.shader_program = ShaderProgram(self)
self.scene = Scene(self)
def update(self):
self.player.update()
self.shader_program.update()
self.scene.update()
self.delta_time = self.clock.tick()
self.time = pg.time.get_ticks() * 0.001
pg.display.set_caption(f"{self.clock.get_fps() :.0f}")
def render(self):
self.ctx.clear(color=BG_COLOR)
self.scene.render()
pg.display.flip()
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
self.running = False
def run(self):
while self.running:
self.handle_events()
self.update()
self.render()
pg.quit()
sys.exit()
if __name__ == "__main__":
app = VoxelEngine()
app.run()

32
mesh/base.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/python3
import numpy
class BaseMesh:
def __init__(self):
# OpenGL context
self.ctx = None
# shader program
self.program = None
# vertex buffer data type format: "3f 3f"
self.vbo_format = None
# attribute names according to the format: ("in_position", "in_color")
self.attributes: tuple[str, ...] = None
# vertex array object
self.vao = None
def get_vertex_data(self) -> numpy.array: ...
def get_vao(self):
vertex_data = self.get_vertex_data()
vbo = self.ctx.buffer(vertex_data)
vao = self.ctx.vertex_array(
self.program,
[(vbo, self.vbo_format, *self.attributes)],
skip_errors=True
)
return vao
def render(self):
self.vao.render()

28
mesh/chunk.py Normal file
View file

@ -0,0 +1,28 @@
#!/usr/bin/python3
from mesh.base import BaseMesh
from mesh.chunk_builder import build_chunk_mesh
class ChunkMesh(BaseMesh):
def __init__(self, chunk):
super().__init__()
self.app = chunk.app
self.chunk = chunk
self.ctx = self.app.ctx
self.program = self.app.shader_program.chunk
self.vbo_format = "3u1 1u1 1u1 1u1 1u1"
self.format_size = sum(int(fmt[:1]) for fmt in self.vbo_format.split())
self.attributes = ("in_position", "voxel_id", "face_id", "ao_id", "flip_id")
self.vao = self.get_vao()
def get_vertex_data(self):
mesh = build_chunk_mesh(
chunk_voxels=self.chunk.voxels,
format_size=self.format_size,
chunk_pos=self.chunk.position,
world_voxels=self.chunk.world.voxels
)
return mesh

211
mesh/chunk_builder.py Normal file
View file

@ -0,0 +1,211 @@
#!/usr/bin/python3
from settings import *
from numba import uint8
@njit
def get_ao(local_pos, world_pos, world_voxels, plane):
x, y, z = local_pos
wx, wy, wz = world_pos
if plane == "Y":
a = is_void((x , y, z - 1), (wx , wy, wz - 1), world_voxels)
b = is_void((x - 1, y, z - 1), (wx - 1, wy, wz - 1), world_voxels)
c = is_void((x - 1, y, z ), (wx - 1, wy, wz ), world_voxels)
d = is_void((x - 1, y, z + 1), (wx - 1, wy, wz + 1), world_voxels)
e = is_void((x , y, z + 1), (wx , wy, wz + 1), world_voxels)
f = is_void((x + 1, y, z + 1), (wx + 1, wy, wz + 1), world_voxels)
g = is_void((x + 1, y, z ), (wx + 1, wy, wz ), world_voxels)
h = is_void((x + 1, y, z - 1), (wx + 1, wy, wz - 1), world_voxels)
elif plane == "X":
a = is_void((x, y , z - 1), (wx, wy , wz - 1), world_voxels)
b = is_void((x, y - 1, z - 1), (wx, wy - 1, wz - 1), world_voxels)
c = is_void((x, y - 1, z ), (wx, wy - 1, wz ), world_voxels)
d = is_void((x, y - 1, z + 1), (wx, wy - 1, wz + 1), world_voxels)
e = is_void((x, y , z + 1), (wx, wy , wz + 1), world_voxels)
f = is_void((x, y + 1, z + 1), (wx, wy + 1, wz + 1), world_voxels)
g = is_void((x, y + 1, z ), (wx, wy + 1, wz ), world_voxels)
h = is_void((x, y + 1, z - 1), (wx, wy + 1, wz - 1), world_voxels)
else: # Z plane
a = is_void((x - 1, y , z), (wx - 1, wy , z), world_voxels)
b = is_void((x - 1, y - 1, z), (wx - 1, wy - 1, z), world_voxels)
c = is_void((x , y - 1, z), (wx , wy - 1, z), world_voxels)
d = is_void((x + 1, y - 1, z), (wx + 1, wy - 1, z), world_voxels)
e = is_void((x + 1, y , z), (wx + 1, wy , z), world_voxels)
f = is_void((x + 1, y + 1, z), (wx + 1, wy + 1, z), world_voxels)
g = is_void((x , y + 1, z), (wx , wy + 1, z), world_voxels)
h = is_void((x - 1, y + 1, z), (wx - 1, wy + 1, z), world_voxels)
ao = (a + b + c), (g + h + a), (e + f + g), (c + d + e)
return ao
@njit
def to_uint8(x, y, z, voxel_id, face_id, ao_id, flip_id):
return (uint8(x), uint8(y), uint8(z),
uint8(voxel_id), uint8(face_id), uint8(ao_id), uint8(flip_id)
)
@njit
def get_chunk_index(world_voxel_pos):
wx, wy, wz = world_voxel_pos
cx = wx // CHUNK_SIZE
cy = wy // CHUNK_SIZE
cz = wz // CHUNK_SIZE
if not (0 <= cx < WORLD_W and 0 <= cy < WORLD_H and 0 <= cz < WORLD_D):
return -1
index = cx + WORLD_W * cz + WORLD_AREA * cy
return index
@njit
def is_void(local_voxel_pos, world_voxel_pos, world_voxels):
chunk_index = get_chunk_index(world_voxel_pos)
if chunk_index == -1:
return False
chunk_voxels = world_voxels[chunk_index]
x, y, z = local_voxel_pos
voxel_index = x % CHUNK_SIZE + z % CHUNK_SIZE * CHUNK_SIZE + y % CHUNK_SIZE * CHUNK_AREA
if chunk_voxels[voxel_index]:
return False
return True
@njit
def add_data(vertex_data, index, *vertices):
for vertex in vertices:
for attribute in vertex:
vertex_data[index] = attribute
index += 1
return index
@njit
def build_chunk_mesh(chunk_voxels, format_size, chunk_pos, world_voxels):
vertex_data = numpy.empty(CHUNK_VOL * 18 * format_size, dtype="uint8")
index = 0
for x in range(CHUNK_SIZE):
for y in range(CHUNK_SIZE):
for z in range(CHUNK_SIZE):
voxel_id = chunk_voxels[x + CHUNK_SIZE * z + CHUNK_AREA * y]
if not voxel_id:
continue
# voxel world position
cx, cy, cz = chunk_pos
wx = x + cx * CHUNK_SIZE
wy = y + cy * CHUNK_SIZE
wz = z + cz * CHUNK_SIZE
# top face
if is_void((x, y + 1, z), (wx, wy + 1, wz), world_voxels):
# get ao values
ao = get_ao((x, y + 1, z), (wx, wy + 1, wz), world_voxels, plane="Y")
flip_id = ao[1] + ao[3] > ao[0] + ao[2]
# format: x, y, z, voxel_id, face_id, ao_id
v0 = to_uint8(x , y + 1, z , voxel_id, 0, ao[0], flip_id)
v1 = to_uint8(x + 1, y + 1, z , voxel_id, 0, ao[1], flip_id)
v2 = to_uint8(x + 1, y + 1, z + 1, voxel_id, 0, ao[2], flip_id)
v3 = to_uint8(x , y + 1, z + 1, voxel_id, 0, ao[3], flip_id)
if flip_id:
index = add_data(vertex_data, index, v1, v0, v3, v1, v3, v2)
else:
index = add_data(vertex_data, index, v0, v3, v2, v0, v2, v1)
# bottom face
if is_void((x, y - 1, z), (wx, wy - 1, wz), world_voxels):
ao = get_ao((x, y - 1, z), (wx, wy - 1, wz), world_voxels, plane="Y")
flip_id = ao[1] + ao[3] > ao[0] + ao[2]
v0 = to_uint8(x , y, z , voxel_id, 1, ao[0], flip_id)
v1 = to_uint8(x + 1, y, z , voxel_id, 1, ao[1], flip_id)
v2 = to_uint8(x + 1, y, z + 1, voxel_id, 1, ao[2], flip_id)
v3 = to_uint8(x , y, z + 1, voxel_id, 1, ao[3], flip_id)
if flip_id:
index = add_data(vertex_data, index, v1, v3, v0, v1, v2, v3)
else:
index = add_data(vertex_data, index, v0, v2, v3, v0, v1, v2)
# right face
if is_void((x + 1, y, z), (wx + 1, wy, wz), world_voxels):
ao = get_ao((x + 1, y, z), (wx + 1, wy, wz), world_voxels, plane="X")
flip_id = ao[1] + ao[3] > ao[0] + ao[2]
v0 = to_uint8(x + 1, y , z , voxel_id, 2, ao[0], flip_id)
v1 = to_uint8(x + 1, y + 1, z , voxel_id, 2, ao[1], flip_id)
v2 = to_uint8(x + 1, y + 1, z + 1, voxel_id, 2, ao[2], flip_id)
v3 = to_uint8(x + 1, y , z + 1, voxel_id, 2, ao[3], flip_id)
if flip_id:
index = add_data(vertex_data, index, v3, v0, v1, v3, v1, v2)
else:
index = add_data(vertex_data, index, v0, v1, v2, v0, v2, v3)
# left face
if is_void((x - 1, y, z), (wx - 1, wy, wz), world_voxels):
ao = get_ao((x - 1, y, z), (wx - 1, wy, wz), world_voxels, plane="X")
flip_id = ao[1] + ao[3] > ao[0] + ao[2]
v0 = to_uint8(x, y , z , voxel_id, 3, ao[0], flip_id)
v1 = to_uint8(x, y + 1, z , voxel_id, 3, ao[1], flip_id)
v2 = to_uint8(x, y + 1, z + 1, voxel_id, 3, ao[2], flip_id)
v3 = to_uint8(x, y , z + 1, voxel_id, 3, ao[3], flip_id)
if flip_id:
index = add_data(vertex_data, index, v3, v1, v0, v3, v2, v1)
else:
index = add_data(vertex_data, index, v0, v2, v1, v0, v3, v2)
# back face
if is_void((x, y, z - 1), (wx, wy, wz - 1), world_voxels):
ao = get_ao((x, y, z - 1), (wx, wy, wz - 1), world_voxels, plane="Z")
flip_id = ao[1] + ao[3] > ao[0] + ao[2]
v0 = to_uint8(x , y , z, voxel_id, 4, ao[0], flip_id)
v1 = to_uint8(x , y + 1, z, voxel_id, 4, ao[1], flip_id)
v2 = to_uint8(x + 1, y + 1, z, voxel_id, 4, ao[2], flip_id)
v3 = to_uint8(x + 1, y , z, voxel_id, 4, ao[3], flip_id)
if flip_id:
index = add_data(vertex_data, index, v3, v0, v1, v3, v1, v2)
else:
index = add_data(vertex_data, index, v0, v1, v2, v0, v2, v3)
# front face
if is_void((x, y, z + 1), (wx, wy, wz + 1), world_voxels):
ao = get_ao((x, y, z + 1), (wx, wy, wz + 1), world_voxels, plane="Z")
flip_id = ao[1] + ao[3] > ao[0] + ao[2]
v0 = to_uint8(x , y , z + 1, voxel_id, 5, ao[0], flip_id)
v1 = to_uint8(x , y + 1, z + 1, voxel_id, 5, ao[1], flip_id)
v2 = to_uint8(x + 1, y + 1, z + 1, voxel_id, 5, ao[2], flip_id)
v3 = to_uint8(x + 1, y , z + 1, voxel_id, 5, ao[3], flip_id)
if flip_id:
index = add_data(vertex_data, index, v3, v1, v0, v3, v2, v1)
else:
index = add_data(vertex_data, index, v0, v2, v1, v0, v3, v2)
return vertex_data[:index + 1]

31
mesh/quad.py Normal file
View file

@ -0,0 +1,31 @@
#!/usr/bin/python3
from settings import *
from mesh.base import BaseMesh
class QuadMesh(BaseMesh):
def __init__(self, app):
super().__init__()
self.app = app
self.ctx = app.ctx
self.program = app.shader_program.quad
self.vbo_format = "3f 3f"
self.attributes = ("in_position", "in_color")
self.vao = self.get_vao()
def get_vertex_data(self):
vertices = [
(0.5, 0.5, 0.0), (-0.5, 0.5, 0.0), (-0.5, -0.5, 0.0),
(0.5, 0.5, 0.0), (-0.5, -0.5, 0.0), (0.5, -0.5, 0.0)
]
colors = [
(0, 1, 0), (1, 0, 0), (1, 1, 0),
(0, 1, 0), (1, 1, 0), (0, 0, 1)
]
vertex_data = numpy.hstack([vertices, colors], dtype="float32")
return vertex_data

47
player.py Normal file
View file

@ -0,0 +1,47 @@
#!/usr/bin/python3
import pygame as pg
from camera import Camera
from settings import *
class Player(Camera):
def __init__(self, app, position=PLAYER_POS, yaw=-90, pitch=0):
self.app = app
super().__init__(position, yaw, pitch)
def update(self):
self.keyboard_control()
self.mouse_control()
super().update()
def mouse_control(self):
mouse_dx, mouse_dy = pg.mouse.get_rel()
if mouse_dx:
self.rotate_yaw(delta_x=mouse_dx * MOUSE_SENSITIVITY)
if mouse_dy:
self.rotate_pitch(delta_y=mouse_dy * MOUSE_SENSITIVITY)
def keyboard_control(self):
key_state = pg.key.get_pressed()
vel = PLAYER_SPEED * self.app.delta_time
if key_state[pg.K_w]:
self.move_forward(vel)
if key_state[pg.K_s]:
self.move_back(vel)
if key_state[pg.K_d]:
self.move_right(vel)
if key_state[pg.K_a]:
self.move_left(vel)
if key_state[pg.K_q]:
self.move_up(vel)
if key_state[pg.K_e]:
self.move_down(vel)

16
scene.py Normal file
View file

@ -0,0 +1,16 @@
#!/usr/bin/python3
from settings import *
from world import World
class Scene:
def __init__(self, app):
self.app = app
self.world = World(self.app)
def update(self):
self.world.update()
def render(self):
self.world.render()

46
settings.py Normal file
View file

@ -0,0 +1,46 @@
#!/usr/bin/python3
from numba import njit
import numpy
import glm
import math
# resolution
WIN_RES = glm.vec2(1600, 1000)
# chunk
CHUNK_SIZE = 32
H_CHUNK_SIZE = CHUNK_SIZE // 2
CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE
CHUNK_VOL = CHUNK_AREA * CHUNK_SIZE
# world
WORLD_W, WORLD_H = 20, 3
WORLD_D = WORLD_W
WORLD_AREA = WORLD_W * WORLD_D
WORLD_VOL = WORLD_AREA * WORLD_H
# world center
CENTER_XZ = WORLD_W * H_CHUNK_SIZE
CENTER_Y = WORLD_H * H_CHUNK_SIZE
# camera
ASPECT_RATIO = WIN_RES.x / WIN_RES.y
FOV_DEG = 50
V_FOV = glm.radians(FOV_DEG) # vertical fov
H_FOV = 2 * math.atan(math.tan(V_FOV * 0.5) * ASPECT_RATIO)
NEAR = 0.1
FAR = 2000.0
PITCH_MAX = glm.radians(89)
# player
PLAYER_SPEED = 0.055
PLAYER_ROT_SPEED = 0.003
PLAYER_POS = glm.vec3(CENTER_XZ, WORLD_H * CHUNK_SIZE, CENTER_XZ)
MOUSE_SENSITIVITY = 0.002
# colors
BG_COLOR = glm.vec3(0.1, 0.16, 0.25)

32
shader_program.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/python3
from settings import *
class ShaderProgram:
def __init__(self, app):
self.app = app
self.ctx = app.ctx
self.player = app.player
# -------- shaders -------- #
self.chunk = self.get_program(shader_name="chunk")
# ------------------------- #
self.set_uniforms_on_init()
self.chunk["u_texture_0"] = 0
def set_uniforms_on_init(self):
self.chunk["m_proj"].write(self.player.m_proj)
self.chunk["m_model"].write(glm.mat4())
def update(self):
self.chunk["m_view"].write(self.player.m_view)
def get_program(self, shader_name):
with open(f"shaders/{shader_name}.vert") as file:
vertex_shader = file.read()
with open(f"shaders/{shader_name}.frag") as file:
fragment_shader = file.read()
program = self.ctx.program(vertex_shader=vertex_shader, fragment_shader=fragment_shader)
return program

25
shaders/chunk.frag Normal file
View file

@ -0,0 +1,25 @@
#version 330 core
layout (location = 0) out vec4 fragColor;
const vec3 gamma = vec3(2.2);
const vec3 inv_gamma = 1 / gamma;
uniform sampler2D u_texture_0;
in vec3 voxel_color;
in vec2 uv;
in float shading;
void main() {
vec3 tex_col = texture(u_texture_0, uv).rgb;
tex_col = pow(tex_col, gamma);
tex_col.rgb *= voxel_color;
//tex_col = tex_col * 0.001 + vec3(1);
tex_col *= shading;
tex_col = pow(tex_col, inv_gamma);
fragColor = vec4(tex_col, 1);
}

51
shaders/chunk.vert Normal file
View file

@ -0,0 +1,51 @@
#version 330 core
layout (location = 0) in ivec3 in_position;
layout (location = 1) in int voxel_id;
layout (location = 2) in int face_id;
layout (location = 3) in int ao_id;
layout (location = 4) in int flip_id;
uniform mat4 m_proj;
uniform mat4 m_view;
uniform mat4 m_model;
out vec3 voxel_color;
out vec2 uv;
out float shading;
const float ao_values[4] = float[4](0.1, 0.25, 0.1, 1.0);
const float face_shading[6] = float[6](
1.0, 0.5, // top bottom
0.5, 0.8, // right left
0.5, 0.8 // front back
);
const vec2 uv_coords[4] = vec2[4](
vec2(0, 0), vec2(0, 1),
vec2(1, 0), vec2(1, 1)
);
const int uv_indices[24] = int[24](
1, 0, 2, 1, 2, 3, // tex coord indices for vertices of an even face
3, 0, 2, 3, 1, 0, // odd face
3, 1, 0, 3, 0, 2, // even flipped face
1, 2, 3, 1, 0, 2 // odd flipped face
);
vec3 hash31(float p) {
vec3 p3 = fract(vec3(p * 21.2) * vec3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.xxy + p3.yzz) * p3.zyx) + 0.05;
}
void main() {
int uv_index = gl_VertexID % 6 + ((face_id & 1) + flip_id * 2) * 6;
uv = uv_coords[uv_indices[uv_index]];
voxel_color = hash31(voxel_id);
shading = face_shading[face_id] * ao_values[ao_id];
gl_Position = m_proj * m_view * m_model * vec4(in_position, 1.0);
}

10
shaders/quad.frag Normal file
View file

@ -0,0 +1,10 @@
#version 330 core
layout (location = 0) out vec4 fragColor;
in vec3 color;
void main() {
fragColor = vec4(color, 1.0);
}

16
shaders/quad.vert Normal file
View file

@ -0,0 +1,16 @@
#version 330 core
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec3 in_color;
uniform mat4 m_proj;
uniform mat4 m_view;
uniform mat4 m_model;
out vec3 color;
void main() {
color = in_color;
gl_Position = m_proj * m_view * m_model * vec4(in_position, 1.0);
}

32
textures.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/python3
import pygame as pg
import moderngl as mgl
class Textures:
def __init__(self, app):
self.app = app
self.ctx = app.ctx
# load textures
#self.texture_0 = self.load("frame.png")
self.texture_0 = self.load("test.png")
# assign texture unit
self.texture_0.use(location=0)
def load(self, file_name):
texture = pg.image.load(f"assets/{file_name}")
texture = pg.transform.flip(texture, flip_x=True, flip_y=False)
texture = self.ctx.texture(
size=texture.get_size(),
components=4,
data=pg.image.tostring(texture, "RGBA", False)
)
texture.anisotropy = 32.0
texture.build_mipmaps()
texture.filter = (mgl.NEAREST, mgl.NEAREST)
return texture

39
world.py Normal file
View file

@ -0,0 +1,39 @@
#!/usr/bin/python3
from settings import *
from world_objects.chunk import Chunk
class World:
def __init__(self, app):
self.app = app
self.chunks = [None for _ in range(WORLD_VOL)]
self.voxels = numpy.empty([WORLD_VOL, CHUNK_VOL], dtype="uint8")
self.build_chunks()
self.build_chunk_mesh()
def build_chunks(self):
for x in range(WORLD_W):
for y in range(WORLD_H):
for z in range(WORLD_D):
chunk = Chunk(self, position=(x, y, z))
chunk_index = x + WORLD_W * z + WORLD_AREA * y
self.chunks[chunk_index] = chunk
# put the chunk voxels in a separate array
self.voxels[chunk_index] = chunk.build_voxels()
# get pointer to voxels
chunk.voxels = self.voxels[chunk_index]
def build_chunk_mesh(self):
for chunk in self.chunks:
chunk.build_mesh()
def update(self):
pass
def render(self):
for chunk in self.chunks:
chunk.render()

54
world_objects/chunk.py Normal file
View file

@ -0,0 +1,54 @@
#!/usr/bin/python3
from settings import *
from mesh.chunk import ChunkMesh
class Chunk:
def __init__(self, world, position):
self.app = world.app
self.world = world
self.position = position
self.m_model = self.get_model_matrix()
self.voxels: numpy.array = None
self.mesh: ChunkMesh = None
self.is_empty = True
def get_model_matrix(self):
m_model = glm.translate(glm.mat4(), glm.vec3(self.position) * CHUNK_SIZE)
return m_model
def set_uniform(self):
self.mesh.program["m_model"].write(self.m_model)
def build_mesh(self):
self.mesh = ChunkMesh(self)
def render(self):
self.set_uniform()
self.mesh.render()
def build_voxels(self):
# empty chunk
voxels = numpy.zeros(CHUNK_VOL, dtype="uint8")
# fill chunk
cx, cy, cz = glm.ivec3(self.position) * CHUNK_SIZE
# fill chunk
for x in range(CHUNK_SIZE):
for z in range(CHUNK_SIZE):
wx = x + cx
wz = z + cz
world_height = int(glm.simplex(glm.vec2(wx, wz) * 0.01) * 32 + 32)
local_height = min(world_height - cy, CHUNK_SIZE)
for y in range(local_height):
wy = y + cy
voxels[x + CHUNK_SIZE * z + CHUNK_AREA * y] = wy + 1
if numpy.any(voxels):
self.is_empty = False
return voxels