Project

General

Profile

Emulator Issues #6196

Paper Mario: The Thousand Year Door, background renders improperly during pageflip (All Backends)

Added by JMC4789 almost 7 years ago.

Status:
Fixed
Priority:
Low
Assignee:
Category:
GFX
% Done:

0%

Operating system:
N/A
Issue type:
Bug
Milestone:
Regression:
No
Relates to usability:
No
Relates to performance:
No
Easy:
No
Relates to maintainability:
No
Regression start:
Fixed in:

Description

Game: Paper Mario: The Thousand Year Door - G8ME01

In all backends, I specifically tested this out of fifo-logs because of the last few issues (This also makes me think that the broken camera happens in all back-ends) being inconsistent with what back-end works or not during fifo-logs.

Oh yeah, you go to the castle in Chapter 1, and there's this switch puzzle where you use Koops to hit a block.

It's supposed to look like this: https://www.youtube.com/watch?list=PL0C2EC316AE78A98D&feature=player_detailpage&v=jJnxO7XV_Rk

But instead looks like this: http://i.imgur.com/34yyB6n.png

Note the see-through wall behind where the stairs move to. That's the glitch. Now, I know what you're saying, how do I possibly get there? Well, I got you covered with a savefile. Or a fifo-log. Note, during the whole transition that open space is there, my screenshot shows it mostly covered up, but it's fully visible during the start of one transition and the end of another. Just use Koops with X to hit the block again to make the transition happen again. The save room is literally one room right (so walk through the left door.)

This happens in all versions of Dolphin, up to latest master (3.5-1155). I have a Core i5 760, GTX 280.

Fifo-log: http://www.mediafire.com/download.php?1xmwup88aummmu1
Memory Card: http://www.mediafire.com/download.php?5vmg6aajaabcayc

History

#1 Updated by Billiard26 almost 7 years ago

  • Issue type set to Bug

#2 Updated by MayImilae over 6 years ago

  • Status changed from New to Accepted
  • Category set to gfx
  • Priority set to Low

Confirmed on 3.5-1394. It's definitely broken.

#3 Updated by JMC4789 over 6 years ago

Does not happen on Software Renderer if you actually play there rather than using the fifo-log.

#4 Updated by crudelios about 6 years ago

  • Status changed from Accepted to Work started

This is an old bounding box problem which I never managed to fix in the past two years.

However, after a lot of head scratching, I think I finally have figured out what's wrong. It seems the bounding box doesn't like the projection hacks.
Indeed, resetting the projection matrix without the hacks made the stairs scene render properly, and also fixes the problem with the water reflections (issue 6154), and should fix issue 6211.
This does make sense, since bounding box values are hardware registers and the hacked projection matrices are simply needed for the emulated graphics.

My code needs testing, however. If anyone is interested, I will post the code change here for you to test.

#5 Updated by NeoBrainX about 6 years ago

.. post it? (not sure why you ask anyway)

#6 Updated by crudelios about 6 years ago

Forget it, I was a bit sleepy yesterday, should have posted the code anyway.

In Core/VideoCommon/VertexLoader.cpp, replace method UpdateBoundingBox() with this one:

