diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp index fa2a6ffd5..7eb7a0608 100644 --- a/desmume/src/OGLRender.cpp +++ b/desmume/src/OGLRender.cpp @@ -3149,12 +3149,21 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, size_t polyR Render3DError OpenGLRenderer_1_2::SetupViewport(const u32 viewportValue) { - const GLfloat wScalar = this->_framebufferWidth / GPU_FRAMEBUFFER_NATIVE_WIDTH; - const GLfloat hScalar = this->_framebufferHeight / GPU_FRAMEBUFFER_NATIVE_HEIGHT; + const GLfloat wScalar = this->_framebufferWidth / (GLfloat)GPU_FRAMEBUFFER_NATIVE_WIDTH; + const GLfloat hScalar = this->_framebufferHeight / (GLfloat)GPU_FRAMEBUFFER_NATIVE_HEIGHT; VIEWPORT viewport; viewport.decode(viewportValue); - glViewport(viewport.x * wScalar, viewport.y * hScalar, viewport.width * wScalar, viewport.height * hScalar); + + // The maximum viewport y-value is 191. Values above 191 need to wrap + // around and go negative. + // + // Test case: The Homie Rollerz character select screen sets the y-value + // to 253, which then wraps around to -2. + glViewport( viewport.x * wScalar, + (viewport.y > 191) ? (viewport.y - 0xFF) * hScalar : viewport.y * hScalar, + viewport.width * wScalar, + viewport.height * hScalar); return OGLERROR_NOERR; } diff --git a/desmume/src/rasterize.cpp b/desmume/src/rasterize.cpp index 3f00fa6e4..c5d194158 100644 --- a/desmume/src/rasterize.cpp +++ b/desmume/src/rasterize.cpp @@ -1006,7 +1006,7 @@ static void* execRasterizerUnit(void *arg) static void* SoftRasterizer_RunCalculateVertices(void *arg) { SoftRasterizerRenderer *softRender = (SoftRasterizerRenderer *)arg; - softRender->performViewportTransforms(); + softRender->performViewportTransforms(); softRender->performBackfaceTests(); softRender->performCoordAdjustment(); @@ -1479,12 +1479,10 @@ size_t SoftRasterizerRenderer::performClipping(const VERTLIST *vertList, const P return clipper.clippedPolyCounter; } -template void SoftRasterizerRenderer::performViewportTransforms() +void SoftRasterizerRenderer::performViewportTransforms() { - const float xfactor = (float)this->_framebufferWidth/(float)GPU_FRAMEBUFFER_NATIVE_WIDTH; - const float yfactor = (float)this->_framebufferHeight/(float)GPU_FRAMEBUFFER_NATIVE_HEIGHT; - const float xmax = (float)this->_framebufferWidth-(CUSTOM?0.001f:0); //fudge factor to keep from overrunning render buffers - const float ymax = (float)this->_framebufferHeight-(CUSTOM?0.001f:0); + const float wScalar = (float)this->_framebufferWidth / (float)GPU_FRAMEBUFFER_NATIVE_WIDTH; + const float hScalar = (float)this->_framebufferHeight / (float)GPU_FRAMEBUFFER_NATIVE_HEIGHT; //viewport transforms for (size_t i = 0; i < this->_clippedPolyCount; i++) @@ -1494,12 +1492,21 @@ template void SoftRasterizerRenderer::performViewportTransforms() { VERT &vert = poly.clipVerts[j]; + // TODO: Possible divide by zero with the w-coordinate. + // Is the vertex being read correctly? Is 0 a valid value for w? + // If both of these questions answer to yes, then how does the NDS handle a NaN? + // For now, simply prevent w from being zero. + // + // Test case: Dance scenes in Princess Debut can generate undefined vertices + // when the -ffast-math option (relaxed IEEE754 compliance) is used. + const float vertw = (vert.coord[3] != 0.0f) ? vert.coord[3] : 0.00000001f; + //homogeneous divide - vert.coord[0] = (vert.coord[0]+vert.coord[3]) / (2*vert.coord[3]); - vert.coord[1] = (vert.coord[1]+vert.coord[3]) / (2*vert.coord[3]); - vert.coord[2] = (vert.coord[2]+vert.coord[3]) / (2*vert.coord[3]); - vert.texcoord[0] /= vert.coord[3]; - vert.texcoord[1] /= vert.coord[3]; + vert.coord[0] = (vert.coord[0]+vertw) / (2*vertw); + vert.coord[1] = (vert.coord[1]+vertw) / (2*vertw); + vert.coord[2] = (vert.coord[2]+vertw) / (2*vertw); + vert.texcoord[0] /= vertw; + vert.texcoord[1] /= vertw; //CONSIDER: do we need to guarantee that these are in bounds? perhaps not. //vert.coord[0] = max(0.0f,min(1.0f,vert.coord[0])); @@ -1507,44 +1514,30 @@ template void SoftRasterizerRenderer::performViewportTransforms() //vert.coord[2] = max(0.0f,min(1.0f,vert.coord[2])); //perspective-correct the colors - vert.fcolor[0] /= vert.coord[3]; - vert.fcolor[1] /= vert.coord[3]; - vert.fcolor[2] /= vert.coord[3]; + vert.fcolor[0] /= vertw; + vert.fcolor[1] /= vertw; + vert.fcolor[2] /= vertw; //viewport transformation VIEWPORT viewport; viewport.decode(poly.poly->viewport); vert.coord[0] *= viewport.width; vert.coord[0] += viewport.x; + + // The maximum viewport y-value is 191. Values above 191 need to wrap + // around and go negative. + // + // Test case: The Homie Rollerz character select screen sets the y-value + // to 253, which then wraps around to -2. vert.coord[1] *= viewport.height; - vert.coord[1] += viewport.y; + vert.coord[1] += (viewport.y > 191) ? (viewport.y - 0xFF) : viewport.y; vert.coord[1] = 192 - vert.coord[1]; - //this is required to fix Homie Rollerz character select. - //this was also a better fix for Princess Debut's giant out of range polys. - //Basically, invalid viewports are blithely used here, and the math overflows and wraps around - //NOTE: this is a crude approximation of the correct fixed point modular arithmetic - //NOTE: you'd think we need >= 256 here, but that just isnt the way it works out (on softrasterizer) right now. coordinates @ 256.0 exactly will come in here. - if(vert.coord[0] > 256) vert.coord[0] -= 256; - if(vert.coord[1] > 256) vert.coord[1] -= 256; - if(vert.coord[0] < 0) vert.coord[0] += 256; - if(vert.coord[1] < 0) vert.coord[1] += 256; - - //now, this is really lame. probably the rasterizer should be dealing with this, but - //1. its easier here - //2. its more likely reusable in opengl this way (well, this is a bunch more calculations that should be earlier in the pipeline and obligatorily shared) - //I do have some evidence that this wrecks the hardware extremely, but nothing's likely to use that effect. - //This is a sanity check only. - if(vert.coord[1] >= 192) vert.coord[1] = 192; - - vert.coord[0] *= xfactor; - vert.coord[1] *= yfactor; + vert.coord[0] *= wScalar; + vert.coord[1] *= hScalar; } } } -//these templates needed to be instantiated manually -template void SoftRasterizerRenderer::performViewportTransforms(); -template void SoftRasterizerRenderer::performViewportTransforms(); void SoftRasterizerRenderer::performCoordAdjustment() { @@ -1665,7 +1658,7 @@ Render3DError SoftRasterizerRenderer::BeginRender(const GFX3D &engine) } else { - this->performViewportTransforms(); + this->performViewportTransforms(); this->performBackfaceTests(); this->performCoordAdjustment(); this->GetAndLoadAllTextures(); diff --git a/desmume/src/rasterize.h b/desmume/src/rasterize.h index 80a242414..6a7ae9b2f 100644 --- a/desmume/src/rasterize.h +++ b/desmume/src/rasterize.h @@ -127,7 +127,7 @@ public: SoftRasterizerRenderer(); virtual ~SoftRasterizerRenderer(); - template void performViewportTransforms(); + void performViewportTransforms(); void performBackfaceTests(); void performCoordAdjustment(); void GetAndLoadAllTextures();