note
	description: "[
		r_plane.c
		Here is a core component: drawing the floors and ceilings
		 while maintaining a per column clipping list only.
		Moreover, the sky areas have to be determined.
	]"
	license: "[
				Copyright (C) 1993-1996 by id Software, Inc.
				Copyright (C) 2005-2014 Simon Howard
				Copyright (C) 2021 Ilgiz Mustafin
		
				This program is free software; you can redistribute it and/or modify
				it under the terms of the GNU General Public License as published by
				the Free Software Foundation; either version 2 of the License, or
				(at your option) any later version.
		
				This program is distributed in the hope that it will be useful,
				but WITHOUT ANY WARRANTY; without even the implied warranty of
				MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
				GNU General Public License for more details.
		
				You should have received a copy of the GNU General Public License along
				with this program; if not, write to the Free Software Foundation, Inc.,
				51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
	]"

class 
	R_PLANE

create 
	make

feature 

	i_main: I_MAIN

	make (a_i_main: like i_main)
		local
			i: INTEGER_32
		do
			i_main := a_i_main
			create cachedheight.make_filled (create {FIXED_T}.from_integer (0), 0, {DOOMDEF_H}.screenheight - 1)
			create openings.make_filled (0, 0, Maxopenings - 1)
			create lastopening.make (0, openings)
			create ceilingclip.make_filled (0, 0, {DOOMDEF_H}.screenwidth - 1)
			create floorclip.make_filled (0, 0, {DOOMDEF_H}.screenwidth - 1)
			create visplanes.make_filled (create {VISPLANE_T}.make, 0, Maxvisplanes - 1)
			from
				i := visplanes.lower
			until
				i > visplanes.upper
			loop
				visplanes [i] := create {VISPLANE_T}.make
				i := i + 1
			end
			create cachedystep.make_filled (create {FIXED_T}.from_integer (0), 0, {DOOMDEF_H}.screenheight - 1)
			create cachedxstep.make_filled (create {FIXED_T}.from_integer (0), 0, {DOOMDEF_H}.screenheight - 1)
			create cacheddistance.make_filled (create {FIXED_T}.from_integer (0), 0, {DOOMDEF_H}.screenheight - 1)
			create spanstart.make_filled (0, 0, {DOOMDEF_H}.screenheight - 1)
		end
	
feature 

	spanstart: ARRAY [INTEGER_32]
			-- spanstart holds the start of a plane span
			-- initialized to 0 at start

	cacheddistance: ARRAY [FIXED_T]

	cachedxstep: ARRAY [FIXED_T]

	cachedystep: ARRAY [FIXED_T]
	
feature -- Clip values are the solid pixel bounding the range.
-- floorclip starts out SCREENHEIGHT
-- ceilingclip starts out -1

	floorclip: ARRAY [INTEGER_16]

	ceilingclip: ARRAY [INTEGER_16]

	lastvisplane: INTEGER_32
			-- originally pointer inside visplanes

	Maxopenings: INTEGER_32
		once
			Result := {DOOMDEF_H}.screenwidth * 64
		ensure
			instance_free: class
		end

	openings: ARRAY [INTEGER_16]

	lastopening: INDEX_IN_ARRAY [INTEGER_16] assign set_lastopening
			-- originally pointer inside openings

	set_lastopening (a_lastopening: like lastopening)
		do
			lastopening := a_lastopening
		end

	cachedheight: ARRAY [FIXED_T]
			-- SCREENHEIGHT
	
feature -- Here comes the obnoxious "visplane".

	Maxvisplanes: INTEGER_32 = 128

	visplanes: ARRAY [VISPLANE_T]

	floorplane: detachable VISPLANE_T assign set_floorplane

	set_floorplane (a_floorplane: like floorplane)
		do
			floorplane := a_floorplane
		end

	ceilingplane: detachable VISPLANE_T assign set_ceilingplane

	set_ceilingplane (a_ceilingplane: like ceilingplane)
		do
			ceilingplane := a_ceilingplane
		end
	