void LOADERDECL UpdateBoundingBox()
{
if (!PixelEngine::bbox_active)
return;

// reset videodata pointer
VertexManager::s_pCurBufferPointer = s_bbox_pCurBufferPointer_orig;

// copy vertex pointers
memcpy(VertexManager::s_pCurBufferPointer, s_bbox_vertex_buffer, 12);
VertexManager::s_pCurBufferPointer += 12;

// We must transform the just loaded point by the current world and projection matrix - in software.
// Then convert to screen space and update the bounding box.
float p[3] = {s_bbox_vertex_buffer[0], s_bbox_vertex_buffer[1], s_bbox_vertex_buffer[2]};

const float *world_matrix  = (float*)xfmem + MatrixIndexA.PosNormalMtxIdx * 4;

// We need to get the raw projection values for the bounding box calculation
// to work properly. That means, no projection hacks!
float *rawProjection = xfregs.projection.rawProjection;
float proj_matrix[16];

switch (xfregs.projection.type)
{
case GX_PERSPECTIVE:
    proj_matrix[0]  = rawProjection[0]; proj_matrix[1]  = 0.0f;             proj_matrix[2]  = rawProjection[1]; proj_matrix[3]  = 0.0f;
    proj_matrix[4]  = 0.0f;             proj_matrix[5]  = rawProjection[2]; proj_matrix[6]  = rawProjection[3]; proj_matrix[7]  = 0.0f;
    proj_matrix[8]  = 0.0f;             proj_matrix[9]  = 0.0f;             proj_matrix[10] = rawProjection[4]; proj_matrix[11] = rawProjection[5];
    proj_matrix[12] = 0.0f;             proj_matrix[13] = 0.0f;             proj_matrix[14] = -1.0f;            proj_matrix[15] = 0.0f;
    break;

case GX_ORTHOGRAPHIC:
    proj_matrix[0]  = rawProjection[0]; proj_matrix[1]  = 0.0f;             proj_matrix[2]  = 0.0f;             proj_matrix[3]  = rawProjection[1];
    proj_matrix[4]  = 0.0f;             proj_matrix[5]  = rawProjection[2]; proj_matrix[6]  = 0.0f;             proj_matrix[7]  = rawProjection[3];
    proj_matrix[8]  = 0.0f;             proj_matrix[9]  = 0.0f;             proj_matrix[10] = rawProjection[4]; proj_matrix[11] = rawProjection[5];
    proj_matrix[12] = 0.0f;             proj_matrix[13] = 0.0f;             proj_matrix[14] = 0.0f;             proj_matrix[15] = 1.0f;
    break;

default:
    ERROR_LOG(VIDEO, "Unknown projection type: %d", xfregs.projection.type);
}


float t[3];
t[0] = p[0] * world_matrix[0] + p[1] * world_matrix[1] + p[2] * world_matrix[2] + world_matrix[3];
t[1] = p[0] * world_matrix[4] + p[1] * world_matrix[5] + p[2] * world_matrix[6] + world_matrix[7];
t[2] = p[0] * world_matrix[8] + p[1] * world_matrix[9] + p[2] * world_matrix[10] + world_matrix[11];

float o[3];
o[0] = t[0] * proj_matrix[0]  + t[1] * proj_matrix[1]  + t[2] * proj_matrix[2] + proj_matrix[3];
o[1] = t[0] * proj_matrix[4]  + t[1] * proj_matrix[5]  + t[2] * proj_matrix[6] + proj_matrix[7];
o[2] = t[0] * proj_matrix[12] + t[1] * proj_matrix[13] + t[2] * proj_matrix[14] + proj_matrix[15];

o[0] /= o[2];
o[1] /= o[2];

// Max width is 608, while max height is 480
// Could this be register dependant? Seems to work this way, though
o[0] = (o[0] + 1.0f) * 304.0f;
o[1] = (1.0f - o[1]) * 240.0f;

if (o[0] < PixelEngine::bbox[0]) PixelEngine::bbox[0] = (u16) std::max(0.0f, o[0]);
if (o[0] > PixelEngine::bbox[1]) PixelEngine::bbox[1] = (u16) o[0];
if (o[1] < PixelEngine::bbox[2]) PixelEngine::bbox[2] = (u16) std::max(0.0f, o[1]);
if (o[1] > PixelEngine::bbox[3]) PixelEngine::bbox[3] = (u16) o[1];

}

This might also fix many minor issues with Super Paper Mario, and hopefully also the hang on world 6-1. Needs testing...

#7 Updated by NeoBrainX about 6 years ago

Can you provide a diff instead for easier review?

#8 Updated by crudelios about 6 years ago

Sorry, I don't have git installed on this computer... I will attach the diff later when I get home, though.

#9 Updated by JMC4789 about 6 years ago

I can confirm it's all working pretty well. There's some problems on the shadows around them, but peeling posters and pageflip stuff is all working properly.

#10 Updated by crudelios about 6 years ago

As requested, here is a diff with the latest changes.

diff --git a/Source/Core/VideoCommon/VertexLoader.cpp b/Source/Core/VideoCommon/VertexLoader.cpp
index 4a389bc..e4542a9 100644
--- a/Source/Core/VideoCommon/VertexLoader.cpp
+++ b/Source/Core/VideoCommon/VertexLoader.cpp
@@ -119,25 +119,39 @@ void LOADERDECL UpdateBoundingBox()
float p[3] = {s_bbox_vertex_buffer[0], s_bbox_vertex_buffer[1], s_bbox_vertex_buffer[2]};

