Raycaster Experiment

Use this forum to share and discuss Uzebox games and demos.
Post Reply
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Raycaster Experiment

Post by D3thAdd3r »

Hmmm I am not understanding the 60 pixels thing exactly but are you thinking from previous posts here? Long story short I have had 3 different attempts in this thread over the last several years and ranted about details of each, but the last one was a custom video mode of 64 @ 24cycles/pixel( :lol: ) I thought I had something big until I saw it run, it was just too low to be useful. It was just straight forward with nothing crafty because I was learning assembly, and I definitely did not manage to hash any bitcoins out in the process! The code I just put up does work at 11 cycles per pixels(in my mind) which I will truncate down to 128 pixels per scanline. This will be double the resolution of the last demo, and very close to the *effective* horizontal resolution that Doom was on the Atari Jaguar/32x/3d0 ports Basically vram is just pairs of bytes saying which vertical slice of a texture to draw, and then which precomputed scaling table to use to look up the pixels in that slice. There is a flag in that data that specifies if it is actually a ram column. A ram column contains all the pixels for the texture slice blit to it(during the main programs time) and then also sprite pixels on top of that. Super fast to update vram, takes a while to actually calculate those values correctly during main program time, but it avoids any amount of waste on twiddling pixels around and all that.

Thanks for the specifics on the interrupt method, I will definitely try implementing it like you said. Failing that, I will copy your code whenever I get my greedy hands on it ;) I am getting some weird issue with the linker when my inlined code gets too long(something with vector table..rjmp vs jmp?)...haven't found a fix but since I don't want wasted flash in the end product so I decided now is the time to make it interrupt terminated. I believe this is the best possible method to use for a raycaster on Uzebox, as now the resolution is good, there is plenty of ram left for sprite columns. I am not interested in optimizing for more horizontal resolution now because calculating even 128 columns in a raycaster take a lot of cycles, I might even try lowering it to 96 with the interest of increasing actual frame updates. I didn't want to start doing a raycasting engine in assembly after I saw 64 pixels wide, but with 128 I am interested again. I think with an asm engine using fixed point, a frame rate of perhaps 6-12 would be achievable at this resolution with sprites..lot of optimization to be done on sprite blitting and stuff
CunningFellow
Posts: 1445
Joined: Mon Feb 11, 2013 8:08 am
Location: Brisbane, Australia

Re: Raycaster Experiment

Post by CunningFellow »

Yeah - I counted 60 pixels across the screen on the last HEX I saw you posted.

Anyways - The video mode does not sound too scary. When I am back to being fighting fit I will see what I can think of to sneak in under 11 pixels.

I'm having some ideas about what could use the (very low) VRAM usage idea your using of just a word per H-Pixel AND add giant scaled sprites over the top of it. So far in my head the raycast thing plus giant scaled sprites is all coming in at 12+ clocks per pixel.
CunningFellow
Posts: 1445
Joined: Mon Feb 11, 2013 8:08 am
Location: Brisbane, Australia

Re: Raycaster Experiment

Post by CunningFellow »

D3thAdd3r wrote:Thanks for the specifics on the interrupt method, I will definitely try implementing it like you said. Failing that, I will copy your code whenever I get my greedy hands on it ;)
Source for T2K in its thread now
D3thAdd3r wrote:I am getting some weird issue with the linker when my inlined code gets too long(something with vector table..rjmp vs jmp?)
I've never found an automatic fix when mixing ASM and C. But on a case by case basis you just have to replace the Relative JMP with absolute ones when the ASM grows too long.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Raycaster Experiment

Post by D3thAdd3r »

Assuming my old calculations were correct, there should now be enough ram for still 50+ ram columns at 64 pixels high with this method(but that is using nearly all available ram)or about half the screen width for sprites of any height. Now that I think of it, if you find a better way to do sprites that kicks the resolution down to even 100 pixels and saves ram for an actual game while doing more work during rendering that would likely be better. I worry the raycasting will be hard to get fast enough to support more than 100 resolution.
CunningFellow
Posts: 1445
Joined: Mon Feb 11, 2013 8:08 am
Location: Brisbane, Australia

Re: Raycaster Experiment

Post by CunningFellow »

Im thinking that maybe you could do 100x112 pixels with arbitary sized SCALED sprites at a maximum of 4 sprites per scanline in under 2K of RAM. It would burn flash though.

This is sounding like a monster of a video mode again as bad as the tempest one.

I won't start working on it till the other game is polished up.

D3athadd3r - Did the interrupt ended scanline ASM code make sense?
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Raycaster Experiment

Post by D3thAdd3r »

The interrupt trick makes sense but I have not tried it for this yet. Once I get my current WIPs done I will actually have a little more focus on this, because I think we could have a nice 2 player deathmatch game with link cable or later over internet. Definitely get Tornado done :mrgreen: this has just been a fun diversion I take now and again, and each time a little progress is made.

If 100 is possible with 4 sprites per scanline then that I think that is actually a better video mode for what we want. I see a possible down side. If it was 110x112(doubled scanlines? so then essentially 224 raster lines) then the frame rate will come to a screeching halt. If you look at the last demo it ran very slow and the reason so much of the screen was blank lines was because I needed all that CPU time to even get that frame rate(and those glitchy screens that happen sometimes is when vsync catches the program while it's swapping data in vram, need double buffers). That engine is floating point but for a real game I think twice that frame rate would be necessary to play "well". I do think a highly optimized fixed point engine that used some of the "moving hacks" from before(only calculate half as many lines while moving) could run 4 or more times faster, but that is what would be necessary to establish the bare minimum of playability. I could be wrong, but I still think the screen will have to be at least 1/3 blank lines no matter what. I suspect 100x60(tripled lines) would be ideal. It's a very interesting idea, let's meet back here when we both have more time!
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Raycaster Experiment

Post by D3thAdd3r »

Just played today on real hardware(SD flash cart), I'm totally blown away!

Wolfenstein on a stock genesis/megadrive! Thought someone might find this interesting, the texturing and resolution is pretty good and it runs a playable rate of 12-15fps. Wolf and Doom ports have always interested me even though they always butchered the control and offered no support for a mouse, and no dedicated strafing buttons(besides SNES) :? ChillyWilly made a good version for 32x which of course looks better, but it's interesting that they managed to pull this off so well and comparable to SNES version without the add-on. The enemies don't just have the front facing sprites as was common for Doom on the lower spec systems. They appear to have all angles included so you can actually get behind an enemy instead of them always facing you no matter what.

I have a genesis 6 button but no way to configure for dedicated left/right strafe, have genesis mouse...not supported..genesis xband modem...and a "Zero Tolerance" link cable, of course not supported either :x Still they get this to play pretty well for a 3 button design, and I think it's one of the best games on the genesis. Anyways it got me dreaming of a simple no enemies, player versus player raycaster on Uzenet some day.

Edit - here is I think the latest version which fully supports circle strafing with dedicated left/right side stepping on the 6 button; it's a night and day difference. It also apparently supports the "Sega Mouse". I just opened a sealed box with a brand new one inside...didn't work so pretty bummed out :(
User avatar
jhhoward
Posts: 76
Joined: Fri Feb 07, 2014 8:11 pm

Re: Raycaster Experiment

Post by jhhoward »

Wow that Wolfenstein 3D mega drive port is really impressive! For a split second when I saw your post I thought you had ported it to uzebox :lol:

Has anyone recently tried writing a raycaster specific video mode? For non-textured walls, this could be fairly simple: have an array for each column of wall 'size' and colours, and then in the main render loop just do a compare against the current scanline to determine whether you are drawing floor, wall or ceiling. Sprites could be a little tricky, but perhaps achievable by using a ram tile system like mode 3?
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Raycaster Experiment

Post by D3thAdd3r »

jhhoward wrote:Sprites could be a little tricky, but perhaps achievable by using a ram tile system like mode 3?
The previous demo already was capable of sprites in the exact way you mention but I didn't get around to showing it. A big problem with that low of resolution is that sprites can't have much detail, I could not make anything that didn't look like a cartoon gun. Tried resizing Doom shotgun to 10x10 or so and you can imagine the results. In retrospect you should probably just draw a crosshair, and a picture of the gun in the tile based HUD and save the ram columns for other things.

I worked on it a tiny amount after the fact and I could be convinced to believe non-textured is the way to go. You save a lot of flash space not needing textures, you should be able to have a higher resolution, it will be faster to blit ram columns, *slightly* faster to calculate rays(since you don't need to determine texture x-offset), and you don't need too detailed of sprites to match the rest of the look. Resolution definitely has to be better than that last demo, but increasing it adds 2 very heavy weights so it is a balancing act. The more horizontal resolution you have the more time that needs to be spent calculating rays, since there is 1 for every pixel of horizontal resolution you have(vertical resolution is "free" except for ram cost). The second issue is that the same amount of available ram covers a smaller portion of the screen. The last demo could probably have enough to cover 2/3 of the screen with sprite columns. If you triple the resolution you only have enough to do a sliver of it. You could start going to lower color depths but then your resolution will probably dive from extra bit twiddling.

CunningFellow's idea might be a good way to go but I would have to see a working example of it being better before I gave up the ram column idea.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Raycaster Experiment

Post by D3thAdd3r »

CunningFellow or other asm experts, I started looking into ijmp and hopefully can try actually utilizing interrupt ended scanlines. I very off and on think about the raycaster again, and I think the engine can be a high enough frame rate to be ok. I think the "perfect" balance is to forget texturing because it looks bad at such low resolution and takes a ton of flash plus the 24 cycle pixel. Too much higher resolution and I think getting 10fps is hopeless. I'm theorize the ideal is 4bpp @ 80x100. The mode would allow any height wall of any color unlike the textured which relied on all same height walls. In place of textures, some amount of shading is possible since the colors per column would be definable though the pallet choices need to cooperate with the sprites. Ram columns only start on the vertical pixel where there is actually sprite pixels, but I think I am out of cycles to pull that off. Even if not, there should be more than enough ram columns to get a good number of sprites on there with full columns. I generated this pseudo-image following the restrictions of the mode I can't seem to make quite work. If it can work, it should generate an image as such, and I think it would be worth doing even if the gameplay was fairly simple.
raycaster4bpp80x100.png
raycaster4bpp80x100.png (3.5 KiB) Viewed 10621 times
raycaster4bpp80x100outline.png
raycaster4bpp80x100outline.png (3.93 KiB) Viewed 10620 times
I am about to let this sit for a while, again, I was just curious if you think this method is a total lost cause or if you see some way I could do some tricky ijmp thing. The problem is I can't fix the vram pointer in all cases but I have to store ram column addresses for every column even if its not used...since there is not time to check such things.

Code: Select all

;vram is laid out like: mode(1), ceiling length(1), wall length(1), wall color(1), ram column addr H(1), ram collumn addr L(1)
align .256
sub_video_mode14:

	;waste line to align with next hsync in render function
	ldi r20,CEILING_COLOR
	ldi r21,FLOOR_COLOR
	clr r22;cleared reg for adc

	ldi r23,VERTICAL_RESOLUTION;total scanlines to draw

	ldi YL,lo8(columns_data)
	ldi YH,hi8(columns_data)

	ldi ZH,hi8(@ram_column);setup MSB for ijmp
	ld ZL,Y+;get mode offset for first column
	
	WAIT r19,xxx

next_scan_line:
	ijmp

ram_column:;once it is a ram column, it stays that way until the end so no mode change checks needed
	adiw YL,2				get past ceiling length, wall length, and color
	ld XL,Y+;				get pointer HI to ram column
	ld XH,Y+;				get pointer LO to ram column
	add XL,scanlinenum/2		add offset for this scanline(2 pixels per scanline)
	adc XH,r22;			add cleared reg for carry				
	ld r16,X;				get ram column color index
;;	sbrc scanlinenum,0		choose 1 of 2 pixels(256 byte pallet for 16 possible colors)
;;make second version of everything, 1 using nop the other using swap to keep this to 18 cycles
	swap r16;				every other scanline do this instead of NOP so we can have 1/2 size ram columns
	lds r16,pallet;			look up pallet color
	out r16				draw pixel for this column
	ld ZL,Y+;				get mode for next column
	ijmp					jump to the mode code


ceil_column_no_ram
	ld r16,Y				get ceiling length
	subi r16,1				see if next time should be a wall
	st Y+	,r16				store decremented result
	ldi r17,(@ceil_column_no_ram);setup ceiling mode address for ijmp
	cpse r16,0				or
	ldi r17,(@wall_column_no_ram);setup wall mode address for ijmp
	st -Y,r17; 			store mode for next time around
	adiw Y,2;				get past ram column bytes(unused here)
	out r18				draw ceiling color
	ld ZL,Y+				get mode for next column
	ijmp					jump to the mode code


wall_column_no_ram_column
	ld r16,Y;					get wall length
	subi r16,1				see if next time should be a floor
	st Y+					store decremented result
	ldi r17,(&wall_column_no_ram); setup wall mode address for ijmp
	cpse r16,0
	ldi r17,(@floor_column_no_ram); setup floor mode address for ijmp
	ld r16,Y+				get wall color for this column
	st Y+,r18; 			store mode for next time around
	out wall_color			draw pixel for this column
	ld ZL					get mode for next column
	ijmp					jump to the mode code

	

ceil_column_then_ram_column
	ld ZL,Y				get ceiling length
	subi ZL,1				see if next time should be a ram_column
	st Y+	,ZL				store decremented result
	ldi r17; 				setup ram column mode address for ijmp
	cpse ZL,0				or
	ldi r17; 				setup ceiling mode address for ijmp
	st Y+,r17; 			store mode for next time around
	out ceiling_color		draw pixel for this column
	ld ZL,Y+				get mode for next column
	ijmp					jump to the mode code


wall_column_then_ram_column
	ld Y;					get wall length
	subi r16,1				see if next time should be a ram_column
	st Y+					store decremented result
	ldi r18; 				setup ram column mode address for ijmp
	cpse r16,0				or
	ldi r18; 				setup wall mode address for ijmp
	st Y+,r18; 			store mode for next time around
	ld r16,Y+				get wall color for this column
	out wall_color			draw pixel for this column
	ld ZL					get mode for next column
	ijmp					jump to the mode code


floor_column_then_ram_column:TODO
	ld ZL,Y				get ceiling length
	subi ZL,1				see if next time should be a ram_column
	st Y+	,ZL				store decremented result
	ldi r17; 				setup ram column mode address for ijmp
	cpse ZL,0				or
	ldi r17; 				setup ceiling mode address for ijmp
	st Y+,r17; 			store mode for next time around
	out ceiling_color		draw pixel for this column
	ld ZL,Y+				get mode for next column
	ijmp					jump to the mode code



ceil_column_then_wall_then_ram_column:TODO
	ld ZL,Y				get ceiling length
	subi ZL,1				see if next time should be a wall then ram
	st Y+	,ZL				store decremented result
	ldi r17; 				setup wall then ram mode address for ijmp
	cpse ZL,0				or
	ldi r17; 				setup ceiling mode address for ijmp
	st Y+,r17; 			store mode for next time around
	out ceiling_color		draw pixel for this column
	ld ZL,Y+				get mode for next column
	ijmp					jump to the mode code


wall_column_then_floor_then_ram_column:TODO
	ld Y;					get wall length
	subi r16,1				see if next time should be a floor then ram
	st Y+					store decremented result
	ldi r18; 				setup floor then ram column mode address for ijmp
	cpse r16,0				or
	ldi r18; 				setup wall mode address for ijmp
	st Y+,r18; 			store mode for next time around
	ld r16,Y+				get wall color for this column
	out wall_color			draw pixel for this column
	ld ZL					get mode for next column
	ijmp					jump to the mode code
Post Reply