Skip to content

Commit 8262fcb

Browse files
WIPs
1 parent b8344cf commit 8262fcb

File tree

2 files changed

+281
-11
lines changed

2 files changed

+281
-11
lines changed

src/infinite terrain.py

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import pygame
2+
import moderngl
3+
import numpy as np
4+
from pyrr import Matrix44, Vector3
5+
from pygame.locals import *
6+
import threading
7+
import time
8+
import pmma
9+
10+
pmma.init()
11+
12+
13+
class ExpandableGrid:
14+
def __init__(self, initial_size, height_function):
15+
self.size = initial_size
16+
self.height_function = height_function
17+
self.vertices = []
18+
self.indices = []
19+
self.generate_grid()
20+
21+
def generate_grid(self):
22+
"""Initializes vertices and indices for the grid."""
23+
half_size = self.size // 2
24+
self.vertices = []
25+
26+
# Generate vertices
27+
for z in range(-half_size, half_size + 1):
28+
for x in range(-half_size, half_size + 1):
29+
y = self.height_function(x, z)
30+
self.vertices.append((x, y, z))
31+
32+
# Generate indices for triangle-based grid cells
33+
self.indices = []
34+
for z in range(self.size):
35+
for x in range(self.size):
36+
i0 = z * (self.size + 1) + x
37+
i1 = i0 + 1
38+
i2 = i0 + (self.size + 1)
39+
i3 = i2 + 1
40+
self.indices.extend([i0, i2, i1, i1, i2, i3])
41+
42+
# Convert to numpy arrays
43+
self.vertices = np.array(self.vertices, dtype='f4')
44+
self.indices = np.array(self.indices, dtype=np.uint32)
45+
46+
def expand_grid(self):
47+
"""Expands the grid by one row and column without regenerating the entire grid."""
48+
self.size += 1
49+
half_size = self.size // 2
50+
51+
# Add new vertices only on the outer edges
52+
new_vertices = []
53+
for z in range(-half_size, half_size + 1):
54+
for x in range(-half_size, half_size + 1):
55+
if abs(x) == half_size or abs(z) == half_size:
56+
y = self.height_function(x, z)
57+
new_vertices.append((x, y, z))
58+
59+
self.vertices = np.vstack((self.vertices, np.array(new_vertices, dtype='f4')))
60+
61+
# Add new indices for the expanded grid cells
62+
new_indices = []
63+
for z in range(self.size - 1, self.size):
64+
for x in range(self.size - 1):
65+
i0 = z * (self.size + 1) + x
66+
i1 = i0 + 1
67+
i2 = i0 + (self.size + 1)
68+
i3 = i2 + 1
69+
new_indices.extend([i0, i2, i1, i1, i2, i3])
70+
71+
for x in range(self.size - 1, self.size):
72+
for z in range(self.size - 1):
73+
i0 = z * (self.size + 1) + x
74+
i1 = i0 + 1
75+
i2 = i0 + (self.size + 1)
76+
i3 = i2 + 1
77+
new_indices.extend([i0, i2, i1, i1, i2, i3])
78+
79+
self.indices = np.concatenate((self.indices, np.array(new_indices, dtype=np.uint32)))
80+
81+
class Renderer:
82+
def __init__(self, width, height, mesh):
83+
pygame.init()
84+
self.width = width
85+
self.height = height
86+
self.window = pygame.display.set_mode((width, height), OPENGL | DOUBLEBUF, vsync=True)
87+
self.ctx = moderngl.create_context()
88+
self.mesh = mesh
89+
90+
self.program = self.ctx.program(
91+
vertex_shader="""
92+
#version 330
93+
in vec3 in_vert;
94+
uniform mat4 model;
95+
uniform mat4 view;
96+
uniform mat4 projection;
97+
void main() {
98+
gl_Position = projection * view * model * vec4(in_vert, 1.0);
99+
}
100+
""",
101+
fragment_shader="""
102+
#version 330
103+
out vec4 fragColor;
104+
void main() {
105+
fragColor = vec4(0.3, 0.6, 0.8, 1.0);
106+
}
107+
"""
108+
)
109+
110+
self.vbo = self.ctx.buffer(self.mesh.vertices.tobytes())
111+
self.ibo = self.ctx.buffer(self.mesh.indices.tobytes())
112+
self.vao = self.ctx.vertex_array(self.program, [(self.vbo, '3f', 'in_vert')], self.ibo)
113+
114+
self.camera_pos = Vector3([0.0, 10.0, 10.0])
115+
self.camera_front = Vector3([0.0, -1.0, -1.0]).normalized
116+
self.camera_up = Vector3([0.0, 1.0, 0.0])
117+
118+
self.projection = Matrix44.perspective_projection(45.0, width / height, 0.1, 100.0)
119+
self.ctx.viewport = (0, 0, width, height)
120+
121+
self.t = threading.Thread(target=self.expander)
122+
self.t.start()
123+
124+
self.update_pending = False
125+
126+
def expander(self):
127+
while True:
128+
if self.update_pending is False:
129+
mesh.expand_grid()
130+
mesh.expand_grid()
131+
self.update_pending = True
132+
time.sleep(0.5)
133+
134+
def render(self):
135+
self.ctx.clear(0.2, 0.3, 0.3)
136+
137+
view = Matrix44.look_at(
138+
self.camera_pos,
139+
self.camera_pos + self.camera_front,
140+
self.camera_up
141+
)
142+
143+
self.program['model'].write(Matrix44.identity().astype('f4').tobytes())
144+
self.program['view'].write(view.astype('f4').tobytes())
145+
self.program['projection'].write(self.projection.astype('f4').tobytes())
146+
147+
self.vao.render(moderngl.TRIANGLES)
148+
149+
if self.update_pending:
150+
self.vbo.release()
151+
self.ibo.release()
152+
self.vao.release()
153+
self.vbo = self.ctx.buffer(self.mesh.vertices.tobytes())
154+
self.ibo = self.ctx.buffer(self.mesh.indices.tobytes())
155+
self.vao = self.ctx.vertex_array(self.program, [(self.vbo, '3f', 'in_vert')], self.ibo)
156+
self.update_pending = False
157+
158+
def process_input(self):
159+
keys = pygame.key.get_pressed()
160+
camera_speed = 0.05
161+
if keys[K_w]:
162+
self.camera_pos += camera_speed * self.camera_front
163+
if keys[K_s]:
164+
self.camera_pos -= camera_speed * self.camera_front
165+
if keys[K_a]:
166+
self.camera_pos -= np.cross(self.camera_front, self.camera_up) * camera_speed
167+
if keys[K_d]:
168+
self.camera_pos += np.cross(self.camera_front, self.camera_up) * camera_speed
169+
170+
for event in pygame.event.get():
171+
if event.type == QUIT:
172+
pygame.quit()
173+
quit()
174+
if event.type == MOUSEBUTTONDOWN:
175+
if event.button == 4: # Scroll up
176+
self.camera_pos += camera_speed * self.camera_front
177+
elif event.button == 5: # Scroll down
178+
self.camera_pos -= camera_speed * self.camera_front
179+
180+
if pygame.mouse.get_pressed()[0]: # Left mouse button
181+
mouse_pos = pygame.mouse.get_pos()
182+
if self.last_mouse_pos:
183+
dx = mouse_pos[0] - self.last_mouse_pos[0]
184+
dy = mouse_pos[1] - self.last_mouse_pos[1]
185+
sensitivity = 0.01
186+
dx *= sensitivity
187+
dy *= sensitivity
188+
189+
# Update camera direction (Euler angles)
190+
self.camera_front[0] += dx
191+
self.camera_front[1] -= dy
192+
self.last_mouse_pos = mouse_pos
193+
else:
194+
self.last_mouse_pos = None
195+
196+
noise = pmma.Perlin()
197+
198+
# Usage example
199+
initial_size = 10
200+
def height_function(x, z):
201+
return noise.generate_2D_perlin_noise(x/100, z/100, new_range=[0, 1])
202+
203+
mesh = ExpandableGrid(initial_size, height_function)
204+
renderer = Renderer(800, 600, mesh)
205+
while True:
206+
renderer.process_input()
207+
renderer.render()
208+
pygame.display.flip()
209+
pmma.compute()

