3d – How do I implement flooring and ceilings correctly to my DOOM Model Renderer? ( Solved )


That is solved by me, however I am preserving this query right here to assist anybody who comes by it; take a look at the underside for the way I did it.

I am engaged on a tiny DOOM Model Renderer and I’ve managed to piece collectively one thing that may render partitions. I pieced it collectively by taking a look at movies from 3DSage and The Previous College Coder; I received a clipping operate from Yuriy Georgiev’s Github for his DOOM Model Renderer which mounted my “bending wall” drawback. My objective for this challenge is to get one thing working effectively sufficient that I can finally make a small sport and study some stuff about software program renderers.

Onto my drawback, I’ve tried to make use of the approach 3DSage utilized in his ‘Let’s Program DOOM’ collection and used that for my factor right here. The approach which can be utilized to render flooring and ceilings is the place you save a Y-Place for a column once you’re creating partitions on the ‘prime’, then save the ‘backside’ Y-Place which lets you draw a ground. This does not work, as a result of each time I am going within a sector or space (no matter you’d wish to name it), the partitions are all the time backfacing and the ‘x’ index for ‘floorx_ylutT’ is rarely set to Y2.

I would wish to understand how I ought to repair my drawback and be pushed within the correct route. I am very happy to supply extra particulars. Apologies if any of that is poorly written. Thanks prematurely

Video showcasing the issue;
https://youtu.be/MewYpAyW0fg

from numba import njit, prange
from clipping import Clipping
from vector import Vector3
import numpy as np
import pygame, math

screenArray = None
W, H = None, None

emptyx_ylut = [0]*320
floorx_ylutT = [0]*320
floorx_ylutB = [0]*320

def clearfloorCeilLut():
    world floorx_ylutT
    world floorx_ylutB
    floorx_ylutT = emptyx_ylut.copy()
    floorx_ylutB = emptyx_ylut.copy()

@staticmethod
@njit(fastmath=True, parallel=True)
def rasterizeFloor(screenArray, floorx_ylutT, floorx_ylutB):
    def canDraw(x, y):
        if x > W-1 or x < 1 or y > H-1 or y < 1:
            return False
        if (screenArray[x, y][0] + screenArray[x, y][1] + screenArray[x, y][2]) > 0:
            return False
        return True
    for x in prange(1, W-1):
        pfy1 = floorx_ylutT[x]
        pfy2 = floorx_ylutB[x]
        if pfy1 < pfy2 and pfy1 != 0 and pfy2 != 0:
            for y in vary(pfy1, pfy2, 1):
                #y = min(max(y, 1), H-1)
                if canDraw(x, y):
                    screenArray[x, y] = (255, 255, 255)
@staticmethod
@njit(fastmath=True, parallel=True)
def rasterizeWall(screenArray, x0, x1, y0, y1, y2, y3, col, floorx_ylutT, floorx_ylutB):
    backfacing = False
    if x0 > x1:
        x1, x0 = x0, x1
        y1, y0, y3, y2 = y0, y1, y2, y3
        backfacing = True
    dyb = y1-y0
    dyt = y3-y2
    dx = max(x1-x0, 1)

    i0 = max(min(int(x0), W-1), 1)
    i1 = max(min(int(x1), W-1), 1)
    def canDraw(x, y):
        if x > W-1 or x < 1 or y > H-1 or y < 1:
            return False
        if (screenArray[x, y][0] + screenArray[x, y][1] + screenArray[x, y][2]) > 0:
            return False
        return True
    for x in prange(i0, i1):
        Y1=dyb*(x-x0+0.5)/dx+y0
        Y2=dyt*(x-x0+0.5)/dx+y2
        if backfacing:
            floorx_ylutB[x] = Y2
        else:
            floorx_ylutT[x] = Y2
        for y in prange(int(Y2), int(Y1)):
            #y = int(max(min(y, H-1), 1))
            if canDraw(x, y):
                screenArray[x, y] = col            
@staticmethod
@njit(fastmath=True)
def transformVector(v, cs, sn):
    x, y = v
    tx = x*cs-y*sn
    ty = y*cs+x*sn
    return (tx, ty)
@staticmethod
@njit(fastmath=True)
def transformToScreen(f, v):
    x, y, z = v
    y = 1+abs(y)
    sx = (x/y)*f+W/2
    sy = (z/y)*f+H/2
    return (sx, sy)
    
