wined3d: Implement linear fog with pixel shader.
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
index aa9d0ab..bba49ee 100644
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -689,6 +689,8 @@
static void state_fog(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) {
/* TODO: Put this into the vertex type block once that is in the state table */
BOOL fogenable = stateblock->renderState[WINED3DRS_FOGENABLE];
+ BOOL is_ps3 = use_ps(stateblock->wineD3DDevice)
+ && ((IWineD3DPixelShaderImpl *)stateblock->pixelShader)->baseShader.hex_version >= WINED3DPS_VERSION(3,0);
float fogstart, fogend;
union {
@@ -700,6 +702,17 @@
/* No fog? Disable it, and we're done :-) */
glDisable(GL_FOG);
checkGLcall("glDisable GL_FOG");
+ if( use_ps(stateblock->wineD3DDevice)
+ && ((IWineD3DPixelShaderImpl *)stateblock->pixelShader)->baseShader.hex_version < WINED3DPS_VERSION(3,0) ) {
+ /* disable fog in the pixel shader
+ * NOTE: For pixel shader, GL_FOG_START and GL_FOG_END don't hold fog start s and end e but
+ * -1/(e-s) and e/(e-s) respectively.
+ */
+ glFogf(GL_FOG_START, 0.0f);
+ checkGLcall("glFogf(GL_FOG_START, fogstart");
+ glFogf(GL_FOG_END, 1.0f);
+ checkGLcall("glFogf(GL_FOG_END, fogend");
+ }
return;
}
@@ -726,18 +739,80 @@
* FOGTABLEMODE == NONE, FOGVERTEXMODE == NONE, untransformed:
* Linear fog with start = 255.0, end = 0.0, input comes from the specular color
*
- * Vertex shaders work in a simmilar way, but need more testing
+ *
+ * Rules for vertex fog with shaders:
+ *
+ * When mixing fixed function functionality with the programmable pipeline, D3D expects
+ * the fog computation to happen during transformation while openGL expects it to happen
+ * during rasterization. Also, prior to pixel shader 3.0 D3D handles fog blending after
+ * the pixel shader while openGL always expects the pixel shader to handle the blending.
+ * To solve this problem, WineD3D does:
+ * 1) implement a linear fog equation and fog blending at the end of every pre 3.0 pixel
+ * shader,
+ * and 2) disables the fog computation (in either the fixed function or programmable
+ * rasterizer) if using a vertex program.
+ *
+ *
+ * If a fogtablemode and a fogvertexmode are specified, table fog is applied (with or
+ * without shaders).
*/
+
+ if( is_ps3 ) {
+ if( !use_vs(stateblock->wineD3DDevice)
+ && stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE ) {
+ FIXME("Implement vertex fog for pixel shader >= 3.0 and fixed function pipeline\n");
+ }
+ }
+
if (use_vs(stateblock->wineD3DDevice)
&& ((IWineD3DVertexShaderImpl *)stateblock->vertexShader)->usesFog) {
- glFogi(GL_FOG_MODE, GL_LINEAR);
- checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)");
- if (stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE) {
+ if( stateblock->renderState[WINED3DRS_FOGTABLEMODE] != WINED3DFOG_NONE ) {
+ if(!is_ps3) FIXME("Implement table fog for foggy vertex shader\n");
+ /* Disable fog */
+ fogenable = FALSE;
+ } else {
+ /* Set fog computation in the rasterizer to pass through the value (just blend it) */
+ glFogi(GL_FOG_MODE, GL_LINEAR);
+ checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)");
fogstart = 1.0;
fogend = 0.0;
}
context->last_was_foggy_shader = TRUE;
}
+ else if( use_ps(stateblock->wineD3DDevice) ) {
+ /* NOTE: For pixel shader, GL_FOG_START and GL_FOG_END don't hold fog start s and end e but
+ * -1/(e-s) and e/(e-s) respectively to simplify fog computation in the shader.
+ */
+ WINED3DFOGMODE mode;
+ context->last_was_foggy_shader = FALSE;
+
+ /* If both fogmodes are set use the table fog mode */
+ if(stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE)
+ mode = stateblock->renderState[WINED3DRS_FOGVERTEXMODE];
+ else
+ mode = stateblock->renderState[WINED3DRS_FOGTABLEMODE];
+
+ switch (mode) {
+ case WINED3DFOG_EXP:
+ case WINED3DFOG_EXP2:
+ if(!is_ps3) FIXME("Implement non linear fog for pixel shader < 3.0\n");
+ /* Disable fog */
+ fogenable = FALSE;
+ break;
+
+ case WINED3DFOG_LINEAR:
+ fogstart = -1.0f/(fogend-fogstart);
+ fogend *= -fogstart;
+ break;
+
+ case WINED3DFOG_NONE:
+ if(!is_ps3) FIXME("Implement software vertex fog for pixel shader < 3.0\n");
+ /* Disable fog */
+ fogenable = FALSE;
+ break;
+ default: FIXME("Unexpected WINED3DRS_FOGVERTEXMODE %d\n", stateblock->renderState[WINED3DRS_FOGVERTEXMODE]);
+ }
+ }
/* DX 7 sdk: "If both render states(vertex and table fog) are set to valid modes,
* the system will apply only pixel(=table) fog effects."
*/
@@ -854,6 +929,16 @@
} else {
glDisable(GL_FOG);
checkGLcall("glDisable GL_FOG");
+ if( use_ps(stateblock->wineD3DDevice) ) {
+ /* disable fog in the pixel shader
+ * NOTE: For pixel shader, GL_FOG_START and GL_FOG_END don't hold fog start s and end e but
+ * -1/(e-s) and e/(e-s) respectively.
+ */
+ glFogf(GL_FOG_START, 0.0f);
+ checkGLcall("glFogf(GL_FOG_START, fogstart");
+ glFogf(GL_FOG_END, 1.0f);
+ checkGLcall("glFogf(GL_FOG_END, fogend");
+ }
}
if (GL_SUPPORT(NV_FOG_DISTANCE)) {
@@ -864,7 +949,6 @@
static void state_fogcolor(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) {
float col[4];
D3DCOLORTOGLFLOAT4(stateblock->renderState[WINED3DRS_FOGCOLOR], col);
- /* Set the default alpha blend color */
glFogfv(GL_FOG_COLOR, &col[0]);
checkGLcall("glFog GL_FOG_COLOR");
}
@@ -2001,6 +2085,7 @@
IWineD3DDeviceImpl *device = stateblock->wineD3DDevice;
BOOL use_pshader = use_ps(device);
BOOL use_vshader = use_vs(device);
+ BOOL update_fog = FALSE;
int i;
if (use_pshader) {
@@ -2014,6 +2099,7 @@
sampler(STATE_SAMPLER(i), stateblock, context);
}
}
+ update_fog = TRUE;
} else {
/* Otherwise all samplers were activated by the code above in earlier draws, or by sampler()
* if a different texture was bound. I don't have to do anything.
@@ -2031,6 +2117,8 @@
tex_colorop(STATE_TEXTURESTAGE(i, WINED3DTSS_COLOROP), stateblock, context);
}
}
+ if(context->last_was_pshader)
+ update_fog = TRUE;
}
if(!isStateDirty(context, StateTable[STATE_VSHADER].representative)) {
@@ -2041,6 +2129,9 @@
}
}
+ if(update_fog)
+ state_fog(state, stateblock, context);
+
context->last_was_pshader = use_pshader;
}