I've spent a couple of days researching the issue of broken/upside-down character/object models in Wine in almost all newer games when you have vertex shaders enabled (Civ4, Half Life 2, Oblivion, Max Payne 2, etc.). I think I've boiled it down to a single case: When "device->renderUpsideDown" is set in the case where vertex shaders are enabled. That flag gets set in device.c:7395 when the current renderTarget is not on the current swapchain. The comments in the source say that the upside-downedness is produced by glCopyTexImage, so it sets a flag to flip everything over.
In the case w/o shaders, there is code in drawprim.c which loads the WORLDVIEW and PROJECTION matrices and then multiplies those matrices by one which inverts the y coordinates when that flag is set. That seems to work in the case without vertex shaders, but when shaders are enabled, they bypass the WORLD, VIEW, and PROJECTION matrices entirely. The shader case was written when only software shaders worked, but that is no longer true. It loads identity matrices and performs the y flip, but that code is entirely irrelevant since the vertex shader doesn't reference those matrices; it only uses constants that are passed by the app, which we can't perform any type of fixup on since we don't know which constants will be used for which calculation.
So, I think what we need to do is prevent ourselves from having to do any flipping whatsoever. That's the part that I'm not sure how to do and is the reason for this email. Can we load the textures in system memory first, perform a software reversing process, then load that up with glCopyTexImage instead? Will we need to do that type of fixup every time the app locks/unlocks/changes part of the texture? Or, is there a better way?
I think I've figured out the problem, it's just the next step of fixing it that I'm unsure of. :-)
From a few hackish tests, I've been able to fix some of the issues
where the only thing wrong was that the images were upside-down with this hack (for GLSL shader mode only):
diff --git a/dlls/wined3d/vertexshader.c b/dlls/wined3d/vertexshader.c index 84f90f5..d0325d9 100644 --- a/dlls/wined3d/vertexshader.c +++ b/dlls/wined3d/vertexshader.c @@ -719,6 +719,7 @@ #endif shader_addline(&buffer, "gl_FogFragCoord = clamp(gl_FogFragCoord, 0.0, 1.0);\n"); }
+ shader_addline(&buffer, "gl_Position = gl_Position * gl_ModelViewProjectionMatrix;\n"); shader_addline(&buffer, "}\n\0");
TRACE("Compiling shader object %u\n", shader_obj);
This works because the gl_ModelViewProjectionMatrix is just a matrix which reverses the y position when This->renderUpsideDown == TRUE, otherwise it's the identity matrix. Here are a few comparison screenshots:
Oblvion: --------- Before: http://www.cmhousing.net/wine/oblivion2.png After: http://www.cmhousing.net/wine/oblivion_sortacorrect3.png
Civ4: ----- Before: http://www.cmhousing.net/wine/civ4_before.png After: http://www.cmhousing.net/wine/civ4_leaderhead.png
Half Life 2: ------------- After: http://www.cmhousing.net/wine/hl2_rightsideup.png (the bottom got screwed up, but normally the guy on the picture is upside-down)
Many of the character models and other objects are still broken even with that hack, but that's because we should be flipping more than just the final position - we should be flipping a whole row of constants that the app is using before it starts its calculations. Instead of trying to do that, we should fix the root of the problem IMHO.
Anyway, any ideas are welcome. :-)
Jason