Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 1 addition & 28 deletions community/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1 @@
# Community

*TODO* It would be cool to make a video to showcase community at some point and link it here!

The `community` directory is for experiments & contributions made by other people on the latest tutorial's code (see PR [#29](https://github.com/obiwac/python-minecraft-clone/pull/29)).
It more generally extends the project with functionality I've yet to cover in a tutorial or that I don't intend on covering at all.

Anyone (you included!) is more than welcome to open a PR to add their own contribution, be it a bug fix, a new build in the world save, or a new feature entirely!

Characteristic contributions are contributions which add something to the code -- bugfixes will get merged to the source of all episodes if relevant to them.

The community has several features and options that can be toggled in `src/options.py`:

- Render Distance: At what distance (in chunks) should chunks stop being rendered
- FOV: Camera field of view

- Indirect Rendering: Alternative way of rendering that has less overhead but is only supported on devices supporting OpenGL 4.2
- Advanced OpenGL: Rudimentary occlusion culling using hardware occlusion queries, however it is not performant and will cause pipeline stalls and decrease performance on most hardware - mostly for testing if it improves framerate
- Chunk Updates: Chunk updates per chunk every tick - 1 gives the best performance and best framerate, however, as Python is an slow language, 1 may increase chunk building time by an ludicrous amount
- Vsync: Vertical sync, may yield smoother framerate but bigger frame times and input lag
- Max CPU Ahead frames: Number of frames that the CPU can go ahead of a frame before syncing with the GPU by waiting for it to complete the execution of the command buffer, using `glClientWaitSync()`
- Smooth FPS: Legacy CPU/GPU sync by forcing the flushing and completion of command buffer using `glFinish()`, not recommended - similar to setting Max CPU Ahead Frames to 0. Mostly for testing whether it makes any difference with `glClientWaitSync()`

- Smooth lighting: Smoothes the light of each vertex to achieve a linear interpolation of light on each fragment, hence creating a smoother light effect - it also adds ambient occlusion, to simulate light blocked by opaque objects (chunk update/build time will be severely affected by this feature)
- Fancy translucency: Better translucency blending, avoid weird looking artefacts - disable on low-end hardware
- Mipmap (minification filtering): Texture filtering used on higher distances. Default is `GL_NEAREST` (no filtering) (more info in `options.py`)
- Colored lighting: Uses an alternative shader program to achieve a more colored lighting; it aims to look similar to Beta 1.8+ (no performance loss should be incurred)
- Antialiasing: Experimental feature
In this branch, I have added perlin noise to generate hills, lakes, caves, ores, etc.
1 change: 0 additions & 1 deletion community/audio/README.md

This file was deleted.

55 changes: 17 additions & 38 deletions community/src/renderer/block_type.py → community/block_type.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
from typing import Any
from src.physics.collider import Collider
import models.cube # default model
import models.cube # default model


class BlockType:
class Block_type:
# new optional model argument (cube model by default)
def __init__(
self, texture_manager, name="unknown", block_face_textures={"all": "cobblestone"}, model: Any = models.cube
):
def __init__(self, texture_manager, name = "unknown", block_face_textures = {"all": "cobblestone"}, model = models.cube, transparent = False):
self.name = name
self.block_face_textures = block_face_textures
self.model = model

# create members based on model attributes

self.transparent = model.transparent
self.transparent = transparent
self.is_cube = model.is_cube
self.glass = model.glass
self.translucent = model.translucent

# create colliders

self.colliders = []

for collider in model.colliders:
self.colliders.append(Collider(*collider))

# replace data contained in numbers.py with model specific data

self.vertex_positions = model.vertex_positions
self.tex_coords = model.tex_coords # to deprecate
self.tex_indices = [0] * len(self.tex_coords)
self.tex_coords = model.tex_coords.copy()
self.shading_values = model.shading_values

def set_block_face(face, texture):
# make sure we don't add nonexistent face
# make sure we don't add inexistent faces
if face > len(self.tex_coords) - 1:
return
self.tex_indices[face] = texture

self.tex_coords[face] = self.tex_coords[face].copy()

for vertex in range(4):
self.tex_coords[face][vertex * 3 + 2] = texture

for face in block_face_textures:
texture = block_face_textures[face]
Expand All @@ -46,26 +33,18 @@ def set_block_face(face, texture):
texture_index = texture_manager.textures.index(texture)

if face == "all":
for i in range(len(self.tex_coords)):
set_block_face(i, texture_index)

elif face == "sides":
set_block_face(0, texture_index)
set_block_face(1, texture_index)
set_block_face(2, texture_index)
set_block_face(3, texture_index)
set_block_face(4, texture_index)
set_block_face(5, texture_index)