const float *world_matrix  = (float*)xfmem + MatrixIndexA.PosNormalMtxIdx * 4;
  • const float *proj_matrix = &g_fProjectionMatrix[0];

    float t[3];
    t[0] = p[0] * world_matrix[0] + p[1] * world_matrix[1] + p[2] * world_matrix[2] + world_matrix[3];
    t[1] = p[0] * world_matrix[4] + p[1] * world_matrix[5] + p[2] * world_matrix[6] + world_matrix[7];
    t[2] = p[0] * world_matrix[8] + p[1] * world_matrix[9] + p[2] * world_matrix[10] + world_matrix[11];

  • float o[3];

  • o[0] = t[0] * proj_matrix[0] + t[1] * proj_matrix[1] + t[2] * proj_matrix[2] + proj_matrix[3];

  • o[1] = t[0] * proj_matrix[4] + t[1] * proj_matrix[5] + t[2] * proj_matrix[6] + proj_matrix[7];

  • o[2] = t[0] * proj_matrix[12] + t[1] * proj_matrix[13] + t[2] * proj_matrix[14] + proj_matrix[15];

  • o[0] /= o[2];

  • o[1] /= o[2];

  • // We need to get the raw projection values for the bounding box calculation

  • // to work properly. That means, no projection hacks!

  • float * const proj_matrix = xfregs.projection.rawProjection;

  • // Max width seems to be 608, while max height is 480

  • // Here height is set to 484 as BBox bottom always seems to be off by a few pixels

  • o[0] = (o[0] + 1.0f) * 304.0f;

  • o[1] = (1.0f - o[1]) * 242.0f;

  • float o[2];
    +

  • // Only calculate what we need, discard the rest

  • switch (xfregs.projection.type)

  • {

  • case GX_PERSPECTIVE:

  •   o[0] = (t[0] * proj_matrix[0] + t[2] * proj_matrix[1]) / (-t[2]);
    
  •   o[1] = (t[1] * proj_matrix[2] + t[2] * proj_matrix[3]) / (-t[2]);
    
  •   break;
    

    +

  • case GX_ORTHOGRAPHIC:

  •   o[0] = t[0] * proj_matrix[0] + proj_matrix[1];
    
  •   o[1] = t[1] * proj_matrix[2] + proj_matrix[3];
    
  •   break;
    

    +

  • default:

  •   ERROR_LOG(VIDEO, "Unknown projection type: %d", xfregs.projection.type);
    
  • }
    +

  • // Computed pixel position depends on viewport register setting

  • o[0] = (o[0] + 1.0f) * xfregs.viewport.wd;

  • o[1] = (o[1] - 1.0f) * xfregs.viewport.ht;

    if (o[0] < PixelEngine::bbox[0]) PixelEngine::bbox[0] = (u16) std::max(0.0f, o[0]);
    if (o[0] > PixelEngine::bbox[1]) PixelEngine::bbox[1] = (u16) o[0];

#11 Updated by NeoBrainX about 6 years ago

How did you chose the value 304.0f? Is there any noticeable change by making it 324.0f instead?

#12 Updated by crudelios about 6 years ago

That 304.0f value is wrong. If you check the diff I posted, I changed it so the width is obtained from xfregs.viewport.wd.

Doing it this way fixes the white shadow in Mickey Magical Mirror,while still working correctly in Paper Mario.

#13 Updated by NeoBrainX about 6 years ago

Right, I was in a hurry and misread the diff, sorry!

#14 Updated by NeoBrainX about 6 years ago

Either way, looks good. Does the projection hack stuff even matter though? AFAIK paper mario doesn't even use PHacks after all.

#15 Updated by degasus about 6 years ago

Hm, 304.0f vs xfregs.viewport.wd, did you miss a *0.5f ?

#16 Updated by JMC4789 about 6 years ago

Should this fix 6193 as well?

#17 Updated by crudelios about 6 years ago

xfregs.viewport.wd is always set to half the viewport value (304.0f)
xfregs.viewport.ht is both half, and negative (-240.0f)

No idea why, but the values fit and it works well, even when the viewport register values are changed.

Removing the hack stuff is what solved the bounding box issues. Besides, using the raw values means less matrix multiplication, and faster code ;)

#18 Updated by crudelios about 6 years ago

Oh and I haven't checked if it fixes 6193. Does anyone have a save file next to that area?

It doesn't fix either the glitched punies, or the hang in chapter 6-1 of Super Paper Mario, however.

#19 Updated by JMC4789 about 6 years ago

Glitched punies can be fixed by exporting their texture and then making a texture pack with the fixed sprites. Maybe it's similar to the Wind Waker problem that delroth fixed?

#20 Updated by crudelios about 6 years ago

I don't think it is. If you look closely to screenshots of the bug, the correct textures are loaded, but they are very warped, almost beyond recognition.

#21 Updated by crudelios about 6 years ago

  • Status changed from Work started to Fixed

Fixed by revision cdfe58f7ede0.

Also available in: Atom PDF