feature -- Texture mapping

	basexscale: FIXED_T

	baseyscale: FIXED_T

	planeheight: FIXED_T

	planezlight: detachable ARRAY [detachable INDEX_IN_ARRAY [LIGHTTABLE_T]]
			-- lighttable_t**
	
feature 

	r_initplanes
		do
		end

	r_clearplanes
			-- At begining of frame.
		local
			i: INTEGER_32
			angle: ANGLE_T
		do
			from
				i := 0
			until
				i >= i_main.R_draw.viewwidth
			loop
				floorclip [i] := i_main.R_draw.viewheight.to_integer_16
				ceilingclip [i] := -1
				i := i + 1
			end
			lastvisplane := 0
			create lastopening.make (0, openings)
			cachedheight.fill_with (create {FIXED_T}.from_integer (0))
			angle := ((i_main.R_main.viewangle - {R_MAIN}.ang90) |>> {R_MAIN}.angletofineshift)
			basexscale := {M_FIXED}.fixeddiv ({R_MAIN}.finecosine [angle.as_integer_32], i_main.R_main.centerxfrac)
			baseyscale := - {M_FIXED}.fixeddiv (create {FIXED_T}.from_integer ({R_MAIN}.finesine [angle.as_integer_32]), i_main.R_main.centerxfrac)
		end

	r_drawplanes
			-- At the end of each frame.
		require
			rangecheck_drawsegs_overflow: i_main.R_bsp.ds_p <= {R_DEFS}.maxdrawsegs
			rangecheck_visplane_overflow: lastvisplane <= Maxvisplanes
			rangecheck_opening_overflow: lastopening.index <= Maxopenings
		local
			pl: INTEGER_32
			light: INTEGER_32
			x: INTEGER_32
			stop: INTEGER_32
			angle: INTEGER_32
		do
			from
				pl := 0
			until
				pl >= lastvisplane
			loop
				if visplanes [pl].minx > visplanes [pl].maxx then
				else
					if visplanes [pl].picnum = i_main.R_sky.skyflatnum then
						i_main.R_draw.dc_iscale := i_main.R_things.pspriteiscale |>> i_main.R_main.detailshift
						i_main.R_draw.dc_colormap := create {INDEX_IN_ARRAY [LIGHTTABLE_T]}.make (0, i_main.R_data.colormaps)
						i_main.R_draw.dc_texturemid := create {FIXED_T}.from_integer (i_main.R_sky.skytexturemid)
						from
							x := visplanes [pl].minx
						until
							x > visplanes [pl].maxx
						loop
							i_main.R_draw.dc_yl := visplanes [pl].top [x].to_integer_32
							i_main.R_draw.dc_yh := visplanes [pl].bottom [x].to_integer_32
							if i_main.R_draw.dc_yl < i_main.R_draw.dc_yh then
								angle := (i_main.R_main.viewangle + i_main.R_main.Xtoviewangle [x]) |>> {R_SKY}.angletoskyshift.as_integer_32
								i_main.R_draw.dc_x := x
								i_main.R_draw.dc_source := i_main.R_data.r_getcolumn (i_main.R_sky.skytexture, angle)
								check
										attached i_main.R_main.colfunc as colfunc
								then
									colfunc.call
								end
							end
							x := x + 1
						end
					else
						i_main.R_draw.ds_source := create {MANAGED_POINTER_WITH_OFFSET}.make (i_main.W_wad.w_cachelumpnum (i_main.R_data.firstflat + i_main.R_data.flattranslation [visplanes [pl].picnum]), 0)
						planeheight := create {FIXED_T}.from_integer ((visplanes [pl].height - i_main.R_main.viewz).abs)
						light := (visplanes [pl].lightlevel |>> {R_MAIN}.lightsegshift) + i_main.R_main.extralight
						if light >= {R_MAIN}.lightlevels then
							light := {R_MAIN}.lightlevels - 1
						end
						if light < 0 then
							light := 0
						end
						planezlight := i_main.R_main.zlight [light]
						visplanes [pl].top [visplanes [pl].maxx + 1] := 255
						visplanes [pl].top [visplanes [pl].minx - 1] := 255
						stop := visplanes [pl].maxx + 1
						from
							x := visplanes [pl].minx
						until
							x > stop
						loop
							r_makespans (x, visplanes [pl].top [x - 1].to_integer_32, visplanes [pl].bottom [x - 1].to_integer_32, visplanes [pl].top [x].to_integer_32, visplanes [pl].bottom [x].to_integer_32)
							x := x + 1
						end
					end
				end
				pl := pl + 1
			end
		end

	r_makespans (x, a_t1, a_b1, a_t2, a_b2: INTEGER_32)
		local
			t1: INTEGER_32
			b1: INTEGER_32
			t2: INTEGER_32
			b2: INTEGER_32
		do
			t1 := a_t1
			b1 := a_b1
			t2 := a_t2
			b2 := a_b2
			from
			until
				not (t1 < t2 and t1 <= b1)
			loop
				r_mapplane (t1, spanstart [t1], x - 1)
				t1 := t1 + 1
			end
			from
			until
				not (b1 > b2 and b1 >= t1)
			loop
				r_mapplane (b1, spanstart [b1], x - 1)
				b1 := b1 - 1
			end
			from
			until
				not (t2 < t1 and t2 <= b2)
			loop
				spanstart [t2] := x
				t2 := t2 + 1
			end
			from
			until
				not (b2 > b1 and b2 >= t2)
			loop
				spanstart [b2] := x
				b2 := b2 - 1
			end
		end

	r_mapplane (y, x1, x2: INTEGER_32)
		require
			rangecheck: x2 >= x1 and x1 >= 0 and x2 < i_main.R_draw.viewwidth and y <= i_main.R_draw.viewheight
		local
			angle: ANGLE_T
			distance: FIXED_T
			length: FIXED_T
			index: NATURAL_32
		do
			if planeheight /= cachedheight [y] then
				cachedheight [y] := planeheight
				distance := {M_FIXED}.fixedmul (planeheight, Yslope [y])
				cacheddistance [y] := distance
				i_main.R_draw.ds_xstep := {M_FIXED}.fixedmul (distance, basexscale)
				cachedxstep [y] := i_main.R_draw.ds_xstep
				i_main.R_draw.ds_ystep := {M_FIXED}.fixedmul (distance, baseyscale)
				cachedystep [y] := i_main.R_draw.ds_ystep
			else
				distance := cacheddistance [y]
				i_main.R_draw.ds_xstep := cachedxstep [y]
				i_main.R_draw.ds_ystep := cachedystep [y]
			end
			length := {M_FIXED}.fixedmul (distance, Distscale [x1])
			angle := (i_main.R_main.viewangle + i_main.R_main.Xtoviewangle [x1]) |>> {R_MAIN}.angletofineshift
			i_main.R_draw.ds_xfrac := i_main.R_main.viewx + {M_FIXED}.fixedmul (i_main.R_main.Finecosine [angle.as_integer_32], length)
			i_main.R_draw.ds_yfrac := - i_main.R_main.viewy - {M_FIXED}.fixedmul (create {FIXED_T}.from_integer (i_main.R_main.Finesine [angle.as_integer_32]), length)
			if attached i_main.R_main.fixedcolormap as fixedcolormap then
				i_main.R_draw.ds_colormap := fixedcolormap
			else
				index := distance |>> {R_MAIN}.lightzshift.as_natural_32
				if index >= {R_MAIN}.maxlightz.to_natural_32 then
					index := {R_MAIN}.maxlightz.to_natural_32 - 1
				end
				check
						attached planezlight as pzl
				then
					check
							attached pzl [index.to_integer_32] as pzli
					then
						i_main.R_draw.ds_colormap := pzli
					end
				end
			end
			i_main.R_draw.ds_y := y
			i_main.R_draw.ds_x1 := x1
			i_main.R_draw.ds_x2 := x2
			check
					attached i_main.R_main.spanfunc as spanfunc
			then
				spanfunc.call
			end
		end

	r_findplane (a_height: FIXED_T; picnum: INTEGER_32; a_lightlevel: INTEGER_32): VISPLANE_T
		local
			c: INTEGER_32
			height: FIXED_T
			lightlevel: INTEGER_32
			found: BOOLEAN
			ch: VISPLANE_T
		do
			height := a_height
			lightlevel := a_lightlevel
			if picnum = i_main.R_sky.skyflatnum then
				height := create {FIXED_T}.from_integer (0)
				lightlevel := 0
			end
			from
				c := 0
				found := False
			until
				found or c >= lastvisplane
			loop
				ch := visplanes [c]
				if height = ch.height and picnum = ch.picnum and lightlevel = ch.lightlevel then
					found := True
				else
					c := c + 1
				end
			end
			if found then
				Result := visplanes [c]
			else
				if lastvisplane = Maxvisplanes then
					{I_MAIN}.i_error ("R_FindPlane: no more visplanes")
				end
				ch := visplanes [lastvisplane]
				lastvisplane := lastvisplane + 1
				ch.height := height
				ch.picnum := picnum
				ch.lightlevel := lightlevel
				ch.minx := {DOOMDEF_H}.screenwidth
				ch.maxx := -1
				ch.top.fill_with (255)
				Result := ch
			end
		end

	r_checkplane (pl: VISPLANE_T; start, stop: INTEGER_32): VISPLANE_T
		local
			intrl: INTEGER_32
			intrh: INTEGER_32
			unionl: INTEGER_32
			unionh: INTEGER_32
			x: INTEGER_32
		do
			if start < pl.minx then
				intrl := pl.minx
				unionl := start
			else
				unionl := pl.minx
				intrl := start
			end
			if stop > pl.maxx then
				intrh := pl.maxx
				unionh := stop
			else
				unionh := pl.maxx
				intrh := stop
			end
			from
				x := intrl
			until
				x > intrh or else pl.top [x] /= 255
			loop
				x := x + 1
			end
			if x > intrh then
				pl.minx := unionl
				pl.maxx := unionh
				Result := pl
			else
				Result := visplanes [lastvisplane]
				lastvisplane := lastvisplane + 1
				Result.height := pl.height
				Result.picnum := pl.picnum
				Result.lightlevel := pl.lightlevel
				Result.minx := start
				Result.maxx := stop
				Result.top.fill_with (255)
			end
		end
	
feature 

	Yslope: ARRAY [FIXED_T]
		once
			create Result.make_filled (create {FIXED_T}.from_integer (0), 0, {DOOMDEF_H}.screenheight - 1)
		end

	Distscale: ARRAY [FIXED_T]
		once
			create Result.make_filled (create {FIXED_T}.from_integer (0), 0, {DOOMDEF_H}.screenwidth - 1)
		end
	
invariant
		floorclip.count = {DOOMDEF_H}.screenwidth
		ceilingclip.count = {DOOMDEF_H}.screenwidth
		visplanes.count = Maxvisplanes
		visplanes.lower = 0
		openings.lower = 0 and openings.count = Maxopenings
		cachedystep.lower = 0 and cachedystep.count = {DOOMDEF_H}.screenheight
		cachedxstep.lower = 0 and cachedxstep.count = {DOOMDEF_H}.screenheight
		cacheddistance.lower = 0 and cacheddistance.count = {DOOMDEF_H}.screenheight
		cachedheight.lower = 0 and cachedheight.count = {DOOMDEF_H}.screenheight
		spanstart.lower = 0 and spanstart.count = {DOOMDEF_H}.screenheight

end -- class R_PLANE

Generated by ISE EiffelStudio