Alpha textures

This commit is contained in:
2026-02-05 23:28:26 -06:00
parent 1af5f238e4
commit 0d56859d94
5 changed files with 286 additions and 109 deletions

View File

@@ -42,6 +42,7 @@ find_package(Python3 COMPONENTS Interpreter REQUIRED)
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "linux")
# set(DUSK_TARGET_SYSTEM "psp")
endif()
# Toolchains

View File

@@ -193,22 +193,33 @@ errorret_t displayUpdate(void) {
);
errorChain(sceneRender());
color_t colors[4 * 4] = {
COLOR_RED_4B, COLOR_GREEN_4B, COLOR_BLUE_4B, COLOR_YELLOW_4B,
COLOR_CYAN_4B, COLOR_MAGENTA_4B, COLOR_WHITE_4B, COLOR_BLACK_4B,
COLOR_ORANGE_4B, COLOR_PURPLE_4B, COLOR_GRAY_4B, COLOR_BROWN_4B,
COLOR_PINK_4B, COLOR_LIME_4B, COLOR_NAVY_4B, COLOR_TEAL_4B
texture_t texture;
// color_t colors[4 * 4] = {
// COLOR_RED_4B, COLOR_GREEN_4B, COLOR_BLUE_4B, COLOR_YELLOW_4B,
// COLOR_CYAN_4B, COLOR_MAGENTA_4B, COLOR_WHITE_4B, COLOR_BLACK_4B,
// COLOR_ORANGE_4B, COLOR_PURPLE_4B, COLOR_GRAY_4B, COLOR_BROWN_4B,
// COLOR_PINK_4B, COLOR_LIME_4B, COLOR_NAVY_4B, COLOR_TEAL_4B
// };
// texturedata_t data = {
// .rgba = { .colors = colors }
// };
// textureInit(&texture, 4, 4, TEXTURE_FORMAT_RGBA, data);
uint8_t alphaData[8 * 4] = {
255, 255, 255, 255, 255, 255, 255, 255,
0, 0, 255, 0, 0, 255, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255,
0, 0, 0, 255, 255, 0, 0, 0,
};
texturedata_t data = {
.rgba = { .colors = colors }
.alpha = { .data = alphaData }
};
textureInit(&texture, 8, 4, TEXTURE_FORMAT_ALPHA, data);
camera_t camera;
cameraInit(&camera);
texture_t texture;
textureInit(&texture, 4, 4, TEXTURE_FORMAT_RGBA, data);
cameraPushMatrix(&camera);
spriteBatchClear();
spriteBatchPush(
@@ -221,7 +232,6 @@ errorret_t displayUpdate(void) {
);
spriteBatchFlush();
cameraPopMatrix();
textureDispose(&texture);
// Render UI
// uiRender();
@@ -245,6 +255,7 @@ errorret_t displayUpdate(void) {
// if(DISPLAY.screenMode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
#endif
textureDispose(&texture);
// For now, we just return an OK error.
errorOk();

View File

@@ -9,6 +9,10 @@
#include "util/memory.h"
#include "assert/assert.h"
#if DOLPHIN
#include "display/texture.h"
#endif
void meshInit(
mesh_t *mesh,
const meshprimitivetype_t primitiveType,
@@ -90,40 +94,8 @@ void meshDraw(
GX_SetArray(GX_VA_POS, &mesh->vertices[offset].pos[0], stride);
GX_SetArray(GX_VA_CLR0, &mesh->vertices[offset].color, stride);
GX_SetArray(GX_VA_TEX0, &mesh->vertices[offset].uv[0], stride);
// GX_SetNumChans(1);// How many color channels are used
// GX_SetNumTexGens(0);// How many texture coordinate generators are used
// GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0);
// GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
// Basically the shader setup
GX_SetNumChans(1);
GX_SetChanCtrl(
GX_COLOR0A0,// Store in color channel 0
GX_DISABLE,// Lighting disabled
GX_SRC_REG,// Ambient color?
GX_SRC_VTX,// Material color?
GX_LIGHTNULL,// Light Mask
GX_DF_NONE,// Diffuse function
GX_AF_NONE// Attenuation function
);
GX_SetNumTexGens(1);
GX_SetTexCoordGen(
GX_TEXCOORD0,
GX_TG_MTX2x4,
GX_TG_TEX0,
GX_IDENTITY
);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
textureDolphinUploadTEV();
GX_Begin(mesh->primitiveType, GX_VTXFMT0, (uint16_t)count);
for(u16 i = 0; i < (u16)count; ++i) {

View File

@@ -147,69 +147,139 @@ void textureInit(
texture->ready = true;
#elif DOLPHIN
assertTrue((width % 4) == 0 && (height % 4) == 0, "RGB5A3 requires w/h multiple of 4 (or pad)");
// Convert to RGB5A3 format
size_t rgbaSize = width * height * sizeof(u16);
texture->rgba = (u16*)memalign(32, rgbaSize);
texture->format = format;
for(uint32_t y = 0; y < height; ++y) {
for(uint32_t x = 0; x < width; ++x) {
const int src = y * width + x;
switch(format) {
case TEXTURE_FORMAT_RGBA:
assertTrue(
(width % 4) == 0 && (height % 4) == 0,
"RGB5A3 requires w/h multiple of 4 (or pad)"
);
const int tileX = x >> 2;
const int tileY = y >> 2;
const int tilesPerRow = width >> 2;
const int tileIndex = tileY * tilesPerRow + tileX;
const int tileBaseWords = tileIndex * 16;
const int inTile = ((y & 3) << 2) + (x & 3);
const int dest = tileBaseWords + inTile;
color4b_t col = data.rgba.colors[src];
// Convert to RGB5A3 format
size_t rgbaSize = width * height * sizeof(u16);
texture->rgba = (u16*)memalign(32, rgbaSize);
assertNotNull(texture->rgba, "Failed to allocate texture RGBA data");
u16 outCol;
if(col.a < 255) {
// 0AAA RRRR GGGG BBBB
outCol = (
(0u << 15) |
((u16)(col.a >> 5) << 12) |
((u16)(col.r >> 4) << 8) |
((u16)(col.g >> 4) << 4) |
((u16)(col.b >> 4) << 0)
);
} else {
// 1RRRR RRGG GGGB BBBB
outCol = (
(1u << 15) |
((u16)(col.r >> 3) << 10) |
((u16)(col.g >> 3) << 5) |
((u16)(col.b >> 3) << 0)
);
for(uint32_t y = 0; y < height; ++y) {
for(uint32_t x = 0; x < width; ++x) {
const int src = y * width + x;
const int tileX = x >> 2;
const int tileY = y >> 2;
const int tilesPerRow = width >> 2;
const int tileIndex = tileY * tilesPerRow + tileX;
const int tileBaseWords = tileIndex * 16;
const int inTile = ((y & 3) << 2) + (x & 3);
const int dest = tileBaseWords + inTile;
color4b_t col = data.rgba.colors[src];
u16 outCol;
if(col.a < 255) {
// 0AAA RRRR GGGG BBBB
outCol = (
(0u << 15) |
((u16)(col.a >> 5) << 12) |
((u16)(col.r >> 4) << 8) |
((u16)(col.g >> 4) << 4) |
((u16)(col.b >> 4) << 0)
);
} else {
// 1RRRR RRGG GGGB BBBB
outCol = (
(1u << 15) |
((u16)(col.r >> 3) << 10) |
((u16)(col.g >> 3) << 5) |
((u16)(col.b >> 3) << 0)
);
}
texture->rgba[dest] = outCol;
}
}
texture->rgba[dest] = outCol;
}
DCFlushRange(texture->rgba, rgbaSize);
GX_InitTexObj(
&texture->texObj,
texture->rgba,
width, height,
GX_TF_RGB5A3,
GX_CLAMP, GX_CLAMP,
GX_FALSE
);
DCFlushRange(texture->rgba, rgbaSize);
GX_InvalidateTexAll();
GX_InitTexObjLOD(
&texture->texObj,
GX_NEAR, GX_NEAR,
0.0f, 0.0f, 0.0f,
GX_FALSE,
GX_FALSE,
GX_ANISO_1
);
break;
case TEXTURE_FORMAT_ALPHA:
assertTrue(
(width % 4) == 0 && (height % 4) == 0,
"GX_TF_I8 requires w/h multiple of 4 (or pad)"
);
// 1 byte per pixel (I8), GX expects 4x4 tiled layout
const size_t alphaSize = (size_t)width * (size_t)height;
texture->alpha = (u8*)memalign(32, alphaSize);
assertNotNull(texture->alpha, "Failed to allocate alpha texture data");
const u32 tilesPerRow = ((u32)width) >> 3; // /8
for (u32 y = 0; y < (u32)height; ++y) {
const u32 tileY = y >> 2; // /4
const u32 inTileY = (y & 3) << 3; // (y%4)*8
for (u32 x = 0; x < (u32)width; ++x) {
const u32 srcI = y * (u32)width + x;
const u8 srcA = data.alpha.data[srcI]; // linear input
const u32 tileX = x >> 3; // /8
const u32 tileIndex = tileY * tilesPerRow + tileX;
const u32 tileBase = tileIndex * 32; // 8*4*1 = 32 bytes per tile
const u32 inTile = inTileY + (x & 7); // (y%4)*8 + (x%8)
texture->alpha[tileBase + inTile] = 0xFF - srcA;// Fixes inverted alpha.
}
}
// Flush CPU cache so GX sees the swizzled I8 texture data
DCFlushRange(texture->alpha, alphaSize);
// Initialize GX texture object with swizzled data
GX_InitTexObj(
&texture->texObj,
texture->alpha,
width, height,
GX_TF_I8,
GX_CLAMP, GX_CLAMP,
GX_FALSE
);
GX_InitTexObjLOD(
&texture->texObj,
GX_NEAR, GX_NEAR,
0.0f, 0.0f, 0.0f,
GX_FALSE,
GX_FALSE,
GX_ANISO_1
);
break;
default:
assertUnreachable("Unsupported texture format for Dolphin");
break;
}
DCFlushRange(texture->rgba, rgbaSize);
GX_InitTexObj(
&texture->texObj,
texture->rgba,
width, height,
GX_TF_RGB5A3,
GX_CLAMP, GX_CLAMP,
GX_FALSE
);
DCFlushRange(texture->rgba, rgbaSize);
GX_InvalidateTexAll();
GX_InitTexObjLOD(
&texture->texObj,
GX_NEAR, GX_NEAR,
0.0f, 0.0f, 0.0f,
GX_FALSE,
GX_FALSE,
GX_ANISO_1
);
texture->ready = true;
#endif
@@ -238,7 +308,6 @@ void textureBind(texture_t *texture) {
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
#elif DOLPHIN
GX_LoadTexObj(&texture->texObj, GX_TEXMAP0);
#endif
TEXTURE_BOUND = texture;
}
@@ -254,10 +323,126 @@ void textureDispose(texture_t *texture) {
#if DISPLAY_SDL2
glDeleteTextures(1, &texture->id);
#elif DOLPHIN
if(texture->rgba) {
free(texture->rgba);
texture->rgba = NULL;
switch(texture->format) {
case TEXTURE_FORMAT_RGBA:
free(texture->rgba);
break;
case TEXTURE_FORMAT_ALPHA:
free(texture->alpha);
break;
default:
break;
}
memoryZero(texture, sizeof(texture_t));
#endif
}
}
#if DOLPHIN
void textureDolphinUploadTEV() {
// Add channel for vertex color
GX_SetNumChans(1);
GX_SetChanCtrl(
GX_COLOR0A0,// Store in color channel 0
GX_DISABLE,// Lighting disabled
GX_SRC_REG,// Ambient color?
GX_SRC_VTX,// Material color?
GX_LIGHTNULL,// Light Mask
GX_DF_NONE,// Diffuse function
GX_AF_NONE// Attenuation function
);
if(!TEXTURE_BOUND) {
GX_SetNumTexGens(0);
GX_SetNumTevStages(1);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
return;
}
// One set of UVs
GX_SetNumTexGens(1);
GX_SetTexCoordGen(
GX_TEXCOORD0,
GX_TG_MTX2x4,
GX_TG_TEX0,
GX_IDENTITY
);
// Basically the shader setup
switch(TEXTURE_BOUND->format) {
case TEXTURE_FORMAT_RGBA:
// One TEV stage: vertex color * texture color
GX_SetNumTevStages(1);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
break;
case TEXTURE_FORMAT_ALPHA:
// One TEV stage: vertex color * texture color
GX_SetNumTevStages(1);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
// Color = vertex color
GX_SetTevColorIn(
GX_TEVSTAGE0,
GX_CC_RASC,
GX_CC_ZERO,
GX_CC_ZERO,
GX_CC_ZERO
);
GX_SetTevColorOp(
GX_TEVSTAGE0,
GX_TEV_ADD,
GX_TB_ZERO,
GX_CS_SCALE_1,
GX_TRUE,
GX_TEVPREV
);
// Alpha = vertex alpha * I8 intensity
GX_SetTevAlphaIn(
GX_TEVSTAGE0,
GX_CA_RASA,
GX_CA_ZERO,
GX_CA_TEXA,
GX_CA_ZERO
);
GX_SetTevAlphaOp(
GX_TEVSTAGE0,
GX_TEV_ADD,
GX_TB_ZERO,
GX_CS_SCALE_1,
GX_TRUE,
GX_TEVPREV
);
GX_SetBlendMode(
GX_BM_BLEND,
GX_BL_SRCALPHA,
GX_BL_INVSRCALPHA,
GX_LO_CLEAR
);
GX_SetColorUpdate(GX_TRUE);
GX_SetAlphaUpdate(GX_TRUE);
break;
default:
assertUnreachable("Unknown texture format in meshDraw");
break;
}
}
#endif

View File

@@ -26,9 +26,10 @@ typedef struct {
GLuint id;
#elif DOLPHIN
GXTexObj texObj;
textureformat_t format;
union {
u16 *rgba;
u8 *alpha;
};
#endif
@@ -83,4 +84,11 @@ void textureBind(texture_t *texture);
*
* @param texture The texture to dispose.
*/
void textureDispose(texture_t *texture);
void textureDispose(texture_t *texture);
#if DOLPHIN
/**
* Uploads the TEV settings for the currently bound texture.
*/
void textureDolphinUploadTEV();
#endif