Friday, 28th April 2006
One of my ongoing projects is Brass, a Z80 assembler.
The newest release adds all sort of goodness, especially nested modules - for example:
.nestmodules .local .module Animals .module Cat Legs = 4 .endmodule .echo "Humans have ", Human.Legs, " legs.\n" .module Human Legs = 2 .module Brother Age = 17 .echo "My sister is ", Animals.Human.Sister.Age, " years old.\n" .endmodule .module Sister Age = 21 .global Arms = 2 .endglobal .endmodule .endmodule .module Spider Legs = 8 .echo "A spider has ", Legs, " legs.\n" .endmodule .endmodule .echo "Cats have ", Animals.Cat.Legs, " legs.\n" .echo "My brother is ", Animals.Human.Brother.Age, " years old.\n" .echo "My sister has ", Arms, " arms (global!)\n"
It also now allows for unsquished binaries (where each byte is expanded to two ASCII characters - the hexadecimal representation of the byte. This is used in native TI-83 programs).
I'm trying to unify (to some extent) 82, 83 and 83+ programming (as the hardware is fairly standard between them) - hopefully, fairly carefully written source code should be able to be assembled to 82, 83 and 83+ binaries for a variety of shells with a single keypress from Latenite. TI haven't made this easy with large inconsistency between system call names and variable names...
Thursday, 20th April 2006
You can download the source to the DOOM project below here.
It's a real mess in places, mainly in the way it loads level geometry. DirectX initialisation is hard-coded with no fallback code if some feature is missing or unsupported.
Usage: DOOM <iwad> <level>
DOOM DOOM2.WAD MAP31
DOOM DOOM.WAD E1M3
DOOM TNT.WAD MAP03
Keys: use the cursor keys to move around, A and Z to move up and down.
- Floor splitting is abysmal. It is highly inefficient and occasionally wrong. The floor splitting code breaks sectors into horizontal spans which it then splits into triangles - uneven vertex placement results in largish cracks between sectors or "hairline" dancing-pixel cracks inside sectors. Large holes are usually the case of a sector that "overflows" another one. Ideally; decode floors via ssector/seg information or just use glBSP.
- Timing (level animation) is done via the primitive Timer class. If something starts to hog CPU time, the timers all slow down leading to inconsistent animation speeds.
- Scrolling walls and light effects are controlled differently; sector light effects (which affect a large amount of geometry) save the vertex array written to the vertex buffer away; this array is changed then sent to a vertex buffer, overwriting the existing data. The scrolling walls (affect a small amount of geometry) read the vertex buffer, alter the texture coordinates, then write it back. This results in a confict; if a linedef has both scrolling AND lighting effects, every time the light level changes the scroll offset is reset to the original linedef's value.
- Skies that occlude geometry that is visible normally aren't handled correctly.
- Player start angle is occasionally backwards.
- Some of the more bizarre walls still don't display properly.
Wednesday, 12th April 2006
I switched from 16/8 division to 24/16 division, and wall heights are now calculated rather than being dragged off a lookup table.
There's also backface culling (the cylinder around the central cube is marked as double-sided, hence no culling there) but there is still no clipping or occlusion.
@philipptr: I'm guessing your technique is to calculate the angle of the point, add an offset to it, then convert back into a new coordinate? I just adapted some 3D point rotation code I had, simplifying (mathematically) it and removing any references to z.
I will probably not use textured walls - not so much for performance issues, but for aesthetic reasons. Scaling a texture down on a wall displayed on a 96x64 monochrome display (without antialiasing) looks pretty ugly. Simple lines look a lot cleaner.
Oh, and hello aCiD2
Wednesday, 5th April 2006
All this DOOM work had got me interested again in simple 3D engines, and so thoughts turned to my favourite Z80 platform, the TI-83 Plus.
There are a handful of 3D engines out there for it already; Matt3D is a vector-based wireframe engine put to good use in a roller-coaster builder/simulation game and a racing game (that even supports two-player over the link port). However, it's 8-bit and so worlds created with it tend to be very small or distorted thanks to low-resolution (I tried writing a Quake game with it years ago).
The best "wall" engines (displaying a 3D world rather than a 3D object) have been the raycasters. Gemini impresses me the most; it's a Wolfenstein-level engine with objects, sliding "half-block" doors and moving wall blocks, all neatly texture-mapped.
I'm going to try, therefore, to get a vector-based "wall" engine (there must be a proper term for it) up and running. Keeping the map to 2D simplifies the maths a lot; hopefully with a few tricks things should be fast enough! Also, I'll use 16-bit arithmetic throughout to enhance the quality and scale of the maps.
Like DOOM, I'm isolating the vertices from the wall definitions. This way, I can cut down on the number of rotations/transformations required. The maths required to rotate a point around the origin is fairly simple;
Yr = Xo × cos(a) - Yo × sin(a)
For the moment, I'll use a simple lookup-table for wall heights. Ultimately, I'd like to have variable height walls, but these will do for the moment:
The translated X is just 48+(Xr/Yr), as the screen is 96 pixels wide.
Throwing in a few extra lines for walls...
As you might be able to see, there is no clipping if any part of a wall falls outside the viewing range. Also, the walls are not occluding eachother, something you'd really want.