diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c index 68c83d3fcd..c2a59846d6 100644 --- a/hw/xbox/nv2a.c +++ b/hw/xbox/nv2a.c @@ -2838,6 +2838,21 @@ static void pgraph_bind_shaders(PGRAPHState *pg) } } + /* Fog */ + state.fog_enable = pg->regs[NV_PGRAPH_CONTROL_3] + & NV_PGRAPH_CONTROL_3_FOGENABLE; + if (state.fog_enable) { + /*FIXME: Use CSV0_D? */ + state.fog_mode = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], + NV_PGRAPH_CONTROL_3_FOG_MODE); + state.foggen = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_FOGGENMODE); + } else { + /* FIXME: Do we still pass the fogmode? */ + state.fog_mode = 0; + state.foggen = 0; + } + /* Texture matrices */ for (i = 0; i < 4; i++) { state.texture_matrix_enable[i] = pg->texture_matrix_enable[i]; @@ -2962,6 +2977,39 @@ static void pgraph_bind_shaders(PGRAPHState *pg) } + /* Fog */ + { + GLint loc; + uint32_t fog_color = pg->regs[NV_PGRAPH_FOGCOLOR]; + loc = glGetUniformLocation(pg->shader_binding->gl_program, "fogColor"); + if (loc != -1) { + glUniform4f(loc, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0); + } + + /* FIXME: PGRAPH regs have a 16 byte stride in emulation! can't just + * upload this as an array =( + */ + loc = glGetUniformLocation(pg->shader_binding->gl_program, + "fogParam[0]"); + if (loc != -1) { + glUniform1f(loc, *(float*)&pg->regs[NV_PGRAPH_FOGPARAM0]); + } + loc = glGetUniformLocation(pg->shader_binding->gl_program, + "fogParam[1]"); + if (loc != -1) { + glUniform1f(loc, *(float*)&pg->regs[NV_PGRAPH_FOGPARAM1]); + } + + loc = glGetUniformLocation(pg->shader_binding->gl_program, "fogPlane"); + if (loc != -1) { + glUniform4fv(loc, 1, pg->fog_plane); + } + } + /* For each vertex weight */ for (i = 0; i < 4; i++) { char name[32]; @@ -4358,7 +4406,11 @@ static void pgraph_method(NV2AState *d, case NV097_SET_FOG_PARAMS ... NV097_SET_FOG_PARAMS + 8: slot = (class_method - NV097_SET_FOG_PARAMS) / 4; - pg->regs[NV_PGRAPH_FOGPARAM0 + slot*4] = parameter; + if (slot < 2) { + pg->regs[NV_PGRAPH_FOGPARAM0 + slot*4] = parameter; + } else { + /* FIXME: No idea where slot = 2 is */ + } break; case NV097_SET_TEXGEN_VIEW_MODEL: SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, @@ -6783,7 +6835,7 @@ static void pgraph_method_log(unsigned int subchannel, static unsigned int last = 0; static unsigned int count = 0; if (last == 0x1800 && method != last) { - NV2A_DPRINTF("pgraph method (%d) 0x%x * %d", + NV2A_GL_DPRINTF(true, "pgraph method (%d) 0x%x * %d", subchannel, last, count); } if (method != 0x1800) { diff --git a/hw/xbox/nv2a_psh.c b/hw/xbox/nv2a_psh.c index 47d2baeb6c..a34c1b6a95 100644 --- a/hw/xbox/nv2a_psh.c +++ b/hw/xbox/nv2a_psh.c @@ -280,9 +280,8 @@ static QString* get_var(struct PixelShader *ps, int reg, bool is_dest) return qstring_from_str("c_0_1"); } break; - case PS_REGISTER_FOG: // TODO - //return qstring_from_str("fog"); - return qstring_from_str("vec4(1.0)"); + case PS_REGISTER_FOG: + return qstring_from_str("clamp(pFog, 0.0, 1.0)"); case PS_REGISTER_V0: return qstring_from_str("v0"); case PS_REGISTER_V1: diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index f94a286340..e18940f8cd 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -115,6 +115,9 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append(s, "\n" /* FIXME: Add these uniforms using code when they are used */ +"uniform vec4 fogColor;\n" +"uniform vec4 fogPlane;\n" +"uniform float fogParam[2];\n" "uniform mat4 texMat0;\n" "uniform mat4 texMat1;\n" "uniform mat4 texMat2;\n" @@ -255,6 +258,85 @@ static QString* generate_fixed_function(const ShaderState state, } } + /* Fog */ + if (state.fog_enable) { + + /* From: https://www.opengl.org/registry/specs/NV/fog_distance.txt */ + switch(state.foggen) { + case FOGGEN_SPEC_ALPHA: + assert(false); /* FIXME: Do this before or after calculations in VSH? */ + if (state.fixed_function) { + /* FIXME: Do we have to clamp here? */ + qstring_append(s, "float fogDistance = clamp(specular.a, 0.0, 1.0);\n"); + } else if (state.vertex_program) { + qstring_append(s, "float fogDistance = oD1.a;\n"); + } else { + assert(false); + } + break; + case FOGGEN_RADIAL: + qstring_append(s, "float fogDistance = length(tPosition.xyz)"); + break; + case FOGGEN_PLANAR: + case FOGGEN_ABS_PLANAR: + qstring_append(s, "float fogDistance = dot(fogPlane.xyz,tPosition.xyz)+fogPlane.w;\n"); + if (state.foggen == FOGGEN_ABS_PLANAR) { + qstring_append(s, "fogDistance = abs(fogDistance);\n"); + } + break; + case FOGGEN_FOG_X: + if (state.fixed_function) { + qstring_append(s, "float fogDistance = fogCoord;\n"); + } else if (state.vertex_program) { + qstring_append(s, "float fogDistance = oFog.x;\n"); + } else { + assert(false); + } + break; + default: + assert(false); + break; + } + + switch (state.fog_mode) { + case FOG_MODE_LINEAR: + case FOG_MODE_LINEAR_ABS: + qstring_append(s, "float fogFactor = fogDistance * fogParam[1] + fogParam[0];\n"); + qstring_append(s, "fogFactor -= 1.0;\n"); /* FIXME: WHHYYY?!! */ + break; + case FOG_MODE_EXP: + case FOG_MODE_EXP_ABS: + assert(false); /* FIXME: fogParam[0] and fogParam[0] ?? */ + qstring_append(s, "float fogFactor = exp(fogDistance);\n"); + break; + case FOG_MODE_EXP2: + case FOG_MODE_EXP2_ABS: + assert(false); /* FIXME: fogParam[0] and fogParam[0] ?? */ + qstring_append(s, "float fogFactor = exp(fogDistance * fogDistance);\n"); + break; + default: + assert(false); + break; + } + /* Calculate absolute for the modes which need it */ + switch (state.fog_mode) { + case FOG_MODE_LINEAR_ABS: + case FOG_MODE_EXP_ABS: + case FOG_MODE_EXP2_ABS: + qstring_append(s, "fogFactor = abs(fogFactor);\n"); + break; + default: + break; + } + /* FIXME: What about fog alpha?! */ + qstring_append(s, "vec4 tFog = vec4(fogColor.rgb, fogFactor);\n"); + } else { + /* FIXME: Is the fog still calculated / passed somehow?! + * Maybe vec4(fogColor, fogCoord) ? + */ + qstring_append(s, "vec4 tFog = vec4(0.0);\n"); + } + /* If skinning is off the composite matrix already includes the MV matrix */ if (state.skinning == SKINNING_OFF) { qstring_append(s, "tPosition = position;\n"); @@ -273,7 +355,7 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append(s, "vtx.D1 = specular * vtx.inv_w;\n"); qstring_append(s, "vtx.B0 = backDiffuse * vtx.inv_w;\n"); qstring_append(s, "vtx.B1 = backSpecular * vtx.inv_w;\n"); - qstring_append(s, "vtx.Fog = vec4(0.0,0.0,0.0,1.0) * vtx.inv_w;\n"); + qstring_append(s, "vtx.Fog = tFog * vtx.inv_w;\n"); qstring_append(s, "vtx.T0 = tTexture0 * vtx.inv_w;\n"); qstring_append(s, "vtx.T1 = tTexture1 * vtx.inv_w;\n"); qstring_append(s, "vtx.T2 = tTexture2 * vtx.inv_w;\n"); diff --git a/hw/xbox/nv2a_shaders.h b/hw/xbox/nv2a_shaders.h index 56f82b8707..3271cb5415 100644 --- a/hw/xbox/nv2a_shaders.h +++ b/hw/xbox/nv2a_shaders.h @@ -65,6 +65,10 @@ typedef struct ShaderState { bool texture_matrix_enable[4]; enum VshTexgen texgen[4][4]; + bool fog_enable; + enum VshFoggen foggen; + enum VshFogMode fog_mode; + enum VshSkinning skinning; bool normalization; diff --git a/hw/xbox/nv2a_vsh.h b/hw/xbox/nv2a_vsh.h index bb23b1dad9..8cb49e9ccf 100644 --- a/hw/xbox/nv2a_vsh.h +++ b/hw/xbox/nv2a_vsh.h @@ -33,6 +33,27 @@ enum VshTexgen { TEXGEN_REFLECTION_MAP, }; +enum VshFogMode { + FOG_MODE_LINEAR, + FOG_MODE_EXP, + FOG_MODE_ERROR2, /* Doesn't exist */ + FOG_MODE_EXP2, + FOG_MODE_LINEAR_ABS, + FOG_MODE_EXP_ABS, + FOG_MODE_ERROR6, /* Doesn't exist */ + FOG_MODE_EXP2_ABS +}; + +enum VshFoggen { + FOGGEN_SPEC_ALPHA, + FOGGEN_RADIAL, + FOGGEN_PLANAR, + FOGGEN_ABS_PLANAR, + FOGGEN_ERROR4, + FOGGEN_ERROR5, + FOGGEN_FOG_X +}; + enum VshSkinning { SKINNING_OFF, SKINNING_1WEIGHTS,