src/terrain generation.py

+72-11
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
# Constants for the terrain
1616
WIDTH, HEIGHT = 1920, 1080
17-
GRID_SIZE = 3000#1000 # Number of vertices along one side of the terrain
17+
GRID_SIZE = 1#1000 # Number of vertices along one side of the terrain
1818
SCALE = 0.1#0.5 # Distance between vertices in the grid
1919
AMPLITUDE = 100#50 # Height amplitude for the noise
2020

@@ -43,6 +43,65 @@ def generate_terrain(noise, grid_size, scale, amplitude):
4343
context = moderngl.create_context()
4444
clock = pygame.time.Clock()
4545

46+
class ExpandableMesh:
47+
def __init__(self, initial_size, height_function):
48+
self.size = initial_size
49+
self.height_function = height_function
50+
self.vertices = []
51+
self.tex_coords = []
52+
self.indices = []
53+
54+
self.generate_initial_grid()
55+
56+
def generate_initial_grid(self):
57+
half_size = self.size // 2
58+
for z in range(-half_size, half_size + 1):
59+
for x in range(-half_size, half_size + 1):
60+
y = self.height_function(x, z)
61+
self.vertices.append((x, y, z))
62+
self.tex_coords.append(((x + half_size) / self.size, (z + half_size) / self.size))
63+
self.generate_indices()
64+
65+
self.vbo = context.buffer(np.array(self.vertices, dtype='f4').tobytes())
66+
self.ibo = context.buffer(self.indices.tobytes())
67+
68+
def generate_indices(self):
69+
n = self.size
70+
indices = []
71+
for z in range(n - 1):
72+
for x in range(n - 1):
73+
i0 = z * n + x
74+
i1 = i0 + 1
75+
i2 = i0 + n
76+
i3 = i2 + 1
77+
indices.extend([i0, i1, i2, i2, i1, i3])
78+
self.indices = np.array(indices, dtype=np.uint32)
79+
80+
def expand_grid(self):
81+
self.size += 1
82+
new_half_size = self.size // 2
83+
for x in range(-new_half_size, new_half_size + 1):
84+
for z in [-new_half_size, new_half_size]:
85+
if (x, z) not in self.vertex_positions():
86+
y = self.height_function(x, z)
87+
self.vertices.append((x, y, z))
88+
self.tex_coords.append(((x + new_half_size) / self.size, (z + new_half_size) / self.size))
89+
90+
for z in range(-new_half_size + 1, new_half_size):
91+
for x in [-new_half_size, new_half_size]:
92+
if (x, z) not in self.vertex_positions():
93+
y = self.height_function(x, z)
94+
self.vertices.append((x, y, z))
95+
self.tex_coords.append(((x + new_half_size) / self.size, (z + new_half_size) / self.size))
96+
97+
self.generate_indices()
98+
99+
self.vbo.update(np.array(self.vertices, dtype='f4').tobytes())
100+
self.ibo.update(self.indices.tobytes())
101+
102+
def vertex_positions(self):
103+
return set((vx, vz) for vx, _, vz in self.vertices)
104+
46105
# Shader Programs
47106
vertex_shader = """
48107
#version 330
@@ -144,7 +203,7 @@ def __init__(self, vertices, normals, indices, texcoords):
144203
def apply(self):
145204
# Combine vertex positions and normals into a single array
146205
vertex_data = np.hstack([self.vertices, self.normals, self.texcoords]).astype('f4')
147-
vbo.write(vertex_data.tobytes()) # Update VBO with both position and normal data
206+
#vbo.write(vertex_data.tobytes()) # Update VBO with both position and normal data
148207

149208
def update(self):
150209
self.noise = pmma.Perlin(octaves=4)
@@ -163,12 +222,9 @@ def terrain_changer(self):
163222

164223
terrain_changer = threading.Thread(target=terrain.terrain_changer)
165224
terrain_changer.daemon = True
166-
terrain_changer.start()
225+
#terrain_changer.start()
167226

168227
# Create ModernGL buffers
169-
vbo = context.buffer(np.hstack([vertices, normals, texcoords]).astype('f4').tobytes())
170-
ibo = context.buffer(indices.tobytes())
171-
vao = context.simple_vertex_array(program, vbo, 'in_vert', 'in_norm', 'in_texcoord', index_buffer=ibo)
172228

173229
grass_texture = load_texture(r"H:\Documents\GitHub\Graphical-Python-Effects\src\resources\grass.png", context)
174230
grass_texture.repeat_x = True
@@ -220,6 +276,11 @@ def get_height_at_origin(vertices, grid_size):
220276

221277
camera_height = get_height_at_origin(vertices, GRID_SIZE)
222278

279+
def height(x, z):
280+
return 0
281+
282+
e = ExpandableMesh(6, height)
283+
vao1 = context.simple_vertex_array(program, e.vbo, 'in_vert', 'in_norm', 'in_texcoord', index_buffer=e.ibo)
223284
while running:
224285
for event in pygame.event.get():
225286
if event.type == pygame.QUIT:
@@ -277,7 +338,7 @@ def get_height_at_origin(vertices, grid_size):
277338

278339
# Render the terrain
279340
context.clear(0.1, 0.2, 0.3)
280-
vao.render(moderngl.TRIANGLES)
341+
vao1.render(moderngl.LINES)
281342
pygame.display.flip()
282343
clock.tick(60)
283344

@@ -286,12 +347,12 @@ def get_height_at_origin(vertices, grid_size):
286347
program['time'].value = timer/2
287348

288349
now_time = time.perf_counter() - start
289-
if (TAU/10) * now_time/2 > TAU:
290-
start = time.perf_counter()
350+
#if (TAU/10) * now_time/2 > TAU:
351+
#start = time.perf_counter()
291352

292-
terrain.apply()
353+
#terrain.apply()
293354

294-
camera_height = get_height_at_origin(terrain.vertices, GRID_SIZE)
355+
#camera_height = get_height_at_origin(terrain.vertices, GRID_SIZE)
295356

296357
pmma.compute()
297358

0 commit comments

Comments
 (0)