Progress on this weird PS1 character adventure game
Yo, I've been working on this character adventure game made with my 3D renderer, Tetra3D, over the past few weeks! I've shown it here a bit before.
Here's what it looks like right now - normally, it's a 2.5D overhead adventure game (kinda like RPG Maker adventure games like Yume Nikki), but when you interact with a dumpster you go into THE TRASH ZONE. It transitions into a first-person, pseudo-lane-based platforming schmovement / Boost Sonic movement thing. There's not much air down there, so you have to keep moving between these disinfecting crystals. I think it'd be cool to have this area be kind of like a ruined city, so I'll see if I can make that happen!
It's a lot of fun, I think! Still working on it; I need to do something about the graphical glitches once you enter the dumpster.
Also, the game is very moddable, so people can make their own entire games in this - almost the entire game is being built in the modding system. It's cool!
The question “How do you know which in-game item a user has clicked on?“ has surprisingly many answers. It’s not a particularly difficult question to answer, but there are several “correct“ answers out there in textbooks and tutorials. Each of them is supposed to be for 3D games, but they all can be made to work in 2D, for obvious reasons.
Render To Buffer
In order to determine which item a user has clicked on, you render the 3D scene to a special buffer, with special colours and without shading. Every object has its own unique colour (maybe you give each clickable object a unique 24-bit or 32-bit ID) that can be looked up somewhere. Objects that aren’t clickable are just 0x000000.
Now you can look up the position of the mouse cursor in that buffer, look up the colour in your look-up table, et voilà, you get the object the user has clicked on.
Render To Pixel
Rendering the whole 3D scene again with false colours is actually rather wasteful. How about instead of rendering a whole buffer the size of the window, you just render a 1x1 pixel buffer, and set up the camera/view frustum/projection matrix so that the perspective of that pixel is the same as the perspective of the corresponding pixel on the screen? The maths here should be simplified if the reticle is in the centre of the screen. If your engine has view frustum culling, this could even be highly performant.
Draw-And-Pick
Do you enjoy living dangerously? Do you hate your colleagues? Your future self?
Just do drawing and picking in one pass. What could possibly go wrong? Inside your drawing code, just check if the pixel you are currently drawing is under the mouse. This only really works if you are doing software rendering, and it will come back to bite you sooner or later, but it saves you from implementing a separate picking step. So who can say if this is really wrong?
Geometry
This is the most boring answer. In Unity, there is an option to do a ray-cast on the collision data. Sometimes this method is not pixel-perfect, because some objects have hitboxes that are smaller than their render geometry, some have hitboxes that are simple shapes circumscribed around their geometry, and some don’t have collision geometry at all, so the mouse click goes right through them.
All in all, having your level geometry in a data structure you can query easily for continuous collision detection is a pretty good deal. If you can, take this deal!
Physics
This is the most fun answer. Just spawn an object, launch it into the direction of the cursor, and trigger an action when it hits something.
Logo of Pixomatic Rendering Technology. This is software renderer. The Sims 2 uses it.
Here's a note about its features:
RAD Launches Pixomatic -- New Software Renderer
RAD has released Pixomatic, a new software renderer for games developed by Michael Abrash and Mike Sartain. According to the company, the features and peformance that Pixomatic delivers is "roughly equivalent to a DX6-class graphics accelerator (with a few more modern features like stencil and dot3 lighting)". Well aware that game developers try to leverage the power of graphics hardware -- not code around it -- RAD takes pains to explain the rationale behind the launch of its software renderer. The company touts the fact that it lets developers write one set of graphics code for many different PC graphics configurations and obviates concerns about buggy 3D drivers, misconfigured 3D APIs, and whether 3D hardware even exists on the client. Features:
Two-texture multitexture
Gouraud, specular, fog, alpha blending
16- and 24-bit z buffers
Stencil, bilinear filtering, texture transforms, and dot3 per-pixel lighting
Handles transformation, clipping, and projection of trilists, tristrips, trifans, quadlists, polygons, and pointsprites, drawn through begin/end primitives or indexed or non-indexed streams
Performs perspective-correct rasterization, with per-polygon mipmapping, subpixel and subtexel accuracy, and 32-bit color depth
The pixel-shading pipeline is optimized MMX code, compiled on the fly
Geometry and vertex-shading pipelines are MMX, SSE, and 3DNow optimized
SSE and 3DNow are automatically used if present, but are not required
RAD says it runs Quake II at 27 FPS on a Pentium III/733 and 60 FPS on a Pentium 4. Licensing the Pixomatic SDK for one PC game costs $10,000.
Source:
RAD has released Pixomatic, a new software renderer for games developed by Michael Abrash and Mike Sartain. According to the company, the fe
Doing some gameplay modding with the original DOS Descent II using HAXMEDIT. The level is by DarkFlameWolf from her project “Descent 1 The Lost Levels” (a set of more or less D1-style levels for Descent II), with most of the Descent II and Vertigo enemies robots by Descent 1 robots except for some particularly interesting and useful D2 robots (Ice Spider, Sidearm, ITD/ITSC, E-Bandit, Smelter, Seeker, and Boarshead).
The gameplay mod adds tracer effects to the vulcan and gauss cannons, makes flares cost no energy, makes all robot versions of player weapons look and sound the same as the player versions, restores missing robot sounds from Descent 1, and rebalances the Class 1 Driller and Fusion Hulk. They both carry new robot versions of their weapons that scale with difficulty. On Rookie, which I’m playing on, it is quite possible to dodge much of the fire from Class 1 Drillers (as you’ll see in the video), though it requires room to maneuver, distance, and impeccable reaction time. They’re still very scary, but not unfair like in Descent 1. I plan on also making homing robot weapons much slower on difficulties below Ace to allow players to outrun them and pull off wild missile dodges like with revenants in Doom. It works with any source port except D2X-XL, which handles sounds differently from the original engine and will not work properly with the modified .s11 and .s22 files.
I plan on eventually making a level or even multiple levels for Descent using this gameplay mod as a base.
Why the Heck I Made My Own 3D Renderer and How I Didn't Explode in The Process (Pt. 1)
Hello, I'm SolarLune, and welcome to Why the Heck I Made My Own 3D Renderer and How I Didn't Explode in The Process (Pt. 1).
So, I'm the creator of Tetra3D, which is an open-source 3D software-hardware hybrid renderer written from scratch in Go. I'd like to talk about why I made this thing in the first place, and what I learned while doing it.
Here is the Tetra3D logo rendered in Tetra3D, in all its PSX-styled glory
Firstly, what is a 3D renderer?
A spooky, run-down bedroom, rendered through Tetra3D
A 3D renderer is a piece of software that transforms data (like 3D points in space) into something you can see.
OpenGL, Metal, Vulkan, and Direct3D are all examples of true, hardware-accelerated 3D rendering APIs / frameworks. By doing a lot of math, a 3D renderer turns a 3D position in space into a 2D position on your computer screen, and usually ends up drawing triangles on these points to construct something recognizable (like a cube or a chair or something). Cool!
There are, generally (to my basic knowledge), two broad classifications of 3D renderers:
Hardware Renderers. A hardware renderer uses the GPU (or graphics processor) to render triangles onscreen. It also uses the GPU to perform the necessary mathematical calculations to do that rendering and transform the triangles with some measure of perspective (or not, as desired).
Software Renderers. A software renderer uses the CPU for transforming 3D data and even uses the CPU for rendering objects. Since 3D objects are usually easily visualized by rendering them with triangles, software renderers implement their own solutions for drawing triangles onscreen. Software renderers are usually slow, but still can be fun to play around with; they can also be useful for rendering system fallbacks for / debugging hardware renderers, or possibly to render something simple for basic information before using that information to render something complex on the GPU.
OK, so now, what is Tetra3D?
Tetra3D is a bit of a hybrid 3D renderer, because it performs the math to transform data on the CPU (like a software renderer), but still uses the GPU to actually render the triangles (like a hardware renderer).
So, this means that it has some downsides from both categories, really - firstly, it is slower than rendering things with hardware acceleration like a hardware renderer (though faster than a software renderer). Secondly, it still requires a non-negligible GPU / graphics chip of some description, despite doing a large amount of work on the CPU (like a pure software renderer).
(As an aside, depth testing, where part of a triangle is discarded when it draws behind closer triangles, is something that hardware renderers usually handle for us. However, because Tetra3D is a hybrid renderer, it doesn't have access to a traditional depth buffer (as all triangles in Tetra3D are just 2D triangles rendered to imitate depth), so I had to make my own depth buffer system (!). How depth testing is handled in Tetra3D will have to be another post for another day!)
So why make Tetra3D in the first place?
Well, firstly, I like Go; I think it's a great programming language. Go is very comfortable, opinionated, and easy to use - generally, there's one "right" way to do things. This makes it easier to focus on actually doing what you need to do and having code be uniform and easy to understand, rather than spending time wondering if you're using the language properly.
Secondly, I really like that chunky, janky, PS1 CD-3D style, mmmmmm
Like, if you look at the following PS1 games from 20 years ago, they still look fantastic, even by today's standards.
Vagrant Story, SquareSoft (2000)
Threads of Fate, SquareSoft (2000)
Megaman Legends 2, Capcom (2000)
I'd love to be able to create something on this level of polish, and I don't think indie game devs necessarily need a complex 3D engine to do it.
The reasons why these screenshots look so good (despite their low poly-count and low-res textures) is because their lighting, art direction, and style is on point, even with the heavy technical restrictions. It's not really about how many polygons you use, but rather, how well you use them.
Also, the restrictions worked in harmony with the resolution of the time - as monitors went up in resolution, games have had to have higher and higher resolution textures and polygon counts to match the same overall level of fidelity. The developers of bygone times knew their limitations, and worked very well within them to execute the style they wanted, and exercised control very well over that style (because there was less to control). That's why excellently executed retro and low-poly graphics hold up, even today.
I decided to make a 3D renderer to try to capture some of that same "control over a small amount of core features" energy that can help to direct the flow of game development. Originally, I wanted to make a 3D renderer to just render small objects or very simple scenes for primarily 2D games, but over time, I've gotten greedier and want to squeeze out "just a little more" - optimization and improvement are, in themselves, addictive when you don't know if what you're doing will even work.
Was it hard to make this?
Yeah, I guess! There have been times where I've been really stumped as to how to solve a problem or add a feature, but it's always been interesting and fun to work on. Years ago, I had no idea how somebody could make a 3D game using pure code; now I know exactly how, haha! I think everybody should work on something like this - something that they deem impossible, just to see that it's not that hard, it just takes time.
___
So!
To sum up, I like Go, I like Playstation / DS-level graphics, and so I made this stupid 3D renderer for gamedev.
As mentioned previously, it's a hybrid renderer, so there are definite downsides to how it works - however, there are advantages as well. Because it's built on Ebitengine, a 2D game development framework for Go, it's cross-platform by design. Anywhere Ebitengine works, Tetra3D should also work.
It's also very good to have the ability to spontaneously do 3D when applicable, rather than strictly needing to begin game development in a "3D-first" game engine just to have the ability to, say, render a 3D object in a primarily 2D game.
Metal Sonic battle captured from the Sonic Mania longplay here on YouTube
In the above GIF, you can see that while Sonic Mania (which is fantastic, by the way!) is a 2D game, there are 3D elements in there, in the form of the Eggman robot spinning in the background. Mixing 3D in with 2D can look great, and so Tetra3D was also made for this kind of application in 2D as well.
Anyway, that about sums up the reasoning for developing this cool-but-weird piece of technology.
Next time, I'll get into the "How I Didn't Explode Part" and talk about how I made it despite not really knowing much about 3D math, or maybe talk about some renderer internals, I guess?
If anybody has any questions, please feel free to ask me - I'll see about answering when I can.
Hello~ I'm working on a game with Ebitengine, a 2D game framework and working on a 3D renderer written largely from scratch, Tetra3D. I just recently added the ability to alter the depth of triangles as they render, which allows me to fix billboarded sprites so they don't cut into the world around them. Neat!