import pygame from pygame.locals import * from OpenGL.GL import * from OpenGL.GLU import * import numpy as np from scipy import ndimage import random # Simple 3D Earth Viewer with atmosphere, clouds, terrain, and stars # Uses PyOpenGL and pygame for rendering def create_sphere(radius, slices, stacks): vertices = [] normals = [] texcoords = [] indices = [] for i in range(stacks + 1): V = i / stacks phi = V * np.pi for j in range(slices + 1): U = j / slices theta = U * 2 * np.pi x = radius * np.sin(phi) * np.cos(theta) y = radius * np.cos(phi) z = radius * np.sin(phi) * np.sin(theta) vertices.append([x, y, z]) nx, ny, nz = x/radius, y/radius, z/radius normals.append([nx, ny, nz]) texcoords.append([U, V]) for i in range(stacks): for j in range(slices): first = i * (slices + 1) + j second = first + slices + 1 indices.append(first) indices.append(second) indices.append(first + 1) indices.append(second) indices.append(second + 1) indices.append(first + 1) return np.array(vertices), np.array(normals), np.array(texcoords), np.array(indices) def create_terrain_noise(resolution=64): # Generate procedural terrain (heightmap) noise = np.random.randn(resolution, resolution) * 0.01 noise = ndimage.gaussian_filter(noise, sigma=2) return noise def create_starfield(num_stars=1000): stars = [] for _ in range(num_stars): theta = np.random.uniform(0, 2*np.pi) phi = np.arccos(1 - np.random.uniform(0, 1)) r = 100 + np.random.uniform(0, 10) x = r * np.sin(phi) * np.cos(theta) y = r * np.cos(phi) z = r * np.sin(phi) * np.sin(theta) stars.append([x, y, z]) return np.array(stars) def load_texture_from_solid_color(color, width=64, height=64): texture_data = np.zeros((height, width, 3), dtype=np.uint8) texture_data[:, :] = color texture_data = texture_data.tobytes() texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data) return texture_id def load_simple_earth_texture(): # Create a simple blue marble-like texture width, height = 1024, 512 texture_data = np.zeros((height, width, 3), dtype=np.uint8) for y in range(height): for x in range(width): # Simple Earth approximation with blue oceans and green/brown continents u, v = x / width, y / height if np.sin(2*np.pi*u) * np.sin(np.pi*v) > 0.5 or (u-0.3)**2 + (v-0.5)**2 < 0.02: texture_data[y, x] = [30, 100, 200] # Ocean elif np.random.rand() < 0.3: texture_data[y, x] = [100, 180, 80] # Land else: texture_data[y, x] = [30, 100, 200] texture_data = texture_data.tobytes() texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data) return texture_id class Earth3DViewer: def __init__(self): pygame.init() self.display = (800, 600) pygame.display.set_mode(self.display, DOUBLEBUF | OPENGL) pygame.display.set_caption("3D Earth Viewer") gluPerspective(45, (self.display[0]/self.display[1]), 0.1, 100.0) glTranslatef(0.0, 0.0, -15) # Create Earth self.vertices, self.normals, self.texcoords, self.indices = create_sphere(6.0, 32, 32) self.cloud_vertices, _, _, _ = create_sphere(6.05, 32, 32) # Slightly bigger for cloud layer self.atmosphere_vertices, _, _, _ = create_sphere(6.2, 32, 32) # Atmosphere glow # Load textures self.earth_texture = load_simple_earth_texture() self.cloud_texture = load_texture_from_solid_color([200, 200, 255], 64, 64) self.atmosphere_texture = load_texture_from_solid_color([150, 200, 255, 100], 64, 64) # Create procedural terrain displacement (conceptual) self.terrain_noise = create_terrain_noise(32) # Stars self.stars = create_starfield(500) self.rotation = 0 self.cloud_rotation = 0 def draw_sphere(self, vertices, texture_id, blend=False, alpha=1.0): if blend: glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE) glDepthMask(GL_FALSE) glBindTexture(GL_TEXTURE_2D, texture_id) glEnable(GL_TEXTURE_2D) glBegin(GL_TRIANGLES) for i in range(0, len(self.indices), 3): for j in range(3): idx = self.indices[i + j] glTexCoord2fv(self.texcoords[idx]) glVertex3fv(vertices[idx]) glEnd() glDisable(GL_TEXTURE_2D) if blend: glDisable(GL_BLEND) glDepthMask(GL_TRUE) def draw_stars(self): glEnable(GL_POINT_SMOOTH) glPointSize(1.0) glColor3f(1, 1, 1) glBegin(GL_POINTS) for star in self.stars: glVertex3fv(star) glEnd() def render(self): while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() return # Handle rotation self.rotation += 0.5 self.cloud_rotation += 0.6 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glEnable(GL_DEPTH_TEST) # Draw stars (background) glPushMatrix() glRotatef(self.rotation * 0.01, 0, 1, 0) self.draw_stars() glPopMatrix() # Draw Earth glPushMatrix() glRotatef(self.rotation, 0, 1, 0) self.draw_sphere(self.vertices, self.earth_texture) glPopMatrix() # Draw clouds glPushMatrix() glRotatef(self.cloud_rotation, 0, 1, 0) self.draw_sphere(self.cloud_vertices, self.cloud_texture, blend=True, alpha=0.3) glPopMatrix() # Draw atmosphere glow glPushMatrix() glRotatef(self.rotation, 0, 1, 0) glColor4f(0.5, 0.8, 1.0, 0.1) self.draw_sphere(self.atmosphere_vertices, self.atmosphere_texture, blend=True, alpha=0.1) glPopMatrix() pygame.display.flip() pygame.time.wait(30) if __name__ == "__main__": app = Earth3DViewer() app.render()