Hello, I'm Orion_, an indie game developer, I mainly code for old video game systems, such as Atari Jaguar, Dreamcast, Megadrive, PC Engine, and Playstation 1 ! I made some games for several systems, and a little puzzle game for the PS1 (released on CD) I'm trying to make a 3D game for the Playstation 1, but it's kind of hard to understand the 3D stuff, even using the official SDK documentation / source example. That's why I've always wondered how the official ps1 games were made back then, and It's been years that I've been searching for an official ps1 game leaked source code to maybe understand it. But such source code are impossible to find, and seems to be kept by collectors, who sometimes have no idea how to use it. So I'm offering help to people having such source code, to try compiling it for them and maybe try to make a usable CD (if the game datas are available with the source code of course) and in exchange I could look at the source code and try to understand better how ps1 games were made and try to make a good one myself too ! Thank you for reading.
The only complete set of leaked PlayStation source code I know of is the one for BeatMania Append 5th mix, which was accidentally shipped as a dummy file on the BeatMania best hits disc.
I read somewhere on this forum that a guy had "a dozen" of ps1 game source code, but didn't know how to use them, that's why I'm posting this topic. I'm aware of the beatmania source, which is the only one known "public".
If you wanna have an idea of how retail games are made, just disassemble them with IDA or whatever software of your choice. Once you figure out the assembly logic behind that, it's easy to reproduce. Also, most tricks aren't even that hard to mimic if you either follow the official samples or get a better understanding of the console. I've seen people failing to make any use of the hardware because they don't even bother going one step past abstraction (i.e. libgs). As for 3D stuff, I would suggest you read some abstract about matrices and vertex transformation. The PlayStation hardware is pretty much bare bone when it comes to 3D processing, which is a nice thing because it's not exactly too hard to figure out, even with inlined GTE commands. LibHMD sources come with some of that commented; it's not the best code around, but it's a good start.
I do some low level asm on megadrive or atari st, but doing low level on psx is too hard for me sorry. I wrote in my first post that I can't understand some of the 3D example C source from the SDK, and you ask me to learn from disassembly or libhmd which is almost 80% asm stuff ... I tried to make a 3D software rendering engine on my own but it's really difficult, after years of hardwork I have somewhat "succeed" but I still have problem with "behind the camera" Z coordinates when it comes to fixed point math, it makes my polygons goes bananas... That's why I was hoping to use the highlevel library from the ps1 SDK, highlevel TMD format is not flexible enough (no area clipping) and the lowlevel is not clear enough for me (I have the same "behind camera" Z problem ..)
LibHMD is mostly C with either inlined GTE commands or preassembled functions (less of the latter). It also comes with comments so there's all the info you can possibly need plus a whole chicken. Most of the directives handled entirely via assembly aren't even required and can be skipped entirely, but those also provide good comments. Not sure what you mean with "behind camera" Z problem. Care to explain in detail?
I mean 3D clipping, when your coordinates are behind the camera, If you don't do 3D clipping (which is not trivial), the projected x/y coordinates result will rapidly overflows and cause graphic glitches. I tried to read libhmd ... it's a mess of gte commands that I don't understand, too low level for me ..
Anything out of the camera, being it sideways or behind the eye point, would simply give a GTE flag error (bit 31) when rtpt/rtps is performed; from there you can either skip it or process it further to see if subdivision can provide a solution (libhmd comes with both active subdivision and fixed subdivision samples). This is how you usually perform perspective transformation of a Goraud triangle: Code: for(i=0, is=obj->tri_count; i<is; t++) { POLY_GT3 *si; gte_ldv3(&vp[t->v0], &vp[t->v1], &vp[t->v2]); /* load model vertices */ i++; si=sx; gte_rtpt_b(); /* perspective */ gte_stflg(&ifo.flg); /* store flag */ if (ifo.flg & GTEFLG_ERROR) { sx+=2; continue; } gte_nclip_b(); /* normal clipping */ gte_stopz(&ifo.otz); /* return orientation */ if (ifo.otz <= 0) { sx+=2; continue; } gte_stsxy3_gt3((u_long *)si); /* store transformed result */ sx+=2; gte_nop(); gte_avsz3_b(); /* calculate depth */ gte_stotz(&ifo.otz); /* store result */ // limit range if (!(ifo.otz >> 6)) continue; /* skip if it's too low or too high */ gte_ldv3(&vn[t->n0],&vn[t->n1],&vn[t->n2]); /* set lighting */ tag=&ot[ifo.otz>>4]; gte_ncct_b(); /* calculate */ gte_strgb3(&si->r0, &si->r1, &si->r2); /* store rgb values */ // sort!! si->tag=(*tag &0x00FFFFFF)|0x09000000; *tag=(u32)si & 0x00FFFFFF; } This applies no extra cases where polygons are double faced (i.e. clipped out because they are in reverse order) or when they overflow coordinate ranges. In order to do the latter, you need to apply subdivision based on either GTE flags (check no$psx documentation to know what they do in detail) or the calculated depth. You can even use outer product to calculate the distance if it's really necessary. If you have no idea what these are, I suggest you read some document about 3D transformations or follow some basic tutorial about trigonometry and model projection. Also keep in mind when reading these that the PlayStation has no projection matrix, just view and world matrices, which are compounded together to get the final transformation. On PC you would have World*View*Projection, while here it's just World*View and that can be easily dealt with pointers to super matrices applied sequentially with CompMatrix, like this: Code: gte_CompMatrix(entity->pSuper,&entity->Matrix,&entity->WorkMatrix); gte_CompMatrix(part->pSuper,&part->Matrix,&part->WorkMatrix); gte_CompMatrix(&M_view,&part->WorkMatrix,&final); gte_SetRotMatrix(&final); gte_SetTransMatrix(&final); Usually the first pSuper binded to an entity is an identity matrix, which means there's no parent bone connected to it (i.e. it's a root node). Just in case, Matrix is a simple matrix with rotation+position of the object, while WorkMatrix contains temporarily transformed position and rotation and it's used for storing transformation chains (aka the hierarchy and World at the same time). M_view is, of course, the view matrix. After this step is done, proceed with your drawing.