3d – How do I implement flooring and ceilings correctly to my DOOM Fashion Renderer?


I am engaged on a tiny DOOM Fashion 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 obtained a clipping operate from Yuriy Georgiev’s Github for his DOOM Fashion Renderer which fastened my “bending wall” drawback. My objective for this challenge is to get one thing working nicely sufficient that I can ultimately make a small recreation and be taught some stuff about software program renderers.

Onto my drawback, I’ve tried to make use of the method 3DSage utilized in his Let’s Program DOOM collection and used that for my factor right here. The method 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 flooring. This does not work, as a result of every time I’m 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 know the way I ought to repair my drawback and be pushed within the correct route. I am more than pleased to supply extra particulars.

Here is a video showcasing the issue.

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():
    international floorx_ylutT
    international 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):
        international screenArray
        international W
        international 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):
        international screenArray
        pygame.surfarray.blit_array(self.display, screenArray)
        screenArray = self.blankArray.copy()
```

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles