softgpu: Implement triangle texture projection.

This commit is contained in:
Unknown W. Brackets 2022-09-26 18:12:20 -07:00
parent 6282f8b05f
commit faa6c2d461
4 changed files with 46 additions and 14 deletions

View File

@ -129,6 +129,7 @@ void ComputeRasterizerState(RasterizerState *state) {
state->mipFilt = gstate.isMipmapFilteringEnabled();
state->minFilt = gstate.isMinifyFilteringEnabled();
state->magFilt = gstate.isMagnifyFilteringEnabled();
state->textureProj = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
}
state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD;
@ -224,8 +225,6 @@ static inline u8 ClampFogDepth(float fogdepth) {
}
static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) {
// All UV gen modes, by the time they get here, behave the same.
// Note that for environment mapping, texture coordinates have been calculated during lighting
float q0 = 1.f / v0.clipw;
float q1 = 1.f / v1.clipw;
@ -233,14 +232,26 @@ static inline void GetTextureCoordinates(const VertexData& v0, const VertexData&
float wq1 = (1.0f - p) * q1;
float q_recip = 1.0f / (wq0 + wq1);
// TODO: Handle projection.
s = (v0.texturecoords.s() * wq0 + v1.texturecoords.s() * wq1) * q_recip;
t = (v0.texturecoords.t() * wq0 + v1.texturecoords.t() * wq1) * q_recip;
}
static inline void GetTextureCoordinatesProj(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) {
// This is for texture matrix projection.
float q0 = 1.f / v0.clipw;
float q1 = 1.f / v1.clipw;
float wq0 = p * q0;
float wq1 = (1.0f - p) * q1;
float q_recip = 1.0f / (wq0 + wq1);
float q = (v0.texturecoords.q() * wq0 + v1.texturecoords.q() * wq1) * q_recip;
q_recip *= 1.0f / q;
s = (v0.texturecoords.s() * wq0 + v1.texturecoords.s() * wq1) * q_recip;
t = (v0.texturecoords.t() * wq0 + v1.texturecoords.t() * wq1) * q_recip;
}
static inline void GetTextureCoordinates(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4<int> &w0, const Vec4<int> &w1, const Vec4<int> &w2, const Vec4<float> &wsum_recip, Vec4<float> &s, Vec4<float> &t) {
// All UV gen modes, by the time they get here, behave the same.
// Note that for environment mapping, texture coordinates have been calculated during lighting.
float q0 = 1.f / v0.clipw;
float q1 = 1.f / v1.clipw;
@ -250,7 +261,23 @@ static inline void GetTextureCoordinates(const VertexData &v0, const VertexData
Vec4<float> wq2 = w2.Cast<float>() * q2;
Vec4<float> q_recip = (wq0 + wq1 + wq2).Reciprocal();
// TODO: Handle projection.
s = Interpolate(v0.texturecoords.s(), v1.texturecoords.s(), v2.texturecoords.s(), wq0, wq1, wq2, q_recip);
t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), wq0, wq1, wq2, q_recip);
}
static inline void GetTextureCoordinatesProj(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4<int> &w0, const Vec4<int> &w1, const Vec4<int> &w2, const Vec4<float> &wsum_recip, Vec4<float> &s, Vec4<float> &t) {
// This is for texture matrix projection.
float q0 = 1.f / v0.clipw;
float q1 = 1.f / v1.clipw;
float q2 = 1.f / v2.clipw;
Vec4<float> wq0 = w0.Cast<float>() * q0;
Vec4<float> wq1 = w1.Cast<float>() * q1;
Vec4<float> wq2 = w2.Cast<float>() * q2;
Vec4<float> q_recip = (wq0 + wq1 + wq2).Reciprocal();
Vec4<float> q = Interpolate(v0.texturecoords.q(), v1.texturecoords.q(), v2.texturecoords.q(), wq0, wq1, wq2, q_recip);
q_recip = q_recip * q.Reciprocal();
s = Interpolate(v0.texturecoords.s(), v1.texturecoords.s(), v2.texturecoords.s(), wq0, wq1, wq2, q_recip);
t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), wq0, wq1, wq2, q_recip);
}
@ -676,6 +703,9 @@ void DrawTriangleSlice(
// For levels > 0, mipmapping is always based on level 0. Simpler to scale first.
s *= 1.0f / (float)(1 << state.samplerID.width0Shift);
t *= 1.0f / (float)(1 << state.samplerID.height0Shift);
} else if (state.textureProj) {
// Texture coordinate interpolation must definitely be perspective-correct.
GetTextureCoordinatesProj(v0, v1, v2, w0, w1, w2, wsum_recip, s, t);
} else {
// Texture coordinate interpolation must definitely be perspective-correct.
GetTextureCoordinates(v0, v1, v2, w0, w1, w2, wsum_recip, s, t);
@ -961,6 +991,8 @@ void DrawPoint(const VertexData &v0, const BinCoords &range, const RasterizerSta
if (state.throughMode) {
s *= 1.0f / (float)(1 << state.samplerID.width0Shift);
t *= 1.0f / (float)(1 << state.samplerID.height0Shift);
} else if (state.textureProj) {
GetTextureCoordinatesProj(v0, v0, 0.0f, s, t);
} else {
// Texture coordinate interpolation must definitely be perspective-correct.
GetTextureCoordinates(v0, v0, 0.0f, s, t);
@ -1278,6 +1310,9 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const BinCoords &range
s1 = tc1.s() * (1.0f / (float)(1 << state.samplerID.width0Shift));
t = tc.t() * (1.0f / (float)(1 << state.samplerID.height0Shift));
t1 = tc1.t() * (1.0f / (float)(1 << state.samplerID.height0Shift));
} else if (state.textureProj) {
GetTextureCoordinatesProj(v0, v1, (float)(steps - i) / steps1, s, t);
GetTextureCoordinatesProj(v0, v1, (float)(steps - i - 1) / steps1, s1, t1);
} else {
// Texture coordinate interpolation must definitely be perspective-correct.
GetTextureCoordinates(v0, v1, (float)(steps - i) / steps1, s, t);

View File

@ -54,6 +54,7 @@ struct RasterizerState {
bool minFilt : 1;
bool magFilt : 1;
bool antialiasLines : 1;
bool textureProj : 1;
};
#if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC)

View File

@ -160,7 +160,7 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = {
{ GE_CMD_LOGICOP, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
{ GE_CMD_LOGICOPENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED },
{ GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC },
{ GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC | SoftDirty::RAST_TEX },
// These are read on every SubmitPrim, no need for dirtying or flushing.
{ GE_CMD_TEXSCALEU },

View File

@ -261,6 +261,8 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) {
state->negateNormals = gstate.areNormalsReversed();
state->uvGenMode = gstate.getUVGenMode();
if (state->uvGenMode == GE_TEXMAP_UNKNOWN)
state->uvGenMode = GE_TEXMAP_TEXTURE_COORDS;
if (state->enableTransform) {
bool canSkipWorldPos = true;
@ -441,17 +443,11 @@ ClipVertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformS
case GE_PROJMAP_NORMAL:
source = normal;
break;
default:
source = Vec3f::AssignToAll(0.0f);
ERROR_LOG_REPORT(G3D, "Software: Unsupported UV projection mode %x", gstate.getUVProjMode());
break;
}
// Note that UV scale/offset are not used in this mode.
Vec3<float> stq = Vec3ByMatrix43(source, gstate.tgenMatrix);
float z_recip = 1.0f / stq.z;
vertex.v.texturecoords = Vec3Packedf(stq.x * z_recip, stq.y * z_recip, 1.0f);
vertex.v.texturecoords = Vec3Packedf(stq.x, stq.y, stq.z);
} else if (state.uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP) {
Lighting::GenerateLightST(vertex.v, worldnormal);
}