🔥 HOT: Forums/post - Uncensored 2025
|
|
ForumsSega Master System / Mark III / Game GearSG-1000 / SC-3000 / SF-7000 / OMV |
Home - Forums - Games - Scans - Maps - Cheats - Credits Music - Videos - Development - Hacks - Translations - Homebrew |
View topic - Maze3d
![]() |
Goto page 1, 2, 3 Next » |
| Author | Message |
|---|---|
|
Maze3d
Last edited by under4mhz on Sun Jun 12, 2022 10:33 pm; edited 2 times in total |
|
After our discussion of 3D graphics, I thought I’d have a look at optimising my Mazenstein3d code.
I converted the graphics to use tiles instead of lines, added a lut for the line angle division, removed the doors being in between the walls, cached the tile data to upload to the vdu and moved to sdcc 4.2. Now that it’s tile based, it was fairly easy to port the code to SMS and GG. The improvements moved the frame rate from 3.5fps up to 7fps for SMS and 9fps for GG. The SG(-1000) is running at 6fps, since it has no memory for tile caching. The poor little GameBoy(GB) port is running at 5fps, but that’s a reflection of the GB only being able to write to the vdu during vsync, my vdu library being written in C. Interestingly the ZX spectrum version runs at the same speed as the SG (but I wrote a bucket load of assembler in the vdu library to get it that fast). The engine only casts one ray per column (32 for SMS and 20 for GG) and the heights between two columns are averaged to get the angle. If a corner is detected, the averaging is disabled, since these walls need to be sharp up to the edge. This is why the GG is significantly faster here, since it builds only 60% of the scene that the SMS version does. It’s possible to make the scene wider for GG, but it would still be only 20 columns wide. All the divide and multiply functions are unrolled loops. One divide that was possible to remove (and replaced with a lookup table(LUT)) was the 1/player rotation angle calculation, since this is constant and only dependent on the player view rotation. The sdcc compiler with its new feature of passing parameters in registers generally gives about a 10% speed improvement. The SMS version using sdcc 4.1 runs at a flat 6fps, whereas the 4.2 version runs about 7fps. The ZX and GB ports didn't work properly in sdcc 4.2, I'll have to look into that at some point. The screenshots for GB and ZX are for 4.1 and are 10% slower than the others. The doors are a bit deeper into the walls than before. This is because to render the doors half way between the walls, the map position (x,y) was halved, so the doors could fit in between. A single unit on the map is a wall thickness, and drawing something in between is difficult. By making the doors aligned with the walls, half the ray casting looks up could be removed. All the tile data is written to a 1024b array (32 cols*16 rows*2b per tile), being (slightly) faster to randomly access an array and blit the data to the vdu. The map is a simple text map stored as strings. These were taken from screenshots of the original maps. A custom converts the 8x8 tiles on the image to text characters. There are a large amount of unique tiles on the maps, but these have been culled down to wall, space, door and starting point for speed and a lack of tile space. The tiles are split between door and wall colours. A tile is stored for each angle between horizontal and diagonal, for each of the 8 possible pixel heights for a tile. Each different colour requires its own tile set. The walls look a bit wonky, since only one tile is rendered for the top of the wall. This means a line that crosses between two vertical tiles, will only render one of them for the column. This is why some columns have a flat top or aren't as sloped as expected. It’s a trade-off between accuracy and speed, the more visually accurate it is, the slower it’ll go. The trade off here is worth it, since once the player starts moving, the small rendering inaccuracies are generally forgiven (as long as the game is fast and fun). The graphics takes advantage of the (SMS’s) ability to flip the tiles vertically and use the sprite palette for tiles to make the floor tiles grey and the bottom row of tiles. It’s possible horizontal flip to remove half the wall tiles again to allow for more colours and possibly textures. The GameBoy doesn’t have this feature, and so runs out of tiles. Either the door will need to be rendered with edges of the wall colour, half the possible angles of the lines or make the doors the same colour as the walls. The ZX version run out of tiles as well, since only 256 tiles (1byte) are cached in the internal tile memory space that's been implemented. The SG version is blocky to allow border lines to be drawn on top of the walls. This gives better definition to the walls, and is worth the trade-off of the blocker look. It is possible to add wall textures, but this will slow it down again. Each texture will have to be added during the wall rendering according to height, and each look up slowing it down. Right now, a single tile is used for the walls, which can be written quickly. The number of tiles would be large as well, requiring 32 tiles for each texture, one for each of the 32 possible angles, and that is if the same texture is used into the distance. The great thing about demos is that we don’t have to worry about trivial details such as opponents and pickups. Which will slow the engine down significantly. So look at these results with that in mind. Tile space is needed for each of these on the screen, for each zoom level. Each item on the screen will need its own set of tiles, each zoom level loaded as required. This will limit the number of objects on the screen at once. There’s definitely some more gains to be had here. The function that does the searching for walls (ray casting) is all in C, which can be written in assembler for a double speed improvement. As can the code to write the scene to the cache. Being a (pseudo) 3D engine, it’s one of the few games where nearly everything is an inner loop and could be re-written in assembler for a speed improvement. Basically, anything labelled “Self Time” in the profile graph below would likely be halved if written in assembler. That’s probably about a 20% improvement, so maybe another 2fps. I’m probably not going to do that. Z80 assembler is fun, but time consuming and unreadable after a week (though somehow incredibly clear at the time). Adding enemies will probably take 1 or 2fps away, and if I rewrite the rendering and casting code in assembler, it may end up steady at about 6fps on an SMS. Next, I’ll probably work on supporting making the rooms different colours. The doors will be marked as a colour and upon opening, the palette will be updated to reflect the rooms’s colour. Two sets of tiles can be used to flip between the current wall colour and the next, so the existing room will have the same colour walls. I’ll also work on making it a complete game with all the levels (minus any enemies), so that it’s a complete game in its own right. That’ll at least make it a more interesting and playable game, even if it’s not a first person shooter. I'll probably port it to Amstrad CPC 464 and Microbee as well, becausing porting stuff to other platforms is fun (and easy, after the first one). |
|
|
|
|
|
|
|
Awesome project, and awesome technical explanation.
Would there be any demo ROMs/source code/video recordings available? Please, keep us updated. ;) |
|
|
|
|
|
|
| Yeah this is really great stuff, very impressive! | |
|
|
|
|
|
| Oh my...very interested to see this developing! | |
|
|
|
|
|
|
I've attached Demo ROMS for your platform of choice.
|
|
|
|
|
|
|
|
This already looks incredible, congrats and keep up the great work!
I am yet completely ignorant in programming for the SMS (although I am about to begin), so please forgive me if this does not make much sense. I once saw a video of toy story the videogame for the genesis made by its main developer who explained that for the FPS part he basically draw only half of the screen (either bottom or top) and the rest was just mirrored, as the 3d scene was symmetric along the horizontal center line. Would the SMS be able to do that, or is it something that you already do/ are considering? Again, sorry of this question does not make much sense, and keep up with the neat project ;) |
|
|
|
|
|
|
Oh, I guess this means you are already doing it :) |
|
|
|
|
|
|
|
You can't mirror the whole screen down or across the middle, if that's what you mean (*)
(I was actually unaware of the mega drive / genesis being able to do that, and although there are a couple of great YouTube videos on fancy stuff in ToyStory, I couldn't find reference to that) You can mirror individual 8x8 background tiles either horizontally, or vertically, or both at the same time, by setting bits 9 and 10 in the 16 bit entry for that tile in the screen map - that's what under4MHz is referring to here. You can't mirror sprite tiles, so they will always always facing the same way. See this thread for an interesting discussion of the only alternative, which is to design a left-facing and right-facing tile (for horizontal scrolling games). * Note, just thinking there are numerous mask bits in some of the VDP registers which might (?) allow repeating sections of the screen, although I can't think of how they would literally reflect portions of it. UPDATE: yeah looks like from this guide you can clear bit 0 of register 2 which will have the effect of repeating the top 16 rows of the screen in the bottom 8 rows of the screen. Again, not reflected, just repeated. |
|
|
|
|
|
|
Thank you for your feedback, really appreciate that :)
I was referring to this video actually, even though I am totally unaware on how it works (about t=4:30 it talks about it): |
|
|
|
|
|
|
|
Ah, okay, so actually what I think is happening here is that there's a fixed screen map and he's updating the tile definitions continuously.
Forgive me if you already know this stuff, but basically the SMS (and Megadrive I believe) has a screen map of 32x24 tiles. Each tile is 8x8 pixels. That means in the VRAM of the VDP there's a table of 768 entries each of which references a tile definition (aka patterns). There are space for 512 (ish...) patterns for the SMS. For most games, like 2D-scrollers, there is a fixed set of tiles and the screen map simply determines where those tiles are placed on the screen. So for example, you might have the top 20 rows of 32 tiles be tile #1 which is plain blue - for the sky; and the bottom 4 rows of 32 tiles be tile #2 which is plain green - for the grass. This way you only need 2 background tiles to draw the whole screen. Those 2 tile never change, but what you can do is change the screen map so that different tiles are sky and different ones are grass. For example, to create the effect of a set of "steps" going up or down hill. Alternatively, you could instead create a fixed screenmap, so it might look something like this: 1 2 3 4 5 6 7 8 9 10 11 12 .......... 32
33, 34, 35, 36, 37, 38, 39...........64 ... ... 736, 737, 738, 739..........768 (that's assuming you can define 768 different tiles, which in the SMS you can't, but see below!) This way, instead of changing the screen map in response to user action, you keep the screenmap fixed but change the tile definitions. e.g. if you change the definition for tile #1 it changes the top left 8x8 pixels of the screen, and if you change the definition for tile #32 it changes the top right 8x8 pixels of the screen. This allows you to more finely control the actual pixels on screen without being limited to a fixed set of tiles. However it's significantly slower because (for the SMS) you need to update 4 bytes of data for each tile, instead of just 1 to modify a single tile reference in the screen map. Now, in this Toy Story 3D example they are doing the fixed screen map technique, but they are storing a screen map which mirrors itself. e.g. it looks like this: 1 2 3 4 5 6 7 8 9 10 11 12 .......... 32
33, 34, 35, 36, 37, 38, 39...........64 ... 321, 322, 323, 324............352 353, 354, 355, 356............384 // this is halfway down the screen 353, 354, 355, 356............384 321, 322, 323, 324............352 ... 33, 34, 35, 36, 37, 38, 39...........64 1 2 3 4 5 6 7 8 9 10 11 12 .......... 32 Crucially, when the screen reflects on rows 13 and above, the tiles are also flipped in the x-axis ("vertical flip") This means that: 1. You now only need 384 tile definitions instead of 768- win! 2. The screen is automatically reflected in the x-axis, i.e. when you update tile #1 it updates both the top left 8x8 pixels in the screen and the bottom left 8x8 pixels in the screen. 3. It's half as many tiles needed to update - win win! Now, is this technique possible on the SMS? In theory, yes. However in practice writing to VRAM on the SMS is significantly slower than in the Megadrive. How much slower I'll have to work out on the back of an enveloper, but the Megadrive support something called DMA which means the CPU can write directly to VRAM, whereas the SMS needs to "talk" to its VDP via a relatively slow interface which only allows a certain limited throughput of data. Will update with some more specifics when I get the chance but I think this would put the theoretical throughput too low for a reasonable 3D experience based on this technique. |
|
|
|
|
|
|
| the only way to mirror the top half of the background is to reuse the same tiles vertically flipped and arranged bottom-up in the bottom part of the screen - yes, it takes time but fortunately you can write to the VDP even outside of vBlank, you just have to accept to do it slower. | |
|
|
|
|
|
| I realise it's early days but this is awesome. Wolfenstein 3D running on Sega Master System. WOW!! | |
|
|
|
|
|
| Hope it gets updated! | |
|
|
|
|
|
|
I like your Doomguy (Wolfensteinguy, Mazeguy?), especially considering the little number of colors you used.
I've created one with more colors, using colors in the palette that were not being used yet. I've also included an alternative version with less colors but still more that yours. |
|
|
|
|
|
|
|
Awesome. That really looks fantastic. I've added it to this demo release. I'll animate him later.
Keep in mind that we need a few colours for the gun firing, and an extra colour to be able to swap wall colours as we move through the rooms. I never realised what a challenge it was to create a good looking screen from only 16 colours, until I started having to do it. (GG: you have 4096 colours, but you can only use 16, argh!). I've tried to clean up the rendering a bit and did some optimisations to bring it up to a whopping 8fps (~average). I've also added three more levels (a total of four), with a static screen at the end of the level. Only 4 since, that fits into 48K without having to deal with bank swapping yet. I realigned the status bar to fit better with the tiles and reduced the screen height to 16 rows, faster and easier to show the weapon. The numbers on the status bar are actually rendered, instead of being a part of the status bar image. I've also added a single static enemy to test the feasibility adding enemies, since the scaling requires loading all the tiles in vdu as required. Not sure how I'll deal with transparency for the soldier, sprites maybe? Though I'll need mixture of tiles and sprites when he's large, which will be a lot of work to implement. |
|
|
|
|
|
|
|
I haven't played it yet, but the screenshots look great.
I guess you can use 32 colors if you use background tiles only, and no sprites. And how many colored walls do you want in one level? Just 2 like i the current version? Perhaps an ingame color swap of the palette is possible to change wall colors of a different section. Although i think it will always cause a slight pauze. If you use orange for fire then you can make doomguys hair orange. The soldier has darkgreenbrown pants in your screenshot. So you could use that for another shade of brown of doomguy. Although simply dark red might look better instead. Oh well, it's an awesome project, that look great already. The solution with all those different angled tiles is genious! I wish i was able make something like that. |
|
|
|
|
|
|
|
I'll probably want 3 colours for the walls and doors, one colour for the doors, one for the current room and one for the next room. I'll cycle through them as the player encounters a new room colour. I'll probably have a third copy of the tiles using a different palette colour so there should be no pause between rooms. Though this will take extra vdu space and so limit how many objects I can have in a room. I'll reduce the tiles used by using the horizontal flip feature, but this reduces the frame rate.
I've reused doomguy's current colours in the gun firing; I think it looks good enough, maybe a bit of red. I have a spare palette slot or two. I'm currently using the tile palette for the background (walls and doors) and status bar, and the sprite palette for any of the objects (soldiers, food, treasure etc). Using a mixture of tile and sprite palettes in a single object would be possible, but that will much harder to implement. Given the vdu size limitations, I'll probably have to limit the soldiers to one per room and only have one type of object per room (eg soldier or food). I need two copies of any object in the vdu at the same time - one for the current image and one for the next image (as you move closer, or he animates or moves). This is so I can load the image in background with out the change in tiles being seen by the player. Thanks, I sat on this engine for a year trying to figure out use tiles instead of lines. Once I sat down and actually did it, it wasn't as difficult as it sounds. |
|
|
|
|
|
Last edited by under4mhz on Tue Jul 19, 2022 10:29 pm; edited 1 time in total |
|
I've added the 10 levels from the first episode (not tested) and added all the enemies (as soldiers). At this point it's all the possible soldiers, so it's effectively on the hardest level. No interactions with the npc's yet. They're just there for show. I'll probably cull the npc's so there's only one on a screen at a time, since loading two at a time takes up too much vdu memory. It may be possible if they're far apart, since smaller images take up less vdu space. No collectables yet and you can walk through soldiers.
There's background music and basic sound effects for the weapon (since that was fairly easy to add). The walls change colour according to the well texture, to give a sense of progress through the game. With all the extra soldiers, it's slowed down to about 6fps. Loading soldier images slows it down to about 4fps. There's probably some gains to be had (ie less npc's, cache images) but there's still npc AI to implement, so it probably won't be much faster. |
|
|
|
|
|
|









