; ============================================================================== ; EBLAN ENGINE - 2.5D Software Raycaster Core & Sprite Rasterizer ; ============================================================================== .code RenderSprites PROTO :DWORD RenderGlass PROTO SelectRenderCamera PROC mov eax, playerX mov viewX, eax mov eax, playerY mov viewY, eax mov eax, playerAngle mov viewAngle, eax cmp isCutsceneActive, 0 je render_camera_ready mov eax, csCameraX mov viewX, eax mov eax, csCameraY mov viewY, eax mov eax, csCameraAngle mov viewAngle, eax render_camera_ready: ret SelectRenderCamera ENDP ; ------------------------------------------------------------------------------ ; 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 LOCAL colLightLevel:DWORD ; -------------------------------------------------------------------------- ; 1. Fast Background Clear: Ceiling (Top Half) and Floor (Bottom Half) ; -------------------------------------------------------------------------- call SelectRenderCamera invoke SamplePointLighting,viewX,viewY,0,0 mov edi, OFFSET screenBuffer mov edx,mapCeilingColor call ShadeLightPixel mov eax,edx cmp lightingMode,0 jne @F cmp playerFlashTimer, 0 je @F add eax, 00202020h ; Dynamic Muzzle Flash ambient lighting boost @@: mov ecx, (SCREEN_W * SCREEN_HALF_H) rep stosd mov edx,mapFloorColor call ShadeLightPixel mov eax,edx cmp lightingMode,0 jne @F 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, viewAngle 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, viewX sar eax, 6 mov mapX, eax mov eax, viewY 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, viewX 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, viewX 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, viewY 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, viewY 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, viewY 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, viewX 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, viewY 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, viewX 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 ; Compute wall lighting (baked lightmap + dynamic point lights + flashlight) invoke ComputeWallLightLevel, screenX, mapX, mapY, sideHit, stepX, stepY,\ rawDist, perpWallDist, rayAngle, rayDirX, rayDirY mov colLightLevel, 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) cmp lightingMode, LIGHTING_MODE_NONE jne apply_col_light ; 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 jmp write_wall_pixel apply_col_light: call ShadeLightPixel 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 LOCAL sprLightLevel: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, viewAngle 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, viewX ; dx imul eax, eax mov ecx, eax mov eax, [ebx].SpriteStruct.y sub eax, viewY ; 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, viewX mov spriteX, eax mov eax, [ebx].SpriteStruct.y sub eax, viewY 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 invoke ComputeSpriteLight, spriteX, spriteY, transY mov sprLightLevel, eax 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 cmp lightingMode, LIGHTING_MODE_NONE jne apply_spr_lighting ; 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 jmp write_sprite_pixel apply_spr_lighting: call ShadeLightPixel 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