I was working on some texture conversion issues recently(I fixed one already) and I am having trouble locating the problem. The problem is with the N64's CI4 (4Bit Palletised) texture format the alpha when is not showing up... I've read about bugs using the GU_PSM_T4(thought it was the same) format and this is the format used by the CI4 format to display it. Could this/these bugs be affecting the alpha. If so what can be done? Should CI8 and GU_PSM_T8 have the same issue?(I don't think they do... but there is another issue there.)
BTW: It was J.F.'s thread here: http://forums.ps2dev.org/viewtopic.php? ... &start=300 that clued me there might be a problem with using GU_PSM_T4. Anyone have his 4 to 8 bit palletised conversion code? (It might solve my problem as well.)
[/url]
Problem with 4Bit Palletised Textures
The "problem" is with the pixel order. As per definition, four bit mode uses four bits for the palette index. That means there are two pixels in every byte. There are two ways that you can put two pixels into a byte: the first pixel can be bits 7 to 4 (msb to lsb), with the second being bits 3 to 0... this is the format everything I've ever worked on EXCEPT the PSP uses. What the PSP does is make the first pixel bits 3 to 0, and the second bits 7 to 4. This is the pixel data, not the palette. So when converting from a platform that uses the "normal" pixel order, you have to swap the nibbles of every byte (nibbles being four bits).
So is your problem with the palette, or the data? Having the nibbles in the wrong order means the image looks funny - a bit blurry with the edges wrong. The colors and alpha are correct as the palette isn't the problem. If you're having a problem with the colors and alpha, you may not be loading the palette before the rendering correctly. Maybe your offset and mask is wrong, or maybe you have the fields in the wrong order (remember that the N64 is big endian).
So is your problem with the palette, or the data? Having the nibbles in the wrong order means the image looks funny - a bit blurry with the edges wrong. The colors and alpha are correct as the palette isn't the problem. If you're having a problem with the colors and alpha, you may not be loading the palette before the rendering correctly. Maybe your offset and mask is wrong, or maybe you have the fields in the wrong order (remember that the N64 is big endian).
No, I know it's not a blend mode issue... it's either texture conversion or a core combiner issue. (Which could also be possible) I'm in the process of using Rice's code to help me rewrite and update DaedalusX64's. I fixed a conversion bug affecting OoT and other that had to do with the alpha and figured this was a similar issue. Those textures loaded correctly but the alpha got lost converting them to PSP format. The the Rev 359 changes below:J.F. wrote:Or maybe you don't have the blend mode set right. Try looking at one of the alpha examples... look that you have all the enables and whatnot set correctly.
Old Code:
Code: Select all
static inline void ConvertRow( OutT * p_dst, const u8 * p_src_base, u32 offset, u32 width )
{
// Do two pixels at a time
for (u32 x = 0; x < width; x+=2)
{
u8 b = p_src_base[offset ^ F];
// Even
p_dst[x + 0] = OutT( TwoToEight[(b & 0xc0) >> 6],
TwoToEight[(b & 0xc0) >> 6],
TwoToEight[(b & 0xc0) >> 6],
TwoToEight[(b & 0x30) >> 4] );
// Odd
p_dst[x + 1] = OutT( TwoToEight[(b & 0x0c) >> 2],
TwoToEight[(b & 0x0c) >> 2],
TwoToEight[(b & 0x0c) >> 2],
TwoToEight[(b & 0x03) ] );
offset++;
}
if(width & 1)
{
u8 b = p_src_base[offset ^ F];
// Even
p_dst[width-1] = OutT( TwoToEight[(b & 0xc0) >> 6],
TwoToEight[(b & 0xc0) >> 6],
TwoToEight[(b & 0xc0) >> 6],
TwoToEight[(b & 0x30) >> 4] );
}
Code: Select all
static inline void ConvertRow( OutT * p_dst, const u8 * p_src_base, u32 offset, u32 width )
{
// Do two pixels at a time
for (u32 x = 0; x < width; x+=2)
{
u8 b = p_src_base[offset ^ F];
// Even
p_dst[x + 0] = OutT( ThreeToEight[(b & 0xE0) >> 5],
ThreeToEight[(b & 0xE0) >> 5],
ThreeToEight[(b & 0xE0) >> 5],
OneToEight[(b & 0x10) >> 4]);
// Odd
p_dst[x + 1] = OutT( ThreeToEight[(b & 0x0E) >> 1],
ThreeToEight[(b & 0x0E) >> 1],
ThreeToEight[(b & 0x0E) >> 1],
OneToEight[(b & 0x01) ] );
offset++;
}
if(width & 1)
{
u8 b = p_src_base[offset ^ F];
// Even
p_dst[width-1] = OutT( ThreeToEight[(b & 0xE0) >> 5],
ThreeToEight[(b & 0xE0) >> 5],
ThreeToEight[(b & 0xE0) >> 5],
OneToEight[(b & 0x10) >> 4]);
}
}
Well, look at the four bit refresh in B2:
The 16 color palette is just the first sixteen entries of the 256 color palette. The important lines are:
Note how we set the shift, mask, and offset into the palette, then load 2 blocks of palette entries. Note the texture mode is set to T4. Other than that, everything is pretty much the same as any other texture operation.
Note how the code starts by reversing the pixels in the bytes that make the source:
That's that wacko PSP pixel order issue I was talking about. You can flip your pixels when the texture is built.
Code: Select all
static unsigned int list[16384] __attribute__((aligned(16)));
struct texVertex
{
unsigned short u, v;
short x, y, z;
};
static void refresh4(void)
{
int32 BLOCKH = 64;
int32 BLOCKV = psp_screen_y <= 512 ? psp_screen_y : psp_screen_y / 2;
int32 i, j;
struct texVertex *vertices;
// Mac 4bit chunky to PSP 4bit chunky
uint32 *sp = (uint32 *)the_buffer;
uint32 *dp = (uint32 *)((uint32)the_buffer + 768*576/2);
for (j=0; j<576; j++)
for (i=0; i<768/8; i++)
{
uint32 v = sp[j*768/8 + i];
dp[j*768/8 + i] = (v>>4)&0x0F0F0F0F | (v<<4)&0xF0F0F0F0;
}
sceGuStart(GU_DIRECT,list);
sceGuClutMode(GU_PSM_8888,0,0x0F,0); // 32-bit palette, shift = 0, mask = 0x0F, start = 0
sceGuClutLoad((16/(32/4)),clut256); // upload 2 blocks (each block is 32 bytes / bytesPerColorEntry)
sceGuEnable(GU_TEXTURE_2D);
sceGuTexMode(GU_PSM_T4,0,0,0);
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
for (j=0; j<psp_screen_y/BLOCKV; j++)
for (i=0; i<psp_screen_x/BLOCKH; i++)
{
// set the texture block
uint32 ts = (uint32)the_buffer + 768*576/2 + j * BLOCKV * 768 / 2 + i * BLOCKH / 2;
sceGuTexImage(0, BLOCKH, 512, 768, (void *)ts);
sceGuTexSync();
vertices = (struct texVertex*)sceGuGetMemory(2 * sizeof(struct texVertex));
// now draw the block with scaling
vertices[0].u = 0;
vertices[1].u = BLOCKH;
vertices[0].v = 0;
vertices[1].v = BLOCKV;
vertices[0].x = d_x + i * BLOCKH * d_w / psp_screen_x;
vertices[1].x = d_x + (i+1) * BLOCKH * d_w / psp_screen_x;
if (vertices[1].x > (d_x + d_w))
vertices[1].x = (d_x + d_w);
vertices[0].y = d_y + j * BLOCKV * d_h / psp_screen_y;
vertices[1].y = d_y + (j+1) * BLOCKV * d_h / psp_screen_y;
if (vertices[1].y > (d_y + d_h))
vertices[1].y = (d_y + d_h);
vertices[0].z = 0;
vertices[1].z = 0;
sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_VERTEX_16BIT|GU_TRANSFORM_2D, 2,0,vertices);
}
}
Code: Select all
sceGuStart(GU_DIRECT,list);
sceGuClutMode(GU_PSM_8888,0,0x0F,0); // 32-bit palette, shift = 0, mask = 0x0F, start = 0
sceGuClutLoad((16/(32/4)),clut256); // upload 2 blocks (each block is 32 bytes / bytesPerColorEntry)
sceGuEnable(GU_TEXTURE_2D);
sceGuTexMode(GU_PSM_T4,0,0,0);
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
Note how the code starts by reversing the pixels in the bytes that make the source:
Code: Select all
for (j=0; j<576; j++)
for (i=0; i<768/8; i++)
{
uint32 v = sp[j*768/8 + i];
dp[j*768/8 + i] = (v>>4)&0x0F0F0F0F | (v<<4)&0xF0F0F0F0;
}