class Engine:
    def __init__(self, w, h, FOV, FocalLength, display):
        world screenArray
        world W
        world H
        self.w=w
        self.h=h
        self.w2=w/2
        self.h=h
        self.h2=h/2
        self.FOV=FOV
        self.FocalLength=FocalLength
        self.sin = [0]*360
        self.cos = [0]*360
        self.blankArray = pygame.surfarray.array3d(pygame.floor.Floor((w,h)))
        screenArray = self.blankArray.copy()
        W=w
        H=h
        self.display = display

        for x in vary(360):
            self.sin[x] = math.sin(x/180*math.pi)
            self.cos[x] = math.cos(x/180*math.pi)
    def clip_behind_player(self, ax, ay, bx, by):
        px1 = 1
        py1 = 1
        px2 = W
        py2 = 1
        a = (px1 - px2) * (ay - py2) - (py1 - py2) * (ax - px2)
        b = (py1 - py2) * (ax - bx) - (px1 - px2) * (ay - by)
        t = a / (b+1)
        ax -= t * (bx - ax)
        ay -= t * (by - ay)
        return ax, ay
    
    def replace(self, degree, character):
        cx, cy, cz = character.x, character.y, character.z
        yaw = int(character.yaw)
        cs = self.cos[yaw]
        sn = self.sin[yaw]
        f = character.FocalLength
        clearfloorCeilLut()
        for sector in degree:
            sector_height = sector[0]
            sector_elevation = sector[1]
            partitions = sector[3]
            for wall in partitions:
                x0, y0, x1, y1, wall_h, wall_clr, wall_portal, portal_bottom, portal_top  = wall
                x0 -= cx
                x1 -= cx
                y0 -= cy
                y1 -= cy
                wx0, wy0 = transformVector((x0, y0), cs, sn)
                wx1, wy1 = transformVector((x1, y1), cs, sn)
                if wy0 < 1 and wy1 < 1:
                    proceed
                elif wy0 < 1:
                    wx0, wy0 = self.clip_behind_player(wx0, wy0, wx1, wy1)
                elif wy1 < 1:
                    wx1, wy1 = self.clip_behind_player(wx1, wy1, wx0, wy0)
                if wall_portal: # is it a portal?
                    ## prime
                    z0 = cz-sector_elevation-sector_height/2-portal_top
                    z1 = z0-sector_height/2+portal_top
                    sx0, sy0 = transformToScreen(f, (wx0, wy0, z0))
                    sx1, sy1 = transformToScreen(f, (wx1, wy1, z0))
                    _, sy2 = transformToScreen(f, (wx0, wy0, z1))
                    _, sy3 = transformToScreen(f, (wx1, wy1, z1))
                    rasterizeWall(screenArray, sx0, sx1, sy0, sy1, sy2, sy3, wall_clr, floorx_ylutT, floorx_ylutB)
                    ## backside
                    z0 = cz-sector_elevation+portal_bottom
                    z1 = z0-sector_height/2+portal_bottom
                    sx0, sy0 = transformToScreen(f, (wx0, wy0, z0))
                    sx1, sy1 = transformToScreen(f, (wx1, wy1, z0))
                    _, sy2 = transformToScreen(f, (wx0, wy0, z1))
                    _, sy3 = transformToScreen(f, (wx1, wy1, z1))
                    rasterizeWall(screenArray, sx0, sx1, sy0, sy1, sy2, sy3, wall_clr, floorx_ylutT, floorx_ylutB)
                else:
                    z0 = cz-sector_elevation
                    z1 = z0-sector_height
                    sx0, sy0 = transformToScreen(f, (wx0, wy0, z0))
                    sx1, sy1 = transformToScreen(f, (wx1, wy1, z0))
                    _, sy2 = transformToScreen(f, (wx0, wy0, z1))
                    _, sy3 = transformToScreen(f, (wx1, wy1, z1))
                    rasterizeWall(screenArray, sx0, sx1, sy0, sy1, sy2, sy3, wall_clr, floorx_ylutT, floorx_ylutB)
            rasterizeFloor(screenArray, floorx_ylutB, floorx_ylutT)
    def draw(self):
        world screenArray
        pygame.surfarray.blit_array(self.display, screenArray)
        screenArray = self.blankArray.copy()

Unsure how I can mark this as solved, however the title ought to hopefully suffice for now. What I did to repair this was set the default values of the ground lookup desk to be the underside of my display. The explanation why, is as a result of should you’re exterior of a sector, the partitions will both be backfacing, or not backfacing as a result of they aren’t being clipped (I feel.). For those who’re inside it nevertheless, they are going to be all the time backfacing which implies floorx_ylutB will all the time be saved reasonably than doing it additionally for T. I assumed; why not set the default values of those tables to be the underside of the display? I did that, and it labored, drawing some good little flooring.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles