#![allow(clippy::indexing_slicing)]
use crate::math::*;
use crate::tables::FINESINE;
use crate::texture::TextureData;
use super::*;
pub const LIGHTZSHIFT: i32 = 20;
pub const MAXLIGHTZ: usize = 128;
pub const ANGLETOSKYSHIFT: u32 = 22;
impl Renderer {
pub fn init_planes(&mut self) {
let half_h = self.view_height / 2;
for i in 0..SCREENHEIGHT {
let dy = ((i as i32 - half_h) << FRACBITS) + FRACUNIT / 2;
let dy = dy.abs().max(1);
self.yslope[i] = fixed_div((self.view_width / 2) * FRACUNIT, dy);
}
for i in 0..SCREENWIDTH {
let cos_adj = finecosine(
(self.xtoviewangle[i] >> ANGLETOFINESHIFT) as usize & FINEMASK,
)
.abs()
.max(1);
self.distscale[i] = fixed_div(FRACUNIT, cos_adj);
}
}
pub fn init_zlight(&mut self) {
const DISTMAP: i32 = 2;
for i in 0..LIGHTLEVELS {
let startmap =
((LIGHTLEVELS - 1 - i) * 2 * NUMCOLORMAPS / LIGHTLEVELS) as i32;
for j in 0..MAXLIGHTZ {
let scale = fixed_div(
(SCREENWIDTH as i32 / 2) * FRACUNIT,
(j as i32 + 1) << LIGHTZSHIFT,
);
let scale = scale >> LIGHTSCALESHIFT;
let level = (startmap - scale / DISTMAP).clamp(0, NUMCOLORMAPS as i32 - 1);
self.zlight[i][j] = level as usize;
}
}
}
pub fn clear_planes(&mut self) {
let angle = self
.view_angle
.wrapping_sub(ANG90)
>> ANGLETOFINESHIFT;
let angle = angle as usize & FINEMASK;
self.basexscale = fixed_div(finecosine(angle), self.center_x_frac);
self.baseyscale = -fixed_div(
FINESINE[angle],
self.center_x_frac,
);
}
pub fn find_plane(&mut self, height: Fixed, picnum: i16, lightlevel: i16) -> usize {
let (h, ll) = if picnum == self.sky_flat_num {
(0, 0)
} else {
(height, lightlevel)
};
for i in 0..self.visplanes.len() {
let pl = &self.visplanes[i];
if pl.height == h && pl.pic_num == picnum && pl.light_level == ll {
return i;
}
}
self.visplanes.push(VisPlane::new(h, picnum, ll));
self.visplanes.len() - 1
}
pub fn check_plane(&mut self, pl_idx: usize, start: i32, stop: i32) -> usize {
let pl = &self.visplanes[pl_idx];
let old_min = pl.min_x;
let old_max = pl.max_x;
let intrl = start.max(old_min);
let intrh = stop.min(old_max);
let unionl = start.min(old_min);
let unionh = stop.max(old_max);
let mut has_overlap = false;
if intrl <= intrh {
for x in intrl..=intrh {
if self.visplanes[pl_idx].top[x as usize] != 0xFF {
has_overlap = true;
break;
}
}
}
if !has_overlap {
self.visplanes[pl_idx].min_x = unionl;
self.visplanes[pl_idx].max_x = unionh;
return pl_idx;
}
let new_pl = VisPlane::new(
self.visplanes[pl_idx].height,
self.visplanes[pl_idx].pic_num,
self.visplanes[pl_idx].light_level,
);
self.visplanes.push(new_pl);
let new_idx = self.visplanes.len() - 1;
self.visplanes[new_idx].min_x = start;
self.visplanes[new_idx].max_x = stop;
new_idx
}
pub fn draw_planes(&mut self, textures: &TextureData) {
for pl_idx in 0..self.visplanes.len() {
let pl = &self.visplanes[pl_idx];
if pl.min_x > pl.max_x {
continue;
}
let picnum = pl.pic_num;
let height = pl.height;
let light_level = pl.light_level;
let minx = pl.min_x;
let maxx = pl.max_x;
if picnum == self.sky_flat_num {
self.draw_sky_plane(textures, pl_idx);
continue;
}
let flat_idx = picnum as usize;
if flat_idx >= textures.flats.len() {
continue;
}
let plane_height = (height - self.view_z).abs();
let light = ((light_level >> LIGHTSEGSHIFT as i16) as i32 + self.extra_light)
.clamp(0, LIGHTLEVELS as i32 - 1) as usize;
let semantic_class = if height > self.view_z {
SemanticClass::Ceiling
} else {
SemanticClass::Floor
};
let mut spanstart = [0i32; SCREENHEIGHT];
let mut top = [0xFFu8; SCREENWIDTH + 2];
let mut bottom = [0u8; SCREENWIDTH + 2];
let off = 1usize; for x in minx..=maxx {
top[x as usize + off] = self.visplanes[pl_idx].top[x as usize];
bottom[x as usize + off] = self.visplanes[pl_idx].bottom[x as usize];
}
if minx as usize + off > 0 {
top[minx as usize + off - 1] = 0xFF;
}
if (maxx + 1) as usize + off < top.len() {
top[(maxx + 1) as usize + off] = 0xFF;
}
let stop = maxx + 1;
for x in minx..=stop {
let xu = x as usize + off;
let t2 = top[xu] as i32;
let b2 = bottom[xu] as i32;
let t1 = top[xu - 1] as i32; let b1 = bottom[xu - 1] as i32;
let mut ct = t1;
while ct < t2 && ct <= b1 {
self.map_and_draw_span(
ct, spanstart[ct as usize], x - 1,
plane_height, light, flat_idx, textures, semantic_class,
);
ct += 1;
}
let mut cb = b1;
while cb > b2 && cb >= t1 {
self.map_and_draw_span(
cb, spanstart[cb as usize], x - 1,
plane_height, light, flat_idx, textures, semantic_class,
);
cb -= 1;
}
let mut ot = t2;
while ot < t1 && ot <= b2 {
spanstart[ot as usize] = x;
ot += 1;
}
let mut ob = b2;
while ob > b1 && ob >= t2 {
spanstart[ob as usize] = x;
ob -= 1;
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn map_and_draw_span(
&mut self,
y: i32,
x1: i32,
x2: i32,
plane_height: Fixed,
light: usize,
flat_idx: usize,
textures: &TextureData,
semantic_class: SemanticClass,
) {
if y < 0 || y >= self.view_height || x1 > x2 {
return;
}
let distance = fixed_mul(plane_height, self.yslope[y as usize]);
let ds_xstep = fixed_mul(distance, self.basexscale);
let ds_ystep = fixed_mul(distance, self.baseyscale);
let length = fixed_mul(distance, self.distscale[x1.max(0) as usize]);
let angle = self
.view_angle
.wrapping_add(self.xtoviewangle[x1.max(0) as usize])
>> ANGLETOFINESHIFT;
let angle = angle as usize;
let ds_xfrac = self.view_x + fixed_mul(finecosine(angle & FINEMASK), length);
let ds_yfrac = 0i32.wrapping_sub(self.view_y)
- fixed_mul(FINESINE[angle & FINEMASK], length);
let z_idx = ((distance >> LIGHTZSHIFT) as usize).min(MAXLIGHTZ - 1);
let colormap_idx = self.zlight[light][z_idx];
let flat = &textures.flats[flat_idx].pixels;
let cmap = if colormap_idx < textures.colormaps.len() {
&textures.colormaps[colormap_idx]
} else if !textures.colormaps.is_empty() {
&textures.colormaps[0]
} else {
return;
};
let mut xfrac = ds_xfrac;
let mut yfrac = ds_yfrac;
for x in x1.max(0)..=x2.min(SCREENWIDTH as i32 - 1) {
let idx = (y * SCREENWIDTH as i32 + x) as usize;
let spot = (((yfrac >> 10) & (63 * 64)) + ((xfrac >> FRACBITS) & 63)) as usize;
let spot = spot & 4095; let pixel = flat[spot];
self.screen[idx] = cmap[pixel as usize];
self.semantic[idx] = semantic_class as u8;
self.depth[idx] = distance;
xfrac += ds_xstep;
yfrac += ds_ystep;
}
}
fn draw_sky_plane(&mut self, textures: &TextureData, pl_idx: usize) {
let pl = &self.visplanes[pl_idx];
let minx = pl.min_x;
let maxx = pl.max_x;
let sky_tex_idx = self.sky_texture_num as usize;
if sky_tex_idx >= textures.textures.len() {
return;
}
let sky_tex = &textures.textures[sky_tex_idx];
let sky_height = sky_tex.height as i32;
if sky_height == 0 {
return;
}
let mut top = [0xFFu8; SCREENWIDTH];
let mut bottom = [0u8; SCREENWIDTH];
let lo = minx.max(0) as usize;
let hi = (maxx as usize).min(SCREENWIDTH - 1) + 1;
top[lo..hi].copy_from_slice(&self.visplanes[pl_idx].top[lo..hi]);
bottom[lo..hi].copy_from_slice(&self.visplanes[pl_idx].bottom[lo..hi]);
let cmap = if !textures.colormaps.is_empty() {
&textures.colormaps[0]
} else {
return;
};
let sky_texturemid = 100 * FRACUNIT;
let sky_iscale = FRACUNIT;
for x in minx.max(0)..=maxx.min(SCREENWIDTH as i32 - 1) {
let yl = top[x as usize] as i32;
let yh = bottom[x as usize] as i32;
if yl > yh || top[x as usize] == 0xFF {
continue;
}
let angle = self
.view_angle
.wrapping_add(self.xtoviewangle[x as usize])
>> ANGLETOSKYSHIFT;
let col_idx = (angle as usize) % sky_tex.width as usize;
let col_data = sky_tex.column(col_idx);
let mut frac = sky_texturemid as i64 + (yl as i64 - self.center_y as i64) * sky_iscale as i64;
for y in yl..=yh {
let mut tex_y = ((frac >> FRACBITS) as i32) % sky_height;
if tex_y < 0 { tex_y += sky_height; }
let pixel = if (tex_y as usize) < col_data.len() {
col_data[tex_y as usize]
} else {
0
};
let idx = (y * SCREENWIDTH as i32 + x) as usize;
self.screen[idx] = cmap[pixel as usize];
self.semantic[idx] = SemanticClass::Ceiling as u8;
self.depth[idx] = 0;
frac += sky_iscale as i64;
}
}
}
}