elif face == "x":
elif face == "sides":
set_block_face(0, texture_index)
set_block_face(1, texture_index)

elif face == "y":
set_block_face(2, texture_index)
set_block_face(3, texture_index)

elif face == "z":
set_block_face(4, texture_index)
set_block_face(5, texture_index)

else:
set_block_face(["right", "left", "top", "bottom", "front", "back"].index(face), texture_index)
set_block_face(["right", "left", "top", "bottom", "front", "back"].index(face), texture_index)
116 changes: 116 additions & 0 deletions community/camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import math
import matrix

class Camera:
def __init__(self, shader, width, height):
# Existing attributes
self.width = width
self.height = height
self.mv_matrix = matrix.Matrix()
self.p_matrix = matrix.Matrix()
self.shader = shader
self.shader_matrix_location = self.shader.find_uniform(b"matrix")
self.input = [0, 0, 0]
self.position = [0, 81.5, 0]
self.rotation = [-math.tau / 4, 0]

# New attribute for vertical velocity
self.velocity_y = 0.0
self.is_jumping = False # To check if the player is in the middle of a jump


def round_position(self, position):
return [round(coord) for coord in position]

def update_camera(self, delta_time, sprinting, world, flying):
base_speed = 8
sprint_multiplier = 2.0 if sprinting else 1.0
multiplier = base_speed * sprint_multiplier * delta_time

gravity = -20.0 * delta_time

# Calculate movement vector for all directions (X, Y, Z)
movement_vector = [0, 0, 0]

if flying:
movement_vector[1] = self.input[1] * multiplier
self.velocity_y = 0 # Reset vertical velocity when flying
self.is_jumping = False
else:
if self.is_jumping:
# Apply velocity to the Y-axis during the jump
movement_vector[1] = self.velocity_y
self.velocity_y = -gravity # Apply gravity to velocity

# Stop the jump if the player reaches the max height or hits an obstacle
if self.velocity_y <= 0 or not self.check_collision(world, [self.position[0], self.position[1] + movement_vector[1], self.position[2]]):
self.is_jumping = False
self.velocity_y = gravity
else:
movement_vector[1] = gravity

# XZ-Axis Movement (left/right/forward/backward)
if self.input[0] != 0 or self.input[2] != 0:
angle = self.rotation[0] - math.atan2(self.input[2], self.input[0]) + math.tau / 4
movement_vector[0] = math.cos(angle) * multiplier
movement_vector[2] = math.sin(angle) * multiplier

# Calculate the next position
next_position = [
self.position[0] + movement_vector[0],
self.position[1] + movement_vector[1],
self.position[2] + movement_vector[2],
]

# Check for collisions along each axis separately
if self.check_collision(world, [next_position[0], self.position[1], self.position[2]]):
self.position[0] = next_position[0]

if self.check_collision(world, [self.position[0], next_position[1], self.position[2]]):
self.position[1] = next_position[1]
else:
if movement_vector[1] < 0:
self.velocity_y = 0
self.is_jumping = False

if self.check_collision(world, [self.position[0], self.position[1], next_position[2]]):
self.position[2] = next_position[2]



def check_collision(self, world, position):
# Player's bounding box is 1x1x2 blocks, centered on the player's position.
x, y, z = position
corners_to_check = [
(x - 0.5, y - 1.5, z - 0.5), # bottom block, front left
(x + 0.5, y - 1.5, z - 0.5), # bottom block, front right
(x - 0.5, y - 1.5, z + 0.5), # bottom block, back left
(x + 0.5, y - 1.5, z + 0.5), # bottom block, back right

(x - 0.5, y - 0.5, z - 0.5), # top block, front left
(x + 0.5, y - 0.5, z - 0.5), # top block, front right
(x - 0.5, y - 0.5, z + 0.5), # top block, back left
(x + 0.5, y - 0.5, z + 0.5), # top block, back right
]

for corner in corners_to_check:
if world.get_block_number(self.round_position(corner)) != 0:
return False
return True




def update_matrices(self):
# create projection matrix
self.p_matrix.load_identity()
self.p_matrix.perspective(90, float(self.width) / self.height, 0.1, 500)

# create modelview matrix
self.mv_matrix.load_identity()
self.mv_matrix.rotate_2d(self.rotation[0] + math.tau / 4, self.rotation[1])
self.mv_matrix.translate(-self.position[0], -self.position[1], -self.position[2])

# modelviewprojection matrix
mvp_matrix = self.mv_matrix * self.p_matrix
self.shader.uniform_matrix(self.shader_matrix_location, mvp_matrix)
Loading