Hi.
I found some problems with D3DRENDERSTATE_TEXTUREMAPBLEND state = D3DTBLEND_MODULATE in ddraw and handling the alpha channel. This old state is currently mapped to texture stage states, with alpha part done as
IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAARG1, WINED3DTA_TEXTURE); IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAOP, WINED3DTOP_SELECTARG1);
This isn't correct, I believe, because in some cases it should be taken from diffuse color (Aliens vs Predator 1 depends on it). But the latter behavior isn't correct in all cases either.
I found this description of this state on some random website via google:
"D3DTBLEND_MODULATE Modulate texture-blending is supported. In this mode, the RGB values of the texture are multiplied with the RGB values that would have been used with no texturing. Any alpha values in the texture replace the alpha values that would have been used with no texturing."
http://www.hi.is/~snorri/SDK-docs/x/exten203.htm
I guess, this means that it either takes alpha from texture or from diffuse color, depending on whether the current texture has alpha channel (which takes priority). I've submitted a test for this to wine-patches that includes checks with both kinds of textures. It passes on XP and thus supports that this is the way it is supposed to happen. Unfortunately, this doesn't easily translate to available texture stage states. So it looks like something more hacky is needed to make it work correctly in all cases in wine. There are two approaches I can think of:
1) introduce an internal D3DTOP_ value in wined3d that will map to texture alpha or diffuse alpha, depending on the pixel format of the currently selected texture.
2) move D3DRENDERSTATE_TEXTUREMAPBLEND handling to wined3d;
So it would be nice if Stefan Dösinger or maybe somebody else of d3d devs gave me some directions - which approach (if any) is ok and acceptable for wine project. I'll attach a diff with my current hacks that show how 1st approach is about to look.
BTW, this worked in wine around 0.9.15
http://source.winehq.org/git/wine.git/?a=blob;f=dlls/ddraw/opengl_utils.c;h=...
There it wasn't mapping to texture stage states, but instead was using such call:
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
From what I read about GL_MODULATE, I'm not sure it always does exactly the thing TEXTUREMAPBLEND=MODULATE is supposed to do, but with Aliens vs Predator it worked better.
diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c index 135d6e9..e2cd23a 100644 --- a/dlls/ddraw/device.c +++ b/dlls/ddraw/device.c @@ -2534,10 +2534,9 @@ IDirect3DDeviceImpl_7_SetRenderState(IDirect3DDevice7 *iface, { case D3DTBLEND_MODULATE: IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_COLORARG1, WINED3DTA_TEXTURE); - IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAARG1, WINED3DTA_TEXTURE); IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_COLORARG2, WINED3DTA_CURRENT); IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_COLOROP, WINED3DTOP_MODULATE); - IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAOP, WINED3DTOP_SELECTARG1); + IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAOP, WINED3DTOP_DX6MODULATE); break;
case D3DTBLEND_MODULATEALPHA: diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 59ff3d4..9f11f73 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -351,6 +351,12 @@ static void state_blend(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D TRACE("glBlendFunc src=%x, dst=%x\n", srcBlend, dstBlend); glBlendFunc(srcBlend, dstBlend); checkGLcall("glBlendFunc"); + + /* colorkey fixup for stage 0 alphaop depends on WINED3DRS_ALPHABLENDENABLE state, + so it may need updating */ + if (stateblock->renderState[WINED3DRS_COLORKEYENABLE]) { + StateTable[STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP)].apply(STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP), stateblock, context); + } }
static void state_blendfactor(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) { @@ -1932,6 +1938,24 @@ static void tex_alphaop(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D arg2 = stateblock->textureState[stage][WINED3DTSS_ALPHAARG2]; arg0 = stateblock->textureState[stage][WINED3DTSS_ALPHAARG0];
+ if (op == WINED3DTOP_DX6MODULATE) { + IWineD3DSurfaceImpl *surf = NULL; + + if (stage > 0) ERR("unexpected WINED3DTOP_DX6MODULATE at stage > 0\n"); + + if (stateblock->textures[0]) + surf = (IWineD3DSurfaceImpl *) ((IWineD3DTextureImpl *) stateblock->textures[0])->surfaces[0]; + + if (surf && getFormatDescEntry(surf->resource.format, NULL, NULL)->alphaMask) { + op = WINED3DTOP_SELECTARG1; + arg1 = WINED3DTA_TEXTURE; + } + else { + op = WINED3DTOP_SELECTARG1; + arg1 = WINED3DTA_CURRENT; + } + } + if(stateblock->renderState[WINED3DRS_COLORKEYENABLE] && stage == 0 && stateblock->textures[0] && (stateblock->textureDimensions[0] == GL_TEXTURE_2D || stateblock->textureDimensions[0] == GL_TEXTURE_RECTANGLE_ARB)) { @@ -1945,13 +1969,29 @@ static void tex_alphaop(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D * cannot remove the texture's alpha channel entirely. * * The fixup is required for Prince of Persia 3D(prison bars), while Moto racer 2 requires D3DTOP_MODULATE to work - * on color keyed surfaces. + * on color keyed surfaces. Aliens vs Predator 1 uses color keyed textures and alpha component of diffuse color to + * draw things like translucent text and perform other blending effects. * * What to do with multitexturing? So far no app has been found that uses color keying with multitexturing */ - if(op == WINED3DTOP_DISABLE) op = WINED3DTOP_SELECTARG1; - if(op == WINED3DTOP_SELECTARG1) arg1 = WINED3DTA_TEXTURE; - else if(op == WINED3DTOP_SELECTARG2) arg2 = WINED3DTA_TEXTURE; + if(op == WINED3DTOP_DISABLE) { + arg1 = WINED3DTA_TEXTURE; + op = WINED3DTOP_SELECTARG1; + } + else if(op == WINED3DTOP_SELECTARG1 && arg1 != WINED3DTA_TEXTURE) { + if (stateblock->renderState[WINED3DRS_ALPHABLENDENABLE]) { + arg2 = WINED3DTA_TEXTURE; + op = WINED3DTOP_MODULATE; + } + else arg1 = WINED3DTA_TEXTURE; + } + else if(op == WINED3DTOP_SELECTARG2 && arg2 != WINED3DTA_TEXTURE) { + if (stateblock->renderState[WINED3DRS_ALPHABLENDENABLE]) { + arg1 = WINED3DTA_TEXTURE; + op = WINED3DTOP_MODULATE; + } + else arg2 = WINED3DTA_TEXTURE; + } } }
@@ -2432,6 +2472,11 @@ static void sampler(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCont glBindTexture(GL_TEXTURE_2D, stateblock->wineD3DDevice->dummyTextureName[sampler]); checkGLcall("glBindTexture(GL_TEXTURE_2D, stateblock->wineD3DDevice->dummyTextureName[sampler])"); } + + if (sampler == 0 && (stateblock->textureState[0][WINED3DTSS_ALPHAOP] == WINED3DTOP_DX6MODULATE)) { + /* fixup in tex_alphaop may depend on currently set stage 0 texture */ + StateTable[STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP)].apply(STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP), stateblock, context); + } }
static void pixelshader(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) { diff --git a/include/wine/wined3d_types.h b/include/wine/wined3d_types.h index 477df24..fa9a6e8 100644 --- a/include/wine/wined3d_types.h +++ b/include/wine/wined3d_types.h @@ -758,6 +758,7 @@ typedef enum _WINED3DTEXTUREOP { WINED3DTOP_DOTPRODUCT3 = 24, WINED3DTOP_MULTIPLYADD = 25, WINED3DTOP_LERP = 26, + WINED3DTOP_DX6MODULATE = 50,
WINED3DTOP_FORCE_DWORD = 0x7fffffff, } WINED3DTEXTUREOP;