1 /*
2 Copyright (C) 2003 Rice1964
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 */
19 
20 #define M64P_PLUGIN_PROTOTYPES 1
21 #include <stdint.h>
22 
23 #include <retro_miscellaneous.h>
24 
25 #include "osal_preproc.h"
26 #include "m64p_plugin.h"
27 
28 #include "ConvertImage.h"
29 #include "DeviceBuilder.h"
30 #include "FrameBuffer.h"
31 #include "Render.h"
32 
33 #include <algorithm>
34 
35 extern FiddledVtx * g_pVtxBase;
36 CRender * CRender::g_pRender=NULL;
37 int CRender::gRenderReferenceCount=0;
38 
39 XMATRIX reverseXY(-1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1);
40 XMATRIX reverseY(1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1);
41 extern char* right (const char * src, int nchars);
42 
43 #if defined(WIN32)
44   #define strcasecmp _stricmp
45 #endif
46 
47 //========================================================================
GetRender(void)48 CRender * CRender::GetRender(void)
49 {
50    return CRender::g_pRender;
51 }
IsAvailable()52 bool CRender::IsAvailable()
53 {
54     return CRender::g_pRender != NULL;
55 }
56 
CRender()57 CRender::CRender() :
58     m_fScreenViewportMultX(2.0f),
59     m_fScreenViewportMultY(2.0f),
60 
61     m_dwTexturePerspective(false),
62     m_bAlphaTestEnable(false),
63 
64     m_bZUpdate(false),
65     m_bZCompare(false),
66     m_dwZBias(0),
67 
68     m_dwMinFilter(FILTER_POINT),
69     m_dwMagFilter(FILTER_POINT),
70     m_dwAlpha(0xFF),
71     m_Mux(0),
72     m_bBlendModeValid(false)
73 {
74     InitRenderBase();
75 
76     for( int i=0; i<MAX_TEXTURES; i++ )
77     {
78         g_textures[i].m_lpsTexturePtr = NULL;
79         g_textures[i].m_pCTexture = NULL;
80 
81         g_textures[i].m_dwTileWidth = 64;       // Value doesn't really matter, as tex not set
82         g_textures[i].m_dwTileHeight = 64;
83         g_textures[i].m_fTexWidth = 64.0f;      // Value doesn't really matter, as tex not set
84         g_textures[i].m_fTexHeight = 64.0f;
85         g_textures[i].pTextureEntry = NULL;
86 
87         TileUFlags[i] = TileVFlags[i] = TEXTURE_UV_FLAG_CLAMP;
88     }
89 
90 
91     //for( int i=0; i<MAX_VERTS; i++)
92     //{
93     //  g_dwVtxFlags[i] = 0;
94     //}
95 
96     m_pColorCombiner = CDeviceBuilder::GetBuilder()->CreateColorCombiner(this);
97     m_pColorCombiner->Initialize();
98 
99     m_pAlphaBlender = CDeviceBuilder::GetBuilder()->CreateAlphaBlender(this);
100 
101 }
102 
~CRender()103 CRender::~CRender()
104 {
105     if( m_pColorCombiner != NULL )
106     {
107         CDeviceBuilder::GetBuilder()->DeleteColorCombiner();
108         m_pColorCombiner = NULL;
109     }
110 
111     if( m_pAlphaBlender != NULL )
112     {
113         CDeviceBuilder::GetBuilder()->DeleteAlphaBlender();
114         m_pAlphaBlender = NULL;
115     }
116 }
117 
ResetMatrices()118 void CRender::ResetMatrices()
119 {
120     Matrix mat;
121 
122     mat.m[0][1] = mat.m[0][2] = mat.m[0][3] =
123     mat.m[1][0] = mat.m[1][2] = mat.m[1][3] =
124     mat.m[2][0] = mat.m[2][1] = mat.m[2][3] =
125     mat.m[3][0] = mat.m[3][1] = mat.m[3][2] = 0.0f;
126 
127     mat.m[0][0] = mat.m[1][1] = mat.m[2][2] = mat.m[3][3] = 1.0f;
128 
129     gRSP.projectionMtxTop = 0;
130     gRSP.modelViewMtxTop = 0;
131     gRSP.projectionMtxs[0] = mat;
132     gRSP.modelviewMtxs[0] = mat;
133 
134     gRSP.bMatrixIsUpdated = true;
135     gRSP.bWorldMatrixIsUpdated = true;
136     UpdateCombinedMatrix();
137 }
138 
SetProjection(const Matrix & mat,bool bPush,bool bReplace)139 void CRender::SetProjection(const Matrix & mat, bool bPush, bool bReplace)
140 {
141     if (bPush)
142     {
143         if (gRSP.projectionMtxTop < (RICE_MATRIX_STACK-1))
144             gRSP.projectionMtxTop++;
145 
146         if (bReplace)   // Load projection matrix
147             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat;
148         else
149             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat * gRSP.projectionMtxs[gRSP.projectionMtxTop-1];
150 
151     }
152     else
153     {
154         if (bReplace)   // Load projection matrix
155             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat;
156         else
157             gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat * gRSP.projectionMtxs[gRSP.projectionMtxTop];
158     }
159 
160     gRSP.bMatrixIsUpdated = true;
161 }
162 
163 bool mtxPopUpError = false;
SetWorldView(const Matrix & mat,bool bPush,bool bReplace)164 void CRender::SetWorldView(const Matrix & mat, bool bPush, bool bReplace)
165 {
166     if (bPush)
167     {
168         if (gRSP.modelViewMtxTop < (RICE_MATRIX_STACK-1))
169             gRSP.modelViewMtxTop++;
170 
171         // We should store the current projection matrix...
172         if (bReplace)
173         {
174             // Load projection matrix
175             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat;
176 
177             //GSI: Hack needed to show heart in OOT & MM
178             //it renders at Z coordinate = 0.0f that gets clipped away.
179             //so we translate them a bit along Z to make them stick
180 
181             if( options.enableHackForGames == HACK_FOR_ZELDA || options.enableHackForGames == HACK_FOR_ZELDA_MM)
182             {
183                if(gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._43 == 0.0f
184                      && gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._42 != 0.0f
185                      && gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._42 <= 94.5f
186                      && gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._42 >= -94.5f)
187                   gRSP.modelviewMtxs[gRSP.modelViewMtxTop]._43 -= 10.1f;
188             }
189         }
190         else            // Multiply projection matrix
191             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat * gRSP.modelviewMtxs[gRSP.modelViewMtxTop-1];
192     }
193     else    // NoPush
194     {
195         if (bReplace)   // Load projection matrix
196             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat;
197         else            // Multiply projection matrix
198             gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat * gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
199     }
200 
201     gRSPmodelViewTop = gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
202     if( options.enableHackForGames == HACK_REVERSE_XY_COOR )
203         gRSPmodelViewTop = gRSPmodelViewTop * reverseXY;
204     if( options.enableHackForGames == HACK_REVERSE_Y_COOR )
205         gRSPmodelViewTop = gRSPmodelViewTop * reverseY;
206     MatrixTranspose(&gRSPmodelViewTopTranspose, &gRSPmodelViewTop);
207 
208     gRSP.bMatrixIsUpdated = true;
209     gRSP.bWorldMatrixIsUpdated = true;
210 }
211 
212 
PopWorldView()213 void CRender::PopWorldView()
214 {
215     if (gRSP.modelViewMtxTop > 0)
216     {
217         gRSP.modelViewMtxTop--;
218         gRSPmodelViewTop = gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
219         if( options.enableHackForGames == HACK_REVERSE_XY_COOR )
220             gRSPmodelViewTop = gRSPmodelViewTop * reverseXY;
221         if( options.enableHackForGames == HACK_REVERSE_Y_COOR )
222             gRSPmodelViewTop = gRSPmodelViewTop * reverseY;
223         MatrixTranspose(&gRSPmodelViewTopTranspose, &gRSPmodelViewTop);
224         gRSP.bMatrixIsUpdated = true;
225         gRSP.bWorldMatrixIsUpdated = true;
226     }
227     else
228     {
229         mtxPopUpError = true;
230     }
231 }
232 
233 
GetWorldProjectMatrix(void)234 Matrix & CRender::GetWorldProjectMatrix(void)
235 {
236     return gRSPworldProject;
237 }
238 
SetWorldProjectMatrix(Matrix & mtx)239 void CRender::SetWorldProjectMatrix(Matrix &mtx)
240 {
241     gRSPworldProject = mtx;
242 
243     gRSP.bMatrixIsUpdated = false;
244     gRSP.bCombinedMatrixIsUpdated = true;
245 }
246 
SetMux(uint32_t dwMux0,uint32_t dwMux1)247 void CRender::SetMux(uint32_t dwMux0, uint32_t dwMux1)
248 {
249     uint64_t tempmux = (((uint64_t)dwMux0) << 32) | (uint64_t)dwMux1;
250     if( m_Mux != tempmux )
251     {
252         m_Mux = tempmux;
253         m_bBlendModeValid = false;
254         m_pColorCombiner->UpdateCombiner(dwMux0, dwMux1);
255     }
256 }
257 
258 
SetCombinerAndBlender()259 void CRender::SetCombinerAndBlender()
260 {
261     InitOtherModes();
262 
263     if( g_curRomInfo.bDisableBlender )
264         m_pAlphaBlender->DisableAlphaBlender();
265     else if( currentRomOptions.bNormalBlender )
266         m_pAlphaBlender->NormalAlphaBlender();
267     else
268         m_pAlphaBlender->InitBlenderMode();
269 
270     m_pColorCombiner->InitCombinerMode();
271 }
272 
RenderReset()273 void CRender::RenderReset()
274 {
275     UpdateClipRectangle();
276     ResetMatrices();
277     SetZBias(0);
278     gRSP.numVertices = 0;
279     gRSP.maxVertexID = 0;
280     gRSP.curTile = 0;
281     gRSP.fTexScaleX = 1/32.0f;;
282     gRSP.fTexScaleY = 1/32.0f;
283 }
284 
FillRect(int nX0,int nY0,int nX1,int nY1,uint32_t dwColor)285 bool CRender::FillRect(int nX0, int nY0, int nX1, int nY1, uint32_t dwColor)
286 {
287     if (g_CI.dwSize != G_IM_SIZ_16b && frameBufferOptions.bIgnore)
288         return true;
289 
290     if (status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM)
291         status.bFrameBufferIsDrawn = true;
292 
293     if(status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE)
294     {
295         status.bVIOriginIsUpdated=false;
296         CGraphicsContext::Get()->UpdateFrame(false);
297     }
298 
299   if (status.bCIBufferIsRendered && status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_BEFORE_SCREEN_CLEAR )
300   {
301       if ((nX0==0 && nY0 == 0 && (nX1 == (int) g_CI.dwWidth || nX1 == (int) g_CI.dwWidth-1)) ||
302           (nX0==gRDP.scissor.left && nY0 == gRDP.scissor.top  && (nX1 == gRDP.scissor.right || nX1 == gRDP.scissor.right-1)) ||
303           ((nX0+nX1 == (int)g_CI.dwWidth || nX0+nX1 == (int)g_CI.dwWidth-1 || nX0+nX1 == gRDP.scissor.left+gRDP.scissor.right || nX0+nX1 == gRDP.scissor.left+gRDP.scissor.right-1) && (nY0 == gRDP.scissor.top || nY0 == 0 || nY0+nY1 == gRDP.scissor.top+gRDP.scissor.bottom || nY0+nY1 == gRDP.scissor.top+gRDP.scissor.bottom-1)))
304       {
305           status.bVIOriginIsUpdated=false;
306           CGraphicsContext::Get()->UpdateFrame(false);
307       }
308   }
309 
310 
311     SetFillMode(RICE_FILLMODE_SOLID);
312 
313     bool res=true;
314 
315     /*
316     // I don't know why this does not work for OpenGL
317     if( gRDP.otherMode.cycle_type == G_CYC_FILL && nX0 == 0 && nY0 == 0 && ((nX1==windowSetting.uViWidth && nY1==windowSetting.uViHeight)||(nX1==windowSetting.uViWidth-1 && nY1==windowSetting.uViHeight-1)) )
318     {
319         CGraphicsContext::g_pGraphicsContext->Clear(CLEAR_COLOR_BUFFER,dwColor, 0xFF000000, 1.0f);
320     }
321     else
322     */
323     {
324         //bool m_savedZBufferFlag = gRSP.bZBufferEnabled;   // Save ZBuffer state
325         ZBufferEnable( false );
326 
327         m_fillRectVtx[0].x = ViewPortTranslatei_x(nX0);
328         m_fillRectVtx[0].y = ViewPortTranslatei_y(nY0);
329         m_fillRectVtx[1].x = ViewPortTranslatei_x(nX1);
330         m_fillRectVtx[1].y = ViewPortTranslatei_y(nY1);
331 
332         SetCombinerAndBlender();
333 
334         if( gRDP.otherMode.cycle_type  >= G_CYC_COPY )
335         {
336             ZBufferEnable(false);
337         }
338         else
339         {
340             //dwColor = PostProcessDiffuseColor(0);
341             dwColor = PostProcessDiffuseColor(gRDP.primitiveColor);
342         }
343 
344         float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
345 
346         ApplyRDPScissor(false);
347         TurnFogOnOff(false);
348         res = RenderFillRect(dwColor, depth);
349         TurnFogOnOff(gRSP.bFogEnabled);
350 
351         if( gRDP.otherMode.cycle_type  >= G_CYC_COPY )
352         {
353             ZBufferEnable(gRSP.bZBufferEnabled);
354         }
355     }
356 
357     if( options.bWinFrameMode )
358         SetFillMode(RICE_FILLMODE_WINFRAME);
359 
360     return res;
361 }
362 
363 
Line3D(uint32_t dwV0,uint32_t dwV1,uint32_t dwWidth)364 bool CRender::Line3D(uint32_t dwV0, uint32_t dwV1, uint32_t dwWidth)
365 {
366     if( !status.bCIBufferIsRendered )
367         g_pFrameBufferManager->ActiveTextureBuffer();
368 
369     m_line3DVtx[0].z = (g_vecProjected[dwV0].z + 1.0f) * 0.5f;
370     m_line3DVtx[1].z = (g_vecProjected[dwV1].z + 1.0f) * 0.5f;
371 
372     if( m_line3DVtx[0].z != m_line3DVtx[1].z )
373         return false;
374 
375     if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )
376         status.bFrameBufferIsDrawn = true;
377 
378     if( status.bHandleN64RenderTexture )
379     {
380         g_pRenderTextureInfo->maxUsedHeight = g_pRenderTextureInfo->N64Height;
381         if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )
382         {
383             status.bFrameBufferIsDrawn = true;
384             status.bFrameBufferDrawnByTriangles = true;
385         }
386     }
387 
388     m_line3DVtx[0].x = ViewPortTranslatef_x(g_vecProjected[dwV0].x);
389     m_line3DVtx[0].y = ViewPortTranslatef_y(g_vecProjected[dwV0].y);
390     m_line3DVtx[0].rhw = g_vecProjected[dwV0].w;
391     m_line3DVtx[0].dcDiffuse = PostProcessDiffuseColor(g_dwVtxDifColor[dwV0]);
392     m_line3DVtx[0].dcSpecular = PostProcessSpecularColor();
393 
394     m_line3DVtx[1].x = ViewPortTranslatef_x(g_vecProjected[dwV1].x);
395     m_line3DVtx[1].y = ViewPortTranslatef_y(g_vecProjected[dwV1].y);
396     m_line3DVtx[1].rhw = g_vecProjected[dwV1].w;
397     m_line3DVtx[1].dcDiffuse = PostProcessDiffuseColor(g_dwVtxDifColor[dwV1]);
398     m_line3DVtx[1].dcSpecular = m_line3DVtx[0].dcSpecular;
399 
400     float width = dwWidth*0.5f+1.5f;
401 
402     if( m_line3DVtx[0].y == m_line3DVtx[1].y )
403     {
404         m_line3DVector[0].x = m_line3DVector[1].x = m_line3DVtx[0].x;
405         m_line3DVector[2].x = m_line3DVector[3].x = m_line3DVtx[1].x;
406 
407         m_line3DVector[0].y = m_line3DVector[2].y = m_line3DVtx[0].y-width/2*windowSetting.fMultY;
408         m_line3DVector[1].y = m_line3DVector[3].y = m_line3DVtx[0].y+width/2*windowSetting.fMultY;
409     }
410     else
411     {
412         m_line3DVector[0].y = m_line3DVector[1].y = m_line3DVtx[0].y;
413         m_line3DVector[2].y = m_line3DVector[3].y = m_line3DVtx[1].y;
414 
415         m_line3DVector[0].x = m_line3DVector[2].x = m_line3DVtx[0].x-width/2*windowSetting.fMultX;
416         m_line3DVector[1].x = m_line3DVector[3].x = m_line3DVtx[0].x+width/2*windowSetting.fMultX;
417     }
418 
419     SetCombinerAndBlender();
420 
421     return RenderLine3D();
422 }
423 
RemapTextureCoordinate(float t0,float t1,uint32_t tileWidth,uint32_t mask,float textureWidth,float & u0,float & u1)424 bool CRender::RemapTextureCoordinate
425     (float t0, float t1, uint32_t tileWidth, uint32_t mask, float textureWidth, float &u0, float &u1)
426 {
427     int s0 = (int)t0;
428     int s1 = (int)t1;
429     int width = mask>0 ? (1<<mask) : tileWidth;
430     if( width == 0 ) return false;
431 
432     int divs0 = s0/width; if( divs0*width > s0 )    divs0--;
433     int divs1 = s1/width; if( divs1*width > s1 )    divs1--;
434 
435     if( divs0 == divs1 )
436     {
437         s0 -= divs0*width;
438         s1 -= divs1*width;
439         //if( s0 > s1 )
440         //  s0++;
441         //else if( s1 > s0 )
442         //  s1++;
443         u0 = s0/textureWidth;
444         u1 = s1/textureWidth;
445 
446         return true;
447     }
448     else if( divs0+1 == divs1 && s0%width==0 && s1%width == 0 )
449     {
450         u0 = 0;
451         u1 = tileWidth/textureWidth;
452         return true;
453     }
454     else if( divs0 == divs1+1 && s0%width==0 && s1%width == 0 )
455     {
456         u1 = 0;
457         u0 = tileWidth/textureWidth;
458         return true;
459     }
460     else
461     {
462         //if( s0 > s1 )
463         //{
464             //s0++;
465         //  u0 = s0/textureWidth;
466         //}
467         //else if( s1 > s0 )
468         //{
469             //s1++;
470         //  u1 = s1/textureWidth;
471         //}
472 
473         return false;
474     }
475 }
476 
TexRect(int nX0,int nY0,int nX1,int nY1,float fS0,float fT0,float fScaleS,float fScaleT,bool colorFlag,uint32_t diffuseColor)477 bool CRender::TexRect(int nX0, int nY0, int nX1, int nY1, float fS0, float fT0, float fScaleS, float fScaleT, bool colorFlag, uint32_t diffuseColor)
478 {
479     if( options.enableHackForGames == HACK_FOR_DUKE_NUKEM )
480     {
481         colorFlag = true;
482         diffuseColor = 0;
483     }
484 
485     if( status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE )
486     {
487         status.bVIOriginIsUpdated=false;
488         CGraphicsContext::Get()->UpdateFrame(false);
489     }
490 
491     if( options.enableHackForGames == HACK_FOR_BANJO_TOOIE )
492     {
493         // Hack for Banjo shadow in Banjo Tooie
494         if( g_TI.dwWidth == g_CI.dwWidth && g_TI.dwFormat == G_IM_FMT_CI && g_TI.dwSize == G_IM_SIZ_8b )
495         {
496            // Skip the text rect
497            if( nX0 == fS0 && nY0 == fT0 )//&& nX0 > 90 && nY0 > 130 && nX1-nX0 > 80 && nY1-nY0 > 20 )
498               return true;
499         }
500     }
501 
502     if( status.bN64IsDrawingTextureBuffer )
503     {
504         if( frameBufferOptions.bIgnore || ( frameBufferOptions.bIgnoreRenderTextureIfHeightUnknown && newRenderTextureInfo.knownHeight == 0 ) )
505             return true;
506     }
507 
508     PrepareTextures();
509 
510     if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == G_IM_SIZ_8b )
511         return true;
512 
513     if( !IsTextureEnabled() &&  gRDP.otherMode.cycle_type  != G_CYC_COPY )
514     {
515         FillRect(nX0, nY0, nX1, nY1, gRDP.primitiveColor);
516         return true;
517     }
518 
519 
520     if( IsUsedAsDI(g_CI.dwAddr) && !status.bHandleN64RenderTexture )
521         status.bFrameBufferIsDrawn = true;
522 
523     if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )   status.bFrameBufferIsDrawn = true;
524 
525     if( options.bEnableHacks )
526     {
527         // Goldeneye HACK
528         if( nY1 - nY0 < 2 )
529             nY1 = nY1+2;
530 
531         //// Text edge hack
532         else if( gRDP.otherMode.cycle_type == G_CYC_1CYCLE && fScaleS == 1 && fScaleT == 1 &&
533             (int)g_textures[gRSP.curTile].m_dwTileWidth == nX1-nX0+1 && (int)g_textures[gRSP.curTile].m_dwTileHeight == nY1-nY0+1 &&
534             g_textures[gRSP.curTile].m_dwTileWidth%2 == 0 && g_textures[gRSP.curTile].m_dwTileHeight%2 == 0 )
535         {
536             nY1++;
537             nX1++;
538         }
539         else if( g_curRomInfo.bIncTexRectEdge )
540         {
541             nX1++;
542             nY1++;
543         }
544     }
545 
546 
547     // Scale to Actual texture coords
548     // The two cases are to handle the oversized textures hack on voodoos
549 
550     SetCombinerAndBlender();
551 
552 
553     if( gRDP.otherMode.cycle_type  >= G_CYC_COPY || !gRDP.otherMode.z_cmp )
554         ZBufferEnable(false);
555 
556     bool accurate = currentRomOptions.bAccurateTextureMapping;
557 
558     RenderTexture &tex0 = g_textures[gRSP.curTile];
559     TileAdditionalInfo *tile0 = &gRDP.tilesinfo[gRSP.curTile];
560     gDPTile *tile0info = &gDP.tiles[gRSP.curTile];
561 
562     float widthDiv = tex0.m_fTexWidth;
563     float heightDiv = tex0.m_fTexHeight;
564     float t0u0;
565     if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
566         t0u0 = (fS0 -tile0->fhilite_sl);
567     else
568         t0u0 = (fS0 * tile0->fShiftScaleS -tile0->fhilite_sl);
569 
570     float t0u1;
571     if( accurate && gRDP.otherMode.cycle_type >= G_CYC_COPY )
572         t0u1 = t0u0 + (fScaleS * (nX1 - nX0 - 1))*tile0->fShiftScaleS;
573     else
574         t0u1 = t0u0 + (fScaleS * (nX1 - nX0))*tile0->fShiftScaleS;
575 
576 
577     if( status.UseLargerTile[0] )
578     {
579         m_texRectTex1UV[0].u = (t0u0+status.LargerTileRealLeft[0])/widthDiv;
580         m_texRectTex1UV[1].u = (t0u1+status.LargerTileRealLeft[0])/widthDiv;
581     }
582     else
583     {
584         m_texRectTex1UV[0].u = t0u0/widthDiv;
585         m_texRectTex1UV[1].u = t0u1/widthDiv;
586         if( accurate && !tile0info->mirrors && RemapTextureCoordinate(t0u0, t0u1, tex0.m_dwTileWidth, tile0info->masks, widthDiv, m_texRectTex1UV[0].u, m_texRectTex1UV[1].u) )
587             SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
588     }
589 
590     float t0v0;
591     if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
592         t0v0 = (fT0 -tile0->fhilite_tl);
593     else
594         t0v0 = (fT0 * tile0->fShiftScaleT -tile0->fhilite_tl);
595 
596     float t0v1;
597     if ( accurate && gRDP.otherMode.cycle_type >= G_CYC_COPY)
598         t0v1 = t0v0 + (fScaleT * (nY1 - nY0-1))*tile0->fShiftScaleT;
599     else
600         t0v1 = t0v0 + (fScaleT * (nY1 - nY0))*tile0->fShiftScaleT;
601 
602     m_texRectTex1UV[0].v = t0v0/heightDiv;
603     m_texRectTex1UV[1].v = t0v1/heightDiv;
604     if( accurate && !tile0info->mirrort && RemapTextureCoordinate(t0v0, t0v1, tex0.m_dwTileHeight, tile0info->maskt, heightDiv, m_texRectTex1UV[0].v, m_texRectTex1UV[1].v) )
605         SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
606 
607     COLOR speColor = PostProcessSpecularColor();
608     COLOR difColor;
609     if( colorFlag )
610         difColor = PostProcessDiffuseColor(diffuseColor);
611     else
612         //difColor = PostProcessDiffuseColor(0);
613         difColor = PostProcessDiffuseColor(gRDP.primitiveColor);
614 
615     g_texRectTVtx[0].x = ViewPortTranslatei_x(nX0);
616     g_texRectTVtx[0].y = ViewPortTranslatei_y(nY0);
617     g_texRectTVtx[0].dcDiffuse = difColor;
618     g_texRectTVtx[0].dcSpecular = speColor;
619 
620     g_texRectTVtx[1].x = ViewPortTranslatei_x(nX1);
621     g_texRectTVtx[1].y = ViewPortTranslatei_y(nY0);
622     g_texRectTVtx[1].dcDiffuse = difColor;
623     g_texRectTVtx[1].dcSpecular = speColor;
624 
625     g_texRectTVtx[2].x = ViewPortTranslatei_x(nX1);
626     g_texRectTVtx[2].y = ViewPortTranslatei_y(nY1);
627     g_texRectTVtx[2].dcDiffuse = difColor;
628     g_texRectTVtx[2].dcSpecular = speColor;
629 
630     g_texRectTVtx[3].x = ViewPortTranslatei_x(nX0);
631     g_texRectTVtx[3].y = ViewPortTranslatei_y(nY1);
632     g_texRectTVtx[3].dcDiffuse = difColor;
633     g_texRectTVtx[3].dcSpecular = speColor;
634 
635     float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
636 
637     g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = depth;
638     g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = 1;
639 
640     if( IsTexel1Enable() )
641     {
642         RenderTexture &tex1 = g_textures[(gRSP.curTile+1)&7];
643         TileAdditionalInfo *tile1 = &gRDP.tilesinfo[(gRSP.curTile+1)&7];
644         gDPTile *tile1info = &gDP.tiles[(gRSP.curTile+1)&7];
645 
646         widthDiv = tex1.m_fTexWidth;
647         heightDiv = tex1.m_fTexHeight;
648 
649         float t0u0 = fS0 * tile1->fShiftScaleS -tile1->fhilite_sl;
650         float t0v0 = fT0 * tile1->fShiftScaleT -tile1->fhilite_tl;
651         float t0u1;
652         float t0v1;
653         if ( accurate && gRDP.otherMode.cycle_type >= G_CYC_COPY)
654         {
655             t0u1 = t0u0 + (fScaleS * (nX1 - nX0 - 1))*tile1->fShiftScaleS;
656             t0v1 = t0v0 + (fScaleT * (nY1 - nY0 - 1))*tile1->fShiftScaleT;
657         }
658         else
659         {
660             t0u1 = t0u0 + (fScaleS * (nX1 - nX0))*tile1->fShiftScaleS;
661             t0v1 = t0v0 + (fScaleT * (nY1 - nY0))*tile1->fShiftScaleT;
662         }
663 
664         if( status.UseLargerTile[1] )
665         {
666             m_texRectTex2UV[0].u = (t0u0+status.LargerTileRealLeft[1])/widthDiv;
667             m_texRectTex2UV[1].u = (t0u1+status.LargerTileRealLeft[1])/widthDiv;
668         }
669         else
670         {
671             m_texRectTex2UV[0].u = t0u0/widthDiv;
672             m_texRectTex2UV[1].u = t0u1/widthDiv;
673             if( accurate && !tile1info->mirrors && RemapTextureCoordinate(t0u0, t0u1, tex1.m_dwTileWidth, tile1info->masks, widthDiv, m_texRectTex2UV[0].u, m_texRectTex2UV[1].u) )
674                 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, (gRSP.curTile+1)&7);
675         }
676 
677         m_texRectTex2UV[0].v = t0v0/heightDiv;
678         m_texRectTex2UV[1].v = t0v1/heightDiv;
679 
680         if( accurate && !tile1info->mirrort && RemapTextureCoordinate(t0v0, t0v1, tex1.m_dwTileHeight, tile1info->maskt, heightDiv, m_texRectTex2UV[0].v, m_texRectTex2UV[1].v) )
681             SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, (gRSP.curTile+1)&7);
682 
683         SetVertexTextureUVCoord(g_texRectTVtx[0], m_texRectTex1UV[0].u, m_texRectTex1UV[0].v, m_texRectTex2UV[0].u, m_texRectTex2UV[0].v);
684         SetVertexTextureUVCoord(g_texRectTVtx[1], m_texRectTex1UV[1].u, m_texRectTex1UV[0].v, m_texRectTex2UV[1].u, m_texRectTex2UV[0].v);
685         SetVertexTextureUVCoord(g_texRectTVtx[2], m_texRectTex1UV[1].u, m_texRectTex1UV[1].v, m_texRectTex2UV[1].u, m_texRectTex2UV[1].v);
686         SetVertexTextureUVCoord(g_texRectTVtx[3], m_texRectTex1UV[0].u, m_texRectTex1UV[1].v, m_texRectTex2UV[0].u, m_texRectTex2UV[1].v);
687     }
688     else
689     {
690         SetVertexTextureUVCoord(g_texRectTVtx[0], m_texRectTex1UV[0].u, m_texRectTex1UV[0].v);
691         SetVertexTextureUVCoord(g_texRectTVtx[1], m_texRectTex1UV[1].u, m_texRectTex1UV[0].v);
692         SetVertexTextureUVCoord(g_texRectTVtx[2], m_texRectTex1UV[1].u, m_texRectTex1UV[1].v);
693         SetVertexTextureUVCoord(g_texRectTVtx[3], m_texRectTex1UV[0].u, m_texRectTex1UV[1].v);
694     }
695 
696 
697     bool res;
698     TurnFogOnOff(false);
699     if( TileUFlags[gRSP.curTile]==TEXTURE_UV_FLAG_CLAMP && TileVFlags[gRSP.curTile]==TEXTURE_UV_FLAG_CLAMP && options.forceTextureFilter == FORCE_DEFAULT_FILTER )
700     {
701         TextureFilter dwFilter = m_dwMagFilter;
702         m_dwMagFilter = m_dwMinFilter = FILTER_LINEAR;
703         ApplyTextureFilter();
704         ApplyRDPScissor(false);
705         res = RenderTexRect();
706         m_dwMagFilter = m_dwMinFilter = dwFilter;
707         ApplyTextureFilter();
708     }
709     else if( fScaleS >= 1 && fScaleT >= 1 && options.forceTextureFilter == FORCE_DEFAULT_FILTER )
710     {
711         TextureFilter dwFilter = m_dwMagFilter;
712         m_dwMagFilter = m_dwMinFilter = FILTER_POINT;
713         ApplyTextureFilter();
714         ApplyRDPScissor(false);
715         res = RenderTexRect();
716         m_dwMagFilter = m_dwMinFilter = dwFilter;
717         ApplyTextureFilter();
718     }
719     else
720     {
721         ApplyRDPScissor(false);
722         res = RenderTexRect();
723     }
724     TurnFogOnOff(gRSP.bFogEnabled);
725 
726     if( gRDP.otherMode.cycle_type  >= G_CYC_COPY || !gRDP.otherMode.z_cmp  )
727         ZBufferEnable(gRSP.bZBufferEnabled);
728 
729     return res;
730 }
731 
732 
TexRectFlip(int nX0,int nY0,int nX1,int nY1,float fS0,float fT0,float fS1,float fT1)733 bool CRender::TexRectFlip(int nX0, int nY0, int nX1, int nY1, float fS0, float fT0, float fS1, float fT1)
734 {
735     if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )
736     {
737         status.bFrameBufferIsDrawn = true;
738         status.bFrameBufferDrawnByTriangles = true;
739     }
740 
741     PrepareTextures();
742 
743     // Save ZBuffer state
744     m_savedZBufferFlag = gRSP.bZBufferEnabled;
745     if( gRDP.otherMode.depth_source == 0 )
746         ZBufferEnable( false );
747 
748     float widthDiv = g_textures[gRSP.curTile].m_fTexWidth;
749     float heightDiv = g_textures[gRSP.curTile].m_fTexHeight;
750 
751     //Tile &tile0 = gRDP.tiles[gRSP.curTile];
752 
753     float t0u0 = fS0 / widthDiv;
754     float t0v0 = fT0 / heightDiv;
755 
756     float t0u1 = (fS1 - fS0)/ widthDiv + t0u0;
757     float t0v1 = (fT1 - fT0)/ heightDiv + t0v0;
758 
759     float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
760 
761     if( t0u0 >= 0 && t0u1 <= 1 && t0u1 >= t0u0 ) SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
762     if( t0v0 >= 0 && t0v1 <= 1 && t0v1 >= t0v0 ) SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
763 
764     SetCombinerAndBlender();
765 
766     COLOR speColor = PostProcessSpecularColor();
767     COLOR difColor = PostProcessDiffuseColor(gRDP.primitiveColor);
768 
769     // Same as TexRect, but with texcoords 0,2 swapped
770     g_texRectTVtx[0].x = ViewPortTranslatei_x(nX0);
771     g_texRectTVtx[0].y = ViewPortTranslatei_y(nY0);
772     g_texRectTVtx[0].dcDiffuse = difColor;
773     g_texRectTVtx[0].dcSpecular = speColor;
774 
775     g_texRectTVtx[1].x = ViewPortTranslatei_x(nX1);
776     g_texRectTVtx[1].y = ViewPortTranslatei_y(nY0);
777     g_texRectTVtx[1].dcDiffuse = difColor;
778     g_texRectTVtx[1].dcSpecular = speColor;
779 
780     g_texRectTVtx[2].x = ViewPortTranslatei_x(nX1);
781     g_texRectTVtx[2].y = ViewPortTranslatei_y(nY1);
782     g_texRectTVtx[2].dcDiffuse = difColor;
783     g_texRectTVtx[2].dcSpecular = speColor;
784 
785     g_texRectTVtx[3].x = ViewPortTranslatei_x(nX0);
786     g_texRectTVtx[3].y = ViewPortTranslatei_y(nY1);
787     g_texRectTVtx[3].dcDiffuse = difColor;
788     g_texRectTVtx[3].dcSpecular = speColor;
789 
790     g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = depth;
791     g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = 1.0f;
792 
793     SetVertexTextureUVCoord(g_texRectTVtx[0], t0u0, t0v0);
794     SetVertexTextureUVCoord(g_texRectTVtx[1], t0u0, t0v1);
795     SetVertexTextureUVCoord(g_texRectTVtx[2], t0u1, t0v1);
796     SetVertexTextureUVCoord(g_texRectTVtx[3], t0u1, t0v0);
797 
798     TurnFogOnOff(false);
799     ApplyRDPScissor(false);
800     bool res = RenderTexRect();
801 
802     TurnFogOnOff(gRSP.bFogEnabled);
803 
804     // Restore state
805     ZBufferEnable( m_savedZBufferFlag );
806 
807     return res;
808 }
809 
810 
StartDrawSimple2DTexture(float x0,float y0,float x1,float y1,float u0,float v0,float u1,float v1,COLOR dif,COLOR spe,float z,float rhw)811 void CRender::StartDrawSimple2DTexture(float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, COLOR dif, COLOR spe, float z, float rhw)
812 {
813     g_texRectTVtx[0].x = ViewPortTranslatei_x(x0);  // << Error here, shouldn't divid by 4
814     g_texRectTVtx[0].y = ViewPortTranslatei_y(y0);
815     g_texRectTVtx[0].dcDiffuse = dif;
816     g_texRectTVtx[0].dcSpecular = spe;
817     g_texRectTVtx[0].tcord[0].u = u0;
818     g_texRectTVtx[0].tcord[0].v = v0;
819 
820 
821     g_texRectTVtx[1].x = ViewPortTranslatei_x(x1);
822     g_texRectTVtx[1].y = ViewPortTranslatei_y(y0);
823     g_texRectTVtx[1].dcDiffuse = dif;
824     g_texRectTVtx[1].dcSpecular = spe;
825     g_texRectTVtx[1].tcord[0].u = u1;
826     g_texRectTVtx[1].tcord[0].v = v0;
827 
828     g_texRectTVtx[2].x = ViewPortTranslatei_x(x1);
829     g_texRectTVtx[2].y = ViewPortTranslatei_y(y1);
830     g_texRectTVtx[2].dcDiffuse = dif;
831     g_texRectTVtx[2].dcSpecular = spe;
832     g_texRectTVtx[2].tcord[0].u = u1;
833     g_texRectTVtx[2].tcord[0].v = v1;
834 
835     g_texRectTVtx[3].x = ViewPortTranslatei_x(x0);
836     g_texRectTVtx[3].y = ViewPortTranslatei_y(y1);
837     g_texRectTVtx[3].dcDiffuse = dif;
838     g_texRectTVtx[3].dcSpecular = spe;
839     g_texRectTVtx[3].tcord[0].u = u0;
840     g_texRectTVtx[3].tcord[0].v = v1;
841 
842     RenderTexture &txtr = g_textures[0];
843     if( txtr.pTextureEntry && txtr.pTextureEntry->txtrBufIdx > 0 )
844     {
845         RenderTextureInfo &info = gRenderTextureInfos[txtr.pTextureEntry->txtrBufIdx-1];
846         g_texRectTVtx[0].tcord[0].u *= info.scaleX;
847         g_texRectTVtx[0].tcord[0].v *= info.scaleY;
848         g_texRectTVtx[1].tcord[0].u *= info.scaleX;
849         g_texRectTVtx[1].tcord[0].v *= info.scaleY;
850         g_texRectTVtx[2].tcord[0].u *= info.scaleX;
851         g_texRectTVtx[2].tcord[0].v *= info.scaleY;
852         g_texRectTVtx[3].tcord[0].u *= info.scaleX;
853         g_texRectTVtx[3].tcord[0].v *= info.scaleY;
854     }
855 
856     g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = z;
857     g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = rhw;
858 }
859 
StartDrawSimpleRect(int nX0,int nY0,int nX1,int nY1,uint32_t dwColor,float depth,float rhw)860 void CRender::StartDrawSimpleRect(int nX0, int nY0, int nX1, int nY1, uint32_t dwColor, float depth, float rhw)
861 {
862     m_simpleRectVtx[0].x = ViewPortTranslatei_x(nX0);
863     m_simpleRectVtx[1].x = ViewPortTranslatei_x(nX1);
864     m_simpleRectVtx[0].y = ViewPortTranslatei_y(nY0);
865     m_simpleRectVtx[1].y = ViewPortTranslatei_y(nY1);
866 }
867 
SetAddressUAllStages(uint32_t dwTile,TextureUVFlag dwFlag)868 void CRender::SetAddressUAllStages(uint32_t dwTile, TextureUVFlag dwFlag)
869 {
870 }
871 
SetAddressVAllStages(uint32_t dwTile,TextureUVFlag dwFlag)872 void CRender::SetAddressVAllStages(uint32_t dwTile, TextureUVFlag dwFlag)
873 {
874 }
875 
SetAllTexelRepeatFlag()876 void CRender::SetAllTexelRepeatFlag()
877 {
878     if( IsTextureEnabled() )
879     {
880         if( IsTexel0Enable() || gRDP.otherMode.cycle_type  == G_CYC_COPY )
881             SetTexelRepeatFlags(gRSP.curTile);
882         if( IsTexel1Enable() )
883             SetTexelRepeatFlags((gRSP.curTile+1)&7);
884     }
885 }
886 
887 
SetTexelRepeatFlags(uint32_t dwTile)888 void CRender::SetTexelRepeatFlags(uint32_t dwTile)
889 {
890     TileAdditionalInfo *tile = &gRDP.tilesinfo[dwTile];
891     gDPTile        *tileinfo = &gDP.tiles[dwTile];
892 
893     if( tile->bForceClampS )
894         SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
895     else if( tile->bForceWrapS )
896             SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
897     else if( tileinfo->masks == 0 || tileinfo->clamps )
898     {
899         if( gRDP.otherMode.cycle_type  >= G_CYC_COPY )
900             SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);  // Can not clamp in COPY/FILL mode
901         else
902             SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
903     }
904     else if (tileinfo->mirrors )
905         SetTextureUFlag(TEXTURE_UV_FLAG_MIRROR, dwTile);
906     else
907         SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
908 
909     if( tile->bForceClampT )
910         SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
911     else if( tile->bForceWrapT )
912         SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
913     else if( tileinfo->maskt == 0 || tileinfo->clampt)
914     {
915         if( gRDP.otherMode.cycle_type  >= G_CYC_COPY )
916             SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);  // Can not clamp in COPY/FILL mode
917         else
918             SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
919     }
920     else if (tileinfo->mirrort )
921         SetTextureVFlag(TEXTURE_UV_FLAG_MIRROR, dwTile);
922     else
923         SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
924 }
925 
Initialize(void)926 void CRender::Initialize(void)
927 {
928     ClearDeviceObjects();
929     InitDeviceObjects();
930 }
931 
CleanUp(void)932 void CRender::CleanUp(void)
933 {
934     m_pColorCombiner->CleanUp();
935     ClearDeviceObjects();
936 }
937 
myVec3Transform(float * vecout,float * vecin,float * m)938 void myVec3Transform(float *vecout, float *vecin, float* m)
939 {
940     float w = m[3]*vecin[0]+m[7]*vecin[1]+m[11]*vecin[2]+m[15];
941     vecout[0] = (m[0]*vecin[0]+m[4]*vecin[1]+m[8]*vecin[2]+m[12])/w;
942     vecout[1] = (m[1]*vecin[0]+m[5]*vecin[1]+m[9]*vecin[2]+m[13])/w;
943     vecout[2] = (m[2]*vecin[0]+m[6]*vecin[1]+m[10]*vecin[2]+m[14])/w;
944 }
945 
SetTextureEnableAndScale(int dwTile,bool bEnable,float fScaleX,float fScaleY)946 void CRender::SetTextureEnableAndScale(int dwTile, bool bEnable, float fScaleX, float fScaleY)
947 {
948     gRSP.bTextureEnabled = bEnable;
949 
950     if( bEnable )
951     {
952         if( gRSP.curTile != (unsigned int)dwTile )
953             gRDP.textureIsChanged = true;
954 
955         gRSP.curTile    = dwTile;
956         gRSP.fTexScaleX = fScaleX;
957         gRSP.fTexScaleY = fScaleY;
958 
959         if( fScaleX == 0 || fScaleY == 0 )
960         {
961             gRSP.fTexScaleX = 1/32.0f;
962             gRSP.fTexScaleY = 1/32.0f;
963         }
964     }
965 }
966 
SetViewport(int nLeft,int nTop,int nRight,int nBottom,int maxZ)967 void CRender::SetViewport(int nLeft, int nTop, int nRight, int nBottom, int maxZ)
968 {
969     if( status.bHandleN64RenderTexture )
970         return;
971 
972     static float MultX=0, MultY=0;
973 
974     if( gRSP.nVPLeftN == nLeft && gRSP.nVPTopN == nTop &&
975         gRSP.nVPRightN == nRight && gRSP.nVPBottomN == nBottom &&
976         MultX==windowSetting.fMultX && MultY==windowSetting.fMultY)
977     {
978         // no changes
979         return;
980     }
981 
982     MultX=windowSetting.fMultX;
983     MultY=windowSetting.fMultY;
984 
985     gRSP.maxZ = maxZ;
986     gRSP.nVPLeftN = nLeft;
987     gRSP.nVPTopN = nTop;
988     gRSP.nVPRightN = nRight;
989     gRSP.nVPBottomN = nBottom;
990     gRSP.nVPWidthN = nRight - nLeft + 1;
991     gRSP.nVPHeightN = nBottom - nTop + 1;
992 
993     UpdateClipRectangle();
994     SetViewportRender();
995 }
996 
997 extern bool bHalfTxtScale;
998 
DrawTriangles()999 bool CRender::DrawTriangles()
1000 {
1001     if( !status.bCIBufferIsRendered )
1002         g_pFrameBufferManager->ActiveTextureBuffer();
1003 
1004     if( status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE )
1005     {
1006         status.bVIOriginIsUpdated=false;
1007         CGraphicsContext::Get()->UpdateFrame(false);
1008     }
1009 
1010     // Hack for Pilotwings 64 (U) [!].v64
1011     static bool skipNext=false;
1012     if( options.enableHackForGames == HACK_FOR_PILOT_WINGS )
1013     {
1014         if( IsUsedAsDI(g_CI.dwAddr) && gRDP.otherMode.z_cmp+gRDP.otherMode.z_upd > 0 )
1015         {
1016             TRACE0("Warning: using Flushtris to write Z-Buffer" );
1017             gRSP.numVertices = 0;
1018             gRSP.maxVertexID = 0;
1019             skipNext = true;
1020             return true;
1021         }
1022         else if( skipNext )
1023         {
1024             skipNext = false;
1025             gRSP.numVertices = 0;
1026             gRSP.maxVertexID = 0;
1027             return true;
1028         }
1029     }
1030 
1031     if( status.bN64IsDrawingTextureBuffer && frameBufferOptions.bIgnore )
1032     {
1033         gRSP.numVertices = 0;
1034         gRSP.maxVertexID = 0;
1035         return true;
1036     }
1037 
1038     extern bool bConkerHideShadow;
1039     if( options.enableHackForGames == HACK_FOR_CONKER && bConkerHideShadow )
1040     {
1041         gRSP.numVertices = 0;
1042         gRSP.maxVertexID = 0;
1043         return true;
1044     }
1045 
1046     if( IsUsedAsDI(g_CI.dwAddr) && !status.bHandleN64RenderTexture )
1047     {
1048         status.bFrameBufferIsDrawn = true;
1049     }
1050 
1051     /*
1052     if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == G_IM_SIZ_8b )
1053     {
1054         gRSP.numVertices = 0;
1055         gRSP.maxVertexID = 0;
1056         return true;
1057     }
1058     */
1059 
1060     if (gRSP.numVertices == 0)
1061         return true;
1062 
1063     if( status.bHandleN64RenderTexture )
1064     {
1065         g_pRenderTextureInfo->maxUsedHeight = g_pRenderTextureInfo->N64Height;
1066         if( !status.bDirectWriteIntoRDRAM )
1067         {
1068             status.bFrameBufferIsDrawn = true;
1069             status.bFrameBufferDrawnByTriangles = true;
1070         }
1071     }
1072 
1073     if( !gRDP.bFogEnableInBlender && gRSP.bFogEnabled )
1074     {
1075         TurnFogOnOff(false);
1076     }
1077 
1078     for( int t=0; t<2; t++ )
1079     {
1080         float halfscaleS = 1;
1081 
1082         // This will get rid of the thin black lines
1083         if( t==0 && !(m_pColorCombiner->m_bTex0Enabled) )
1084         {
1085             continue;
1086         }
1087         else
1088         {
1089            if( (    gDP.tiles[gRSP.curTile].size == G_IM_SIZ_32b &&
1090                     options.enableHackForGames == HACK_FOR_RUMBLE ) ||
1091                  (bHalfTxtScale && g_curRomInfo.bTextureScaleHack ) ||
1092                  (
1093                   options.enableHackForGames == HACK_FOR_POLARISSNOCROSS &&
1094                   gDP.tiles[7].format == G_IM_FMT_CI &&
1095                   gDP.tiles[7].size == G_IM_SIZ_8b   &&
1096                   gDP.tiles[0].format == G_IM_FMT_CI &&
1097                   gDP.tiles[0].size == G_IM_SIZ_8b   &&
1098                   gRSP.curTile == 0 )
1099              )
1100             {
1101                 halfscaleS = 0.5;
1102             }
1103         }
1104 
1105         if( t==1 && !(m_pColorCombiner->m_bTex1Enabled) )
1106             break;
1107 
1108         if( halfscaleS < 1 )
1109         {
1110             for( uint32_t i=0; i<gRSP.numVertices; i++ )
1111             {
1112                 if( t == 0 )
1113                 {
1114                     g_vtxBuffer[i].tcord[t].u += gRSP.tex0OffsetX;
1115                     g_vtxBuffer[i].tcord[t].u /= 2;
1116                     g_vtxBuffer[i].tcord[t].u -= gRSP.tex0OffsetX;
1117                     g_vtxBuffer[i].tcord[t].v += gRSP.tex0OffsetY;
1118                     g_vtxBuffer[i].tcord[t].v /= 2;
1119                     g_vtxBuffer[i].tcord[t].v -= gRSP.tex0OffsetY;
1120                 }
1121                 else
1122                 {
1123                     g_vtxBuffer[i].tcord[t].u += gRSP.tex1OffsetX;
1124                     g_vtxBuffer[i].tcord[t].u /= 2;
1125                     g_vtxBuffer[i].tcord[t].u -= gRSP.tex1OffsetX;
1126                     g_vtxBuffer[i].tcord[t].v += gRSP.tex1OffsetY;
1127                     g_vtxBuffer[i].tcord[t].v /= 2;
1128                     g_vtxBuffer[i].tcord[t].v -= gRSP.tex1OffsetY;
1129                 }
1130             }
1131         }
1132 
1133         /*
1134         // The code here is disabled because it could cause incorrect texture repeating flag
1135         // for later DrawTriangles
1136         bool clampS=true;
1137         bool clampT=true;
1138 
1139         for( uint32_t i=0; i<gRSP.numVertices; i++ )
1140         {
1141             float w = g_vtxProjected5[i][3];
1142             if( w < 0 || g_vtxBuffer[i].tcord[t].u > 1.0 || g_vtxBuffer[i].tcord[t].u < 0.0  )
1143             {
1144                 clampS = false;
1145                 break;
1146             }
1147         }
1148 
1149         for( uint32_t i=0; i<gRSP.numVertices; i++ )
1150         {
1151             float w = g_vtxProjected5[i][3];
1152             if( w < 0 || g_vtxBuffer[i].tcord[t].v > 1.0 || g_vtxBuffer[i].tcord[t].v < 0.0  )
1153             {
1154                 clampT = false;
1155                 break;
1156             }
1157         }
1158 
1159         if( clampS )
1160         {
1161             SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile+t);
1162         }
1163         if( clampT )
1164         {
1165             SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile+t);
1166         }
1167         */
1168     }
1169 
1170     if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == G_IM_SIZ_8b )
1171     {
1172         ZBufferEnable(false);
1173     }
1174 
1175     ApplyScissorWithClipRatio(false);
1176 
1177     if( g_curRomInfo.bZHack )
1178     {
1179         extern void HackZAll();
1180         HackZAll();
1181     }
1182 
1183     bool res = RenderFlushTris();
1184     g_clippedVtxCount = 0;
1185 
1186     gRSP.numVertices = 0;   // Reset index
1187     gRSP.maxVertexID = 0;
1188 
1189     if( !gRDP.bFogEnableInBlender && gRSP.bFogEnabled )
1190         TurnFogOnOff(true);
1191 
1192     return res;
1193 }
1194 
ReverseCITableLookup(uint32_t * pTable,int size,uint32_t val)1195 inline int ReverseCITableLookup(uint32_t *pTable, int size, uint32_t val)
1196 {
1197     for( int i=0; i<size; i++)
1198     {
1199         if( pTable[i] == val )
1200             return i;
1201     }
1202 
1203     TRACE0("Cannot find value in CI table");
1204     return 0;
1205 }
1206 
1207 extern RenderTextureInfo gRenderTextureInfos[];
SetVertexTextureUVCoord(TexCord & dst,const TexCord & src,int tile,TxtrCacheEntry * pEntry)1208 void SetVertexTextureUVCoord(TexCord &dst, const TexCord &src, int tile, TxtrCacheEntry *pEntry)
1209 {
1210     RenderTexture &txtr = g_textures[tile];
1211     RenderTextureInfo &info = gRenderTextureInfos[pEntry->txtrBufIdx-1];
1212     float s = src.u;
1213     float t = src.v;
1214 
1215     uint32_t addrOffset = g_TI.dwAddr-info.CI_Info.dwAddr;
1216     uint32_t extraTop = (addrOffset>>(info.CI_Info.dwSize-1)) /info.CI_Info.dwWidth;
1217     uint32_t extraLeft = (addrOffset>>(info.CI_Info.dwSize-1))%info.CI_Info.dwWidth;
1218 
1219     if( pEntry->txtrBufIdx > 0  )
1220     {
1221         // Loading from render_texture or back buffer
1222         s += (extraLeft+pEntry->ti.LeftToLoad)/txtr.m_fTexWidth;
1223         t += (extraTop+pEntry->ti.TopToLoad)/txtr.m_fTexHeight;
1224 
1225         s *= info.scaleX;
1226         t *= info.scaleY;
1227     }
1228 
1229     dst.u = s;
1230     dst.v = t;
1231 }
1232 
SetVertexTextureUVCoord(TLITVERTEX & v,const TexCord & fTex0)1233 void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, const TexCord &fTex0)
1234 {
1235     RenderTexture &txtr = g_textures[0];
1236     if( txtr.pTextureEntry && txtr.pTextureEntry->txtrBufIdx > 0 )
1237     {
1238         ::SetVertexTextureUVCoord(v.tcord[0], fTex0, 0, txtr.pTextureEntry);
1239     }
1240     else
1241     {
1242         v.tcord[0] = fTex0;
1243     }
1244 }
1245 
SetVertexTextureUVCoord(TLITVERTEX & v,float fTex0S,float fTex0T)1246 void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, float fTex0S, float fTex0T)
1247 {
1248     TexCord t = { fTex0S, fTex0T };
1249     SetVertexTextureUVCoord(v, t);
1250 }
1251 
SetVertexTextureUVCoord(TLITVERTEX & v,const TexCord & fTex0_,const TexCord & fTex1_)1252 void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, const TexCord &fTex0_, const TexCord &fTex1_)
1253 {
1254     TexCord fTex0 = fTex0_;
1255     TexCord fTex1 = fTex1_;
1256 
1257     if( (options.enableHackForGames == HACK_FOR_ZELDA||options.enableHackForGames == HACK_FOR_ZELDA_MM) && m_Mux == 0x00262a60150c937fLL && gRSP.curTile == 0 )
1258     {
1259         // Hack for Zelda Sun
1260         gDPTile *t0 = &gDP.tiles[0];
1261         gDPTile *t1 = &gDP.tiles[1];
1262         if( t0->format == G_IM_FMT_I && t0->size == G_IM_SIZ_8b && t0->width == 64 &&
1263             t1->format == G_IM_FMT_I && t1->size == G_IM_SIZ_8b && t1->width == 64 &&
1264             t0->height == t1->height )
1265         {
1266             fTex0.u /= 2;
1267             fTex0.v /= 2;
1268             fTex1.u /= 2;
1269             fTex1.v /= 2;
1270         }
1271     }
1272 
1273     RenderTexture &txtr0 = g_textures[0];
1274     if( txtr0.pTextureEntry && txtr0.pTextureEntry->txtrBufIdx > 0 )
1275         ::SetVertexTextureUVCoord(v.tcord[0], fTex0, 0, txtr0.pTextureEntry);
1276     else
1277         v.tcord[0] = fTex0;
1278 
1279     RenderTexture &txtr1 = g_textures[1];
1280     if( txtr1.pTextureEntry && txtr1.pTextureEntry->txtrBufIdx > 0 )
1281         ::SetVertexTextureUVCoord(v.tcord[1], fTex1, 1, txtr1.pTextureEntry);
1282     else
1283         v.tcord[1] = fTex1;
1284 }
1285 
SetVertexTextureUVCoord(TLITVERTEX & v,float fTex0S,float fTex0T,float fTex1S,float fTex1T)1286 void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, float fTex0S, float fTex0T, float fTex1S, float fTex1T)
1287 {
1288     TexCord t0 = { fTex0S, fTex0T };
1289     TexCord t1 = { fTex1S, fTex1T };
1290     SetVertexTextureUVCoord(v, t0, t1);
1291 }
1292 
SetClipRatio(uint32_t type,uint32_t w1)1293 void CRender::SetClipRatio(uint32_t type, uint32_t w1)
1294 {
1295    bool modified = false;
1296    switch(type)
1297    {
1298       case G_MWO_CLIP_RNX:
1299          if( gRSP.clip_ratio_negx != (short)w1 )
1300          {
1301             gRSP.clip_ratio_negx = (short)w1;
1302             modified = true;
1303          }
1304          break;
1305       case G_MWO_CLIP_RNY:
1306          if( gRSP.clip_ratio_negy != (short)w1 )
1307          {
1308             gRSP.clip_ratio_negy = (short)w1;
1309             modified = true;
1310          }
1311          break;
1312       case G_MWO_CLIP_RPX:
1313          if( gRSP.clip_ratio_posx != -(short)w1 )
1314          {
1315             gRSP.clip_ratio_posx = -(short)w1;
1316             modified = true;
1317          }
1318          break;
1319       case G_MWO_CLIP_RPY:
1320          if( gRSP.clip_ratio_posy != -(short)w1 )
1321          {
1322             gRSP.clip_ratio_posy = -(short)w1;
1323             modified = true;
1324          }
1325          break;
1326    }
1327 
1328    if( modified )
1329       UpdateClipRectangle();
1330 }
1331 
UpdateClipRectangle()1332 void CRender::UpdateClipRectangle()
1333 {
1334     if( status.bHandleN64RenderTexture )
1335     {
1336         //windowSetting.fMultX = windowSetting.fMultY = 1;
1337         windowSetting.vpLeftW = 0;
1338         windowSetting.vpTopW = 0;
1339         windowSetting.vpRightW = newRenderTextureInfo.bufferWidth;
1340         windowSetting.vpBottomW = newRenderTextureInfo.bufferHeight;
1341         windowSetting.vpWidthW = newRenderTextureInfo.bufferWidth;
1342         windowSetting.vpHeightW = newRenderTextureInfo.bufferHeight;
1343 
1344         gRSP.vtxXMul = windowSetting.vpWidthW/2.0f;
1345         gRSP.vtxXAdd = gRSP.vtxXMul + windowSetting.vpLeftW;
1346         gRSP.vtxYMul = -windowSetting.vpHeightW/2.0f;
1347         gRSP.vtxYAdd = windowSetting.vpHeightW/2.0f + windowSetting.vpTopW;
1348 
1349         // Update clip rectangle by setting scissor
1350 
1351         int halfx = newRenderTextureInfo.bufferWidth/2;
1352         int halfy = newRenderTextureInfo.bufferHeight/2;
1353         int centerx = halfx;
1354         int centery = halfy;
1355 
1356         gRSP.clip_ratio_left = centerx - halfx * gRSP.clip_ratio_negx;
1357         gRSP.clip_ratio_top = centery - halfy * gRSP.clip_ratio_negy;
1358         gRSP.clip_ratio_right = centerx + halfx * gRSP.clip_ratio_posx;
1359         gRSP.clip_ratio_bottom = centery + halfy * gRSP.clip_ratio_posy;
1360     }
1361     else
1362     {
1363         windowSetting.vpLeftW = int(gRSP.nVPLeftN * windowSetting.fMultX);
1364         windowSetting.vpTopW = int(gRSP.nVPTopN  * windowSetting.fMultY);
1365         windowSetting.vpRightW = int(gRSP.nVPRightN* windowSetting.fMultX);
1366         windowSetting.vpBottomW = int(gRSP.nVPBottomN* windowSetting.fMultY);
1367         windowSetting.vpWidthW = int((gRSP.nVPRightN - gRSP.nVPLeftN + 1) * windowSetting.fMultX);
1368         windowSetting.vpHeightW = int((gRSP.nVPBottomN - gRSP.nVPTopN + 1) * windowSetting.fMultY);
1369 
1370         gRSP.vtxXMul = windowSetting.vpWidthW/2.0f;
1371         gRSP.vtxXAdd = gRSP.vtxXMul + windowSetting.vpLeftW;
1372         gRSP.vtxYMul = -windowSetting.vpHeightW/2.0f;
1373         gRSP.vtxYAdd = windowSetting.vpHeightW/2.0f + windowSetting.vpTopW;
1374 
1375         // Update clip rectangle by setting scissor
1376 
1377         int halfx = gRSP.nVPWidthN/2;
1378         int halfy = gRSP.nVPHeightN/2;
1379         int centerx = gRSP.nVPLeftN+halfx;
1380         int centery = gRSP.nVPTopN+halfy;
1381 
1382         gRSP.clip_ratio_left = centerx - halfx * gRSP.clip_ratio_negx;
1383         gRSP.clip_ratio_top = centery - halfy * gRSP.clip_ratio_negy;
1384         gRSP.clip_ratio_right = centerx + halfx * gRSP.clip_ratio_posx;
1385         gRSP.clip_ratio_bottom = centery + halfy * gRSP.clip_ratio_posy;
1386     }
1387 
1388     UpdateScissorWithClipRatio();
1389 }
1390 
UpdateScissorWithClipRatio()1391 void CRender::UpdateScissorWithClipRatio()
1392 {
1393     gRSP.real_clip_scissor_left   = MAX(gRDP.scissor.left, gRSP.clip_ratio_left);
1394     gRSP.real_clip_scissor_top    = MAX(gRDP.scissor.top, gRSP.clip_ratio_top);
1395     gRSP.real_clip_scissor_right  = MIN(gRDP.scissor.right,gRSP.clip_ratio_right);
1396     gRSP.real_clip_scissor_bottom = MIN(gRDP.scissor.bottom, gRSP.clip_ratio_bottom);
1397 
1398     gRSP.real_clip_scissor_left   = MAX(gRSP.real_clip_scissor_left, 0);
1399     gRSP.real_clip_scissor_top    = MAX(gRSP.real_clip_scissor_top, 0);
1400     gRSP.real_clip_scissor_right  = MIN(gRSP.real_clip_scissor_right,windowSetting.uViWidth-1);
1401     gRSP.real_clip_scissor_bottom = MIN(gRSP.real_clip_scissor_bottom, windowSetting.uViHeight-1);
1402 
1403     WindowSettingStruct &w = windowSetting;
1404     w.clipping.left = (uint32_t)(gRSP.real_clip_scissor_left*windowSetting.fMultX);
1405     w.clipping.top  = (uint32_t)(gRSP.real_clip_scissor_top*windowSetting.fMultY);
1406     w.clipping.bottom = (uint32_t)(gRSP.real_clip_scissor_bottom*windowSetting.fMultY);
1407     w.clipping.right = (uint32_t)(gRSP.real_clip_scissor_right*windowSetting.fMultX);
1408 
1409     if( w.clipping.left > 0 || w.clipping.top > 0 || w.clipping.right < (uint32_t)windowSetting.uDisplayWidth-1 ||
1410         w.clipping.bottom < (uint32_t)windowSetting.uDisplayHeight-1 )
1411         w.clipping.needToClip = true;
1412     else
1413         w.clipping.needToClip = false;
1414 
1415     w.clipping.width = (uint32_t)((gRSP.real_clip_scissor_right-gRSP.real_clip_scissor_left+1)*windowSetting.fMultX);
1416     w.clipping.height = (uint32_t)((gRSP.real_clip_scissor_bottom-gRSP.real_clip_scissor_top+1)*windowSetting.fMultY);
1417 
1418     float halfx = gRSP.nVPWidthN/2.0f;
1419     float halfy = gRSP.nVPHeightN/2.0f;
1420     float centerx = gRSP.nVPLeftN+halfx;
1421     float centery = gRSP.nVPTopN+halfy;
1422 
1423     gRSP.real_clip_ratio_negx = (gRSP.real_clip_scissor_left - centerx)/halfx;
1424     gRSP.real_clip_ratio_negy = (gRSP.real_clip_scissor_top - centery)/halfy;
1425     gRSP.real_clip_ratio_posx = (gRSP.real_clip_scissor_right - centerx)/halfx;
1426     gRSP.real_clip_ratio_posy = (gRSP.real_clip_scissor_bottom - centery)/halfy;
1427 
1428     ApplyScissorWithClipRatio(true);
1429 }
1430 
1431 
1432 // Set other modes not covered by color combiner or alpha blender
InitOtherModes(void)1433 void CRender::InitOtherModes(void)
1434 {
1435     ApplyTextureFilter();
1436 
1437     //
1438     // I can't think why the hand in Mario's menu screen is rendered with an opaque rendermode,
1439     // and no alpha threshold. We set the alpha reference to 1 to ensure that the transparent pixels
1440     // don't get rendered. I hope this doesn't fuck anything else up.
1441     //
1442     if ( gRDP.otherMode.alpha_compare == 0 )
1443     {
1444         if ( gRDP.otherMode.cvg_x_alpha && (gRDP.otherMode.alpha_cvg_sel || gRDP.otherMode.aa_en ) )
1445         {
1446             ForceAlphaRef(128); // Strange, I have to use value=2 for pixel shader combiner for Nvidia FX5200
1447                                 // for other video cards, value=1 is good enough.
1448             SetAlphaTestEnable(true);
1449         }
1450         else
1451             SetAlphaTestEnable(false);
1452     }
1453     else if ( gRDP.otherMode.alpha_compare == 3 )
1454     {
1455         //RDP_ALPHA_COMPARE_DITHER
1456         SetAlphaTestEnable(false);
1457     }
1458     else
1459     {
1460        // Use CVG for pixel alpha
1461         if( (gRDP.otherMode.alpha_cvg_sel ) && !gRDP.otherMode.cvg_x_alpha )
1462             SetAlphaTestEnable(false);
1463         else
1464         {
1465             // RDP_ALPHA_COMPARE_THRESHOLD || RDP_ALPHA_COMPARE_DITHER
1466             if( m_dwAlpha==0 )
1467                 ForceAlphaRef(1);
1468             else
1469                 ForceAlphaRef(m_dwAlpha);
1470             SetAlphaTestEnable(true);
1471         }
1472     }
1473 
1474     if( options.enableHackForGames == HACK_FOR_SOUTH_PARK_RALLY && m_Mux == 0x00121824ff33ffffLL &&
1475         gRSP.bCullFront && gRDP.otherMode.aa_en && gRDP.otherMode.z_cmp && gRDP.otherMode.z_upd )
1476     {
1477         SetZCompare(false);
1478     }
1479 
1480 
1481     if( gRDP.otherMode.cycle_type  >= G_CYC_COPY )
1482     {
1483         // Disable zbuffer for COPY and FILL mode
1484         SetZCompare(false);
1485     }
1486     else
1487     {
1488         SetZCompare(gRDP.otherMode.z_cmp);
1489         SetZUpdate(gRDP.otherMode.z_upd);
1490     }
1491 
1492     /*
1493     if( options.enableHackForGames == HACK_FOR_SOUTH_PARK_RALLY && m_Mux == 0x00121824ff33ffff &&
1494         gRSP.bCullFront && gRDP.otherMode.z_cmp && gRDP.otherMode.z_upd )//&& gRDP.otherMode.aa_en )
1495     {
1496         SetZCompare(false);
1497         SetZUpdate(false);
1498     }
1499     */
1500 }
1501 
1502 
SetTextureFilter(uint32_t dwFilter)1503 void CRender::SetTextureFilter(uint32_t dwFilter)
1504 {
1505     if( options.forceTextureFilter == FORCE_DEFAULT_FILTER )
1506     {
1507         switch(dwFilter)
1508         {
1509             case RDP_TFILTER_AVERAGE:   //?
1510             case RDP_TFILTER_BILERP:
1511                 m_dwMinFilter = m_dwMagFilter = FILTER_LINEAR;
1512                 break;
1513             default:
1514                 m_dwMinFilter = m_dwMagFilter = FILTER_POINT;
1515                 break;
1516         }
1517     }
1518     else
1519     {
1520         switch( options.forceTextureFilter )
1521         {
1522         case FORCE_POINT_FILTER:
1523             m_dwMinFilter = m_dwMagFilter = FILTER_POINT;
1524             break;
1525         case FORCE_LINEAR_FILTER:
1526             m_dwMinFilter = m_dwMagFilter = FILTER_LINEAR;
1527             break;
1528         }
1529     }
1530 
1531     ApplyTextureFilter();
1532 }
1533