obiwac / python-minecraft-clone

Source code for each episode of my Minecraft clone in Python YouTube tutorial series.
https://www.youtube.com/playlist?list=PL6_bLxRDFzoKjaa3qCGkwR5L_ouSreaVP
MIT License
166 stars 83 forks source link

A PURE BLACK SCREEN. """BUG""" #100

Closed joyhyunki closed 1 year ago

joyhyunki commented 1 year ago

Working on episode 8, and the chunks don't load. All I see is a big black screen. Here is all the main.py and chunks.py file (the reason I named the file chunks is that there is some module named chunks. So main.py thinks I'm loading the module, not the file.) Also, I think there is a problem in the world.py file...

This is main.py. It is before coding the world, where I am currently coding.

'import math import ctypes import pyglet

pyglet.options["shadow_window"] = False pyglet.options["debug_gl"] = False

import pyglet.gl as gl

import matrix import shader import camera

import block_type import texture_manager

import chunks

class Window(pyglet.window.Window): def init(self, args): super().init(args)

    # create blocks

    self.texture_manager = texture_manager.Texture_manager(16, 16, 256)

    self.cobblestone = block_type.Block_type(self.texture_manager, "cobblestone", {"all": "cobblestone"})
    self.grass = block_type.Block_type(self.texture_manager, "grass", {"top": "grass", "bottom": "dirt", "sides": "grass_side"})
    self.dirt = block_type.Block_type(self.texture_manager, "dirt", {"all": "dirt"})
    self.stone = block_type.Block_type(self.texture_manager, "stone", {"all": "stone"})
    self.sand = block_type.Block_type(self.texture_manager, "sand", {"all": "sand"})
    self.planks = block_type.Block_type(self.texture_manager, "planks", {"all": "planks"})
    self.log = block_type.Block_type(self.texture_manager, "log", {"top": "log_top", "bottom": "log_top", "sides": "log_side"})

    self.texture_manager.generate_mipmaps()

    # create vertex array object
    self.chunks = {}
    self.chunks[(0, 0, 0)] = chunks.Chunk((0, 0, 0))
    self.chunks[(0, 0, 0)].update_mesh(self.grass)

    self.shader = shader.Shader("vert.glsl", "frag.glsl")
    self.shader_sampler_location = self.shader.find_uniform(b"texture_array_sampler")
    self.shader.use()

    # pyglet stuff

    pyglet.clock.schedule_interval(self.update, 1.0 / 10000)
    self.mouse_captured = False

    # camera stuff

    self.camera = camera.Camera(self.shader, self.width, self.height)

def update(self, delta_time):
    print(f"FPS: {1.0 / delta_time}")

    if not self.mouse_captured:
        self.camera.input = [0, 0, 0]

    self.camera.update_camera(delta_time)

def on_draw(self):
    # bind textures

    gl.glActiveTexture(gl.GL_TEXTURE0)
    gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, self.texture_manager.texture_array)
    gl.glUniform1i(self.shader_sampler_location, 0)

    # draw stuff

    gl.glEnable(gl.GL_DEPTH_TEST)
    gl.glClearColor(0.0, 0.0, 0.0, 1.0)
    self.clear()

    for chunk_position in self.chunks:
        self.chunks[chunk_position].draw()

# input functions

def on_resize(self, width, height):
    print(f"Resize {width} * {height}")
    gl.glViewport(0, 0, width, height)

    self.camera.width = width
    self.camera.height = height

def on_mouse_press(self, x, y, button, modifiers):
    self.mouse_captured = not self.mouse_captured
    self.set_exclusive_mouse(self.mouse_captured)

def on_mouse_motion(self, x, y, delta_x, delta_y):
    if self.mouse_captured:
        sensitivity = 0.004

        self.camera.rotation[0] -= delta_x * sensitivity
        self.camera.rotation[1] += delta_y * sensitivity

        self.camera.rotation[1] = max(-math.tau / 4, min(math.tau / 4, self.camera.rotation[1]))

def on_key_press(self, key, modifiers):
    if not self.mouse_captured:
        return

    if   key == pyglet.window.key.D: self.camera.input[0] += 1
    elif key == pyglet.window.key.A: self.camera.input[0] -= 1
    elif key == pyglet.window.key.W: self.camera.input[2] += 1
    elif key == pyglet.window.key.S: self.camera.input[2] -= 1

    elif key == pyglet.window.key.SPACE : self.camera.input[1] += 1
    elif key == pyglet.window.key.LSHIFT: self.camera.input[1] -= 1

