Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created November 20, 2024 22:24
Show Gist options
  • Save EncodeTheCode/de87a169861e42f70017910b8e03b745 to your computer and use it in GitHub Desktop.
Save EncodeTheCode/de87a169861e42f70017910b8e03b745 to your computer and use it in GitHub Desktop.
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from PIL import Image
import numpy as np
# Cube vertices and faces
vertices = [
[1, 1, -1],
[1, -1, -1],
[-1, -1, -1],
[-1, 1, -1],
[1, 1, 1],
[1, -1, 1],
[-1, -1, 1],
[-1, 1, 1]
]
faces = [
(0, 1, 2, 3),
(3, 2, 6, 7),
(7, 6, 5, 4),
(4, 5, 1, 0),
(0, 3, 7, 4),
(1, 5, 6, 2)
]
def load_texture(image_path):
try:
img = Image.open(image_path).transpose(Image.FLIP_TOP_BOTTOM)
img_data = np.array(img.convert("RGBA"), dtype=np.uint8)
texture_id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
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_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data)
return texture_id
except Exception as e:
print(f"Failed to load texture: {e}")
return None
def draw_textured_cube(texture_id, opacity):
if texture_id is None:
print("No texture loaded!")
return
glBindTexture(GL_TEXTURE_2D, texture_id)
glColor4f(1.0, 1.0, 1.0, opacity) # Apply opacity
glBegin(GL_QUADS)
texture_coords = [
(0, 0), (1, 0), (1, 1), (0, 1)
]
for face in faces:
for j, vertex in enumerate(face):
glTexCoord2f(*texture_coords[j])
glVertex3fv(vertices[vertex])
glEnd()
glColor4f(1.0, 1.0, 1.0, 1.0) # Reset opacity after drawing
def draw_text(position, text, color):
font = pygame.font.SysFont("Arial", 24)
text_surface = font.render(text, True, color)
text_data = pygame.image.tostring(text_surface, "RGBA", True)
glWindowPos2i(position[0], position[1])
glDrawPixels(text_surface.get_width(), text_surface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_data)
class Camera:
def __init__(self, fov=45, view_distance=50):
self.position = np.array([0.0, 0.5, -3.0], dtype=float)
self.yaw = 0.0
self.pitch = 0.0
self.sensitivity = 0.1 # Mouse sensitivity for looking around
self.max_speed = 0.1 # Speed of movement
self.fov = fov
self.view_distance = view_distance
self.update_perspective()
pygame.mouse.set_visible(False) # Hide the mouse cursor
pygame.event.set_grab(True) # Capture the mouse within the window
def update_perspective(self):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(self.fov, 800 / 600, 0.1, self.view_distance)
glMatrixMode(GL_MODELVIEW)
def update(self):
# Calculate the direction vector based on yaw and pitch
direction = np.array([
np.cos(np.radians(self.yaw)) * np.cos(np.radians(self.pitch)),
np.sin(np.radians(self.pitch)),
np.sin(np.radians(self.yaw)) * np.cos(np.radians(self.pitch))
])
look_at = self.position + direction
glLoadIdentity()
gluLookAt(*self.position, *look_at, 0, 1, 0) # Camera looks forward at the calculated direction
def handle_mouse(self):
x, y = pygame.mouse.get_rel() # Get mouse movement
self.yaw += x * self.sensitivity
self.pitch -= y * self.sensitivity # Invert pitch for natural movement
# Normalize yaw to [0, 360) degrees
self.yaw = self.yaw % 360
# Clamp pitch to avoid flipping
self.pitch = max(-90, min(90, self.pitch))
def move(self, keys):
# Forward/backward and strafe directions
forward = np.array([
np.cos(np.radians(self.yaw)),
0, # No vertical movement yet
np.sin(np.radians(self.yaw))
])
right = np.array([
np.sin(np.radians(self.yaw)),
0, # No vertical movement yet
-np.cos(np.radians(self.yaw))
])
# Adjust vertical movement based on pitch
vertical_movement = np.sin(np.radians(self.pitch)) # Movement up/down based on pitch
if keys[pygame.K_w]: # Move forward
self.position += forward * self.max_speed
self.position[1] += vertical_movement * self.max_speed # Adjust Y position
if keys[pygame.K_s]: # Move backward
self.position -= forward * self.max_speed
self.position[1] -= vertical_movement * self.max_speed # Adjust Y position
if keys[pygame.K_a]: # Move left
self.position += right * self.max_speed # Strafe left
if keys[pygame.K_d]: # Move right
self.position -= right * self.max_speed # Strafe right
def calculate_opacity(camera, cube_position, fade_start=20.0, fade_end=5.0):
distance = np.linalg.norm(camera.position - cube_position)
if distance <= fade_end:
return 1.0 # Fully opaque when near
elif distance >= fade_start:
return 0.0 # Fully transparent when far
else:
return (fade_start - distance) / (fade_start - fade_end)
def draw_pitch_yaw_roll(camera):
pitch_yaw_roll_text = f"Pitch: {camera.pitch:.2f}°, Yaw: {camera.yaw:.2f}°"
draw_text((10, 40), pitch_yaw_roll_text, (255, 255, 0)) # Yellow color
def draw_camera_position(position):
camera_position_text = f"X: {position[0]:.2f}, Y: {position[1]:.2f}, Z: {position[2]:.2f}"
draw_text((10, 10), camera_position_text, (255, 255, 0)) # Yellow color
# Initialize PyGame and OpenGL
pygame.init()
display = (800, 600)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
glEnable(GL_TEXTURE_2D)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
# Load texture for the cube
texture_id = load_texture("texture.png")
# Position the mouse cursor at the center of the screen.
def center_mouse():
width, height = pygame.display.get_window_size()
pygame.mouse.set_pos(width // 2, height // 2)
# Set up camera and cube position
camera = Camera(fov=45, view_distance=50)
cube_position = np.array([0.0, 0.0, 0.0]) # Correct initialization of cube position
camera_control_enabled = False # Initialize the camera control flag
c_key_pressed = False # Track if the 'C' key was just pressed
# Main loop
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
keys = pygame.key.get_pressed() # Get the currently pressed keys
# Detect if 'C' key is pressed and toggle camera control only on key press
if keys[pygame.K_c]:
if not c_key_pressed: # Only toggle if 'C' was not previously pressed
camera_control_enabled = not camera_control_enabled # Toggle camera control
c_key_pressed = True # Set the flag to indicate the key was pressed
else:
c_key_pressed = False # Reset the flag when 'C' is not pressed
# ESC key to disengage camera control and release the mouse
if keys[pygame.K_ESCAPE]:
pygame.mouse.set_visible(True) # Show the mouse cursor
pygame.event.set_grab(False) # Release the mouse from the window
elif camera_control_enabled: # Only apply the following logic if camera control is enabled
center_mouse()
pygame.mouse.set_visible(False) # Hide the mouse cursor
pygame.event.set_grab(True) # Capture the mouse within the window
# Handle the camera movements (handle_mouse and move should be your camera functions)
camera.handle_mouse() # Handle mouse movement
camera.move(keys) # Move the camera
elif camera_control_enabled == False:
pygame.mouse.set_visible(True) # Show the mouse cursor
else:
pygame.mouse.set_visible(True) # Show the mouse cursor
pygame.event.set_grab(False) # Release the mouse from the window
camera.update() # Update the camera view
# Calculate opacity based on distance to the cube
opacity = calculate_opacity(camera, cube_position)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
draw_textured_cube(texture_id, opacity)
draw_camera_position(camera.position)
draw_pitch_yaw_roll(camera)
pygame.display.flip()
clock.tick(60)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment