Skip to content

Instantly share code, notes, and snippets.

@jeanCarloMachado
Last active December 21, 2025 20:20
Show Gist options
  • Select an option

  • Save jeanCarloMachado/47fca792c1b6e6359804e9ee5d6db8b1 to your computer and use it in GitHub Desktop.

Select an option

Save jeanCarloMachado/47fca792c1b6e6359804e9ee5d6db8b1 to your computer and use it in GitHub Desktop.
3d world simulator
#!/usr/bin/env python3
"""
3D World Simulator - An impressive 3D world with mountains, buildings, and exploration
Controls:
- WASD: Move forward/left/backward/right
- Q/E: Move down/up
- Mouse: Look around
- Shift: Sprint
- ESC: Exit
"""
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import math
import sys
import random
import time
class Camera:
"""First-person camera"""
def __init__(self):
self.pos = [0.0, 15.0, 45.0] # Start at good viewing height
self.rot = [0.0, 0.0] # pitch, yaw - look straight ahead
self.speed = 20.0
self.sensitivity = 0.15 # Mouse sensitivity
def mouse_move(self, dx, dy):
self.rot[1] += dx * self.sensitivity
self.rot[0] += dy * self.sensitivity
self.rot[0] = max(-89, min(89, self.rot[0]))
def move(self, forward=0, right=0, up=0):
# Convert yaw to radians - note we use negative because of OpenGL camera
yaw_rad = math.radians(-self.rot[1])
if forward:
# Forward/backward along view direction
self.pos[0] += forward * math.sin(yaw_rad)
self.pos[2] += forward * math.cos(yaw_rad)
if right:
# Right/left perpendicular to view (90 degrees from forward)
self.pos[0] += right * math.sin(yaw_rad + math.pi/2)
self.pos[2] += right * math.cos(yaw_rad + math.pi/2)
if up:
# Up/down (vertical)
self.pos[1] += up
def update(self):
glRotatef(-self.rot[0], 1, 0, 0)
glRotatef(-self.rot[1], 0, 1, 0)
glTranslatef(-self.pos[0], -self.pos[1], -self.pos[2])
def draw_cube(size=1.0):
"""Draw a simple cube"""
s = size / 2
glBegin(GL_QUADS)
# Front
glNormal3f(0, 0, 1)
glVertex3f(-s, -s, s)
glVertex3f(s, -s, s)
glVertex3f(s, s, s)
glVertex3f(-s, s, s)
# Back
glNormal3f(0, 0, -1)
glVertex3f(-s, -s, -s)
glVertex3f(-s, s, -s)
glVertex3f(s, s, -s)
glVertex3f(s, -s, -s)
# Top
glNormal3f(0, 1, 0)
glVertex3f(-s, s, -s)
glVertex3f(-s, s, s)
glVertex3f(s, s, s)
glVertex3f(s, s, -s)
# Bottom
glNormal3f(0, -1, 0)
glVertex3f(-s, -s, -s)
glVertex3f(s, -s, -s)
glVertex3f(s, -s, s)
glVertex3f(-s, -s, s)
# Right
glNormal3f(1, 0, 0)
glVertex3f(s, -s, -s)
glVertex3f(s, s, -s)
glVertex3f(s, s, s)
glVertex3f(s, -s, s)
# Left
glNormal3f(-1, 0, 0)
glVertex3f(-s, -s, -s)
glVertex3f(-s, -s, s)
glVertex3f(-s, s, s)
glVertex3f(-s, s, -s)
glEnd()
def draw_pyramid(size=1.0):
"""Draw a pyramid"""
s = size / 2
h = size * 1.5
glBegin(GL_TRIANGLES)
# Front
glNormal3f(0, 0.7, 0.7)
glVertex3f(0, h, 0)
glVertex3f(-s, 0, s)
glVertex3f(s, 0, s)
# Right
glNormal3f(0.7, 0.7, 0)
glVertex3f(0, h, 0)
glVertex3f(s, 0, s)
glVertex3f(s, 0, -s)
# Back
glNormal3f(0, 0.7, -0.7)
glVertex3f(0, h, 0)
glVertex3f(s, 0, -s)
glVertex3f(-s, 0, -s)
# Left
glNormal3f(-0.7, 0.7, 0)
glVertex3f(0, h, 0)
glVertex3f(-s, 0, -s)
glVertex3f(-s, 0, s)
glEnd()
# Base
glBegin(GL_QUADS)
glNormal3f(0, -1, 0)
glVertex3f(-s, 0, -s)
glVertex3f(s, 0, -s)
glVertex3f(s, 0, s)
glVertex3f(-s, 0, s)
glEnd()
def draw_building(width, height, depth):
"""Draw a building"""
glPushMatrix()
glScalef(width, height, depth)
draw_cube(1.0)
glPopMatrix()
def draw_tree(x, z, color=None):
"""Draw a colorful tree"""
glPushMatrix()
glTranslatef(x, 0, z)
# Trunk
glColor3f(0.4, 0.25, 0.1)
glPushMatrix()
glTranslatef(0, 1.5, 0)
glScalef(0.3, 3, 0.3)
draw_cube(1.0)
glPopMatrix()
# Colorful leaves
if color:
glColor3f(*color)
else:
glColor3f(0.1, 0.6, 0.1)
glTranslatef(0, 4, 0)
draw_pyramid(2.5)
glPopMatrix()
def draw_bird(x, y, z, phase):
"""Draw a simple flying bird"""
glPushMatrix()
glTranslatef(x, y, z)
# Body
glColor3f(0.2, 0.2, 0.3)
glScalef(0.5, 0.3, 0.8)
draw_cube(1.0)
glPopMatrix()
# Wings flapping
wing_angle = math.sin(phase) * 45
# Left wing
glPushMatrix()
glTranslatef(x - 0.4, y, z)
glRotatef(wing_angle, 0, 0, 1)
glColor3f(0.3, 0.3, 0.4)
glScalef(1.2, 0.1, 0.6)
draw_cube(1.0)
glPopMatrix()
# Right wing
glPushMatrix()
glTranslatef(x + 0.4, y, z)
glRotatef(-wing_angle, 0, 0, 1)
glColor3f(0.3, 0.3, 0.4)
glScalef(1.2, 0.1, 0.6)
draw_cube(1.0)
glPopMatrix()
def draw_cloud(x, y, z, size=1.0):
"""Draw a fluffy cloud"""
glColor3f(1.0, 1.0, 1.0)
# Multiple spheres to make fluffy cloud
cloud_parts = [
(0, 0, 0, 1.0),
(-0.7, 0, 0, 0.8),
(0.7, 0, 0, 0.8),
(-0.4, 0.3, 0, 0.6),
(0.4, 0.3, 0, 0.6),
(0, 0.4, 0, 0.5),
]
for dx, dy, dz, scale in cloud_parts:
glPushMatrix()
glTranslatef(x + dx * size, y + dy * size, z + dz * size)
glScalef(scale * size, scale * size * 0.7, scale * size)
draw_cube(1.0)
glPopMatrix()
def draw_person(x, y, z, rot, color):
"""Draw a simple person"""
glPushMatrix()
glTranslatef(x, y, z)
glRotatef(rot, 0, 1, 0)
# Head
glColor3f(0.9, 0.7, 0.6)
glPushMatrix()
glTranslatef(0, 1.5, 0)
glScalef(0.4, 0.4, 0.4)
draw_cube(1.0)
glPopMatrix()
# Body
glColor3f(*color)
glPushMatrix()
glTranslatef(0, 0.8, 0)
glScalef(0.5, 0.8, 0.3)
draw_cube(1.0)
glPopMatrix()
# Legs
glColor3f(0.2, 0.2, 0.4)
for offset in [-0.15, 0.15]:
glPushMatrix()
glTranslatef(offset, 0.2, 0)
glScalef(0.15, 0.4, 0.15)
draw_cube(1.0)
glPopMatrix()
glPopMatrix()
def draw_river():
"""Draw a winding river"""
# River is blue and flows across the terrain
glColor3f(0.2, 0.5, 0.9)
glBegin(GL_QUADS)
segments = 50
for i in range(segments):
z = -60 + i * 2.5
# River winds left and right
center_x = math.sin(z * 0.1) * 15
width = 6
z_next = z + 2.5
center_x_next = math.sin(z_next * 0.1) * 15
# River quad
glNormal3f(0, 1, 0)
glVertex3f(center_x - width, 0.1, z)
glVertex3f(center_x + width, 0.1, z)
glVertex3f(center_x_next + width, 0.1, z_next)
glVertex3f(center_x_next - width, 0.1, z_next)
glEnd()
def draw_mountain(x, z, height, base_size):
"""Draw a mountain grounded at y=0"""
# Mountain base - starts from ground
glColor3f(0.5, 0.5, 0.55)
glPushMatrix()
glTranslatef(x, 0, z)
glScalef(base_size, height, base_size)
draw_pyramid(1.0)
glPopMatrix()
# Snow peak
glColor3f(0.95, 0.95, 1.0)
glPushMatrix()
glTranslatef(x, height * 0.7, z)
glScalef(base_size * 0.4, height * 0.4, base_size * 0.4)
draw_pyramid(1.0)
glPopMatrix()
def draw_sun(x, y, z):
"""Draw a bright sun"""
glColor3f(1.0, 1.0, 0.3) # Bright yellow
# Sun center
glPushMatrix()
glTranslatef(x, y, z)
glScalef(4, 4, 4)
draw_cube(1.0)
glPopMatrix()
# Sun rays
num_rays = 12
for i in range(num_rays):
angle = i * (360 / num_rays)
rad = math.radians(angle)
ray_x = x + math.cos(rad) * 6
ray_y = y + math.sin(rad) * 6
glColor3f(1.0, 0.9, 0.2)
glPushMatrix()
glTranslatef(ray_x, ray_y, z)
glRotatef(angle, 0, 0, 1)
glScalef(2, 0.5, 0.5)
draw_cube(1.0)
glPopMatrix()
def draw_moon(x, y, z):
"""Draw a bright moon"""
# Make moon brighter and more visible
glColor3f(1.0, 1.0, 0.95) # Bright white/cream
# Moon body - larger
glPushMatrix()
glTranslatef(x, y, z)
glScalef(5, 5, 5) # Bigger moon
draw_cube(1.0)
glPopMatrix()
# Craters (darker spots)
glColor3f(0.8, 0.8, 0.85)
crater_positions = [
(0.8, 0.8, 0),
(-1.2, -0.5, 0),
(0.5, -1.0, 0),
(-0.6, 1.0, 0),
]
for cx, cy, cz in crater_positions:
glPushMatrix()
glTranslatef(x + cx, y + cy, z + cz + 0.5)
glScalef(0.8, 0.8, 0.2)
draw_cube(1.0)
glPopMatrix()
# Moon glow effect (semi-transparent halo)
glColor4f(1.0, 1.0, 0.9, 0.3)
glPushMatrix()
glTranslatef(x, y, z)
glScalef(7, 7, 7)
draw_cube(1.0)
glPopMatrix()
def draw_stars(time_of_day):
"""Draw twinkling stars at night"""
# Only visible at night
if 0.2 < time_of_day < 0.8:
return
random.seed(42) # Consistent star positions
num_stars = 100
glDisable(GL_LIGHTING)
glPointSize(2.0)
glBegin(GL_POINTS)
for i in range(num_stars):
x = random.uniform(-100, 100)
y = random.uniform(20, 80)
z = random.uniform(-120, -80)
# Twinkle effect
brightness = 0.8 + 0.2 * math.sin(time_of_day * 20 + i)
glColor3f(brightness, brightness, brightness)
glVertex3f(x, y, z)
glEnd()
glEnable(GL_LIGHTING)
def draw_flower(x, z, color):
"""Draw a simple flower"""
# Stem
glColor3f(0.2, 0.6, 0.2)
glPushMatrix()
glTranslatef(x, 0.3, z)
glScalef(0.05, 0.6, 0.05)
draw_cube(1.0)
glPopMatrix()
# Flower petals (5 small cubes around center)
glColor3f(*color)
petal_positions = [
(0, 0),
(0.15, 0),
(-0.15, 0),
(0, 0.15),
(0, -0.15),
]
for px, pz in petal_positions:
glPushMatrix()
glTranslatef(x + px, 0.7, z + pz)
glScalef(0.15, 0.15, 0.15)
draw_cube(1.0)
glPopMatrix()
def draw_castle():
"""Draw a medieval castle"""
# Main keep
glColor3f(0.6, 0.6, 0.65)
glPushMatrix()
glTranslatef(0, 0, 0)
draw_building(12, 18, 12)
glPopMatrix()
# Four corner towers
tower_positions = [
(-7, -7), (7, -7), (-7, 7), (7, 7)
]
for tx, tz in tower_positions:
# Tower body
glColor3f(0.55, 0.55, 0.6)
glPushMatrix()
glTranslatef(tx, 12, tz)
glScalef(2.5, 24, 2.5)
draw_cube(1.0)
glPopMatrix()
# Crenellations (battlements)
for i in range(4):
angle = i * 90
rad = math.radians(angle)
bx = tx + math.cos(rad) * 1.5
bz = tz + math.sin(rad) * 1.5
glColor3f(0.5, 0.5, 0.55)
glPushMatrix()
glTranslatef(bx, 25, bz)
glScalef(0.8, 2, 0.8)
draw_cube(1.0)
glPopMatrix()
# Tower roof
glColor3f(0.7, 0.2, 0.2)
glPushMatrix()
glTranslatef(tx, 26, tz)
draw_pyramid(3)
glPopMatrix()
# Main gate
glColor3f(0.3, 0.2, 0.1)
glPushMatrix()
glTranslatef(0, 3, 6.5)
glScalef(3, 5, 0.5)
draw_cube(1.0)
glPopMatrix()
# Windows
window_positions = [
(-3, 10, 6.5), (3, 10, 6.5),
(-3, 14, 6.5), (3, 14, 6.5),
]
glColor3f(0.3, 0.3, 0.5)
for wx, wy, wz in window_positions:
glPushMatrix()
glTranslatef(wx, wy, wz)
glScalef(1, 1.5, 0.3)
draw_cube(1.0)
glPopMatrix()
def draw_ground():
"""Draw ground with grass texture pattern"""
size = 150
grid = 40 # More detail
step = size / grid
glBegin(GL_QUADS)
for i in range(grid):
for j in range(grid):
x = -size/2 + i * step
z = -size/2 + j * step
# Gentle rolling hills
h1 = math.sin(x * 0.15) * math.cos(z * 0.15) * 3
h2 = math.sin((x + step) * 0.15) * math.cos(z * 0.15) * 3
h3 = math.sin((x + step) * 0.15) * math.cos((z + step) * 0.15) * 3
h4 = math.sin(x * 0.15) * math.cos((z + step) * 0.15) * 3
# Grass texture - vary green shades for texture
base_seed = int((x * 10 + z * 10) * 1000) % 100
if base_seed < 70:
glColor3f(0.3, 0.7, 0.3) # Dark grass
elif base_seed < 85:
glColor3f(0.4, 0.8, 0.3) # Medium grass
elif base_seed < 95:
glColor3f(0.5, 0.85, 0.4) # Light grass
else:
glColor3f(0.6, 0.9, 0.5) # Very light grass
glNormal3f(0, 1, 0)
glVertex3f(x, h1, z)
glVertex3f(x + step, h2, z)
glVertex3f(x + step, h3, z + step)
glVertex3f(x, h4, z + step)
glEnd()
def draw_compass():
"""Draw a small compass/direction indicator"""
# Draw at origin for reference
# North (positive Z) - Red line
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_LINES)
glVertex3f(0, 0.5, 0)
glVertex3f(0, 0.5, 10)
glEnd()
# East (positive X) - Green line
glColor3f(0.0, 1.0, 0.0)
glBegin(GL_LINES)
glVertex3f(0, 0.5, 0)
glVertex3f(10, 0.5, 0)
glEnd()
# Up (positive Y) - Blue line
glColor3f(0.0, 0.0, 1.0)
glBegin(GL_LINES)
glVertex3f(0, 0, 0)
glVertex3f(0, 10, 0)
glEnd()
# Center marker
glColor3f(1.0, 1.0, 0.0)
glPushMatrix()
glTranslatef(0, 0.5, 0)
glScalef(1, 1, 1)
draw_cube(1)
glPopMatrix()
class DayNightCycle:
"""Manages day/night cycle and weather"""
def __init__(self):
self.time_of_day = 0.35 # Start mid-morning (0.25=sunrise, 0.5=noon, 0.75=sunset)
self.cycle_speed = 0.02 # How fast day progresses (full cycle = 50 seconds)
self.is_raining = False
self.rain_timer = 0
self.next_rain_check = random.uniform(10, 30) # Random time until next rain check
def update(self, dt):
"""Update day/night cycle"""
self.time_of_day += self.cycle_speed * dt
if self.time_of_day >= 1.0:
self.time_of_day -= 1.0
# Random rain events
self.rain_timer += dt
if self.rain_timer >= self.next_rain_check:
# Random chance to start/stop rain
if random.random() < 0.3: # 30% chance to change rain state
self.is_raining = not self.is_raining
self.rain_timer = 0
self.next_rain_check = random.uniform(15, 40)
def get_sun_position(self):
"""Calculate sun position based on time of day"""
# Sun rises at 0.25, sets at 0.75
# Move in an arc across the sky from east to west
angle = (self.time_of_day - 0.25) * math.pi # 0 to pi (sunrise to sunset)
# Sun moves perpendicular to player starting direction
x = math.cos(angle) * 120 # Move along X axis (east-west)
y = math.sin(angle) * 80 + 10 # Arc height (higher)
z = -70 # Far away from player start
return x, max(y, -20), z # Don't go too far below
def get_moon_position(self):
"""Calculate moon position (opposite of sun)"""
angle = (self.time_of_day + 0.5) * 2 * math.pi # Opposite of sun
# Moon moves opposite to sun
moon_angle = (self.time_of_day + 0.5 - 0.25) * math.pi
x = math.cos(moon_angle) * 120
y = math.sin(moon_angle) * 80 + 10
z = -70
return x, max(y, -20), z
def is_daytime(self):
"""Check if it's daytime"""
return 0.2 < self.time_of_day < 0.8
def get_sky_color(self):
"""Get sky color based on time of day"""
if self.is_raining:
return (0.4, 0.5, 0.6)
# Dawn (0.2 - 0.3)
if 0.2 < self.time_of_day < 0.3:
t = (self.time_of_day - 0.2) * 10
r = 0.5 + t * 0.1
g = 0.5 + t * 0.3
b = 0.7 + t * 0.3
return (r, g, b)
# Day (0.3 - 0.7)
elif 0.3 <= self.time_of_day <= 0.7:
return (0.6, 0.8, 1.0)
# Dusk (0.7 - 0.8)
elif 0.7 < self.time_of_day < 0.8:
t = (self.time_of_day - 0.7) * 10
r = 0.6 + t * 0.3
g = 0.8 - t * 0.5
b = 1.0 - t * 0.6
return (r, g, b)
# Night (0.8 - 1.0 and 0.0 - 0.2)
else:
return (0.1, 0.1, 0.2)
def get_ambient_light(self):
"""Get ambient light level"""
if self.is_daytime():
return 0.5
else:
return 0.2
class RainSystem:
"""Particle system for rain"""
def __init__(self, num_drops=300):
self.drops = []
self.num_drops = num_drops
self.reset_drops()
def reset_drops(self):
"""Initialize rain drops"""
self.drops = []
for _ in range(self.num_drops):
self.drops.append({
'x': random.uniform(-80, 80),
'y': random.uniform(10, 60),
'z': random.uniform(-80, 80),
'speed': random.uniform(30, 50),
})
def update(self, dt):
"""Update rain drop positions"""
for drop in self.drops:
drop['y'] -= drop['speed'] * dt
# Reset to top when hit ground
if drop['y'] < 0:
drop['y'] = random.uniform(50, 60)
drop['x'] = random.uniform(-80, 80)
drop['z'] = random.uniform(-80, 80)
drop['speed'] = random.uniform(30, 50)
def draw(self):
"""Draw rain drops"""
glColor4f(0.7, 0.7, 0.9, 0.6) # Slightly transparent blue-gray
glBegin(GL_LINES)
for drop in self.drops:
x, y, z = drop['x'], drop['y'], drop['z']
# Draw as small vertical lines
glVertex3f(x, y, z)
glVertex3f(x, y - 1.5, z) # Line length
glEnd()
def draw_world(t, rain_system=None, day_night=None):
"""Draw all world objects with animation time t"""
if day_night:
# Draw stars at night
draw_stars(day_night.time_of_day)
# Draw sun or moon based on time
if day_night.is_daytime():
sun_x, sun_y, sun_z = day_night.get_sun_position()
if sun_y > 0: # Only draw if above horizon
draw_sun(sun_x, sun_y, sun_z)
else:
moon_x, moon_y, moon_z = day_night.get_moon_position()
if moon_y > 0: # Only draw if above horizon
draw_moon(moon_x, moon_y, moon_z)
# Draw compass at origin for navigation reference
draw_compass()
# Ground with grass texture
draw_ground()
# River flowing through the land
draw_river()
# Flowers scattered around
random.seed(100) # Different seed than trees
flower_colors = [
(1.0, 0.2, 0.3), # Red
(1.0, 0.5, 0.0), # Orange
(1.0, 1.0, 0.2), # Yellow
(1.0, 0.3, 0.8), # Pink
(0.6, 0.2, 0.8), # Purple
(0.2, 0.5, 1.0), # Blue
(1.0, 1.0, 1.0), # White
]
# Lots of flowers in open areas
for i in range(150):
x = random.uniform(-60, 60)
z = random.uniform(-60, 60)
# Don't place on river or too close to buildings
river_center = math.sin(z * 0.1) * 15
if abs(x - river_center) > 8: # Not on river
color = flower_colors[i % len(flower_colors)]
draw_flower(x, z, color)
# Draw rain if available
if rain_system:
rain_system.draw()
# Mountains in the background
mountain_positions = [
(-60, -50, 35, 20), # left back
(-50, -60, 40, 22), # left back
(60, -50, 38, 21), # right back
(50, -60, 42, 24), # right back
(0, -70, 45, 26), # center back (tallest)
(30, -55, 32, 18), # right mid
(-30, -55, 34, 19), # left mid
]
for mx, mz, height, base in mountain_positions:
draw_mountain(mx, mz, height, base)
# Castle on a hill
glPushMatrix()
glTranslatef(-45, 8, 30)
draw_castle()
glPopMatrix()
# Colorful houses scattered around
houses = [
((-15, -15), (1.0, 0.3, 0.3), 6), # Red house
((15, -15), (0.3, 0.3, 1.0), 5), # Blue house
((-15, 15), (1.0, 0.8, 0.2), 7), # Yellow house
((15, 15), (0.8, 0.3, 1.0), 5), # Purple house
((25, 5), (1.0, 0.5, 0.2), 8), # Orange house
((-25, 5), (0.5, 1.0, 0.8), 5), # Mint house
]
for (x, z), color, height in houses:
glColor3f(*color)
glPushMatrix()
glTranslatef(x, height/2, z)
draw_building(4, height, 4)
glPopMatrix()
# Roof
glColor3f(color[0] * 0.7, color[1] * 0.7, color[2] * 0.7)
glPushMatrix()
glTranslatef(x, height + 1, z)
draw_pyramid(3.5)
glPopMatrix()
# Green trees (many more!)
random.seed(42)
green_color = (0.1, 0.7, 0.1)
# Forest on left side
for i in range(40):
x = random.uniform(-60, -30)
z = random.uniform(-40, 40)
draw_tree(x, z, green_color)
# Forest on right side
for i in range(40):
x = random.uniform(30, 60)
z = random.uniform(-40, 40)
draw_tree(x, z, green_color)
# Trees near castle
for i in range(20):
angle = random.uniform(0, 360)
rad = math.radians(angle)
distance = random.uniform(15, 25)
x = -45 + math.cos(rad) * distance
z = 30 + math.sin(rad) * distance
draw_tree(x, z, green_color)
# Colorful rainbow trees (fewer, more special)
tree_colors = [
(1.0, 0.3, 0.5), # Pink
(0.5, 1.0, 0.3), # Lime
(0.3, 0.5, 1.0), # Sky blue
(1.0, 0.8, 0.2), # Yellow
(0.8, 0.3, 1.0), # Purple
]
for i in range(15):
x = random.uniform(-25, 25)
z = random.uniform(-25, 25)
# Avoid house areas
if abs(x) > 10 or abs(z) > 10:
color = tree_colors[i % len(tree_colors)]
draw_tree(x, z, color)
# People walking around
person_paths = [
# Path 1: Walking in a circle
(0, lambda t: math.cos(t * 0.5) * 20, lambda t: math.sin(t * 0.5) * 20, (0.8, 0.3, 0.3)),
# Path 2: Walking along path
(1, lambda t: (t * 2) % 40 - 20, lambda t: 10, (0.3, 0.8, 0.3)),
# Path 3: Walking other direction
(2, lambda t: 15, lambda t: -(t * 2.5) % 40 + 20, (0.3, 0.3, 0.8)),
# Path 4: Walking near castle
(3, lambda t: -45 + math.cos(t * 0.4) * 12, lambda t: 30 + math.sin(t * 0.4) * 12, (0.8, 0.8, 0.3)),
# Path 5: Walking by river
(4, lambda t: math.sin((t * 2) * 0.1) * 15, lambda t: (t * 2) % 40 - 20, (0.8, 0.3, 0.8)),
# Path 6: Random walker
(5, lambda t: math.sin(t * 0.3) * 15, lambda t: math.cos(t * 0.4) * 15, (0.5, 0.8, 0.8)),
]
for person_id, x_func, z_func, color in person_paths:
x = x_func(t)
z = z_func(t)
# Calculate rotation based on movement direction
dx = x_func(t + 0.1) - x
dz = z_func(t + 0.1) - z
rot = math.degrees(math.atan2(dx, dz))
draw_person(x, 0, z, rot, color)
# Colorful cubes floating around (fewer)
for i in range(5):
angle = i * 72
rad = math.radians(angle)
x = math.cos(rad) * 30
z = math.sin(rad) * 30
y = 8 + math.sin(t * 0.5 + i) * 2
color = [
(math.sin(i * 0.7) + 1) / 2,
(math.cos(i * 0.9) + 1) / 2,
(math.sin(i * 1.1) + 1) / 2,
]
glColor3f(*color)
glPushMatrix()
glTranslatef(x, y, z)
glRotatef(t * 30 + i * 45, 1, 1, 0)
draw_cube(2)
glPopMatrix()
# Animated birds flying around
for i in range(12):
angle = (t * 0.3 + i * 30) % 360
rad = math.radians(angle)
radius = 40 + i * 3
x = math.cos(rad) * radius
z = math.sin(rad) * radius
y = 20 + math.sin(t + i) * 5
phase = t * 5 + i
draw_bird(x, y, z, phase)
# Floating clouds
cloud_positions = [
(30, 35, -20, 4),
(-40, 40, -30, 5),
(20, 38, 40, 3.5),
(-30, 42, 30, 4.5),
(50, 45, 10, 3),
(-50, 37, -10, 4),
(0, 50, -50, 5),
(10, 43, -35, 4.2),
]
for i, (base_x, base_y, base_z, size) in enumerate(cloud_positions):
# Clouds drift slowly
x = base_x + math.sin(t * 0.1 + i) * 10
y = base_y + math.cos(t * 0.15 + i) * 3
z = base_z + math.cos(t * 0.1 + i) * 10
draw_cloud(x, y, z, size)
def main():
"""Main function"""
# Initialize Pygame
pygame.init()
width, height = 1400, 900
try:
pygame.display.set_mode((width, height), DOUBLEBUF | OPENGL)
pygame.display.set_caption("โ˜€๏ธ๐ŸŒ™ Magical 3D World - Day/Night Cycle, Random Rain & More! โญ")
except pygame.error as e:
print(f"\nโš ๏ธ Display Error: {e}")
print("Please run from a terminal with display access")
return
# OpenGL setup
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
# Enable blending for transparency (rain)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
# Sun-like lighting
glLightfv(GL_LIGHT0, GL_POSITION, [1, 2, 1, 0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.55, 1])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.95, 0.8, 1])
# Fog for atmosphere
glEnable(GL_FOG)
glFogi(GL_FOG_MODE, GL_LINEAR)
glFogfv(GL_FOG_COLOR, [0.6, 0.75, 0.95, 1.0])
glFogf(GL_FOG_START, 60.0)
glFogf(GL_FOG_END, 180.0)
# Perspective
glMatrixMode(GL_PROJECTION)
gluPerspective(75, width / height, 0.5, 250.0)
glMatrixMode(GL_MODELVIEW)
# Hide and grab cursor
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
pygame.mouse.set_pos(width // 2, height // 2)
camera = Camera()
clock = pygame.time.Clock()
rain_system = RainSystem(num_drops=300)
day_night = DayNightCycle()
manual_rain_control = False # If True, user controls rain; if False, automatic
print("\n" + "="*75)
print(" โ˜€๏ธ๐ŸŒ™ MAGICAL 3D WORLD - Day/Night Cycle & Random Rain ๐ŸŒง๏ธโญ")
print("="*75)
print("\n๐ŸŒ FEATURES:")
print(" โ€ข โ˜€๏ธ๐ŸŒ™ Full day/night cycle (50 second cycle)")
print(" โ€ข โ˜€๏ธ Sun moves across the sky in realistic arc (rises/sets)")
print(" โ€ข ๐ŸŒ™ BRIGHT MOON with craters and glow at night!")
print(" โ€ข โญ 100 twinkling stars at night")
print(" โ€ข โฐ Quick time controls: N=Night, M=Noon")
print(" โ€ข ๐ŸŒˆ Dynamic sky colors (dawn, day, dusk, night)")
print(" โ€ข ๐ŸŒง๏ธ Random rain events at any time!")
print(" โ€ข ๐ŸŒธ 150 colorful flowers (red, orange, yellow, pink, purple, blue, white)")
print(" โ€ข ๐ŸŒฟ Textured grass (varied green shades)")
print(" โ€ข ๐Ÿฐ Medieval castle with 4 towers on a hill")
print(" โ€ข ๐Ÿ”๏ธ 7 grounded mountains in the background")
print(" โ€ข ๐ŸŒŠ Winding river flowing through the land")
print(" โ€ข ๐ŸŒฒ 100+ green trees (dense forests on both sides)")
print(" โ€ข ๐ŸŒณ 15 rainbow trees (pink, lime, blue, yellow, purple)")
print(" โ€ข ๐Ÿšถ 6 people walking on different paths")
print(" โ€ข ๐Ÿก 6 colorful houses with roofs")
print(" โ€ข ๐Ÿฆ 12 animated birds flying in circles")
print(" โ€ข โ˜๏ธ 8 floating clouds drifting in the sky")
print(" โ€ข ๐Ÿ“ฆ 5 spinning magical cubes")
print("\n๐ŸŽฎ CONTROLS:")
print(" W/S or โ†‘/โ†“ - Move forward/backward")
print(" A/D or โ†/โ†’ - Move left/right")
print(" Q/E - Move down/up")
print(" Shift - Sprint (3x faster)")
print(" Mouse - Look around (360ยฐ)")
print(" N - Jump to NIGHT (see the moon!) ๐ŸŒ™")
print(" M - Jump to NOON (midday) โ˜€๏ธ")
print(" R - Toggle manual rain")
print(" -/+ - Mouse sensitivity")
print(" ESC - Exit")
print("\n๐Ÿ’ก TIPS:")
print(" โ€ข Press N to jump to NIGHT and see the MOON immediately! ๐ŸŒ™")
print(" โ€ข Press M to jump back to NOON โ˜€๏ธ")
print(" โ€ข Moon is BRIGHT with craters and a glow - look up at night!")
print(" โ€ข 100 twinkling stars appear at night")
print(" โ€ข Full day/night cycle takes 50 seconds")
print(" โ€ข Sun rises east to west in realistic arc")
print(" โ€ข Rain happens RANDOMLY - any time of day or night!")
print(" โ€ข Sky colors: Dawn (orange) โ†’ Day (blue) โ†’ Dusk (pink) โ†’ Night (dark)")
print(" โ€ข Console shows: โ˜€๏ธ=day, ๐ŸŒ™=night, ๐ŸŒง๏ธ=raining")
print(" โ€ข Fly high (E) to watch sun/moon move across the sky!")
print("="*70 + "\n")
running = True
frame = 0
start_time = time.time()
while running:
dt = clock.tick(60) / 1000.0
frame += 1
t = time.time() - start_time # Animation time
# Events
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.key == K_MINUS or event.key == K_KP_MINUS:
camera.sensitivity = max(0.05, camera.sensitivity - 0.05)
print(f"๐ŸŽฏ Mouse sensitivity: {camera.sensitivity:.2f}")
elif event.key == K_EQUALS or event.key == K_KP_PLUS:
camera.sensitivity = min(0.5, camera.sensitivity + 0.05)
print(f"๐ŸŽฏ Mouse sensitivity: {camera.sensitivity:.2f}")
elif event.key == K_r:
manual_rain_control = not manual_rain_control
if manual_rain_control:
day_night.is_raining = not day_night.is_raining
print(f"๐ŸŒง๏ธ Manual rain control: {'ON' if day_night.is_raining else 'OFF'}")
else:
print(f"๐ŸŒง๏ธ Automatic rain mode (random)")
elif event.key == K_n:
# Skip to night to see the moon
day_night.time_of_day = 0.0 # Midnight
hour = int(day_night.time_of_day * 24)
print(f"๐ŸŒ™ Jumped to night! Time: {hour:02d}:00 - Look for the moon!")
elif event.key == K_m:
# Skip to noon
day_night.time_of_day = 0.5 # Noon
hour = int(day_night.time_of_day * 24)
print(f"โ˜€๏ธ Jumped to noon! Time: {hour:02d}:00")
# Mouse look - smooth and responsive
mouse_x, mouse_y = pygame.mouse.get_pos()
center_x, center_y = width // 2, height // 2
dx = mouse_x - center_x
dy = mouse_y - center_y
# Apply mouse movement with deadzone for tiny jitters
if abs(dx) > 0 or abs(dy) > 0:
camera.mouse_move(dx, dy)
pygame.mouse.set_pos(center_x, center_y)
# Keyboard movement
keys = pygame.key.get_pressed()
speed = camera.speed * dt
if keys[K_LSHIFT] or keys[K_RSHIFT]:
speed *= 3
# WASD or Arrow keys for movement
forward = (keys[K_w] or keys[K_UP]) - (keys[K_s] or keys[K_DOWN])
right = (keys[K_d] or keys[K_RIGHT]) - (keys[K_a] or keys[K_LEFT])
up = keys[K_e] - keys[K_q]
camera.move(forward * speed, right * speed, up * speed)
# Update day/night cycle
if not manual_rain_control:
day_night.update(dt)
# Update rain
if day_night.is_raining:
rain_system.update(dt)
# Debug info every 2 seconds
if frame % 120 == 0:
x, y, z = camera.pos
yaw_dir = "N" if -45 < camera.rot[1] <= 45 else "E" if 45 < camera.rot[1] <= 135 else "S" if 135 < camera.rot[1] <= 225 or camera.rot[1] <= -135 else "W"
# Time display
hour = int(day_night.time_of_day * 24)
time_icon = "โ˜€๏ธ" if day_night.is_daytime() else "๐ŸŒ™"
rain_status = "๐ŸŒง๏ธ" if day_night.is_raining else ""
print(f"๐Ÿ“ Pos: ({x:5.1f}, {y:5.1f}, {z:5.1f}) | {yaw_dir} | {time_icon} {hour:02d}:00 {rain_status}")
# Render
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# Sky color based on time of day
sky_r, sky_g, sky_b = day_night.get_sky_color()
glClearColor(sky_r, sky_g, sky_b, 1.0)
# Update ambient lighting based on time
ambient = day_night.get_ambient_light()
glLightfv(GL_LIGHT0, GL_AMBIENT, [ambient, ambient, ambient + 0.1, 1])
glLoadIdentity()
camera.update()
# Draw sky gradient during daytime (not raining)
if day_night.is_daytime() and not day_night.is_raining:
# Disable depth test and lighting for sky
glDisable(GL_DEPTH_TEST)
glDisable(GL_LIGHTING)
# Draw sky gradient quad far away
glBegin(GL_QUADS)
# Time-based colors
if 0.2 < day_night.time_of_day < 0.3: # Dawn
top_color = (0.4, 0.5, 0.8)
horizon_color = (1.0, 0.5, 0.3)
elif 0.7 < day_night.time_of_day < 0.8: # Dusk
top_color = (0.4, 0.4, 0.7)
horizon_color = (1.0, 0.4, 0.2)
else: # Day
top_color = (0.3, 0.5, 0.9)
horizon_color = (1.0, 0.7, 0.5)
# Top of sky
glColor3f(*top_color)
glVertex3f(-200, 80, -150)
glVertex3f(200, 80, -150)
# Horizon
glColor3f(*horizon_color)
glVertex3f(200, -10, -150)
glVertex3f(-200, -10, -150)
glEnd()
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
draw_world(t, rain_system if day_night.is_raining else None, day_night=day_night)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment