Skip to content

Commit 8dfc359

Browse files
ooooh pretty!
1 parent 333ac7f commit 8dfc359

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

src/sphere sphere.py

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import pygame
2+
import numpy as np
3+
import moderngl as mgl
4+
from pyrr import Matrix44
5+
import pmma
6+
7+
pmma.init()
8+
9+
# Initialize Pygame
10+
pygame.init()
11+
screen = pygame.display.set_mode((1920, 1080), pygame.OPENGL | pygame.DOUBLEBUF)
12+
clock = pygame.time.Clock()
13+
14+
# Initialize ModernGL context
15+
ctx = mgl.create_context()
16+
17+
# Vertex Shader
18+
vertex_shader = """
19+
#version 330
20+
uniform mat4 projection;
21+
uniform mat4 view;
22+
uniform mat4 model;
23+
24+
in vec3 in_position; // Base sphere vertex
25+
in vec3 instance_position; // Small sphere instance position
26+
in vec3 instance_color; // Small sphere instance color
27+
in float instance_radius; // Small sphere instance radius
28+
29+
out vec3 frag_color;
30+
31+
void main() {
32+
vec3 scaled_position = in_position * instance_radius; // Scale small sphere
33+
vec3 world_position = scaled_position + instance_position; // Translate to instance position
34+
gl_Position = projection * view * model * vec4(world_position, 1.0);
35+
frag_color = instance_color;
36+
}
37+
"""
38+
39+
# Fragment Shader
40+
fragment_shader = """
41+
#version 330
42+
in vec3 frag_color;
43+
out vec4 color;
44+
45+
void main() {
46+
color = vec4(frag_color, 1.0);
47+
}
48+
"""
49+
50+
# Compile shaders and link program
51+
program = ctx.program(
52+
vertex_shader=vertex_shader,
53+
fragment_shader=fragment_shader,
54+
)
55+
56+
# Uniforms
57+
projection = program['projection']
58+
view = program['view']
59+
model = program['model']
60+
61+
# Generate base sphere geometry (triangular mesh for the small spheres)
62+
def generate_sphere_mesh(radius, segments):
63+
vertices = []
64+
indices = []
65+
for i in range(segments):
66+
theta1 = np.pi * i / segments
67+
theta2 = np.pi * (i + 1) / segments
68+
69+
for j in range(segments):
70+
phi1 = 2 * np.pi * j / segments
71+
phi2 = 2 * np.pi * (j + 1) / segments
72+
73+
# Sphere vertices
74+
x1, y1, z1 = radius * np.sin(theta1) * np.cos(phi1), radius * np.sin(theta1) * np.sin(phi1), radius * np.cos(theta1)
75+
x2, y2, z2 = radius * np.sin(theta2) * np.cos(phi1), radius * np.sin(theta2) * np.sin(phi1), radius * np.cos(theta2)
76+
x3, y3, z3 = radius * np.sin(theta2) * np.cos(phi2), radius * np.sin(theta2) * np.sin(phi2), radius * np.cos(theta2)
77+
x4, y4, z4 = radius * np.sin(theta1) * np.cos(phi2), radius * np.sin(theta1) * np.sin(phi2), radius * np.cos(theta1)
78+
79+
# Append vertices
80+
vertices.extend([(x1, y1, z1), (x2, y2, z2), (x3, y3, z3), (x4, y4, z4)])
81+
82+
# Add triangle indices
83+
base = len(vertices) - 4
84+
indices.extend([base, base + 1, base + 2, base, base + 2, base + 3])
85+
86+
return np.array(vertices, dtype='f4'), np.array(indices, dtype='i4')
87+
88+
sphere_vertices, sphere_indices = generate_sphere_mesh(1.0, 16)
89+
90+
# Create buffers for sphere geometry
91+
vbo_sphere = ctx.buffer(sphere_vertices.tobytes())
92+
ibo_sphere = ctx.buffer(sphere_indices.tobytes())
93+
94+
# Distribute points on a sphere (outer sphere)
95+
def fibonacci_sphere(samples=100, radius=5.0):
96+
points = []
97+
phi = (1 + np.sqrt(5)) / 2 # Golden ratio
98+
for i in range(samples):
99+
z = 1 - (i / (samples - 1)) * 2 # Map z to [-1, 1]
100+
r = np.sqrt(1 - z * z) # Radius of circle at z
101+
theta = 2 * np.pi * i / phi # Angle based on golden ratio
102+
x = np.cos(theta) * r
103+
y = np.sin(theta) * r
104+
points.append((x * radius, y * radius, z * radius))
105+
return np.array(points, dtype='f4')
106+
107+
# Generate instance data
108+
instance_positions = fibonacci_sphere(samples=1000, radius=5.0)
109+
instance_colors = np.random.uniform(0.0, 1.0, (len(instance_positions), 3)).astype('f4')
110+
instance_radii = np.random.uniform(0.1, 0.1, (len(instance_positions),)).astype('f4')
111+
112+
vbo_instance_position = ctx.buffer(instance_positions.tobytes())
113+
vbo_instance_color = ctx.buffer(instance_colors.tobytes())
114+
vbo_instance_radius = ctx.buffer(instance_radii.tobytes())
115+
116+
# Create VAO
117+
vao = ctx.vertex_array(
118+
program,
119+
[
120+
(vbo_sphere, '3f', 'in_position'),
121+
(vbo_instance_position, '3f/i', 'instance_position'),
122+
(vbo_instance_color, '3f/i', 'instance_color'),
123+
(vbo_instance_radius, '1f/i', 'instance_radius'),
124+
],
125+
index_buffer=ibo_sphere,
126+
)
127+
128+
# Projection and view matrices
129+
projection_matrix = Matrix44.perspective_projection(45.0, 1920 / 1080, 0.1, 100.0)
130+
view_matrix = Matrix44.look_at(
131+
eye=(10.0, 10.0, 10.0),
132+
target=(0.0, 0.0, 0.0),
133+
up=(0.0, 0.0, 1.0)
134+
)
135+
model_matrix = Matrix44.identity()
136+
137+
# Main loop
138+
running = True
139+
angle = 0.0
140+
x_noise = pmma.Perlin()
141+
y_noise = pmma.Perlin()
142+
z_noise = pmma.Perlin()
143+
while running:
144+
for event in pygame.event.get():
145+
if event.type == pygame.QUIT:
146+
running = False
147+
148+
# Clear screen
149+
ctx.clear(0, 0, 0)
150+
ctx.enable(mgl.DEPTH_TEST)
151+
152+
# Rotate model matrix
153+
angle += 0.01
154+
model_matrix = Matrix44.from_eulers((angle, angle / 2, angle / 3))
155+
156+
for i, position in enumerate(instance_positions):
157+
# Use the starting position and time as inputs to the noise function
158+
r = x_noise.generate_2D_perlin_noise(position[0]/25 + pmma.get_application_run_time()/5, position[1]/25 + pmma.get_application_run_time()/5, new_range=[0, 1])
159+
g = y_noise.generate_2D_perlin_noise(position[1]/25 + pmma.get_application_run_time()/5, position[2]/25 + pmma.get_application_run_time()/5, new_range=[0, 1])
160+
b = z_noise.generate_2D_perlin_noise(position[2]/25 + pmma.get_application_run_time()/5, position[0]/25 + pmma.get_application_run_time()/5, new_range=[0, 1])
161+
162+
instance_colors[i] = (r, g, b)
163+
164+
# Update the color buffer
165+
vbo_instance_color.write(instance_colors.tobytes())
166+
167+
# Update uniforms
168+
projection.write(projection_matrix.astype('f4').tobytes())
169+
view.write(view_matrix.astype('f4').tobytes())
170+
model.write(model_matrix.astype('f4').tobytes())
171+
172+
# Render spheres
173+
vao.render(instances=len(instance_positions))
174+
175+
# Swap buffers
176+
pygame.display.flip()
177+
clock.tick(60)
178+
179+
pygame.quit()

0 commit comments

Comments
 (0)