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.