def on_key_release(self, key, modifiers):
    if not self.mouse_captured:
        return

    if   key == pyglet.window.key.D: self.camera.input[0] -= 1
    elif key == pyglet.window.key.A: self.camera.input[0] += 1
    elif key == pyglet.window.key.W: self.camera.input[2] -= 1
    elif key == pyglet.window.key.S: self.camera.input[2] += 1

    elif key == pyglet.window.key.SPACE : self.camera.input[1] -= 1
    elif key == pyglet.window.key.LSHIFT: self.camera.input[1] += 1

class Game: def init(self): self.config = gl.Config(double_buffer = True, major_version = 3, minor_version = 3, depth_size = 16) self.window = Window(config = self.config, width = 800, height = 600, caption = "Minecraft clone", resizable = True, vsync = False)

def run(self):
    pyglet.app.run()

if name == "main": game = Game() game.run()`

And this is chunks.py file. `import ctypes

import pyglet.gl as gl

CHUNK_WIDTH = 16 CHUNK_HEIGHT = 16 CHUNK_LENGTH = 16

class Chunk: def init(self, chunk_position): self.chunk_position = chunk_position

    self.position = (
        self.chunk_position[0] * CHUNK_WIDTH,
        self.chunk_position[1] * CHUNK_HEIGHT,
        self.chunk_position[2] * CHUNK_LENGTH)

    self.has_mesh = False

    self.mesh_vertex_positions = []
    self.mesh_tex_coords = []
    self.mesh_shading_values = []

    self.mesh_index_counter = 0
    self.mesh_indices = []

    self.vao = gl.GLuint(0)
    gl.glGenVertexArrays(1, self.vao)
    gl.glBindVertexArray(self.vao)

    self.vertex_position_vbo = gl.GLuint(0)
    gl.glGenBuffers(1, self.vertex_position_vbo)

    self.tex_coord_vbo = gl.GLuint(0)
    gl.glGenBuffers(1, self.tex_coord_vbo)

    self.shading_values_vbo = gl.GLuint(0)
    gl.glGenBuffers(1, self.shading_values_vbo)

    self.ibo = gl.GLuint(0)
    gl.glGenBuffers(1, self.ibo)

def update_mesh(self, block_type):
    self.has_mesh = True

    self.mesh_vertex_positions = []
    self.mesh_tex_coords = []
    self.mesh_shading_values = []

    self.mesh_index_counter = 0
    self.mesh_indices = []

    for local_x in range(CHUNK_WIDTH):
        for local_y in range(CHUNK_HEIGHT):
            for local_z in range(CHUNK_LENGTH):
                x, y, z = (
                    self.position[0] + local_x,
                    self.position[1] + local_y,
                    self.position[2] + local_z)

                vertex_positions = block_type.vertex_positions.copy()

                for i in range(24):
                    vertex_positions[i * 3 + 0] += x
                    vertex_positions[i * 3 + 1] += y
                    vertex_positions[i * 3 + 2] += z

                self.mesh_vertex_positions.extend(vertex_positions)

                indices = block_type.indices.copy()
                for i in range(36):
                    indices[i] += self.mesh_index_counter

                self.mesh_indices.extend(indices)
                self.mesh_index_counter += 24

                self.mesh_tex_coords.extend(block_type.tex_coords)
                self.mesh_shading_values.extend((block_type.shading_values))

    gl.glBindVertexArray(self.vao)

    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertex_position_vbo)
    gl.glBufferData(
        gl.GL_ARRAY_BUFFER,
        ctypes.sizeof(gl.GLfloat * len(self.mesh_vertex_positions)),
        (gl.GLfloat * len(self.mesh_vertex_positions)) (*self.mesh_vertex_positions),
        gl.GL_STATIC_DRAW)

    gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, 0)
    gl.glEnableVertexAttribArray(0)

    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.tex_coord_vbo)
    gl.glBufferData(
        gl.GL_ARRAY_BUFFER,
        ctypes.sizeof(gl.GLfloat * len(self.mesh_tex_coords)),
        (gl.GLfloat * len(self.mesh_tex_coords)) (*self.mesh_tex_coords),
        gl.GL_STATIC_DRAW)

    gl.glVertexAttribPointer(1, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, 0)
    gl.glEnableVertexAttribArray(1)

    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.shading_values_vbo)
    gl.glBufferData(
        gl.GL_ARRAY_BUFFER,
        ctypes.sizeof(gl.GLfloat * len(self.mesh_shading_values)),
        (gl.GLfloat * len(self.mesh_shading_values)) (*self.mesh_shading_values),
        gl.GL_STATIC_DRAW)

    gl.glVertexAttribPointer(2, 1, gl.GL_FLOAT, gl.GL_FALSE, 0, 0)
    gl.glEnableVertexAttribArray(2)

    gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.ibo)
    gl.glBufferData(
        gl.GL_ELEMENT_ARRAY_BUFFER,
        ctypes.sizeof(gl.GLuint * len(self.mesh_indices)),
        (gl.GLuint * len(self.mesh_indices)) (*self.mesh_indices),
        gl.GL_STATIC_DRAW
    )

def draw(self):
    if not self.mesh_index_counter:
        return

    gl.glBindVertexArray(self.vao)

    gl.glDrawElements(
        gl.GL_TRIANGLES,
        len(self.mesh_indices),
        gl.GL_UNSIGNED_INT,
        None)`

And I also attached camera.py just in case. `import math import matrix

class Camera: def init(self, shader, width, height): self.width = width self.height = height

    # create matrices

    self.mv_matrix = matrix.Matrix()
    self.p_matrix = matrix.Matrix()

    # shaders

    self.shader = shader
    self.shader_matrix_location = self.shader.find_uniform(b"matrix")

    # camera variables

    self.input = [0, 0, 0]

    self.position = [0, 0, -3]
    self.rotation = [math.tau / 4, 0]

def update_camera(self, delta_time):
    speed = 7
    multiplier = speed * delta_time

    self.position[1] += self.input[1] * multiplier

    if self.input[0] or self.input[2]:
        angle = self.rotation[0] + math.atan2(self.input[2], self.input[0]) - math.tau / 4

        self.position[0] += math.cos(angle) * multiplier
        self.position[2] += math.sin(angle) * multiplier

def update_matrices(self, block_position):
    # 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] + block_position[0], -self.position[1] + block_position[1], self.position[2] + block_position[2])

    # modelviewprojection matrix

    mvp_matrix = self.p_matrix * self.mv_matrix
    self.shader.uniform_matrix(self.shader_matrix_location, mvp_matrix)`

This is all the files I have trouble with while loading the basic 16 x 16 x 16 chunk. Also there is a problem in world.py file. I'll just attach a part of your source code here where problem occurs.

` self.block_types = [None] # "None" is the block type for air

    self.block_types.append(block_type.Block_type(self.texture_manager, "cobblestone", {"all": "cobblestone"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "grass", {"top": "grass", "bottom": "dirt", "sides": "grass_side"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "grass_block", {"all": "grass"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "dirt", {"all": "dirt"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "stone", {"all": "stone"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "sand", {"all": "sand"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "planks", {"all": "planks"}))
    self.block_types.append(block_type.Block_type(self.texture_manager, "log", {"top": "log_top", "bottom": "log_top", "sides": "log_side"}))`

I get the following error message from VS Code. """ Argument of type "Block_type" cannot be assigned to parameter "__object" of type "None" in function "append" Type cannot be assigned to type "None"Pylance[reportGeneralTypeIssues] (https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportGeneralTypeIssues) class Block_type( texture_manager: Texture_manager, name: str = "unknown", block_face_textures: Unknown = { "all": "cobblestone" } ) """

Please, help me! Removing the whole chunks.py and world.py and re-coding this for the 4th time.

obiwac commented 1 year ago

This issue tracker is for bugs in this repo. Please ask questions like yours on my Discord server or on YouTube directly :)

That being said, obvious question, have you tried running/comparing the code in this repo?

joyhyunki commented 1 year ago

My mistake :( Apologies for posting my question here. I did compare it with the code here, and still, it did not work.

obiwac commented 1 year ago

Does the code here run at least? Do note that, because Python is a bit slow, you should expect to wait a few seconds for things to load - it's not normal if it takes like more than 10 seconds though

joyhyunki commented 1 year ago

Whoa it suddenly worked. I guess it was because of a slow computer that I was using. Thank you for answering :) 👍 I'll close this issue. I am sorry for posting here. I still have a few problems with my original chunk generation but I finished the code and it works.

obiwac commented 1 year ago

No worries and glad it's working for you now :)