5 ms·
No, that idea was crazy enough. :) The collision detection system and camera control code were also insane. Mark Cerny helped me make the collision code fast en
by dmbaggett 10y ago
No, that idea was crazy enough. :) The collision detection system and camera control code were also insane. Mark Cerny helped me make the collision code fast enough. Very clever assembly language tricks there, and we used 2K of "scratchpad" static RAM built in to the PS1 (for some reason) to make it fast enough.
- seanwilson 10y ago> No, that idea was crazy enough. :) The collision detection system and camera control code were also insane. Mark Cerny helped me make the collision code fast enough. Very clever assembly language tricks there, and we used 2K of "scratchpad" static RAM built in to the PS1 (for some reason) to make it fast enough. Can you expand on how the precompute trick works further? So you're precomputing the order of the scenery/level polygons because of their predictable movement but how does that combine with objects with unpredictable movement (Crash, enemies etc.) when you render a frame? Did the limitations of this trick limit the gameplay you wanted?
- dmbaggett 10y agoI've been meaning to write a blog post about this, but here's a brief summary. We wrote a software renderer for the SGI that exactly replicated how the PS1 hardware would render. I wrote an imperfect clone for Crash 1, then Stephen White wrote a pixel-accurate one for Crash 2. (This is why Crash 1 has "crispies", as Andy called them -- little pixel-sized glitches in the background -- and Crash 2 does not.) When computing a level, we'd sample N points along the rail as we moved the camera forward through the level. At each point, we'd render the entire scene using the software renderer, then recover the rendered list of polygons, in sorted order, from the (software) Z buffer. We'd then store this sorted list of polygons for each of the N frames, using delta compression between frames to keep the size manageable. The pre-computation phase would choose N empirically, based on much change happened from frame to frame in any given section of the level; basically it would make N as large as it could be without blowing out the page allocation (which was in turn determined by how fast we could read from the CD). This handled all the polygons in the background, which was completely static. For foreground objects like Crash, enemies, gems, etc, we'd bucket sort them in as we rendered the background. This required manual tuning in some cases, so we had an editor where we could manually adjust the "push-back" (i.e., bucket adjustment) for each object in real time while playing the game. These magic values were then stored with the associated objects as part of the level data. If we couldn't make an object sort in right via bucket adjustment, we'd change the background slightly until it worked. In other words, it was a massive hack. :) In terms of gameplay, we took our inspiration primarily from Donkey Kong Country, which had linear gameplay. So the rail model actually worked really well for the gameplay we were trying to achieve. (DKC is still an awesome SNES game; check it out if you haven't played it recently.)
- seanwilson 10y ago> This required manual tuning in some cases, so we had an editor where we could manually adjust the "push-back" (i.e., bucket adjustment) for each object in real time while playing the game. Sounds like one of those hacks where you're simultaneously laughing and shocked at how "kludgy yet it works" it is but relieved you've actually solved the problem in a way that meets the limitations and deadlines you have. Thanks for the replies, great to know this background information on a classic game!
- dmbaggett 10y agoExactly. I was actually very worried about how much data would need to be stored per frame for this to work until I tried it. Most frames required under 100 bytes, incredibly.
- oppositelock 10y agoWhy did you have to simulate pixel level output to sort your polygons, out of curiosity? I've also written rendering engines for hardware lacking Z-buffers, and our approach was to build a BSP tree of the level, which gets you part of the way to working around no Z buffer, since there's no Z overlap in static polygons after being partitioned, but the downside is you split triangles and increase the triangle count, so it reduces model complexity if the splitting blows your poly budget. Once you have your BSP cells, you can precompute all the visible cells from any given cell - heh, we too did this on SGI's, but by rendering each cell is a unique color and seeing which colors were visible in an output frame. Then, within each cell, we'd have six draw orders for triangles, picking whichever was closest to the eye vector. Not pretty, and sometimes this produces quite a bit of overdraw, but it works. I'm guessing your approach minimized overdraw as compared to a heavily pre-computed BSP approach. Anyhow, thanks for the post, it's fun to read about the the hacks form the early days of graphics hardware.
- dmbaggett 10y ago>Why did you have to simulate pixel level output to sort your polygons ... ? We essentially rendered every frame with a Z-buffer; it's just that the Z-buffer was running in software on the SGI ahead of time, as the level was processed. We needed pixel accuracy so that the occlusion was perfect. The BSP tree technique was well understood by then, thanks to Carmack's work on Quake, but we didn't want to split polygons so we used this brute force method instead. However, as a little bit of disinformation to other developers, we put a big file of random data on the Crash CD called BSPTREE.DAT. You still have to download this huge, pointless data file when you buy the game on PSN. I feel slightly guilty about that. :)