RE: Pseudo-reflections in TRII on Psx - the code part II
Following up on my follow-up to an article about a pseudo-reflection effect in the Tomb Raider I & II games on psx, picking up where we left of ;
In this pic, you can see that Lara's reflection looks cloudy. This could be because a second primitive with semi-transparency is drawn above the one doing the reflection.
Now this might sound a bit expensive to draw two primitives instead of one, but it's not really. In fact, you can do the calculations once, and apply the resulting coordinates to 2 different primitives.
// cast nextpri as POLY_GT3
poly[0] = (POLY_GT3 *)nextpri;
poly[1] = (POLY_GT3 *)nextpri+sizeof(POLY_GT3);
// Initialize the primitives
SetPolyGT3(poly[0]);
SetPolyGT3(poly[1]);
// Reflection Cube
// This cube has its UVs mapped directly to VRAM coordinates
// We're using the framebuffers as a texture (0,0 and 0,256)
// Get 256x256 texture page that's at x0, y0
poly[1]->tpage = getTPage( 2, stpRate,
0,
!(db) * 256 // Here, we're using db's value that can be either 0 or 1 to determine the texture page Y coordinate.
);
// Set STP
SetSemiTrans(poly[1], stpFlag);
// Map coordinates from drawarea (320x240) to texture size (256x240) in fixed point math
// x = x * (256 / 320) => ( x * ( 256 * 4096 ) / 320 ) / 4096 => x * 3276 >> 12
// y = y * (240 / 240) => ( y * ( 240 * 4096 ) / 240 ) / 4096 => y * 4096 >> 12 == y
setUV3( poly[1],
(poly[1]->x0 * normH) >> 12,
poly[1]->y0 - (!(db) * 16) , // We're using db's value again to add a 16 pixels offset to the Y's coordinates of the UVs
(poly[1]->x1 * normH) >> 12,
poly[1]->y1 - (!(db) * 16), // We have to do that because the buffer is 240 high, whereas our texture page is 256, hence 256 - 240 == 16
(poly[1]->x2 * normH) >> 12,
poly[1]->y2 - (!(db) * 16)
);
// Draw "container" cube
// This cube has a texture with transparent areas.
// STP bit is set on PNG's alpha channel : img2tim -usealpha -org 320 0 -o cube.tim cube.png
poly[0]->tpage = getTPage( timImages[0].mode&0x3, stpRate,
timImages[0].prect->x,
timImages[0].prect->y
);
// If 8/4bpp, load CLUT to vram
if ( (timImages[0].mode & 0x3) < 2 ) {
setClut( poly[0],
timImages[0].crect->x,
timImages[0].crect->y
);
}
// Set UV coordinates
setUV3(poly[0], modelCube.u[i].vx, modelCube.u[i].vy,
modelCube.u[i+2].vx, modelCube.u[i+2].vy,
modelCube.u[i+1].vx, modelCube.u[i+1].vy
);
// Rotate, translate, and project the vectors and output the results into a primitive
// curTriangle, +1, +2 point to the vertices index of the triangle we're drawing.
OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[1]->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[1]->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[1]->x2, &p, &Flag);
// Instead of doing the same calculations for the "prism" cube, use results from above directly
poly[0]->x0 = poly[1]->x0;
poly[0]->y0 = poly[1]->y0;
poly[0]->x1 = poly[1]->x1;
poly[0]->y1 = poly[1]->y1;
poly[0]->x2 = poly[1]->x2;
poly[0]->y2 = poly[1]->y2;
And there you have it :
Here's a pic that hopefully shows a bit more :
You can see the two cubes with the right one being the "reflection"...
You can easily get various graphical result by tweaking the colors/depth/size of the primitives !
Full code and more infos
The full code is available at https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_fx
You can find more details about semi-transparency and primitive transparency on the PSX here :
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STP
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/TIM#transparency
Links and notes
Original articles :
https://www.schnappy.xyz/?tr2_not_shaders
https://www.schnappy.xyz/?tr2_not_shaders-follow_up
Thanks to psxdev users gwald and sicklebrick, and to Paul Douglas for their time and answers.