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.

TR2 on Psx : The Great Wall level

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 :

Double primitive with transparency

Here's a pic that hopefully shows a bit more :

Double primitive with transparency

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 !

Double primitive with transparency

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.