EBLAN_ENGINE/include/engine_raycaster.inc

1131 lines
28 KiB
PHP

; ==============================================================================
; EBLAN ENGINE - 2.5D Software Raycaster Core & Sprite Rasterizer
; ==============================================================================
.code
RenderSprites PROTO :DWORD
RenderGlass PROTO
; ------------------------------------------------------------------------------
; Render3DScene: Full frame 2.5D raycasting pass (Floor, Ceiling, Walls, Sprites)
; ------------------------------------------------------------------------------
Render3DScene PROC uses ebx ecx edx esi edi
LOCAL screenX:DWORD
LOCAL rayAngle:DWORD
LOCAL rayDirX:DWORD, rayDirY:DWORD
LOCAL mapX:DWORD, mapY:DWORD
LOCAL stepX:DWORD, stepY:DWORD
LOCAL sideDistX:DWORD, sideDistY:DWORD
LOCAL deltaDistX:DWORD, deltaDistY:DWORD
LOCAL rawDist:DWORD
LOCAL perpWallDistFixed:DWORD ; 24.8 until projection
LOCAL perpWallDist:DWORD
LOCAL sideHit:DWORD ; 0 = X-wall (Vertical), 1 = Y-wall (Horizontal)
LOCAL wallType:DWORD
LOCAL lineHeight:DWORD
LOCAL drawStart:DWORD, drawEnd:DWORD
LOCAL texX:DWORD, texY:DWORD
LOCAL texStep:DWORD, texPos:DWORD
LOCAL texPtr:DWORD
LOCAL pScreenPixel:DWORD
LOCAL hasGlassHit:DWORD
LOCAL glassRawDist:DWORD
LOCAL glassPerpDistFixed:DWORD
LOCAL glassPerpDist:DWORD
LOCAL glassLineHeight:DWORD
LOCAL glassDrawStart:DWORD, glassDrawEnd:DWORD
LOCAL glassTexX:DWORD, glassSideHit:DWORD
; --------------------------------------------------------------------------
; 1. Fast Background Clear: Ceiling (Top Half) and Floor (Bottom Half)
; --------------------------------------------------------------------------
mov edi, OFFSET screenBuffer
mov eax, mapCeilingColor
cmp playerFlashTimer, 0
je @F
add eax, 00202020h ; Dynamic Muzzle Flash ambient lighting boost
@@:
mov ecx, (SCREEN_W * SCREEN_HALF_H)
rep stosd
mov eax, mapFloorColor
cmp playerFlashTimer, 0
je @F
add eax, 00202020h ; Dynamic Muzzle Flash ambient lighting boost
@@:
mov ecx, (SCREEN_W * SCREEN_HALF_H)
rep stosd
; --------------------------------------------------------------------------
; 2. Raycasting: 320 Columns across Field of View
; --------------------------------------------------------------------------
mov screenX, 0
ray_column_loop:
mov hasGlassHit, 0
; Lookup Tangential Ray Angle Offset (flat plane perspective correction)
mov eax, screenX
mov edx, [tableRayAngleOffset + eax*4]
add edx, playerAngle
and edx, ANGLE_MASK
mov rayAngle, edx
mov eax, edx
; Ray Direction (16.16 Fixed Point)
mov edx, [tableCos + eax*4]
mov rayDirX, edx
mov edx, [tableSin + eax*4]
mov rayDirY, edx
; Current Grid Cell
mov eax, playerX
sar eax, 6
mov mapX, eax
mov eax, playerY
sar eax, 6
mov mapY, eax
; Delta Dist X in 24.8: (64 << 24) / abs(rayDirX)
mov eax, rayDirX
test eax, eax
jnz calc_delta_x
mov deltaDistX, 3FFFFFFFh
jmp delta_x_ready
calc_delta_x:
cdq
xor eax, edx
sub eax, edx ; eax = abs(rayDirX)
mov ecx, eax
mov eax, (64 shl 24)
xor edx, edx
div ecx
mov deltaDistX, eax
delta_x_ready:
; Delta Dist Y in 24.8: (64 << 24) / abs(rayDirY)
mov eax, rayDirY
test eax, eax
jnz calc_delta_y
mov deltaDistY, 3FFFFFFFh
jmp delta_y_ready
calc_delta_y:
cdq
xor eax, edx
sub eax, edx ; eax = abs(rayDirY)
mov ecx, eax
mov eax, (64 shl 24)
xor edx, edx
div ecx
mov deltaDistY, eax
delta_y_ready:
; Calculate Step and Initial Side Dist
cmp rayDirX, 0
jne nonzero_step_x
mov stepX, 1
mov sideDistX, 3FFFFFFFh
jmp check_step_y
nonzero_step_x:
cmp rayDirX, 0
jge ray_dir_x_pos
mov stepX, -1
; sideDistX = (playerX - (mapX * 64)) * deltaDistX / 64
mov eax, mapX
shl eax, 6
mov edx, playerX
sub edx, eax
mov eax, edx
imul deltaDistX
shrd eax, edx, 6
mov sideDistX, eax
jmp check_step_y
ray_dir_x_pos:
mov stepX, 1
; sideDistX = ((mapX + 1) * 64 - playerX) * deltaDistX / 64
mov eax, mapX
inc eax
shl eax, 6
sub eax, playerX
imul deltaDistX
shrd eax, edx, 6
mov sideDistX, eax
check_step_y:
cmp rayDirY, 0
jne nonzero_step_y
mov stepY, 1
mov sideDistY, 3FFFFFFFh
jmp dda_loop
nonzero_step_y:
cmp rayDirY, 0
jge ray_dir_y_pos
mov stepY, -1
mov eax, mapY
shl eax, 6
mov edx, playerY
sub edx, eax
mov eax, edx
imul deltaDistY
shrd eax, edx, 6
mov sideDistY, eax
jmp dda_loop
ray_dir_y_pos:
mov stepY, 1
mov eax, mapY
inc eax
shl eax, 6
sub eax, playerY
imul deltaDistY
shrd eax, edx, 6
mov sideDistY, eax
; --------------------------------------------------------------------------
; DDA Traversal
; --------------------------------------------------------------------------
dda_loop:
mov eax, sideDistX
cmp eax, sideDistY
jge step_in_y
; Step in X
mov eax, sideDistX
add eax, deltaDistX
mov sideDistX, eax
mov eax, mapX
add eax, stepX
mov mapX, eax
mov sideHit, 0
jmp test_hit
step_in_y:
mov eax, sideDistY
add eax, deltaDistY
mov sideDistY, eax
mov eax, mapY
add eax, stepY
mov mapY, eax
mov sideHit, 1
test_hit:
invoke GetMapCell, mapX, mapY
mov wallType, eax
cmp eax, CELL_GLASS
jne not_glass_hit
; If this ray already recorded a glass wall, continue stepping through subsequent glass cells
cmp hasGlassHit, 0
jne dda_loop
; Record first glass hit:
mov hasGlassHit, 1
cmp sideHit, 0
jne glass_dist_from_y
mov eax, sideDistX
sub eax, deltaDistX
jmp glass_apply_fisheye
glass_dist_from_y:
mov eax, sideDistY
sub eax, deltaDistY
glass_apply_fisheye:
mov glassRawDist, eax
mov ecx, screenX
mov ecx, [tableRayCos + ecx*4]
imul ecx
shrd eax, edx, 16
cmp eax, 1
jg @F
mov eax, 1
@@:
mov glassPerpDistFixed, eax
add eax, 128
shr eax, 8
cmp eax, 1
jae @F
mov eax, 1
@@:
mov glassPerpDist, eax
; Glass line height & drawStart / drawEnd
mov eax, (64 * PROJ_DIST shl 8)
xor edx, edx
div glassPerpDistFixed
cmp eax, 1
jge @F
mov eax, 1
@@:
mov glassLineHeight, eax
mov eax, SCREEN_HALF_H
mov edx, glassLineHeight
shr edx, 1
sub eax, edx
cmp eax, 0
jge @F
xor eax, eax
@@:
mov glassDrawStart, eax
mov eax, SCREEN_HALF_H
mov edx, glassLineHeight
shr edx, 1
add eax, edx
cmp eax, SCREEN_H
jl @F
mov eax, SCREEN_H - 1
@@:
mov glassDrawEnd, eax
; Glass texX
cmp sideHit, 0
jne glass_tex_hit_y
mov eax, glassRawDist
mov ebx, rayDirY
imul ebx
shrd eax, edx, 8
mov ebx, playerY
shl ebx, 16
add eax, ebx
jmp glass_compute_tex_x
glass_tex_hit_y:
mov eax, glassRawDist
mov ebx, rayDirX
imul ebx
shrd eax, edx, 8
mov ebx, playerX
shl ebx, 16
add eax, ebx
glass_compute_tex_x:
shr eax, 15
and eax, TEX_MASK
cmp sideHit, 0
jne glass_check_flip_y
cmp rayDirX, 0
jle @F
mov ecx, TEX_MASK
sub ecx, eax
mov eax, ecx
jmp @F
glass_check_flip_y:
cmp rayDirY, 0
jge @F
mov ecx, TEX_MASK
sub ecx, eax
mov eax, ecx
@@:
mov glassTexX, eax
mov eax, sideHit
mov glassSideHit, eax
; Continue stepping through the glass to the back wall!
jmp dda_loop
not_glass_hit:
invoke IsSolidCell, wallType
test eax, eax
jz dda_loop
; --------------------------------------------------------------------------
; Calculate Perpendicular Wall Distance & Fish-eye Correction
; --------------------------------------------------------------------------
cmp sideHit, 0
jne dist_from_y
mov eax, sideDistX
sub eax, deltaDistX
jmp apply_fisheye_fix
dist_from_y:
mov eax, sideDistY
sub eax, deltaDistY
apply_fisheye_fix:
mov rawDist, eax ; Euclidean distance in 24.8, also used for texture X
; Fast tableRayCos lookup (precomputed perpendicular cosine per screen column)
mov ecx, screenX
mov ecx, [tableRayCos + ecx*4]
imul ecx ; edx:eax = rawDist * cos(rayAngleOffset)
shrd eax, edx, 16
cmp eax, 1 ; Prevent division by zero
jg @F
mov eax, 1
@@:
mov perpWallDistFixed, eax
add eax, 128
shr eax, 8
cmp eax, 1
jae rounded_distance_ready
mov eax, 1
rounded_distance_ready:
mov perpWallDist, eax
; Store in Depth Buffer for Sprite Occlusion
mov ecx, screenX
mov [depthBuffer + ecx*4], eax
; --------------------------------------------------------------------------
; Projected Line Height on Screen: (64 * PROJ_DIST) / perpWallDist
; --------------------------------------------------------------------------
mov eax, (64 * PROJ_DIST shl 8)
xor edx, edx
div perpWallDistFixed
cmp eax, 1
jge @F
mov eax, 1 ; Clamp to >= 1 to prevent div by zero in texStep
@@:
mov lineHeight, eax
; Calculate drawStart and drawEnd
mov eax, SCREEN_HALF_H
mov edx, lineHeight
shr edx, 1
sub eax, edx
mov drawStart, eax
cmp drawStart, 0
jge @F
mov drawStart, 0
@@:
mov eax, SCREEN_HALF_H
mov edx, lineHeight
shr edx, 1
add eax, edx
mov drawEnd, eax
cmp drawEnd, SCREEN_H
jl @F
mov drawEnd, SCREEN_H - 1
@@:
; --------------------------------------------------------------------------
; Texture Coordinate X (texX) with full 128-texel sub-unit precision
; --------------------------------------------------------------------------
cmp sideHit, 0
jne tex_hit_y
; wallHitFixed = (playerY << 16) + rawDist * rayDirY
mov eax, rawDist
mov ebx, rayDirY
imul ebx
shrd eax, edx, 8 ; (24.8 * 16.16) -> 16.16
mov ebx, playerY
shl ebx, 16
add eax, ebx
jmp compute_tex_x
tex_hit_y:
; wallHitFixed = (playerX << 16) + rawDist * rayDirX
mov eax, rawDist
mov ebx, rayDirX
imul ebx
shrd eax, edx, 8
mov ebx, playerX
shl ebx, 16
add eax, ebx
compute_tex_x:
shr eax, 15
and eax, TEX_MASK ; full 0..127 range (all 128 texture columns)
mov texX, eax
; Flip texture coordinate depending on ray direction
cmp sideHit, 0
jne check_flip_y
cmp rayDirX, 0
jle set_tex_ptr
mov eax, TEX_MASK
sub eax, texX
mov texX, eax
jmp set_tex_ptr
check_flip_y:
cmp rayDirY, 0
jge set_tex_ptr
mov eax, TEX_MASK
sub eax, texX
mov texX, eax
set_tex_ptr:
; Pointer to Texture = textureBuffer + (wallType * TEX_SIZE_BYTES)
mov eax, wallType
and eax, 15
shl eax, 16 ; eax = wallType * 65536
add eax, OFFSET textureBuffer
cmp wallType, CELL_PORTAL
jne wall_texture_ready
mov eax, OFFSET portalTextureBuffer
wall_texture_ready:
mov texPtr, eax
; Texture vertical sampling step (16.16 fixed point)
; texStep = (128 << 16) / lineHeight
mov eax, (128 shl 16)
xor edx, edx
div lineHeight
mov texStep, eax
; texPos = (drawStart - SCREEN_HALF_H + lineHeight / 2) * texStep
mov eax, drawStart
sub eax, SCREEN_HALF_H
mov ebx, lineHeight
shr ebx, 1
add eax, ebx
imul eax, texStep
mov texPos, eax
; --------------------------------------------------------------------------
; Draw Vertical Wall Stripe
; --------------------------------------------------------------------------
; Screen pixel address = screenBuffer + (drawStart * SCREEN_PITCH) + (screenX * 4)
mov eax, drawStart
imul eax, SCREEN_PITCH
add eax, screenX
add eax, screenX
add eax, screenX
add eax, screenX ; screenX * 4
add eax, OFFSET screenBuffer
mov pScreenPixel, eax
mov ecx, drawStart
wall_pixel_loop:
cmp ecx, drawEnd
jg end_wall_stripe
; Sample Texture: texY = (texPos >> 16) & TEX_MASK
mov eax, texPos
shr eax, 16
and eax, TEX_MASK
shl eax, 7 ; texY * 128
add eax, texX
shl eax, 2 ; * 4 bytes
add eax, texPtr
mov edx, [eax] ; Sampled Color (BGRA)
; Wall shading: Y-facing walls darkened 50% for 3D depth perception
cmp sideHit, 1
jne apply_distance_fog
and edx, 00FEFEFEh
shr edx, 1
apply_distance_fog:
; Distance Fog: if perpWallDist > 250, blend towards darkness
mov eax, perpWallDist
cmp eax, 350
jl write_wall_pixel
cmp eax, 700
jge make_pitch_black
; Moderate distance dimming
and edx, 00FEFEFEh
shr edx, 1
jmp write_wall_pixel
make_pitch_black:
mov edx, 00111111h
write_wall_pixel:
mov eax, pScreenPixel
mov [eax], edx
; Advance texture position and screen pointer
mov eax, texPos
add eax, texStep
mov texPos, eax
add pScreenPixel, SCREEN_PITCH
inc ecx
jmp wall_pixel_loop
end_wall_stripe:
mov ecx, screenX
cmp hasGlassHit, 0
je no_glass_in_col
mov eax, glassPerpDist
mov [glassDistBuffer + ecx*4], eax
mov eax, glassDrawStart
mov [glassDrawStartBuf + ecx*4], eax
mov eax, glassDrawEnd
mov [glassDrawEndBuf + ecx*4], eax
mov eax, glassLineHeight
mov [glassLineHeightBuf + ecx*4], eax
mov eax, glassTexX
mov [glassTexXBuf + ecx*4], eax
mov eax, glassSideHit
mov [glassSideHitBuf + ecx*4], eax
jmp col_glass_saved
no_glass_in_col:
mov DWORD PTR [glassDistBuffer + ecx*4], 0
col_glass_saved:
inc screenX
cmp screenX, SCREEN_W
jl ray_column_loop
; --------------------------------------------------------------------------
; 3. Render Sprites (Behind Glass & Normal Columns)
; --------------------------------------------------------------------------
invoke RenderSprites, 1
; --------------------------------------------------------------------------
; 4. Render Glass (Semi-Transparent Wall Pass)
; --------------------------------------------------------------------------
call RenderGlass
; --------------------------------------------------------------------------
; 5. Render Sprites (In Front of Glass)
; --------------------------------------------------------------------------
invoke RenderSprites, 2
ret
Render3DScene ENDP
; ------------------------------------------------------------------------------
; RenderSprites: Projects and renders 3D billboard sprites with depth testing
; Pipeline (same as KAZIK casino3d):
; 1. Distance pass - squared dist per sprite, fill spriteOrder
; 2. Sort - insertion sort far-to-near (painter's algorithm)
; 3. Raster pass - camera transform, projection, textured stripes,
; per-column depth test vs depthBuffer, chroma key
; Sprite x/y are RAW world units (1 cell = 64 units), same scale as playerX/Y.
; ------------------------------------------------------------------------------
RenderSprites PROC uses ebx ecx edx esi edi passFilter:DWORD
LOCAL i:DWORD, j:DWORD, sprIdx:DWORD
LOCAL spriteX:DWORD, spriteY:DWORD
LOCAL transX:DWORD, transY:DWORD
LOCAL sprScreenX:DWORD, sprSize:DWORD, baseSize:DWORD
LOCAL origStartX:DWORD, origStartY:DWORD
LOCAL drawStartX:DWORD, drawEndX:DWORD
LOCAL drawStartY:DWORD, drawEndY:DWORD
LOCAL stripe:DWORD, texX:DWORD
LOCAL texStepX:DWORD, texStepY:DWORD, texPosY:DWORD
LOCAL texPtr:DWORD, pPixel:DWORD
LOCAL camCos:DWORD, camSin:DWORD
LOCAL keyIdx:DWORD, keyDist:DWORD
mov eax, spriteCount
test eax, eax
jz sprites_done
; Camera basis for -playerAngle (rotate world into camera space)
mov eax, ANGLE_512
sub eax, playerAngle
and eax, ANGLE_MASK
mov ecx, [tableCos + eax*4]
mov camCos, ecx
mov edx, [tableSin + eax*4]
mov camSin, edx
; --------------------------------------------------------------------------
; 1. Distance pass: dist = dx^2 + dy^2 (raw units^2), spriteOrder[i] = i
; --------------------------------------------------------------------------
mov i, 0
dist_loop:
mov eax, i
cmp eax, spriteCount
jge dist_done
mov ebx, i
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
mov eax, [ebx].SpriteStruct.x
sub eax, playerX ; dx
imul eax, eax
mov ecx, eax
mov eax, [ebx].SpriteStruct.y
sub eax, playerY ; dy
imul eax, eax
add eax, ecx
mov [ebx].SpriteStruct.dist, eax
mov ecx, i
mov [spriteOrder + ecx*4], ecx
inc i
jmp dist_loop
dist_done:
; --------------------------------------------------------------------------
; 2. Insertion sort spriteOrder by dist, far-to-near (descending)
; --------------------------------------------------------------------------
mov i, 1
sort_outer:
mov eax, i
cmp eax, spriteCount
jge sort_done
mov ecx, i
mov eax, [spriteOrder + ecx*4]
mov keyIdx, eax
; keyDist = spriteList[keyIdx].dist
mov ebx, eax
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
mov eax, [ebx].SpriteStruct.dist
mov keyDist, eax
mov eax, i
dec eax
mov j, eax
sort_inner:
mov eax, j
test eax, eax
js sort_insert ; j < 0 -> insert at 0
; Compare spriteList[spriteOrder[j]].dist with keyDist
mov ecx, j
mov ebx, [spriteOrder + ecx*4]
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
mov eax, [ebx].SpriteStruct.dist
cmp eax, keyDist
jae sort_insert ; dist[j] >= key -> keep far-to-near order
; Shift spriteOrder[j] -> spriteOrder[j+1]
mov ecx, j
mov eax, [spriteOrder + ecx*4]
mov [spriteOrder + ecx*4 + 4], eax
dec j
jmp sort_inner
sort_insert:
mov ecx, j
inc ecx
mov eax, keyIdx
mov [spriteOrder + ecx*4], eax
inc i
jmp sort_outer
sort_done:
; --------------------------------------------------------------------------
; 3. Raster pass (far to near)
; --------------------------------------------------------------------------
mov i, 0
sprite_loop:
mov eax, i
cmp eax, spriteCount
jge sprites_done
mov ecx, i
mov eax, [spriteOrder + ecx*4]
mov sprIdx, eax
mov ebx, eax
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
; Sprite position relative to player (raw units)
mov eax, [ebx].SpriteStruct.x
sub eax, playerX
mov spriteX, eax
mov eax, [ebx].SpriteStruct.y
sub eax, playerY
mov spriteY, eax
; Transform to camera space:
; transY (depth) = spriteX * camCos - spriteY * camSin
; transX = spriteX * camSin + spriteY * camCos
mov eax, spriteX
imul camCos
shrd eax, edx, 16
mov ecx, eax
mov eax, spriteY
imul camSin
shrd eax, edx, 16
sub ecx, eax
mov transY, ecx
; Behind camera / too close -> skip
cmp transY, 8
jle next_sprite
mov eax, spriteX
imul camSin
shrd eax, edx, 16
mov ecx, eax
mov eax, spriteY
imul camCos
shrd eax, edx, 16
add ecx, eax
mov transX, ecx
; Screen X of sprite center
mov eax, transX
imul eax, PROJ_DIST
cdq
idiv transY
add eax, SCREEN_HALF_W
mov sprScreenX, eax
; Projected size: (64 * PROJ_DIST / transY) * scale (16.16)
mov eax, (64 * PROJ_DIST)
xor edx, edx
div transY
mov baseSize, eax
mov ecx, [ebx].SpriteStruct.scale
test ecx, ecx
jnz @F
mov ecx, FIXED_ONE ; scale 0 treated as 1.0
@@:
imul ecx
shrd eax, edx, 16
test eax, eax
jg @F
mov eax, 1
@@:
mov sprSize, eax
; Unclipped bounds (needed for texture mapping)
mov eax, sprScreenX
mov edx, sprSize
shr edx, 1
sub eax, edx
mov origStartX, eax
; Floor-anchored: sprite bottom sits at the floor line of its depth
; (wall bottom = horizon + size/2), top = bottom - size
mov eax, SCREEN_HALF_H
mov edx, baseSize
shr edx, 1
add eax, edx
mov drawEndY, eax
sub eax, sprSize
mov origStartY, eax
; Clipped bounds
mov eax, origStartX
mov drawStartX, eax
cmp drawStartX, 0
jge @F
mov drawStartX, 0
@@:
mov eax, origStartX
add eax, sprSize
dec eax
mov drawEndX, eax
cmp drawEndX, SCREEN_W
jl @F
mov drawEndX, SCREEN_W - 1
@@:
mov eax, drawStartX
cmp eax, drawEndX
jg next_sprite ; fully off-screen horizontally
mov eax, origStartY
mov drawStartY, eax
cmp drawStartY, 0
jge @F
mov drawStartY, 0
@@:
cmp drawEndY, SCREEN_H
jl @F
mov drawEndY, SCREEN_H - 1
@@:
mov eax, drawStartY
cmp eax, drawEndY
jg next_sprite ; fully off-screen vertically
; Texture pointer: slot 0 = solid marker color (no texture)
mov ebx, sprIdx
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
mov eax, [ebx].SpriteStruct.textureId
and eax, 15
shl eax, 16 ; * TEX_SIZE_BYTES
add eax, OFFSET textureBuffer
mov texPtr, eax
; Texture stepping (16.16)
mov eax, (128 shl 16)
xor edx, edx
div sprSize
mov texStepX, eax
mov texStepY, eax
; Render vertical stripes
mov eax, drawStartX
mov stripe, eax
stripe_loop:
mov eax, stripe
cmp eax, drawEndX
jg next_sprite
; Filter by glass pass
mov ecx, stripe
mov edx, [glassDistBuffer + ecx*4]
cmp passFilter, 1
jne check_pass_front
; Pass 1 (behind glass or columns without glass):
test edx, edx
jz normal_sprite_depth_test
cmp transY, edx
jl advance_stripe ; Sprite is in front of glass -> defer to Pass 2
jmp normal_sprite_depth_test
check_pass_front:
cmp passFilter, 2
jne normal_sprite_depth_test
; Pass 2 (in front of glass only):
test edx, edx
jz advance_stripe ; No glass in this column -> already drawn
cmp transY, edx
jge advance_stripe ; Sprite is behind glass -> already drawn
normal_sprite_depth_test:
; Depth test: sprite visible only if closer than wall in this column
mov eax, transY
mov ecx, stripe
cmp eax, [depthBuffer + ecx*4]
jge advance_stripe
; texX = (stripe - origStartX) * 128 / sprSize
mov eax, stripe
sub eax, origStartX
imul eax, texStepX
shrd eax, edx, 16
and eax, TEX_MASK
mov texX, eax
; texPosY starts at the clipped top
mov eax, drawStartY
sub eax, origStartY
imul eax, texStepY
mov texPosY, eax
; Pixel pointer at (stripe, drawStartY)
mov eax, drawStartY
imul eax, SCREEN_PITCH
mov ecx, stripe
shl ecx, 2
add eax, ecx
add eax, OFFSET screenBuffer
mov pPixel, eax
mov ecx, drawStartY
sprite_pixel_y:
cmp ecx, drawEndY
jg advance_stripe
; Sample texture texel (texX, texY)
mov eax, texPosY
shr eax, 16
and eax, TEX_MASK
shl eax, 7 ; texY * 128
add eax, texX
shl eax, 2
add eax, texPtr
mov edx, [eax]
; Chroma key: magenta texels are transparent
cmp edx, COLOR_CHROMA_KEY
je skip_sprite_pixel
; Distance fog (same thresholds as walls)
mov eax, transY
cmp eax, 350
jl write_sprite_pixel
cmp eax, 700
jge sprite_pixel_dark
and edx, 00FEFEFEh
shr edx, 1
jmp write_sprite_pixel
sprite_pixel_dark:
mov edx, 00111111h
write_sprite_pixel:
mov eax, pPixel
mov [eax], edx
skip_sprite_pixel:
mov eax, texPosY
add eax, texStepY
mov texPosY, eax
add pPixel, SCREEN_PITCH
inc ecx
jmp sprite_pixel_y
advance_stripe:
inc stripe
jmp stripe_loop
next_sprite:
inc i
jmp sprite_loop
sprites_done:
ret
RenderSprites ENDP
; ------------------------------------------------------------------------------
; RenderGlass: Renders semi-transparent glass wall pass over background & sprites
; ------------------------------------------------------------------------------
RenderGlass PROC uses ebx ecx edx esi edi
LOCAL screenX:DWORD
LOCAL drawStart:DWORD, drawEnd:DWORD, lineHeight:DWORD
LOCAL texX:DWORD, texPos:DWORD, texStep:DWORD
LOCAL texPtr:DWORD, pScreenPixel:DWORD
LOCAL sideHit:DWORD, perpDist:DWORD
mov eax, CELL_GLASS
shl eax, 16 ; CELL_GLASS * TEX_SIZE_BYTES (8 * 65536)
add eax, OFFSET textureBuffer
mov texPtr, eax
mov screenX, 0
glass_col_loop:
mov ecx, screenX
cmp ecx, SCREEN_W
jge glass_done
mov eax, [glassDistBuffer + ecx*4]
test eax, eax
jz next_glass_col
mov perpDist, eax
mov eax, [glassDrawStartBuf + ecx*4]
mov drawStart, eax
mov eax, [glassDrawEndBuf + ecx*4]
mov drawEnd, eax
mov eax, [glassLineHeightBuf + ecx*4]
mov lineHeight, eax
mov eax, [glassTexXBuf + ecx*4]
mov texX, eax
mov eax, [glassSideHitBuf + ecx*4]
mov sideHit, eax
; Texture vertical step: texStep = (128 << 16) / lineHeight
mov eax, (128 shl 16)
xor edx, edx
div lineHeight
mov texStep, eax
; texPos = (drawStart - SCREEN_HALF_H + lineHeight / 2) * texStep
mov eax, drawStart
sub eax, SCREEN_HALF_H
mov ebx, lineHeight
shr ebx, 1
add eax, ebx
imul eax, texStep
mov texPos, eax
; Screen pixel address: screenBuffer + (drawStart * SCREEN_PITCH) + (screenX * 4)
mov eax, drawStart
imul eax, SCREEN_PITCH
mov ecx, screenX
shl ecx, 2
add eax, ecx
add eax, OFFSET screenBuffer
mov pScreenPixel, eax
mov ecx, drawStart
glass_pixel_loop:
cmp ecx, drawEnd
jg next_glass_col
; Sample texture: texY = (texPos >> 16) & TEX_MASK
mov eax, texPos
shr eax, 16
and eax, TEX_MASK
mov ebx, eax ; ebx = texY
shl eax, 7 ; texY * 128
add eax, texX
shl eax, 2
add eax, texPtr
mov edx, [eax] ; edx = sampled texel (00RRGGBBh)
; Wall shading: Y-facing walls darkened 50% for 3D depth perception
cmp sideHit, 1
jne @F
and edx, 00FEFEFEh
shr edx, 1
@@:
; Distance fog for glass
mov eax, perpDist
cmp eax, 350
jl glass_blend
cmp eax, 700
jge glass_fog_black
and edx, 00FEFEFEh
shr edx, 1
jmp glass_blend
glass_fog_black:
mov edx, 00111111h
glass_blend:
; Check if this texel is the solid metal frame:
; Outer 6 pixels: texX < 6 || texX >= 122 || texY < 6 || texY >= 122
cmp texX, 6
jl write_solid_frame
cmp texX, 122
jge write_solid_frame
cmp ebx, 6
jl write_solid_frame
cmp ebx, 122
jge write_solid_frame
; Inside the glass pane: SEMI-TRANSPARENT BLEND!
; Background pixel from screenBuffer
mov eax, pScreenPixel
mov eax, [eax]
; Check diagonal reflection streak:
mov esi, texX
add esi, ebx
and esi, 63
cmp esi, 4
jl glass_shine_streak
cmp esi, 24
jl glass_pane_blend
cmp esi, 28
jl glass_shine_streak
glass_pane_blend:
; 50% background + 50% glass tint:
and eax, 00FEFEFEh
shr eax, 1
and edx, 00FEFEFEh
shr edx, 1
add eax, edx
jmp write_glass_out
glass_shine_streak:
; Specular reflection: 50% background + 50% glass + specular glare
and eax, 00FEFEFEh
shr eax, 1
and edx, 00FEFEFEh
shr edx, 1
add eax, edx
; Add specular highlight:
add eax, 00203040h
write_glass_out:
mov edx, pScreenPixel
mov [edx], eax
jmp next_glass_pixel
write_solid_frame:
mov eax, pScreenPixel
mov [eax], edx
next_glass_pixel:
mov eax, texPos
add eax, texStep
mov texPos, eax
add pScreenPixel, SCREEN_PITCH
inc ecx
jmp glass_pixel_loop
next_glass_col:
inc screenX
jmp glass_col_loop
glass_done:
ret
RenderGlass ENDP