note
	description: "[
		r_draw.c
		The actual span/column drawing functios.
		Here find the main potential for optimization
		 e.g. inline assembly, different algorithms.
	]"
	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_DRAW

inherit
	DOOMDEF_H

create 
	make

feature 

	i_main: I_MAIN

	make (a_i_main: like i_main)
		do
			i_main := a_i_main
			create ylookup.make_filled (create {PIXEL_T_BUFFER}.make (1), 0, Maxheight - 1)
		end
	
feature 

	Sbarheight: INTEGER_32 = 32
			-- status bar height at bottom of screen

	background_buffer: detachable PIXEL_T_BUFFER
	
feature -- ?

	Maxwidth: INTEGER_32 = 1120

	Maxheight: INTEGER_32 = 832
	
feature 

	columnofs: ARRAY [INTEGER_32]
		attribute
			create Result.make_filled (0, 0, Maxwidth - 1)
		end

	ylookup: ARRAY [PIXEL_T_BUFFER]
	
feature 

	r_inittranslationtables
			-- Creates the translation tables to map
			--  the green color rmap to gray, brown, red.
			-- Assumes a given structure of the PLAYPAL.
			-- Could be read from a lump instead.
		local
			i: INTEGER_32
		do
			create translationtables.make_filled (0, 0, 256 * 3 + 255 - 1)
			from
				i := 0
			until
				i >= 256
			loop
				check
						attached translationtables as tt
				then
					if i >= 112 and i <= 127 then
						tt [i] := (96 + (i & 15)).to_natural_16
						tt [i + 256] := (64 + (i & 15)).to_natural_16
						tt [i + 512] := (32 + (i & 15)).to_natural_16
					else
						tt [i] := i.to_natural_16
						tt [i + 256] := i.to_natural_16
						tt [i + 512] := i.to_natural_16
					end
				end
				i := i + 1
			end
		end
	
feature 

	viewwidth: INTEGER_32 assign set_viewwidth

	set_viewwidth (a_viewwidth: like viewwidth)
		do
			viewwidth := a_viewwidth
		end

	scaledviewwidth: INTEGER_32 assign set_scaledviewwidth

	set_scaledviewwidth (a_scaledviewwidth: like scaledviewwidth)
		do
			scaledviewwidth := a_scaledviewwidth
		end

	viewheight: INTEGER_32 assign set_viewheight

	set_viewheight (a_viewheight: like viewheight)
		do
			viewheight := a_viewheight
		end

	viewwindowx: INTEGER_32

	viewwindowy: INTEGER_32
	
feature -- R_DrawColumn

	dc_colormap: detachable INDEX_IN_ARRAY [LIGHTTABLE_T] assign set_dc_colormap

	set_dc_colormap (a_dc_colormap: like dc_colormap)
		do
			dc_colormap := a_dc_colormap
		end

	dc_x: INTEGER_32 assign set_dc_x

	set_dc_x (a_dc_x: like dc_x)
		do
			dc_x := a_dc_x
		end

	dc_yl: INTEGER_32 assign set_dc_yl

	set_dc_yl (a_dc_yl: like dc_yl)
		do
			dc_yl := a_dc_yl
		end

	dc_yh: INTEGER_32 assign set_dc_yh

	set_dc_yh (a_dc_yh: like dc_yh)
		do
			dc_yh := a_dc_yh
		end

	dc_iscale: FIXED_T assign set_dc_iscale

	set_dc_iscale (a_dc_iscale: like dc_iscale)
		do
			dc_iscale := a_dc_iscale
		end

	dc_texturemid: FIXED_T assign set_dc_texturemid

	set_dc_texturemid (a_dc_texturemid: like dc_texturemid)
		do
			dc_texturemid := a_dc_texturemid
		end

	dc_source: detachable BYTE_SEQUENCE assign set_dc_source
			-- first pixel in a column (possibly virtual)

	set_dc_source (a_dc_source: like dc_source)
		do
			dc_source := a_dc_source
		end

	dccount: INTEGER_32
			-- just for profiling

	r_drawcolumn
			-- A column is a vertical slice/span from a wall texture that,
			-- given the DOOM style restrictions on the view orientation,
			-- will always have constant z depth.
			-- Thus a special case loop for very fast rendering can be used.
			-- It has also been used with Wolfenshtein 3D.
		require
			rangecheck: dc_x < Screenwidth and dc_yl >= 0 and dc_yh < Screenheight
		local
			count: INTEGER_32
			dest: PIXEL_T_BUFFER
			ofs: INTEGER_32
			frac: FIXED_T
			fracstep: FIXED_T
			val: NATURAL_8
			dc_source_val: INTEGER_32
			i: INTEGER_32
		do
			count := dc_yh - dc_yl
			if count >= 0 then
				dest := ylookup [dc_yl]
				ofs := columnofs [dc_x]
				fracstep := dc_iscale
				frac := dc_texturemid + create {FIXED_T}.from_integer ((dc_yl - i_main.R_main.centery) * fracstep.as_integer_32)
				from
					count := count + 1
				until
					count <= 0
				loop
					check
							attached dc_source as dcs
					then
						i := (frac |>> {M_FIXED}.fracbits) & create {FIXED_T}.from_integer (127).as_integer_32
						if dcs.valid_index (i) then
							dc_source_val := dcs [i].to_integer_32
						else
							print ("R_DrawColumn: dc_source read out of bounds [" + i.out + "]%N")
							dc_source_val := 0
						end
					end
					check
							attached dc_colormap as dc_cmap
					then
						val := dc_cmap [dc_source_val].to_natural_8
					end
					dest.put (val, ofs)
					ofs := ofs + Screenwidth
					frac := frac + fracstep
					count := count - 1
				end
			end
		end
	
feature 

	r_fillbackscreen
			-- Fills the back screen with a pattern
			--  for variable screen sizes
			-- Also draws a beveled edge.
		local
			src: PIXEL_T_BUFFER
			dest: PIXEL_T_BUFFER
			border_patch: MANAGED_POINTER
			x, y: INTEGER_32
			patch: PATCH_T
			name1: STRING_8
			name2: STRING_8
			name: STRING_8
		do
			name1 := "FLOOR7_2"
			name2 := "GRNROCK"
			if scaledviewwidth = Screenwidth then
				if background_buffer /= Void then
					background_buffer := Void
				end
			else
				if background_buffer = Void then
					create background_buffer.make (Screenwidth * (Screenheight - Sbarheight))
				end
				if i_main.Doomstat_h.gamemode = {GAME_MODE_T}.commercial then
					name := name2
				else
					name := name1
				end
				border_patch := i_main.W_wad.w_cachelumpname (name)
				create src.share_from_pointer (border_patch.item, border_patch.count)
				dest := background_buffer
				check
						attached dest
				then
					from
						y := 0
					until
						y >= Screenheight - Sbarheight
					loop
						from
							x := 0
						until
							x >= Screenwidth // 64
						loop
							dest.copy_from_count (src + ((y & 63) |<< 6), 64)
							dest := dest + 64
							x := x + 1
						end
						if Screenwidth & 63 /= 0 then
							dest.copy_from_count (src + ((y & 63) |<< 6), Screenwidth & 63)
							dest := dest + (Screenwidth & 63)
						end
						y := y + 1
					end
				end
				check
						attached background_buffer as bb
				then
					i_main.V_video.v_usebuffer (bb)
				end
				patch := create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_t"))
				from
					x := 0
				until
					x >= scaledviewwidth
				loop
					i_main.V_video.v_drawpatch (viewwindowx + x, viewwindowy - 8, patch)
					x := x + 8
				end
				patch := create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_b"))
				from
					x := 0
				until
					x >= scaledviewwidth
				loop
					i_main.V_video.v_drawpatch (viewwindowx + x, viewwindowy + viewheight, patch)
					x := x + 8
				end
				patch := create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_l"))
				from
					y := 0
				until
					y >= viewheight
				loop
					i_main.V_video.v_drawpatch (viewwindowx - 8, viewwindowy + y, patch)
					y := y + 8
				end
				patch := create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_r"))
				from
					y := 0
				until
					y >= viewheight
				loop
					i_main.V_video.v_drawpatch (viewwindowx + scaledviewwidth, viewwindowy + y, patch)
					y := y + 8
				end
				i_main.V_video.v_drawpatch (viewwindowx - 8, viewwindowy - 8, create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_tl")))
				i_main.V_video.v_drawpatch (viewwindowx + scaledviewwidth, viewwindowy - 8, create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_tr")))
				i_main.V_video.v_drawpatch (viewwindowx - 8, viewwindowy + viewheight, create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_bl")))
				i_main.V_video.v_drawpatch (viewwindowx + scaledviewwidth, viewwindowy + viewheight, create {PATCH_T}.from_pointer (i_main.W_wad.w_cachelumpname ("brdr_br")))
				i_main.V_video.v_restorebuffer
			end
		end

	r_videoerase (ofs: INTEGER_32; count: INTEGER_32)
			-- Copy a screen buffer
			--
			-- ofs was originally unsigned
		do
			if attached background_buffer as bb then
				(i_main.I_video.i_videobuffer + ofs).copy_from_count (bb + ofs, count)
			end
		end

	r_drawviewborder
			-- Draws the border around the view
			--  for different size windows?
		local
			top: INTEGER_32
			side: INTEGER_32
			ofs: INTEGER_32
			i: INTEGER_32
		do
			if scaledviewwidth = Screenwidth then
			else
				top := ((Screenheight - Sbarheight) - viewheight) // 2
				side := (Screenwidth - scaledviewwidth) // 2
				r_videoerase (0, top * Screenwidth + side)
				ofs := (viewheight + top) * Screenwidth - side
				r_videoerase (ofs, top * Screenwidth + side)
				ofs := top * Screenwidth + Screenwidth - side
				side := side |<< 1
				from
					i := 1
				until
					i >= viewheight
				loop
					r_videoerase (ofs, side)
					ofs := ofs + Screenwidth
					i := i + 1
				end
				i_main.V_video.v_markrect (0, 0, Screenwidth, Screenheight - Sbarheight)
			end
		end
	
feature -- R_DrawTranslatedColumn

	dc_translation: detachable INDEX_IN_ARRAY [NATURAL_16] assign set_dc_translation

	set_dc_translation (a_dc_translation: like dc_translation)
		do
			dc_translation := a_dc_translation
		end

	translationtables: detachable ARRAY [NATURAL_16]

	r_drawtranslatedcolumn
			-- Used to draw player sprites
			-- with the green colorramp mapped to others.
			-- Could be used with different translation
			-- tables, e.g. the lighter colored version
			-- of the BaronOfHell, the HellKnight, uses
			-- identical sprites, kinda brightened up.
		require
			rangecheck: dc_x < Screenwidth and dc_yl >= 0 and dc_yh < Screenheight
		local
			count: INTEGER_32
			dest: PIXEL_T_BUFFER
			frac: FIXED_T
			fracstep: FIXED_T
		do
			count := dc_yh - dc_yl
			if count >= 0 then
				check
					rangecheck: dc_x < Screenwidth and dc_yl >= 0 and dc_yh < Screenheight
				end
				dest := ylookup [dc_yl] + columnofs [dc_x]
				fracstep := dc_iscale
				frac := dc_texturemid + create {FIXED_T}.from_integer ((dc_yl - i_main.R_main.centery) * fracstep.as_integer_32)
				from
					count := count + 1
				until
					count = 0
				loop
					check
							attached dc_colormap as dcc and then attached dc_translation as dct and then attached dc_source as dcs
					then
						dest.put (dcc [dct [dcs [frac |>> {M_FIXED}.fracbits.as_integer_32].to_integer_32].to_integer_32].to_natural_8, 0)
					end
					dest := dest + Screenwidth
					frac := frac + fracstep
					count := count - 1
				end
			end
		end
	
feature -- R_DrawSpan

	ds_xstep: FIXED_T assign set_ds_xstep

	set_ds_xstep (a_ds_xstep: like ds_xstep)
		do
			ds_xstep := a_ds_xstep
		end

	ds_ystep: FIXED_T assign set_ds_ystep

	set_ds_ystep (a_ds_ystep: like ds_ystep)
		do
			ds_ystep := a_ds_ystep
		end

	ds_xfrac: FIXED_T assign set_ds_xfrac

	set_ds_xfrac (a_ds_xfrac: like ds_xfrac)
		do
			ds_xfrac := a_ds_xfrac
		end

	ds_yfrac: FIXED_T assign set_ds_yfrac

	set_ds_yfrac (a_ds_yfrac: like ds_yfrac)
		do
			ds_yfrac := a_ds_yfrac
		end

	ds_colormap: detachable INDEX_IN_ARRAY [LIGHTTABLE_T] assign set_ds_colormap
			-- lighttable_t*

	set_ds_colormap (a_ds_colormap: like ds_colormap)
		do
			ds_colormap := a_ds_colormap
		end

	ds_y: INTEGER_32 assign set_ds_y

	set_ds_y (a_ds_y: like ds_y)
		do
			ds_y := a_ds_y
		end

	ds_x1: INTEGER_32 assign set_ds_x1

	set_ds_x1 (a_ds_x1: like ds_x1)
		do
			ds_x1 := a_ds_x1
		end

	ds_x2: INTEGER_32 assign set_ds_x2

	set_ds_x2 (a_ds_x2: like ds_x2)
		do
			ds_x2 := a_ds_x2
		end

	ds_source: detachable MANAGED_POINTER_WITH_OFFSET assign set_ds_source

	set_ds_source (a_ds_source: like ds_source)
		do
			ds_source := a_ds_source
		end

	r_drawspan
			-- Draws the actual span
		require
			rangecheck: ds_x2 >= ds_x1 and ds_x1 >= 0 and ds_x2 < Screenwidth and ds_y <= Screenheight
		local
			xfrac: FIXED_T
			yfrac: FIXED_T
			dest: PIXEL_T_BUFFER
			ofs: INTEGER_32
			count: INTEGER_32
			spot: INTEGER_32
			ds_source_val: INTEGER_32
		do
			xfrac := ds_xfrac
			yfrac := ds_yfrac
			dest := ylookup [ds_y]
			ofs := columnofs [ds_x1]
			count := ds_x2 - ds_x1
			from
				count := count + 1
			until
				count = 0
			loop
				spot := (((yfrac |>> (16 - 6)) & create {FIXED_T}.from_integer ((63 * 64))) + ((xfrac |>> 16) & create {FIXED_T}.from_integer (63))).to_integer_32
				check
						attached ds_source as src
				then
					ds_source_val := src [spot].to_integer_32
				end
				check
						attached ds_colormap as dsc
				then
					dest.put (dsc [ds_source_val].to_natural_8, ofs)
					ofs := ofs + 1
				end
				xfrac := xfrac + ds_xstep
				yfrac := yfrac + ds_ystep
				count := count - 1
			end
		end
	
feature 

	r_drawcolumnlow
		require
			rangecheck: dc_x < Screenwidth and dc_yl >= 0 and dc_yh < Screenheight
		do
			{NOT_IMPLEMENTED}.not_implemented ("R_DrawColumnLow", True)
		end

	r_drawfuzzcolumn
		require
			rangecheck: dc_x < Screenwidth and dc_yl >= 0 and dc_yh < Screenheight
		do
			{NOT_IMPLEMENTED}.not_implemented ("R_DrawFuzzColumn", True)
		end

	r_drawspanlow
		require
			rangecheck: ds_x2 >= ds_x1 and ds_x1 >= 0 and ds_x2 < Screenwidth and ds_y <= Screenheight
		do
			{NOT_IMPLEMENTED}.not_implemented ("R_DrawSpanLow", True)
		end
	
feature 

	r_initbuffer (width, height: INTEGER_32)
		local
			i: INTEGER_32
			ofs: INTEGER_32
			left: INTEGER_32
		do
			viewwindowx := (Screenwidth - width) |>> 1
			from
				i := 0
			until
				i >= width
			loop
				columnofs [i] := viewwindowx + i
				i := i + 1
			end
			if width = Screenwidth then
				viewwindowy := 0
			else
				viewwindowy := (Screenheight - Sbarheight - height) |>> 1
			end
			from
				i := 0
			until
				i >= height
			loop
				ofs := (i + viewwindowy) * Screenwidth
				left := i_main.I_video.i_videobuffer.p.count - ofs
				check
						left > 0
				end
				ylookup [i] := create {PIXEL_T_BUFFER}.share_from_pointer (i_main.I_video.i_videobuffer.p.item + ofs, left)
				i := i + 1
			end
		end
	
invariant
		ylookup.lower = 0
		ylookup.count = Maxheight

end -- class R_DRAW

Generated by ISE EiffelStudio