GLES2RICE: Update from upstream
[mupen64plus-pandora.git] / source / rice_gles / src / Render.cpp
CommitLineData
d07c171f 1/*
2Copyright (C) 2003 Rice1964
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18*/
19
20#define M64P_PLUGIN_PROTOTYPES 1
21#include "osal_preproc.h"
22#include "m64p_plugin.h"
23
24#include "ConvertImage.h"
25#include "DeviceBuilder.h"
26#include "FrameBuffer.h"
27#include "Render.h"
28
29#include "liblinux/BMGLibPNG.h"
30
31#include <algorithm>
32
33extern FiddledVtx * g_pVtxBase;
34CRender * CRender::g_pRender=NULL;
35int CRender::gRenderReferenceCount=0;
36
37XMATRIX reverseXY(-1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1);
38XMATRIX reverseY(1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1);
39extern char* right (const char * src, int nchars);
40
41#if defined(WIN32)
42 #define strcasecmp _stricmp
43#endif
44
45#ifdef min
46#undef min
47#endif
48#ifdef max
49#undef max
50#endif
51
52//========================================================================
53CRender * CRender::GetRender(void)
54{
55 if( CRender::g_pRender == NULL )
56 {
57 DebugMessage(M64MSG_ERROR, "g_pRender is NULL");
58 exit(0);
59 }
60 else
61 return CRender::g_pRender;
62}
63bool CRender::IsAvailable()
64{
65 return CRender::g_pRender != NULL;
66}
67
68CRender::CRender() :
69 m_fScreenViewportMultX(2.0f),
70 m_fScreenViewportMultY(2.0f),
71
72 m_dwTexturePerspective(FALSE),
73 m_bAlphaTestEnable(FALSE),
74 m_bZUpdate(FALSE),
75 m_bZCompare(FALSE),
76 m_dwZBias(0),
77
78 m_dwMinFilter(FILTER_POINT),
79 m_dwMagFilter(FILTER_POINT),
80 m_dwAlpha(0xFF),
81 m_Mux(0),
82 m_bBlendModeValid(FALSE)
83{
84 int i;
85 InitRenderBase();
86
87 for( i=0; i<MAX_TEXTURES; i++ )
88 {
89 g_textures[i].m_lpsTexturePtr = NULL;
90 g_textures[i].m_pCTexture = NULL;
91
92 g_textures[i].m_dwTileWidth = 64; // Value doesn't really matter, as tex not set
93 g_textures[i].m_dwTileHeight = 64;
94 g_textures[i].m_fTexWidth = 64.0f; // Value doesn't really matter, as tex not set
95 g_textures[i].m_fTexHeight = 64.0f;
96 g_textures[i].pTextureEntry = NULL;
97
98 TileUFlags[i] = TileVFlags[i] = TEXTURE_UV_FLAG_CLAMP;
99 }
100
101
102 //for( i=0; i<MAX_VERTS; i++)
103 //{
104 // g_dwVtxFlags[i] = 0;
105 //}
106
107 m_pColorCombiner = CDeviceBuilder::GetBuilder()->CreateColorCombiner(this);
108 m_pColorCombiner->Initialize();
109
110 m_pAlphaBlender = CDeviceBuilder::GetBuilder()->CreateAlphaBlender(this);
111
112}
113
114CRender::~CRender()
115{
116 if( m_pColorCombiner != NULL )
117 {
118 CDeviceBuilder::GetBuilder()->DeleteColorCombiner();
119 m_pColorCombiner = NULL;
120 }
121
122 if( m_pAlphaBlender != NULL )
123 {
124 CDeviceBuilder::GetBuilder()->DeleteAlphaBlender();
125 m_pAlphaBlender = NULL;
126 }
127}
128
129void CRender::ResetMatrices()
130{
131 Matrix mat;
132
133 mat.m[0][1] = mat.m[0][2] = mat.m[0][3] =
134 mat.m[1][0] = mat.m[1][2] = mat.m[1][3] =
135 mat.m[2][0] = mat.m[2][1] = mat.m[2][3] =
136 mat.m[3][0] = mat.m[3][1] = mat.m[3][2] = 0.0f;
137
138 mat.m[0][0] = mat.m[1][1] = mat.m[2][2] = mat.m[3][3] = 1.0f;
139
140 gRSP.projectionMtxTop = 0;
141 gRSP.modelViewMtxTop = 0;
142 gRSP.projectionMtxs[0] = mat;
143 gRSP.modelviewMtxs[0] = mat;
144
145 gRSP.bMatrixIsUpdated = true;
146 gRSP.bWorldMatrixIsUpdated = true;
147 UpdateCombinedMatrix();
148}
149
150void CRender::SetProjection(const Matrix & mat, bool bPush, bool bReplace)
151{
152 if (bPush)
153 {
154 if (gRSP.projectionMtxTop >= (RICE_MATRIX_STACK-1))
155 {
156 TRACE0("Pushing past proj stack limits!");
157 }
158 else
159 gRSP.projectionMtxTop++;
160
161 if (bReplace)
162 {
163 // Load projection matrix
164 gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat;
165 }
166 else
167 {
168 gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat * gRSP.projectionMtxs[gRSP.projectionMtxTop-1];
169 }
170
171 }
172 else
173 {
174 if (bReplace)
175 {
176 // Load projection matrix
177 gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat;
178 }
179 else
180 {
181 gRSP.projectionMtxs[gRSP.projectionMtxTop] = mat * gRSP.projectionMtxs[gRSP.projectionMtxTop];
182 }
183 }
184
185 gRSP.bMatrixIsUpdated = true;
186
187 DumpMatrix(mat,"Set Projection Matrix");
188}
189
190bool mtxPopUpError = false;
191void CRender::SetWorldView(const Matrix & mat, bool bPush, bool bReplace)
192{
193 if (bPush)
194 {
195 if (gRSP.modelViewMtxTop >= (RICE_MATRIX_STACK-1))
196 DebuggerAppendMsg("Pushing past modelview stack limits! %s", bReplace?"Load":"Mul");
197 else
198 gRSP.modelViewMtxTop++;
199
200 // We should store the current projection matrix...
201 if (bReplace)
202 {
203 // Load projection matrix
204 gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat;
205 }
206 else // Multiply projection matrix
207 {
208 gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat * gRSP.modelviewMtxs[gRSP.modelViewMtxTop-1];
209 }
210 }
211 else // NoPush
212 {
213 if (bReplace)
214 {
215 // Load projection matrix
216 gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat;
217 }
218 else
219 {
220 // Multiply projection matrix
221 gRSP.modelviewMtxs[gRSP.modelViewMtxTop] = mat * gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
222 }
223 }
224
225 gRSPmodelViewTop = gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
226 if( options.enableHackForGames == HACK_REVERSE_XY_COOR )
227 {
228 gRSPmodelViewTop = gRSPmodelViewTop * reverseXY;
229 }
230 if( options.enableHackForGames == HACK_REVERSE_Y_COOR )
231 {
232 gRSPmodelViewTop = gRSPmodelViewTop * reverseY;
233 }
234 MatrixTranspose(&gRSPmodelViewTopTranspose, &gRSPmodelViewTop);
235
236 gRSP.bMatrixIsUpdated = true;
237 gRSP.bWorldMatrixIsUpdated = true;
238
239 DumpMatrix(mat,"Set WorldView Matrix");
240}
241
242
243void CRender::PopWorldView()
244{
245 if (gRSP.modelViewMtxTop > 0)
246 {
247 gRSP.modelViewMtxTop--;
248 gRSPmodelViewTop = gRSP.modelviewMtxs[gRSP.modelViewMtxTop];
249 if( options.enableHackForGames == HACK_REVERSE_XY_COOR )
250 {
251 gRSPmodelViewTop = gRSPmodelViewTop * reverseXY;
252 }
253 if( options.enableHackForGames == HACK_REVERSE_Y_COOR )
254 {
255 gRSPmodelViewTop = gRSPmodelViewTop * reverseY;
256 }
257 MatrixTranspose(&gRSPmodelViewTopTranspose, &gRSPmodelViewTop);
258 gRSP.bMatrixIsUpdated = true;
259 gRSP.bWorldMatrixIsUpdated = true;
260 }
261 else
262 {
263#ifdef DEBUGGER
264 if( pauseAtNext )
265 TRACE0("Popping past worldview stack limits");
266#endif
267 mtxPopUpError = true;
268 }
269}
270
271
272Matrix & CRender::GetWorldProjectMatrix(void)
273{
274 return gRSPworldProject;
275}
276
277void CRender::SetWorldProjectMatrix(Matrix &mtx)
278{
279#ifdef DEBUGGER
280 if( pauseAtNext && (eventToPause == NEXT_TRIANGLE || eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_MATRIX_CMD ) )
281 {
282 uint32 dwPC = gDlistStack[gDlistStackPointer].pc-8;
283 DebuggerAppendMsg("Force Matrix: pc=%08X", dwPC);
284 DumpMatrix(mtx, "Force Matrix, loading new world-project matrix");
285 }
286#endif
287 gRSPworldProject = mtx;
288
289 gRSP.bMatrixIsUpdated = false;
290 gRSP.bCombinedMatrixIsUpdated = true;
291}
292
293void CRender::SetMux(uint32 dwMux0, uint32 dwMux1)
294{
295 uint64 tempmux = (((uint64)dwMux0) << 32) | (uint64)dwMux1;
296 if( m_Mux != tempmux )
297 {
298 m_Mux = tempmux;
299 m_bBlendModeValid = FALSE;
300 m_pColorCombiner->UpdateCombiner(dwMux0, dwMux1);
301 }
302}
303
304
305void CRender::SetCombinerAndBlender()
306{
307 InitOtherModes();
308
309 if( g_curRomInfo.bDisableBlender )
310 m_pAlphaBlender->DisableAlphaBlender();
311 else if( currentRomOptions.bNormalBlender )
312 m_pAlphaBlender->NormalAlphaBlender();
313 else
314 m_pAlphaBlender->InitBlenderMode();
315
316 m_pColorCombiner->InitCombinerMode();
317}
318
319void CRender::RenderReset()
320{
321 UpdateClipRectangle();
322 ResetMatrices();
323 SetZBias(0);
324 gRSP.numVertices = 0;
325 gRSP.maxVertexID = 0;
326 gRSP.curTile = 0;
327 gRSP.fTexScaleX = 1/32.0f;;
328 gRSP.fTexScaleY = 1/32.0f;
329}
330
331bool CRender::FillRect(int nX0, int nY0, int nX1, int nY1, uint32 dwColor)
332{
333 LOG_UCODE("FillRect: X0=%d, Y0=%d, X1=%d, Y1=%d, Color=0x%8X", nX0, nY0, nX1, nY1, dwColor);
334
335 if (g_CI.dwSize != TXT_SIZE_16b && frameBufferOptions.bIgnore)
336 return true;
337
338 if (status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM)
339 status.bFrameBufferIsDrawn = true;
340
341 if(status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE)
342 {
343 status.bVIOriginIsUpdated=false;
344 CGraphicsContext::Get()->UpdateFrame();
345 DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG, {DebuggerAppendMsg("Screen Update at 1st FillRectangle");});
346 }
347
348 if (status.bCIBufferIsRendered && status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_BEFORE_SCREEN_CLEAR )
349 {
350 if ((nX0==0 && nY0 == 0 && (nX1 == (int) g_CI.dwWidth || nX1 == (int) g_CI.dwWidth-1)) ||
351 (nX0==gRDP.scissor.left && nY0 == gRDP.scissor.top && (nX1 == gRDP.scissor.right || nX1 == gRDP.scissor.right-1)) ||
352 ((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)))
353 {
354 status.bVIOriginIsUpdated=false;
355 CGraphicsContext::Get()->UpdateFrame();
356 DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,{DebuggerAppendMsg("Screen Update Before Screen Clear");});
357 }
358 }
359
360
361 SetFillMode(RICE_FILLMODE_SOLID);
362
363 bool res=true;
364
365 /*
366 // I don't know why this does not work for OpenGL
367 if( gRDP.otherMode.cycle_type == CYCLE_TYPE_FILL && nX0 == 0 && nY0 == 0 && ((nX1==windowSetting.uViWidth && nY1==windowSetting.uViHeight)||(nX1==windowSetting.uViWidth-1 && nY1==windowSetting.uViHeight-1)) )
368 {
369 CGraphicsContext::g_pGraphicsContext->Clear(CLEAR_COLOR_BUFFER,dwColor);
370 }
371 else
372 */
373 {
374 //BOOL m_savedZBufferFlag = gRSP.bZBufferEnabled; // Save ZBuffer state
375 ZBufferEnable( FALSE );
376
377 m_fillRectVtx[0].x = ViewPortTranslatei_x(nX0);
378 m_fillRectVtx[0].y = ViewPortTranslatei_y(nY0);
379 m_fillRectVtx[1].x = ViewPortTranslatei_x(nX1);
380 m_fillRectVtx[1].y = ViewPortTranslatei_y(nY1);
381
382 SetCombinerAndBlender();
383
384 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
385 {
386 ZBufferEnable(FALSE);
387 }
388 else
389 {
390 //dwColor = PostProcessDiffuseColor(0);
391 dwColor = PostProcessDiffuseColor(gRDP.primitiveColor);
392 }
393
394 float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
395
396 ApplyRDPScissor();
397 TurnFogOnOff(false);
398 res = RenderFillRect(dwColor, depth);
399 TurnFogOnOff(gRSP.bFogEnabled);
400
401 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
402 {
403 ZBufferEnable(gRSP.bZBufferEnabled);
404 }
405 }
406
407 if( options.bWinFrameMode ) SetFillMode(RICE_FILLMODE_WINFRAME );
408
409 DEBUGGER_PAUSE_AND_DUMP_COUNT_N( NEXT_FILLRECT, {DebuggerAppendMsg("FillRect: X0=%d, Y0=%d, X1=%d, Y1=%d, Color=0x%08X", nX0, nY0, nX1, nY1, dwColor);
410 DebuggerAppendMsg("Pause after FillRect: Color=%08X\n", dwColor);if( logCombiners ) m_pColorCombiner->DisplayMuxString();});
411 DEBUGGER_PAUSE_AND_DUMP_COUNT_N( NEXT_FLUSH_TRI, {DebuggerAppendMsg("FillRect: X0=%d, Y0=%d, X1=%d, Y1=%d, Color=0x%08X", nX0, nY0, nX1, nY1, dwColor);
412 DebuggerAppendMsg("Pause after FillRect: Color=%08X\n", dwColor);if( logCombiners ) m_pColorCombiner->DisplayMuxString();});
413
414 return res;
415}
416
417
418bool CRender::Line3D(uint32 dwV0, uint32 dwV1, uint32 dwWidth)
419{
420 LOG_UCODE("Line3D: Vtx0=%d, Vtx1=%d, Width=%d", dwV0, dwV1, dwWidth);
421 if( !status.bCIBufferIsRendered ) g_pFrameBufferManager->ActiveTextureBuffer();
422
423 m_line3DVtx[0].z = (g_vecProjected[dwV0].z + 1.0f) * 0.5f;
424 m_line3DVtx[1].z = (g_vecProjected[dwV1].z + 1.0f) * 0.5f;
425
426 if( m_line3DVtx[0].z != m_line3DVtx[1].z )
427 return false;
428
429 if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM ) status.bFrameBufferIsDrawn = true;
430 if( status.bHandleN64RenderTexture )
431 {
432 g_pRenderTextureInfo->maxUsedHeight = g_pRenderTextureInfo->N64Height;
433 if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )
434 {
435 status.bFrameBufferIsDrawn = true;
436 status.bFrameBufferDrawnByTriangles = true;
437 }
438 }
439
440 m_line3DVtx[0].x = ViewPortTranslatef_x(g_vecProjected[dwV0].x);
441 m_line3DVtx[0].y = ViewPortTranslatef_y(g_vecProjected[dwV0].y);
442 m_line3DVtx[0].rhw = g_vecProjected[dwV0].w;
443 m_line3DVtx[0].dcDiffuse = PostProcessDiffuseColor(g_dwVtxDifColor[dwV0]);
444 m_line3DVtx[0].dcSpecular = PostProcessSpecularColor();
445
446 m_line3DVtx[1].x = ViewPortTranslatef_x(g_vecProjected[dwV1].x);
447 m_line3DVtx[1].y = ViewPortTranslatef_y(g_vecProjected[dwV1].y);
448 m_line3DVtx[1].rhw = g_vecProjected[dwV1].w;
449 m_line3DVtx[1].dcDiffuse = PostProcessDiffuseColor(g_dwVtxDifColor[dwV1]);
450 m_line3DVtx[1].dcSpecular = m_line3DVtx[0].dcSpecular;
451
452 float width = dwWidth*0.5f+1.5f;
453
454 if( m_line3DVtx[0].y == m_line3DVtx[1].y )
455 {
456 m_line3DVector[0].x = m_line3DVector[1].x = m_line3DVtx[0].x;
457 m_line3DVector[2].x = m_line3DVector[3].x = m_line3DVtx[1].x;
458
459 m_line3DVector[0].y = m_line3DVector[2].y = m_line3DVtx[0].y-width/2*windowSetting.fMultY;
460 m_line3DVector[1].y = m_line3DVector[3].y = m_line3DVtx[0].y+width/2*windowSetting.fMultY;
461 }
462 else
463 {
464 m_line3DVector[0].y = m_line3DVector[1].y = m_line3DVtx[0].y;
465 m_line3DVector[2].y = m_line3DVector[3].y = m_line3DVtx[1].y;
466
467 m_line3DVector[0].x = m_line3DVector[2].x = m_line3DVtx[0].x-width/2*windowSetting.fMultX;
468 m_line3DVector[1].x = m_line3DVector[3].x = m_line3DVtx[0].x+width/2*windowSetting.fMultX;
469 }
470
471 SetCombinerAndBlender();
472
473 bool res=RenderLine3D();
474
475 DEBUGGER_PAUSE_AND_DUMP_COUNT_N(NEXT_FLUSH_TRI, {
476 DebuggerAppendMsg("Pause after Line3D: v%d(%f,%f,%f), v%d(%f,%f,%f), Width=%d\n", dwV0, m_line3DVtx[0].x, m_line3DVtx[0].y, m_line3DVtx[0].z,
477 dwV1, m_line3DVtx[1].x, m_line3DVtx[1].y, m_line3DVtx[1].z, dwWidth);
478 });
479
480 DEBUGGER_PAUSE_AND_DUMP_COUNT_N(NEXT_OBJ_TXT_CMD, {
481 DebuggerAppendMsg("Pause after Line3D: v%d(%f,%f,%f), v%d(%f,%f,%f), Width=%d\n", dwV0, m_line3DVtx[0].x, m_line3DVtx[0].y, m_line3DVtx[0].z,
482 dwV1, m_line3DVtx[1].x, m_line3DVtx[1].y, m_line3DVtx[1].z, dwWidth);
483 });
484
485 return res;
486}
487
488bool CRender::RemapTextureCoordinate
489 (float t0, float t1, uint32 tileWidth, uint32 mask, float textureWidth, float &u0, float &u1)
490{
491 int s0 = (int)t0;
492 int s1 = (int)t1;
493 int width = mask>0 ? (1<<mask) : tileWidth;
494 if( width == 0 ) return false;
495
496 int divs0 = s0/width; if( divs0*width > s0 ) divs0--;
497 int divs1 = s1/width; if( divs1*width > s1 ) divs1--;
498
499 if( divs0 == divs1 )
500 {
501 s0 -= divs0*width;
502 s1 -= divs1*width;
503 //if( s0 > s1 )
504 // s0++;
505 //else if( s1 > s0 )
506 // s1++;
507 u0 = s0/textureWidth;
508 u1 = s1/textureWidth;
509
510 return true;
511 }
512 else if( divs0+1 == divs1 && s0%width==0 && s1%width == 0 )
513 {
514 u0 = 0;
515 u1 = tileWidth/textureWidth;
516 return true;
517 }
518 else if( divs0 == divs1+1 && s0%width==0 && s1%width == 0 )
519 {
520 u1 = 0;
521 u0 = tileWidth/textureWidth;
522 return true;
523 }
524 else
525 {
526 //if( s0 > s1 )
527 //{
528 //s0++;
529 // u0 = s0/textureWidth;
530 //}
531 //else if( s1 > s0 )
532 //{
533 //s1++;
534 // u1 = s1/textureWidth;
535 //}
536
537 return false;
538 }
539}
540
541bool CRender::TexRect(int nX0, int nY0, int nX1, int nY1, float fS0, float fT0, float fScaleS, float fScaleT, bool colorFlag, uint32 diffuseColor)
542{
543 if( options.enableHackForGames == HACK_FOR_DUKE_NUKEM )
544 {
545 colorFlag = true;
546 diffuseColor = 0;
547 }
548
549 if( status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE )
550 {
551 status.bVIOriginIsUpdated=false;
552 CGraphicsContext::Get()->UpdateFrame();
553 DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,{DebuggerAppendMsg("Screen Update at 1st textRect");});
554 }
555
556 if( options.enableHackForGames == HACK_FOR_BANJO_TOOIE )
557 {
558 // Hack for Banjo shadow in Banjo Tooie
559 if( g_TI.dwWidth == g_CI.dwWidth && g_TI.dwFormat == TXT_FMT_CI && g_TI.dwSize == TXT_SIZE_8b )
560 {
561 if( nX0 == fS0 && nY0 == fT0 )//&& nX0 > 90 && nY0 > 130 && nX1-nX0 > 80 && nY1-nY0 > 20 )
562 {
563 // Skip the text rect
564 return true;
565 }
566 }
567 }
568
569 if( status.bN64IsDrawingTextureBuffer )
570 {
571 if( frameBufferOptions.bIgnore || ( frameBufferOptions.bIgnoreRenderTextureIfHeightUnknown && newRenderTextureInfo.knownHeight == 0 ) )
572 {
573 return true;
574 }
575 }
576
577 PrepareTextures();
578
579 if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == TXT_SIZE_8b )
580 {
581 return true;
582 }
583
584 if( !IsTextureEnabled() && gRDP.otherMode.cycle_type != CYCLE_TYPE_COPY )
585 {
586 FillRect(nX0, nY0, nX1, nY1, gRDP.primitiveColor);
587 return true;
588 }
589
590
591 if( IsUsedAsDI(g_CI.dwAddr) && !status.bHandleN64RenderTexture )
592 {
593 status.bFrameBufferIsDrawn = true;
594 }
595
596 if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM ) status.bFrameBufferIsDrawn = true;
597
598 LOG_UCODE("TexRect: X0=%d, Y0=%d, X1=%d, Y1=%d,\n\t\tfS0=%f, fT0=%f, ScaleS=%f, ScaleT=%f ",
599 nX0, nY0, nX1, nY1, fS0, fT0, fScaleS, fScaleT);
600
601 if( options.bEnableHacks )
602 {
603 // Goldeneye HACK
604 if( nY1 - nY0 < 2 )
605 nY1 = nY1+2;
606
607 //// Text edge hack
608 else if( gRDP.otherMode.cycle_type == CYCLE_TYPE_1 && fScaleS == 1 && fScaleT == 1 &&
609 (int)g_textures[gRSP.curTile].m_dwTileWidth == nX1-nX0+1 && (int)g_textures[gRSP.curTile].m_dwTileHeight == nY1-nY0+1 &&
610 g_textures[gRSP.curTile].m_dwTileWidth%2 == 0 && g_textures[gRSP.curTile].m_dwTileHeight%2 == 0 )
611 {
612 nY1++;
613 nX1++;
614 }
615 else if( g_curRomInfo.bIncTexRectEdge )
616 {
617 nX1++;
618 nY1++;
619 }
620 }
621
622
623 // Scale to Actual texture coords
624 // The two cases are to handle the oversized textures hack on voodoos
625
626 SetCombinerAndBlender();
627
628
629 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY || !gRDP.otherMode.z_cmp )
630 {
631 ZBufferEnable(FALSE);
632 }
633
634 BOOL accurate = currentRomOptions.bAccurateTextureMapping;
635
636 RenderTexture &tex0 = g_textures[gRSP.curTile];
637 Tile &tile0 = gRDP.tiles[gRSP.curTile];
638
639 float widthDiv = tex0.m_fTexWidth;
640 float heightDiv = tex0.m_fTexHeight;
641 float t0u0;
642 if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
643 {
644 t0u0 = (fS0 -tile0.fhilite_sl);
645 }
646 else
647 {
648 t0u0 = (fS0 * tile0.fShiftScaleS -tile0.fhilite_sl);
649 }
650
651 float t0u1;
652 if( accurate && gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
653 {
654 t0u1 = t0u0 + (fScaleS * (nX1 - nX0 - 1))*tile0.fShiftScaleS;
655 }
656 else
657 {
658 t0u1 = t0u0 + (fScaleS * (nX1 - nX0))*tile0.fShiftScaleS;
659 }
660
661
662 if( status.UseLargerTile[0] )
663 {
664 m_texRectTex1UV[0].u = (t0u0+status.LargerTileRealLeft[0])/widthDiv;
665 m_texRectTex1UV[1].u = (t0u1+status.LargerTileRealLeft[0])/widthDiv;
666 }
667 else
668 {
669 m_texRectTex1UV[0].u = t0u0/widthDiv;
670 m_texRectTex1UV[1].u = t0u1/widthDiv;
671 if( accurate && !tile0.bMirrorS && RemapTextureCoordinate(t0u0, t0u1, tex0.m_dwTileWidth, tile0.dwMaskS, widthDiv, m_texRectTex1UV[0].u, m_texRectTex1UV[1].u) )
672 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
673 }
674
675 float t0v0;
676 if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
677 {
678 t0v0 = (fT0 -tile0.fhilite_tl);
679 }
680 else
681 {
682 t0v0 = (fT0 * tile0.fShiftScaleT -tile0.fhilite_tl);
683 }
684
685 float t0v1;
686 if ( accurate && gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY)
687 {
688 t0v1 = t0v0 + (fScaleT * (nY1 - nY0-1))*tile0.fShiftScaleT;
689 }
690 else
691 {
692 t0v1 = t0v0 + (fScaleT * (nY1 - nY0))*tile0.fShiftScaleT;
693 }
694
695 m_texRectTex1UV[0].v = t0v0/heightDiv;
696 m_texRectTex1UV[1].v = t0v1/heightDiv;
697 if( accurate && !tile0.bMirrorT && RemapTextureCoordinate(t0v0, t0v1, tex0.m_dwTileHeight, tile0.dwMaskT, heightDiv, m_texRectTex1UV[0].v, m_texRectTex1UV[1].v) )
698 SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
699
700 COLOR speColor = PostProcessSpecularColor();
701 COLOR difColor;
702 if( colorFlag )
703 difColor = PostProcessDiffuseColor(diffuseColor);
704 else
705 //difColor = PostProcessDiffuseColor(0);
706 difColor = PostProcessDiffuseColor(gRDP.primitiveColor);
707
708 g_texRectTVtx[0].x = ViewPortTranslatei_x(nX0);
709 g_texRectTVtx[0].y = ViewPortTranslatei_y(nY0);
710 g_texRectTVtx[0].dcDiffuse = difColor;
711 g_texRectTVtx[0].dcSpecular = speColor;
712
713 g_texRectTVtx[1].x = ViewPortTranslatei_x(nX1);
714 g_texRectTVtx[1].y = ViewPortTranslatei_y(nY0);
715 g_texRectTVtx[1].dcDiffuse = difColor;
716 g_texRectTVtx[1].dcSpecular = speColor;
717
718 g_texRectTVtx[2].x = ViewPortTranslatei_x(nX1);
719 g_texRectTVtx[2].y = ViewPortTranslatei_y(nY1);
720 g_texRectTVtx[2].dcDiffuse = difColor;
721 g_texRectTVtx[2].dcSpecular = speColor;
722
723 g_texRectTVtx[3].x = ViewPortTranslatei_x(nX0);
724 g_texRectTVtx[3].y = ViewPortTranslatei_y(nY1);
725 g_texRectTVtx[3].dcDiffuse = difColor;
726 g_texRectTVtx[3].dcSpecular = speColor;
727
728 float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
729
730 g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = depth;
731 g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = 1;
732
733 if( IsTexel1Enable() )
734 {
735 RenderTexture &tex1 = g_textures[(gRSP.curTile+1)&7];
736 Tile &tile1 = gRDP.tiles[(gRSP.curTile+1)&7];
737
738 widthDiv = tex1.m_fTexWidth;
739 heightDiv = tex1.m_fTexHeight;
740 //if( tile1.dwMaskS == 0 ) widthDiv = tile1.dwWidth;
741 //if( tile1.dwMaskT == 0 ) heightDiv = tile1.dwHeight;
742
743 float t0u0 = fS0 * tile1.fShiftScaleS -tile1.fhilite_sl;
744 float t0v0 = fT0 * tile1.fShiftScaleT -tile1.fhilite_tl;
745 float t0u1;
746 float t0v1;
747 if ( accurate && gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY)
748 {
749 t0u1 = t0u0 + (fScaleS * (nX1 - nX0 - 1))*tile1.fShiftScaleS;
750 t0v1 = t0v0 + (fScaleT * (nY1 - nY0 - 1))*tile1.fShiftScaleT;
751 }
752 else
753 {
754 t0u1 = t0u0 + (fScaleS * (nX1 - nX0))*tile1.fShiftScaleS;
755 t0v1 = t0v0 + (fScaleT * (nY1 - nY0))*tile1.fShiftScaleT;
756 }
757
758 if( status.UseLargerTile[1] )
759 {
760 m_texRectTex2UV[0].u = (t0u0+status.LargerTileRealLeft[1])/widthDiv;
761 m_texRectTex2UV[1].u = (t0u1+status.LargerTileRealLeft[1])/widthDiv;
762 }
763 else
764 {
765 m_texRectTex2UV[0].u = t0u0/widthDiv;
766 m_texRectTex2UV[1].u = t0u1/widthDiv;
767 if( accurate && !tile1.bMirrorS && RemapTextureCoordinate(t0u0, t0u1, tex1.m_dwTileWidth, tile1.dwMaskS, widthDiv, m_texRectTex2UV[0].u, m_texRectTex2UV[1].u) )
768 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, (gRSP.curTile+1)&7);
769 }
770
771 m_texRectTex2UV[0].v = t0v0/heightDiv;
772 m_texRectTex2UV[1].v = t0v1/heightDiv;
773
774 if( accurate && !tile1.bMirrorT && RemapTextureCoordinate(t0v0, t0v1, tex1.m_dwTileHeight, tile1.dwMaskT, heightDiv, m_texRectTex2UV[0].v, m_texRectTex2UV[1].v) )
775 SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, (gRSP.curTile+1)&7);
776
777 SetVertexTextureUVCoord(g_texRectTVtx[0], m_texRectTex1UV[0].u, m_texRectTex1UV[0].v, m_texRectTex2UV[0].u, m_texRectTex2UV[0].v);
778 SetVertexTextureUVCoord(g_texRectTVtx[1], m_texRectTex1UV[1].u, m_texRectTex1UV[0].v, m_texRectTex2UV[1].u, m_texRectTex2UV[0].v);
779 SetVertexTextureUVCoord(g_texRectTVtx[2], m_texRectTex1UV[1].u, m_texRectTex1UV[1].v, m_texRectTex2UV[1].u, m_texRectTex2UV[1].v);
780 SetVertexTextureUVCoord(g_texRectTVtx[3], m_texRectTex1UV[0].u, m_texRectTex1UV[1].v, m_texRectTex2UV[0].u, m_texRectTex2UV[1].v);
781 }
782 else
783 {
784 SetVertexTextureUVCoord(g_texRectTVtx[0], m_texRectTex1UV[0].u, m_texRectTex1UV[0].v);
785 SetVertexTextureUVCoord(g_texRectTVtx[1], m_texRectTex1UV[1].u, m_texRectTex1UV[0].v);
786 SetVertexTextureUVCoord(g_texRectTVtx[2], m_texRectTex1UV[1].u, m_texRectTex1UV[1].v);
787 SetVertexTextureUVCoord(g_texRectTVtx[3], m_texRectTex1UV[0].u, m_texRectTex1UV[1].v);
788 }
789
790
791 bool res;
792 TurnFogOnOff(false);
793 if( TileUFlags[gRSP.curTile]==TEXTURE_UV_FLAG_CLAMP && TileVFlags[gRSP.curTile]==TEXTURE_UV_FLAG_CLAMP && options.forceTextureFilter == FORCE_DEFAULT_FILTER )
794 {
795 TextureFilter dwFilter = m_dwMagFilter;
796 m_dwMagFilter = m_dwMinFilter = FILTER_LINEAR;
797 ApplyTextureFilter();
798 ApplyRDPScissor();
799 res = RenderTexRect();
800 m_dwMagFilter = m_dwMinFilter = dwFilter;
801 ApplyTextureFilter();
802 }
803 else if( fScaleS >= 1 && fScaleT >= 1 && options.forceTextureFilter == FORCE_DEFAULT_FILTER )
804 {
805 TextureFilter dwFilter = m_dwMagFilter;
806 m_dwMagFilter = m_dwMinFilter = FILTER_POINT;
807 ApplyTextureFilter();
808 ApplyRDPScissor();
809 res = RenderTexRect();
810 m_dwMagFilter = m_dwMinFilter = dwFilter;
811 ApplyTextureFilter();
812 }
813 else
814 {
815 ApplyRDPScissor();
816 res = RenderTexRect();
817 }
818 TurnFogOnOff(gRSP.bFogEnabled);
819
820 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY || !gRDP.otherMode.z_cmp )
821 {
822 ZBufferEnable(gRSP.bZBufferEnabled);
823 }
824
825 DEBUGGER_PAUSE_AT_COND_AND_DUMP_COUNT_N((eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_TEXTRECT), {
826 DebuggerAppendMsg("TexRect: tile=%d, X0=%d, Y0=%d, X1=%d, Y1=%d,\nfS0=%f, fT0=%f, ScaleS=%f, ScaleT=%f\n",
827 gRSP.curTile, nX0, nY0, nX1, nY1, fS0, fT0, fScaleS, fScaleT);
828 DebuggerAppendMsg(" : x0=%f, y0=%f, x1=%f, y1=%f\n", g_texRectTVtx[0].x, g_texRectTVtx[0].y, g_texRectTVtx[2].x, g_texRectTVtx[2].y);
829 DebuggerAppendMsg(" Tex0: u0=%f, v0=%f, u1=%f, v1=%f\n", m_texRectTex1UV[0].u, m_texRectTex1UV[0].v, m_texRectTex1UV[1].u, m_texRectTex1UV[1].v);
830 if( IsTexel1Enable() )
831 {
832 DebuggerAppendMsg(" Tex1: u0=%f, v0=%f, u1=%f, v1=%f\n", m_texRectTex2UV[0].u, m_texRectTex2UV[0].v, m_texRectTex2UV[1].u, m_texRectTex2UV[1].v);
833 }
834 DebuggerAppendMsg("color=%08X, %08X\n", g_texRectTVtx[0].dcDiffuse, g_texRectTVtx[0].dcSpecular);
835 DebuggerAppendMsg("Pause after TexRect\n");
836 if( logCombiners ) m_pColorCombiner->DisplayMuxString();
837 });
838
839 return res;
840}
841
842
843bool CRender::TexRectFlip(int nX0, int nY0, int nX1, int nY1, float fS0, float fT0, float fS1, float fT1)
844{
845 LOG_UCODE("TexRectFlip: X0=%d, Y0=%d, X1=%d, Y1=%d,\n\t\tfS0=%f, fT0=%f, fS1=%f, fT1=%f ",
846 nX0, nY0, nX1, nY1, fS0, fT0, fS1, fT1);
847
848 if( status.bHandleN64RenderTexture && !status.bDirectWriteIntoRDRAM )
849 {
850 status.bFrameBufferIsDrawn = true;
851 status.bFrameBufferDrawnByTriangles = true;
852 }
853
854 PrepareTextures();
855
856 // Save ZBuffer state
857 m_savedZBufferFlag = gRSP.bZBufferEnabled;
858 if( gRDP.otherMode.depth_source == 0 ) ZBufferEnable( FALSE );
859
860 float widthDiv = g_textures[gRSP.curTile].m_fTexWidth;
861 float heightDiv = g_textures[gRSP.curTile].m_fTexHeight;
862
863 //Tile &tile0 = gRDP.tiles[gRSP.curTile];
864
865 float t0u0 = fS0 / widthDiv;
866 float t0v0 = fT0 / heightDiv;
867
868 float t0u1 = (fS1 - fS0)/ widthDiv + t0u0;
869 float t0v1 = (fT1 - fT0)/ heightDiv + t0v0;
870
871 float depth = (gRDP.otherMode.depth_source == 1 ? gRDP.fPrimitiveDepth : 0 );
872
873 if( t0u0 >= 0 && t0u1 <= 1 && t0u1 >= t0u0 ) SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
874 if( t0v0 >= 0 && t0v1 <= 1 && t0v1 >= t0v0 ) SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile);
875
876 SetCombinerAndBlender();
877
878 COLOR speColor = PostProcessSpecularColor();
879 COLOR difColor = PostProcessDiffuseColor(gRDP.primitiveColor);
880
881 // Same as TexRect, but with texcoords 0,2 swapped
882 g_texRectTVtx[0].x = ViewPortTranslatei_x(nX0);
883 g_texRectTVtx[0].y = ViewPortTranslatei_y(nY0);
884 g_texRectTVtx[0].dcDiffuse = difColor;
885 g_texRectTVtx[0].dcSpecular = speColor;
886
887 g_texRectTVtx[1].x = ViewPortTranslatei_x(nX1);
888 g_texRectTVtx[1].y = ViewPortTranslatei_y(nY0);
889 g_texRectTVtx[1].dcDiffuse = difColor;
890 g_texRectTVtx[1].dcSpecular = speColor;
891
892 g_texRectTVtx[2].x = ViewPortTranslatei_x(nX1);
893 g_texRectTVtx[2].y = ViewPortTranslatei_y(nY1);
894 g_texRectTVtx[2].dcDiffuse = difColor;
895 g_texRectTVtx[2].dcSpecular = speColor;
896
897 g_texRectTVtx[3].x = ViewPortTranslatei_x(nX0);
898 g_texRectTVtx[3].y = ViewPortTranslatei_y(nY1);
899 g_texRectTVtx[3].dcDiffuse = difColor;
900 g_texRectTVtx[3].dcSpecular = speColor;
901
902 g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = depth;
903 g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = 1.0f;
904
905 SetVertexTextureUVCoord(g_texRectTVtx[0], t0u0, t0v0);
906 SetVertexTextureUVCoord(g_texRectTVtx[1], t0u0, t0v1);
907 SetVertexTextureUVCoord(g_texRectTVtx[2], t0u1, t0v1);
908 SetVertexTextureUVCoord(g_texRectTVtx[3], t0u1, t0v0);
909
910 TurnFogOnOff(false);
911 ApplyRDPScissor();
912 bool res = RenderTexRect();
913
914 TurnFogOnOff(gRSP.bFogEnabled);
915
916 // Restore state
917 ZBufferEnable( m_savedZBufferFlag );
918
919 DEBUGGER_PAUSE_AT_COND_AND_DUMP_COUNT_N((eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_TEXTRECT), {
920 DebuggerAppendMsg("TexRectFlip: tile=%d, X0=%d, Y0=%d, X1=%d, Y1=%d,\nfS0=%f, fT0=%f, nfS1=%f, fT1=%f\n",
921 gRSP.curTile, nX0, nY0, nX1, nY1, fS0, fT0, fS1, fT1);
922 DebuggerAppendMsg(" : x0=%f, y0=%f, x1=%f, y1=%f\n", g_texRectTVtx[0].x, g_texRectTVtx[0].y, g_texRectTVtx[2].x, g_texRectTVtx[2].y);
923 DebuggerAppendMsg(" Tex0: u0=%f, v0=%f, u1=%f, v1=%f\n", g_texRectTVtx[0].tcord[0].u, g_texRectTVtx[0].tcord[0].v, g_texRectTVtx[2].tcord[0].u, g_texRectTVtx[2].tcord[0].v);
924 TRACE0("Pause after TexRectFlip\n");
925 if( logCombiners ) m_pColorCombiner->DisplayMuxString();
926 });
927
928 return res;
929}
930
931
932void 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)
933{
934 g_texRectTVtx[0].x = ViewPortTranslatei_x(x0); // << Error here, shouldn't divid by 4
935 g_texRectTVtx[0].y = ViewPortTranslatei_y(y0);
936 g_texRectTVtx[0].dcDiffuse = dif;
937 g_texRectTVtx[0].dcSpecular = spe;
938 g_texRectTVtx[0].tcord[0].u = u0;
939 g_texRectTVtx[0].tcord[0].v = v0;
940
941
942 g_texRectTVtx[1].x = ViewPortTranslatei_x(x1);
943 g_texRectTVtx[1].y = ViewPortTranslatei_y(y0);
944 g_texRectTVtx[1].dcDiffuse = dif;
945 g_texRectTVtx[1].dcSpecular = spe;
946 g_texRectTVtx[1].tcord[0].u = u1;
947 g_texRectTVtx[1].tcord[0].v = v0;
948
949 g_texRectTVtx[2].x = ViewPortTranslatei_x(x1);
950 g_texRectTVtx[2].y = ViewPortTranslatei_y(y1);
951 g_texRectTVtx[2].dcDiffuse = dif;
952 g_texRectTVtx[2].dcSpecular = spe;
953 g_texRectTVtx[2].tcord[0].u = u1;
954 g_texRectTVtx[2].tcord[0].v = v1;
955
956 g_texRectTVtx[3].x = ViewPortTranslatei_x(x0);
957 g_texRectTVtx[3].y = ViewPortTranslatei_y(y1);
958 g_texRectTVtx[3].dcDiffuse = dif;
959 g_texRectTVtx[3].dcSpecular = spe;
960 g_texRectTVtx[3].tcord[0].u = u0;
961 g_texRectTVtx[3].tcord[0].v = v1;
962
963 RenderTexture &txtr = g_textures[0];
964 if( txtr.pTextureEntry && txtr.pTextureEntry->txtrBufIdx > 0 )
965 {
966 RenderTextureInfo &info = gRenderTextureInfos[txtr.pTextureEntry->txtrBufIdx-1];
967 g_texRectTVtx[0].tcord[0].u *= info.scaleX;
968 g_texRectTVtx[0].tcord[0].v *= info.scaleY;
969 g_texRectTVtx[1].tcord[0].u *= info.scaleX;
970 g_texRectTVtx[1].tcord[0].v *= info.scaleY;
971 g_texRectTVtx[2].tcord[0].u *= info.scaleX;
972 g_texRectTVtx[2].tcord[0].v *= info.scaleY;
973 g_texRectTVtx[3].tcord[0].u *= info.scaleX;
974 g_texRectTVtx[3].tcord[0].v *= info.scaleY;
975 }
976
977 g_texRectTVtx[0].z = g_texRectTVtx[1].z = g_texRectTVtx[2].z = g_texRectTVtx[3].z = z;
978 g_texRectTVtx[0].rhw = g_texRectTVtx[1].rhw = g_texRectTVtx[2].rhw = g_texRectTVtx[3].rhw = rhw;
979}
980
981void CRender::StartDrawSimpleRect(int nX0, int nY0, int nX1, int nY1, uint32 dwColor, float depth, float rhw)
982{
983 m_simpleRectVtx[0].x = ViewPortTranslatei_x(nX0);
984 m_simpleRectVtx[1].x = ViewPortTranslatei_x(nX1);
985 m_simpleRectVtx[0].y = ViewPortTranslatei_y(nY0);
986 m_simpleRectVtx[1].y = ViewPortTranslatei_y(nY1);
987}
988
989void CRender::SetAddressUAllStages(uint32 dwTile, TextureUVFlag dwFlag)
990{
991}
992
993void CRender::SetAddressVAllStages(uint32 dwTile, TextureUVFlag dwFlag)
994{
995}
996
997void CRender::SetAllTexelRepeatFlag()
998{
999 if( IsTextureEnabled() )
1000 {
1001 if( IsTexel0Enable() || gRDP.otherMode.cycle_type == CYCLE_TYPE_COPY )
1002 SetTexelRepeatFlags(gRSP.curTile);
1003 if( IsTexel1Enable() )
1004 SetTexelRepeatFlags((gRSP.curTile+1)&7);
1005 }
1006}
1007
1008
1009void CRender::SetTexelRepeatFlags(uint32 dwTile)
1010{
1011 Tile &tile = gRDP.tiles[dwTile];
1012
1013 if( tile.bForceClampS )
1014 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1015 else if( tile.bForceWrapS )
1016 SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1017 else if( tile.dwMaskS == 0 || tile.bClampS )
1018 {
1019 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
1020 SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile); // Can not clamp in COPY/FILL mode
1021 else
1022 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1023 }
1024 else if (tile.bMirrorS )
1025 SetTextureUFlag(TEXTURE_UV_FLAG_MIRROR, dwTile);
1026 else
1027 SetTextureUFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1028
1029 if( tile.bForceClampT )
1030 SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1031 else if( tile.bForceWrapT )
1032 SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1033 else if( tile.dwMaskT == 0 || tile.bClampT)
1034 {
1035 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
1036 SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile); // Can not clamp in COPY/FILL mode
1037 else
1038 SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, dwTile);
1039 }
1040 else if (tile.bMirrorT )
1041 SetTextureVFlag(TEXTURE_UV_FLAG_MIRROR, dwTile);
1042 else
1043 SetTextureVFlag(TEXTURE_UV_FLAG_WRAP, dwTile);
1044}
1045
1046void CRender::Initialize(void)
1047{
1048 ClearDeviceObjects();
1049 InitDeviceObjects();
1050}
1051
1052void CRender::CleanUp(void)
1053{
1054 m_pColorCombiner->CleanUp();
1055 ClearDeviceObjects();
1056}
1057
1058void myVec3Transform(float *vecout, float *vecin, float* m)
1059{
1060 float w = m[3]*vecin[0]+m[7]*vecin[1]+m[11]*vecin[2]+m[15];
1061 vecout[0] = (m[0]*vecin[0]+m[4]*vecin[1]+m[8]*vecin[2]+m[12])/w;
1062 vecout[1] = (m[1]*vecin[0]+m[5]*vecin[1]+m[9]*vecin[2]+m[13])/w;
1063 vecout[2] = (m[2]*vecin[0]+m[6]*vecin[1]+m[10]*vecin[2]+m[14])/w;
1064}
1065
1066void CRender::SetTextureEnableAndScale(int dwTile, bool bEnable, float fScaleX, float fScaleY)
1067{
1068 gRSP.bTextureEnabled = bEnable;
1069
1070 if( bEnable )
1071 {
1072 if( gRSP.curTile != (unsigned int)dwTile )
1073 gRDP.textureIsChanged = true;
1074
1075 gRSP.curTile = dwTile;
1076
1077 gRSP.fTexScaleX = fScaleX;
1078 gRSP.fTexScaleY = fScaleY;
1079
1080 if( fScaleX == 0 || fScaleY == 0 )
1081 {
1082 gRSP.fTexScaleX = 1/32.0f;
1083 gRSP.fTexScaleY = 1/32.0f;
1084 }
1085 }
1086}
1087
1088void CRender::SetFogFlagForNegativeW()
1089{
1090 if( !gRSP.bFogEnabled ) return;
1091
1092 m_bFogStateSave = gRSP.bFogEnabled;
1093
1094 bool flag=gRSP.bFogEnabled;
1095
1096 for (uint32 i = 0; i < gRSP.numVertices; i++)
1097 {
1098 if( g_vtxBuffer[i].rhw < 0 )
1099 flag = FALSE;
1100 }
1101
1102 TurnFogOnOff(flag);
1103}
1104
1105void CRender::RestoreFogFlag()
1106{
1107 if( !gRSP.bFogEnabled ) return;
1108 TurnFogOnOff(m_bFogStateSave);
1109}
1110
1111void CRender::SetViewport(int nLeft, int nTop, int nRight, int nBottom, int maxZ)
1112{
1113 if( status.bHandleN64RenderTexture )
1114 return;
1115
1116 static float MultX=0, MultY=0;
1117
1118 if( gRSP.nVPLeftN == nLeft && gRSP.nVPTopN == nTop &&
1119 gRSP.nVPRightN == nRight && gRSP.nVPBottomN == nBottom &&
1120 MultX==windowSetting.fMultX && MultY==windowSetting.fMultY)
1121 {
1122 // no changes
1123 return;
1124 }
1125
1126 MultX=windowSetting.fMultX;
1127 MultY=windowSetting.fMultY;
1128
1129 gRSP.maxZ = maxZ;
1130 gRSP.nVPLeftN = nLeft;
1131 gRSP.nVPTopN = nTop;
1132 gRSP.nVPRightN = nRight;
1133 gRSP.nVPBottomN = nBottom;
1134 gRSP.nVPWidthN = nRight - nLeft + 1;
1135 gRSP.nVPHeightN = nBottom - nTop + 1;
1136
1137 UpdateClipRectangle();
1138 SetViewportRender();
1139
1140 LOG_UCODE("SetViewport (%d,%d - %d,%d)",gRSP.nVPLeftN, gRSP.nVPTopN, gRSP.nVPRightN, gRSP.nVPBottomN);
1141}
1142
1143extern bool bHalfTxtScale;
1144
1145bool CRender::DrawTriangles()
1146{
1147 if( !status.bCIBufferIsRendered ) g_pFrameBufferManager->ActiveTextureBuffer();
1148
1149 DEBUGGER_ONLY_IF( (!debuggerEnableZBuffer), {ZBufferEnable( FALSE );} );
1150
1151 if( status.bVIOriginIsUpdated == true && currentRomOptions.screenUpdateSetting==SCREEN_UPDATE_AT_1ST_PRIMITIVE )
1152 {
1153 status.bVIOriginIsUpdated=false;
1154 CGraphicsContext::Get()->UpdateFrame();
1155 DEBUGGER_PAUSE_AND_DUMP_NO_UPDATE(NEXT_SET_CIMG,{DebuggerAppendMsg("Screen Update at 1st triangle");});
1156 }
1157
1158 // Hack for Pilotwings 64 (U) [!].v64
1159 static bool skipNext=false;
1160 if( options.enableHackForGames == HACK_FOR_PILOT_WINGS )
1161 {
1162 if( IsUsedAsDI(g_CI.dwAddr) && gRDP.otherMode.z_cmp+gRDP.otherMode.z_upd > 0 )
1163 {
1164 TRACE0("Warning: using Flushtris to write Zbuffer" );
1165 gRSP.numVertices = 0;
1166 gRSP.maxVertexID = 0;
1167 skipNext = true;
1168 return true;
1169 }
1170 else if( skipNext )
1171 {
1172 skipNext = false;
1173 gRSP.numVertices = 0;
1174 gRSP.maxVertexID = 0;
1175 return true;
1176 }
1177 }
1178
1179 if( status.bN64IsDrawingTextureBuffer && frameBufferOptions.bIgnore )
1180 {
1181 gRSP.numVertices = 0;
1182 gRSP.maxVertexID = 0;
1183 return true;
1184 }
1185
1186 extern bool bConkerHideShadow;
1187 if( options.enableHackForGames == HACK_FOR_CONKER && bConkerHideShadow )
1188 {
1189 gRSP.numVertices = 0;
1190 gRSP.maxVertexID = 0;
1191 return true;
1192 }
1193
1194 if( IsUsedAsDI(g_CI.dwAddr) && !status.bHandleN64RenderTexture )
1195 {
1196 status.bFrameBufferIsDrawn = true;
1197 }
1198
1199 /*
1200 if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == TXT_SIZE_8b )
1201 {
1202 gRSP.numVertices = 0;
1203 gRSP.maxVertexID = 0;
1204 return true;
1205 }
1206 */
1207
1208 if (gRSP.numVertices == 0) return true;
1209 if( status.bHandleN64RenderTexture )
1210 {
1211 g_pRenderTextureInfo->maxUsedHeight = g_pRenderTextureInfo->N64Height;
1212 if( !status.bDirectWriteIntoRDRAM )
1213 {
1214 status.bFrameBufferIsDrawn = true;
1215 status.bFrameBufferDrawnByTriangles = true;
1216 }
1217 }
1218
1219 if( !gRDP.bFogEnableInBlender && gRSP.bFogEnabled )
1220 {
1221 TurnFogOnOff(false);
1222 }
1223
1224 for( int t=0; t<2; t++ )
1225 {
1226 float halfscaleS = 1;
1227
1228 // This will get rid of the thin black lines
1229 if( t==0 && !(m_pColorCombiner->m_bTex0Enabled) )
1230 continue;
1231 else
1232 {
1233 if( ( gRDP.tiles[gRSP.curTile].dwSize == TXT_SIZE_32b && options.enableHackForGames == HACK_FOR_RUMBLE ) ||
1234 (bHalfTxtScale && g_curRomInfo.bTextureScaleHack ) ||
1235 (options.enableHackForGames == HACK_FOR_POLARISSNOCROSS && gRDP.tiles[7].dwFormat == TXT_FMT_CI && gRDP.tiles[7].dwSize == TXT_SIZE_8b
1236 && gRDP.tiles[0].dwFormat == TXT_FMT_CI && gRDP.tiles[0].dwSize == TXT_SIZE_8b && gRSP.curTile == 0 ))
1237 {
1238 halfscaleS = 0.5;
1239 }
1240 }
1241
1242 if( t==1 && !(m_pColorCombiner->m_bTex1Enabled) )
1243 break;
1244
1245 if( halfscaleS < 1 )
1246 {
1247 for( uint32 i=0; i<gRSP.numVertices; i++ )
1248 {
1249 if( t == 0 )
1250 {
1251 g_vtxBuffer[i].tcord[t].u += gRSP.tex0OffsetX;
1252 g_vtxBuffer[i].tcord[t].u /= 2;
1253 g_vtxBuffer[i].tcord[t].u -= gRSP.tex0OffsetX;
1254 g_vtxBuffer[i].tcord[t].v += gRSP.tex0OffsetY;
1255 g_vtxBuffer[i].tcord[t].v /= 2;
1256 g_vtxBuffer[i].tcord[t].v -= gRSP.tex0OffsetY;
1257 }
1258 else
1259 {
1260 g_vtxBuffer[i].tcord[t].u += gRSP.tex1OffsetX;
1261 g_vtxBuffer[i].tcord[t].u /= 2;
1262 g_vtxBuffer[i].tcord[t].u -= gRSP.tex1OffsetX;
1263 g_vtxBuffer[i].tcord[t].v += gRSP.tex1OffsetY;
1264 g_vtxBuffer[i].tcord[t].v /= 2;
1265 g_vtxBuffer[i].tcord[t].v -= gRSP.tex1OffsetY;
1266 }
1267 }
1268 }
1269
1270 /*
1271 // The code here is disabled because it could cause incorrect texture repeating flag
1272 // for later DrawTriangles
1273 bool clampS=true;
1274 bool clampT=true;
1275
1276 for( uint32 i=0; i<gRSP.numVertices; i++ )
1277 {
1278 float w = CDeviceBuilder::GetGeneralDeviceType() == OGL_DEVICE ? g_vtxProjected5[i][3] : g_vtxBuffer[i].rhw;
1279 if( w < 0 || g_vtxBuffer[i].tcord[t].u > 1.0 || g_vtxBuffer[i].tcord[t].u < 0.0 )
1280 {
1281 clampS = false;
1282 break;
1283 }
1284 }
1285
1286 for( uint32 i=0; i<gRSP.numVertices; i++ )
1287 {
1288 float w = CDeviceBuilder::GetGeneralDeviceType() == OGL_DEVICE ? g_vtxProjected5[i][3] : g_vtxBuffer[i].rhw;
1289 if( w < 0 || g_vtxBuffer[i].tcord[t].v > 1.0 || g_vtxBuffer[i].tcord[t].v < 0.0 )
1290 {
1291 clampT = false;
1292 break;
1293 }
1294 }
1295
1296 if( clampS )
1297 {
1298 SetTextureUFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile+t);
1299 }
1300 if( clampT )
1301 {
1302 SetTextureVFlag(TEXTURE_UV_FLAG_CLAMP, gRSP.curTile+t);
1303 }
1304 */
1305 }
1306
1307 if( status.bHandleN64RenderTexture && g_pRenderTextureInfo->CI_Info.dwSize == TXT_SIZE_8b )
1308 {
1309 ZBufferEnable(FALSE);
1310 }
1311
1312 ApplyScissorWithClipRatio();
1313
1314 if( g_curRomInfo.bZHack )
1315 {
1316 extern void HackZAll();
1317 HackZAll();
1318 }
1319
1320 bool res = RenderFlushTris();
1321 g_clippedVtxCount = 0;
1322
1323 LOG_UCODE("DrawTriangles: Draw %d Triangles", gRSP.numVertices/3);
1324
1325 gRSP.numVertices = 0; // Reset index
1326 gRSP.maxVertexID = 0;
1327
1328 DEBUGGER_PAUSE_AND_DUMP_COUNT_N(NEXT_FLUSH_TRI, {
1329 TRACE0("Pause after DrawTriangles\n");
1330 if( logCombiners ) m_pColorCombiner->DisplayMuxString();
1331 });
1332
1333 if( !gRDP.bFogEnableInBlender && gRSP.bFogEnabled )
1334 {
1335 TurnFogOnOff(true);
1336 }
1337
1338 return res;
1339}
1340
1341inline int ReverseCITableLookup(uint32 *pTable, int size, uint32 val)
1342{
1343 for( int i=0; i<size; i++)
1344 {
1345 if( pTable[i] == val )
1346 return i;
1347 }
1348
1349 TRACE0("Cannot find value in CI table");
1350 return 0;
1351}
1352
1353bool SaveCITextureToFile(TxtrCacheEntry &entry, char *filename, bool bShow, bool bWholeTexture )
1354{
1355 if( !( (gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b ) )
1356 {
1357 // No a CI texture
1358 return false;
1359 }
1360 if ( entry.ti.TLutFmt != TLUT_FMT_RGBA16 && entry.ti.TLutFmt != TLUT_FMT_IA16 )
1361 {
1362 TRACE0("Invalid Tlut format");
1363 return false;
1364 }
1365
1366 if( !entry.pTexture )
1367 {
1368 TRACE0("Null texture");
1369 return false;
1370 }
1371
1372 uint32 *pTable = NULL;
1373 int tableSize;
1374 uint16 * pPal = (uint16 *)entry.ti.PalAddress;
1375
1376 // Create the pallette table
1377 if( entry.ti.Size == TXT_SIZE_4b )
1378 {
1379 // 4-bit table
1380 tableSize = 16;
1381 pTable = new uint32[16];
1382
1383 for( int i=0; i<16; i++ )
1384 {
1385 pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]);
1386 }
1387 }
1388 else
1389 {
1390 // 8-bit table
1391 tableSize = 256;
1392 pTable = new uint32[256];
1393
1394 for( int i=0; i<256; i++ )
1395 {
1396 pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]);
1397 }
1398 }
1399
1400 // Reversely convert current texture to indexed textures
1401 CTexture &texture = *entry.pTexture;
1402 //int width = bWholeTexture ? texture.m_dwCreatedTextureWidth : texture.m_dwWidth;
1403 //int height = bWholeTexture ? texture.m_dwCreatedTextureHeight : texture.m_dwHeight;
1404 int width = bWholeTexture ? texture.m_dwCreatedTextureWidth : entry.ti.WidthToLoad;
1405 int height = bWholeTexture ? texture.m_dwCreatedTextureHeight : entry.ti.HeightToLoad;
1406 int bufSizePerLine = (((((width << entry.ti.Size) + 1 ) >> 1)+3) >> 2)*4; // pad to 32bit boundary
1407 int bufSize = bufSizePerLine*height;
1408 unsigned char *pbuf = new unsigned char[bufSize];
1409
1410 DrawInfo srcInfo;
1411 if( texture.StartUpdate(&srcInfo) )
1412 {
1413 int idx = 0;
1414 for( int i=height-1; i>=0; i--)
1415 {
1416 uint32 *pSrc = (uint32*)((unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i);
1417 for( int j=0; j<width; j++)
1418 {
1419 int val = ReverseCITableLookup(pTable, tableSize, *pSrc);
1420 pSrc++;
1421
1422 if( entry.ti.Size == TXT_SIZE_4b )
1423 {
1424 // 4 bits
1425 if( idx%2 )
1426 {
1427 // 1
1428 pbuf[idx>>1] = (pbuf[idx>>1]<<4) | val;
1429 idx++;
1430 }
1431 else
1432 {
1433 // 0
1434 pbuf[idx>>1] = (unsigned char)val;
1435 idx++;
1436 }
1437 }
1438 else
1439 {
1440 // 8 bits
1441 pbuf[idx++] = (unsigned char)val;
1442 }
1443 }
1444 if( entry.ti.Size == TXT_SIZE_4b )
1445 {
1446 if( idx%8 ) idx = (idx/8+1)*8;
1447 }
1448 else
1449 {
1450 if( idx%4 ) idx = (idx/4+1)*4;
1451 }
1452 }
1453
1454
1455 texture.EndUpdate(&srcInfo);
1456 }
1457
1458 // Create BMP color indexed file
1459 if( strcasecmp(right(filename,4),".bmp") != 0 )
1460 strcat(filename,".bmp");
1461
1462 BITMAPFILEHEADER fileHeader;
1463 BITMAPINFOHEADER infoHeader;
1464
1465 infoHeader.biSize = sizeof( BITMAPINFOHEADER );
1466 infoHeader.biWidth = width;
1467 infoHeader.biHeight = height;
1468 infoHeader.biPlanes = 1;
1469 infoHeader.biBitCount = entry.ti.Size == TXT_SIZE_4b ? 4 : 8;
1470 infoHeader.biCompression = BI_RGB;
1471 infoHeader.biSizeImage = bufSize;
1472 infoHeader.biXPelsPerMeter = 0;
1473 infoHeader.biYPelsPerMeter = 0;
1474 infoHeader.biClrUsed = 0;
1475 infoHeader.biClrImportant = 0;
1476
1477 fileHeader.bfType = 19778;
1478 fileHeader.bfSize = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + infoHeader.biSizeImage + tableSize*4;
1479 fileHeader.bfReserved1 = fileHeader.bfReserved2 = 0;
1480 fileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + tableSize*4;
1481
1482
1483 FILE *f;
1484 f = fopen(filename, "wb");
1485 if(f != NULL)
1486 {
1487 if (fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 ||
1488 fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1 ||
1489 fwrite(pTable, tableSize*4, 1, f) != 1 ||
1490 fwrite(pbuf, infoHeader.biSizeImage, 1, f) != 1)
1491 printf("failed to write out texture data to image file '%s'", filename);
1492
1493 fclose(f);
1494 }
1495 else
1496 {
1497 // Do something
1498 TRACE1("Fail to create file %s", filename);
1499 }
1500
1501 // Clean up
1502 delete [] pTable;
1503 delete [] pbuf;
1504
1505 return true;
1506}
1507
1508void CRender::SaveTextureToFile(CTexture &texture, char *filename, TextureChannel channel, bool bShow, bool bWholeTexture, int width, int height)
1509{
1510 if( width < 0 || height < 0 )
1511 {
1512 width = bWholeTexture ? texture.m_dwCreatedTextureWidth : texture.m_dwWidth;
1513 height = bWholeTexture ? texture.m_dwCreatedTextureHeight : texture.m_dwHeight;
1514 }
1515
1516 unsigned char *pbuf = new unsigned char[width*height* (channel == TXT_RGBA ? 4 : 3)];
1517 if( pbuf )
1518 {
1519 DrawInfo srcInfo;
1520 if( texture.StartUpdate(&srcInfo) )
1521 {
1522 if( channel == TXT_RGBA )
1523 {
1524 uint32 *pbuf2 = (uint32*)pbuf;
1525 for( int i=height-1; i>=0; i--)
1526 {
1527 uint32 *pSrc = (uint32*)((unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i);
1528 for( int j=0; j<width; j++)
1529 {
1530 *pbuf2++ = *pSrc++;
1531 }
1532 }
1533
1534 if( SaveRGBABufferToPNGFile(filename, (unsigned char*)pbuf, width, height ) )
1535 //if( SaveRGBABufferToPNGFile(filename, (unsigned char*)srcInfo.lpSurface, width, height, srcInfo.lPitch ) )
1536 {
1537 }
1538 }
1539 else
1540 {
1541 unsigned char *pbuf2 = pbuf;
1542 for( int i=height-1; i>=0; i--)
1543 {
1544 unsigned char *pSrc = (unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i;
1545 for( int j=0; j<width; j++)
1546 {
1547 if( channel == TXT_ALPHA )
1548 {
1549 pbuf2[0] = pbuf2[1] = pbuf2[2] = pSrc[3];
1550 }
1551 else
1552 {
1553 pbuf2[0] = pSrc[0];
1554 pbuf2[1] = pSrc[1];
1555 pbuf2[2] = pSrc[2];
1556 }
1557
1558 pbuf2 += 3;
1559 pSrc += 4;
1560 }
1561 }
1562
1563 if( SaveRGBBufferToFile(filename, pbuf, width, height ) )
1564 {
1565 }
1566 }
1567 texture.EndUpdate(&srcInfo);
1568 }
1569 else
1570 {
1571 TRACE0("Cannot lock texture");
1572 }
1573 delete [] pbuf;
1574 }
1575 else
1576 {
1577 TRACE0("Out of memory");
1578 }
1579}
1580
1581
1582#ifdef DEBUGGER
1583bool CRender::DrawTexture(int tex, TextureChannel channel)
1584{
1585 if( g_textures[tex].m_pCTexture == NULL )
1586 {
1587 TRACE0("Can't draw null texture");
1588 return false;
1589 }
1590
1591 SaveTextureToFile(tex, channel, true); // Save to file instead of draw to screen
1592 DebuggerAppendMsg("Texture %d (CurTile:%d): W=%f, H=%f, Real W=%d, H=%d", tex, gRSP.curTile,
1593 g_textures[tex].m_fTexWidth, g_textures[tex].m_fTexHeight, g_textures[tex].m_dwTileWidth, g_textures[tex].m_dwTileHeight);
1594 DebuggerAppendMsg("X scale: %f, Y scale: %f, %s", gRSP.fTexScaleX, gRSP.fTexScaleY, gRSP.bTextureEnabled?"Enabled":"Disabled");
1595
1596 return true;
1597}
1598
1599void CRender::SaveTextureToFile(int tex, TextureChannel channel, bool bShow)
1600{
1601 TxtrCacheEntry &entry = *(g_textures[tex].pTextureEntry);
1602
1603 CTexture *pBaseTexture = entry.pTexture;
1604 if( pBaseTexture == NULL )
1605 {
1606 TRACE0("Can't dump null texture");
1607 return;
1608 }
1609 CTexture *pEnhancedTexture = entry.pEnhancedTexture ? entry.pEnhancedTexture : entry.pTexture;
1610
1611 bool bInWhole = false;
1612 if( entry.ti.HeightToCreate == entry.ti.HeightToLoad && entry.ti.WidthToCreate == entry.ti.WidthToLoad
1613 && pEnhancedTexture == pBaseTexture )
1614 bInWhole = true;
1615
1616 char filename[256];
1617 if( (gRDP.otherMode.text_tlut>=2 || g_textures[tex].pTextureEntry->ti.Format == TXT_FMT_CI || g_textures[tex].pTextureEntry->ti.Format == TXT_FMT_RGBA) && g_textures[tex].pTextureEntry->ti.Size <= TXT_SIZE_8b )
1618 {
1619 sprintf(filename, "\\%s#%08X#%d#%d_ci", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC,
1620 g_textures[tex].pTextureEntry->ti.Format,
1621 g_textures[tex].pTextureEntry->ti.Size);
1622 SaveCITextureToFile(*(g_textures[tex].pTextureEntry), filename, bShow && bInWhole, false);
1623
1624 sprintf(filename, "\\%s#%08X#%d#%d#%08X_ci", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC,
1625 g_textures[tex].pTextureEntry->ti.Format,
1626 g_textures[tex].pTextureEntry->ti.Size, g_textures[tex].pTextureEntry->dwPalCRC);
1627 SaveCITextureToFile(*(g_textures[tex].pTextureEntry), filename, false, false);
1628
1629 sprintf(filename, "\\%s#%08X#%d#%d#%08X_ciByRGBA", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC,
1630 g_textures[tex].pTextureEntry->ti.Format,
1631 g_textures[tex].pTextureEntry->ti.Size, g_textures[tex].pTextureEntry->dwPalCRC);
1632 SaveTextureToFile(*pBaseTexture, filename, TXT_RGBA, false, false, g_textures[tex].pTextureEntry->ti.WidthToLoad, g_textures[tex].pTextureEntry->ti.HeightToLoad);
1633
1634 DebuggerAppendMsg("Base texture is stored at: %s", filename);
1635
1636 if( !bInWhole && bShow )
1637 {
1638 sprintf(filename, "\\%s#%08X#%d#%d_ci_%s_debugger", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC,
1639 g_textures[tex].pTextureEntry->ti.Format,
1640 g_textures[tex].pTextureEntry->ti.Size, channel == TXT_ALPHA ? "a" : channel == TXT_RGBA ? "all" : "rgb");
1641 SaveTextureToFile(*pEnhancedTexture, filename, channel, true, true);
1642 DebuggerAppendMsg("Whole texture is stored at: %s", filename);
1643 }
1644 }
1645 else
1646 {
1647 sprintf(filename, "\\%s#%08X#%d#%d_%s", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC,
1648 g_textures[tex].pTextureEntry->ti.Format,
1649 g_textures[tex].pTextureEntry->ti.Size, channel == TXT_ALPHA ? "a" : channel == TXT_RGBA ? "all" : "rgb");
1650 SaveTextureToFile(*pBaseTexture, filename, channel, bShow && bInWhole, false,g_textures[tex].pTextureEntry->ti.WidthToLoad, g_textures[tex].pTextureEntry->ti.HeightToLoad);
1651 DebuggerAppendMsg("Base texture is stored at: %s", filename);
1652
1653 if( !bInWhole && bShow )
1654 {
1655 sprintf(filename, "\\%s#%08X#%d#%d_%s_debugger", g_curRomInfo.szGameName, g_textures[tex].pTextureEntry->dwCRC,
1656 g_textures[tex].pTextureEntry->ti.Format,
1657 g_textures[tex].pTextureEntry->ti.Size, channel == TXT_ALPHA ? "a" : channel == TXT_RGBA ? "all" : "rgb");
1658 SaveTextureToFile(*pEnhancedTexture, filename, channel, true, true);
1659 DebuggerAppendMsg("Whole texture is stored at: %s", filename);
1660 }
1661 }
1662}
1663#endif
1664
1665extern RenderTextureInfo gRenderTextureInfos[];
1666void SetVertexTextureUVCoord(TexCord &dst, float s, float t, int tile, TxtrCacheEntry *pEntry)
1667{
1668 RenderTexture &txtr = g_textures[tile];
1669 RenderTextureInfo &info = gRenderTextureInfos[pEntry->txtrBufIdx-1];
1670
1671 uint32 addrOffset = g_TI.dwAddr-info.CI_Info.dwAddr;
1672 uint32 extraTop = (addrOffset>>(info.CI_Info.dwSize-1)) /info.CI_Info.dwWidth;
1673 uint32 extraLeft = (addrOffset>>(info.CI_Info.dwSize-1))%info.CI_Info.dwWidth;
1674
1675 if( pEntry->txtrBufIdx > 0 )
1676 {
1677 // Loading from render_texture or back buffer
1678 s += (extraLeft+pEntry->ti.LeftToLoad)/txtr.m_fTexWidth;
1679 t += (extraTop+pEntry->ti.TopToLoad)/txtr.m_fTexHeight;
1680
1681 s *= info.scaleX;
1682 t *= info.scaleY;
1683 }
1684
1685 dst.u = s;
1686 dst.v = t;
1687}
1688
1689void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, float fTex0S, float fTex0T)
1690{
1691 RenderTexture &txtr = g_textures[0];
1692 if( txtr.pTextureEntry && txtr.pTextureEntry->txtrBufIdx > 0 )
1693 {
1694 ::SetVertexTextureUVCoord(v.tcord[0], fTex0S, fTex0T, 0, txtr.pTextureEntry);
1695 }
1696 else
1697 {
1698 v.tcord[0].u = fTex0S;
1699 v.tcord[0].v = fTex0T;
1700 }
1701}
1702void CRender::SetVertexTextureUVCoord(TLITVERTEX &v, float fTex0S, float fTex0T, float fTex1S, float fTex1T)
1703{
1704 if( (options.enableHackForGames == HACK_FOR_ZELDA||options.enableHackForGames == HACK_FOR_ZELDA_MM) && m_Mux == 0x00262a60150c937fLL && gRSP.curTile == 0 )
1705 {
1706 // Hack for Zelda Sun
1707 Tile &t0 = gRDP.tiles[0];
1708 Tile &t1 = gRDP.tiles[1];
1709 if( t0.dwFormat == TXT_FMT_I && t0.dwSize == TXT_SIZE_8b && t0.dwWidth == 64 &&
1710 t1.dwFormat == TXT_FMT_I && t1.dwSize == TXT_SIZE_8b && t1.dwWidth == 64 &&
1711 t0.dwHeight == t1.dwHeight )
1712 {
1713 fTex0S /= 2;
1714 fTex0T /= 2;
1715 fTex1S /= 2;
1716 fTex1T /= 2;
1717 }
1718 }
1719
1720 RenderTexture &txtr0 = g_textures[0];
1721 if( txtr0.pTextureEntry && txtr0.pTextureEntry->txtrBufIdx > 0 )
1722 {
1723 ::SetVertexTextureUVCoord(v.tcord[0], fTex0S, fTex0T, 0, txtr0.pTextureEntry);
1724 }
1725 else
1726 {
1727 v.tcord[0].u = fTex0S;
1728 v.tcord[0].v = fTex0T;
1729 }
1730
1731 RenderTexture &txtr1 = g_textures[1];
1732 if( txtr1.pTextureEntry && txtr1.pTextureEntry->txtrBufIdx > 0 )
1733 {
1734 ::SetVertexTextureUVCoord(v.tcord[1], fTex1S, fTex1T, 1, txtr1.pTextureEntry);
1735 }
1736 else
1737 {
1738 v.tcord[1].u = fTex1S;
1739 v.tcord[1].v = fTex1T;
1740 }
1741}
1742
1743void CRender::SetClipRatio(uint32 type, uint32 w1)
1744{
1745 bool modified = false;
1746 switch(type)
1747 {
1748 case RSP_MV_WORD_OFFSET_CLIP_RNX:
1749 LOG_UCODE(" RSP_MOVE_WORD_CLIP NegX: %d", (int)(short)w1);
1750 if( gRSP.clip_ratio_negx != (short)w1 )
1751 {
1752 gRSP.clip_ratio_negx = (short)w1;
1753 modified = true;
1754 }
1755 break;
1756 case RSP_MV_WORD_OFFSET_CLIP_RNY:
1757 LOG_UCODE(" RSP_MOVE_WORD_CLIP NegY: %d", (int)(short)w1);
1758 if( gRSP.clip_ratio_negy != (short)w1 )
1759 {
1760 gRSP.clip_ratio_negy = (short)w1;
1761 modified = true;
1762 }
1763 break;
1764 case RSP_MV_WORD_OFFSET_CLIP_RPX:
1765 LOG_UCODE(" RSP_MOVE_WORD_CLIP PosX: %d", (int)(short)w1);
1766 if( gRSP.clip_ratio_posx != -(short)w1 )
1767 {
1768 gRSP.clip_ratio_posx = -(short)w1;
1769 modified = true;
1770 }
1771 break;
1772 case RSP_MV_WORD_OFFSET_CLIP_RPY:
1773 LOG_UCODE(" RSP_MOVE_WORD_CLIP PosY: %d", (int)(short)w1);
1774 if( gRSP.clip_ratio_posy != -(short)w1 )
1775 {
1776 gRSP.clip_ratio_posy = -(short)w1;
1777 modified = true;
1778 }
1779 break;
1780 }
1781
1782 if( modified )
1783 {
1784 UpdateClipRectangle();
1785 }
1786
1787}
1788void CRender::UpdateClipRectangle()
1789{
1790 if( status.bHandleN64RenderTexture )
1791 {
1792 //windowSetting.fMultX = windowSetting.fMultY = 1;
1793 windowSetting.vpLeftW = 0;
1794 windowSetting.vpTopW = 0;
1795 windowSetting.vpRightW = newRenderTextureInfo.bufferWidth;
1796 windowSetting.vpBottomW = newRenderTextureInfo.bufferHeight;
1797 windowSetting.vpWidthW = newRenderTextureInfo.bufferWidth;
1798 windowSetting.vpHeightW = newRenderTextureInfo.bufferHeight;
1799
1800 gRSP.vtxXMul = windowSetting.vpWidthW/2.0f;
1801 gRSP.vtxXAdd = gRSP.vtxXMul + windowSetting.vpLeftW;
1802 gRSP.vtxYMul = -windowSetting.vpHeightW/2.0f;
1803 gRSP.vtxYAdd = windowSetting.vpHeightW/2.0f + windowSetting.vpTopW+windowSetting.toolbarHeightToUse;
1804
1805 // Update clip rectangle by setting scissor
1806
1807 int halfx = newRenderTextureInfo.bufferWidth/2;
1808 int halfy = newRenderTextureInfo.bufferHeight/2;
1809 int centerx = halfx;
1810 int centery = halfy;
1811
1812 gRSP.clip_ratio_left = centerx - halfx * gRSP.clip_ratio_negx;
1813 gRSP.clip_ratio_top = centery - halfy * gRSP.clip_ratio_negy;
1814 gRSP.clip_ratio_right = centerx + halfx * gRSP.clip_ratio_posx;
1815 gRSP.clip_ratio_bottom = centery + halfy * gRSP.clip_ratio_posy;
1816 }
1817 else
1818 {
1819 windowSetting.vpLeftW = int(gRSP.nVPLeftN * windowSetting.fMultX);
1820 windowSetting.vpTopW = int(gRSP.nVPTopN * windowSetting.fMultY);
1821 windowSetting.vpRightW = int(gRSP.nVPRightN* windowSetting.fMultX);
1822 windowSetting.vpBottomW = int(gRSP.nVPBottomN* windowSetting.fMultY);
1823 windowSetting.vpWidthW = int((gRSP.nVPRightN - gRSP.nVPLeftN + 1) * windowSetting.fMultX);
1824 windowSetting.vpHeightW = int((gRSP.nVPBottomN - gRSP.nVPTopN + 1) * windowSetting.fMultY);
1825
1826 gRSP.vtxXMul = windowSetting.vpWidthW/2.0f;
1827 gRSP.vtxXAdd = gRSP.vtxXMul + windowSetting.vpLeftW;
1828 gRSP.vtxYMul = -windowSetting.vpHeightW/2.0f;
1829 gRSP.vtxYAdd = windowSetting.vpHeightW/2.0f + windowSetting.vpTopW+windowSetting.toolbarHeightToUse;
1830
1831 // Update clip rectangle by setting scissor
1832
1833 int halfx = gRSP.nVPWidthN/2;
1834 int halfy = gRSP.nVPHeightN/2;
1835 int centerx = gRSP.nVPLeftN+halfx;
1836 int centery = gRSP.nVPTopN+halfy;
1837
1838 gRSP.clip_ratio_left = centerx - halfx * gRSP.clip_ratio_negx;
1839 gRSP.clip_ratio_top = centery - halfy * gRSP.clip_ratio_negy;
1840 gRSP.clip_ratio_right = centerx + halfx * gRSP.clip_ratio_posx;
1841 gRSP.clip_ratio_bottom = centery + halfy * gRSP.clip_ratio_posy;
1842 }
1843
1844 UpdateScissorWithClipRatio();
1845}
1846
1847void CRender::UpdateScissorWithClipRatio()
1848{
1849 gRSP.real_clip_scissor_left = std::max(gRDP.scissor.left, gRSP.clip_ratio_left);
1850 gRSP.real_clip_scissor_top = std::max(gRDP.scissor.top, gRSP.clip_ratio_top);
1851 gRSP.real_clip_scissor_right = std::min(gRDP.scissor.right,gRSP.clip_ratio_right);
1852 gRSP.real_clip_scissor_bottom = std::min(gRDP.scissor.bottom, gRSP.clip_ratio_bottom);
1853
1854 gRSP.real_clip_scissor_left = std::max(gRSP.real_clip_scissor_left, 0);
1855 gRSP.real_clip_scissor_top = std::max(gRSP.real_clip_scissor_top, 0);
1856 gRSP.real_clip_scissor_right = std::min(gRSP.real_clip_scissor_right,windowSetting.uViWidth-1);
1857 gRSP.real_clip_scissor_bottom = std::min(gRSP.real_clip_scissor_bottom, windowSetting.uViHeight-1);
1858
1859 WindowSettingStruct &w = windowSetting;
1860 w.clipping.left = (uint32)(gRSP.real_clip_scissor_left*windowSetting.fMultX);
1861 w.clipping.top = (uint32)(gRSP.real_clip_scissor_top*windowSetting.fMultY);
1862 w.clipping.bottom = (uint32)(gRSP.real_clip_scissor_bottom*windowSetting.fMultY);
1863 w.clipping.right = (uint32)(gRSP.real_clip_scissor_right*windowSetting.fMultX);
1864 if( w.clipping.left > 0 || w.clipping.top > 0 || w.clipping.right < (uint32)windowSetting.uDisplayWidth-1 ||
1865 w.clipping.bottom < (uint32)windowSetting.uDisplayHeight-1 )
1866 {
1867 w.clipping.needToClip = true;
1868 }
1869 else
1870 {
1871 w.clipping.needToClip = false;
1872 }
1873 w.clipping.width = (uint32)((gRSP.real_clip_scissor_right-gRSP.real_clip_scissor_left+1)*windowSetting.fMultX);
1874 w.clipping.height = (uint32)((gRSP.real_clip_scissor_bottom-gRSP.real_clip_scissor_top+1)*windowSetting.fMultY);
1875
1876 float halfx = gRSP.nVPWidthN/2.0f;
1877 float halfy = gRSP.nVPHeightN/2.0f;
1878 float centerx = gRSP.nVPLeftN+halfx;
1879 float centery = gRSP.nVPTopN+halfy;
1880
1881 gRSP.real_clip_ratio_negx = (gRSP.real_clip_scissor_left - centerx)/halfx;
1882 gRSP.real_clip_ratio_negy = (gRSP.real_clip_scissor_top - centery)/halfy;
1883 gRSP.real_clip_ratio_posx = (gRSP.real_clip_scissor_right - centerx)/halfx;
1884 gRSP.real_clip_ratio_posy = (gRSP.real_clip_scissor_bottom - centery)/halfy;
1885
1886 ApplyScissorWithClipRatio(true);
1887}
1888
1889
1890// Set other modes not covered by color combiner or alpha blender
1891void CRender::InitOtherModes(void)
1892{
1893 ApplyTextureFilter();
1894
1895 //
1896 // I can't think why the hand in mario's menu screen is rendered with an opaque rendermode,
1897 // and no alpha threshold. We set the alpha reference to 1 to ensure that the transparent pixels
1898 // don't get rendered. I hope this doesn't fuck anything else up.
1899 //
1900 if ( gRDP.otherMode.alpha_compare == 0 )
1901 {
1902 if ( gRDP.otherMode.cvg_x_alpha && (gRDP.otherMode.alpha_cvg_sel || gRDP.otherMode.aa_en ) )
1903 {
1904 ForceAlphaRef(128); // Strange, I have to use value=2 for pixel shader combiner for Nvidia FX5200
1905 // for other video cards, value=1 is good enough.
1906 SetAlphaTestEnable(TRUE);
1907 }
1908 else
1909 {
1910 SetAlphaTestEnable(FALSE);
1911 }
1912 }
1913 else if ( gRDP.otherMode.alpha_compare == 3 )
1914 {
1915 //RDP_ALPHA_COMPARE_DITHER
1916 SetAlphaTestEnable(FALSE);
1917 }
1918 else
1919 {
1920 if( (gRDP.otherMode.alpha_cvg_sel ) && !gRDP.otherMode.cvg_x_alpha )
1921 {
1922 // Use CVG for pixel alpha
1923 SetAlphaTestEnable(FALSE);
1924 }
1925 else
1926 {
1927 // RDP_ALPHA_COMPARE_THRESHOLD || RDP_ALPHA_COMPARE_DITHER
1928 if( m_dwAlpha==0 )
1929 ForceAlphaRef(1);
1930 else
1931 ForceAlphaRef(m_dwAlpha);
1932 SetAlphaTestEnable(TRUE);
1933 }
1934 }
1935
1936 if( options.enableHackForGames == HACK_FOR_SOUTH_PARK_RALLY && m_Mux == 0x00121824ff33ffffLL &&
1937 gRSP.bCullFront && gRDP.otherMode.aa_en && gRDP.otherMode.z_cmp && gRDP.otherMode.z_upd )
1938 {
1939 SetZCompare(FALSE);
1940 }
1941
1942
1943 if( gRDP.otherMode.cycle_type >= CYCLE_TYPE_COPY )
1944 {
1945 // Disable zbuffer for COPY and FILL mode
1946 SetZCompare(FALSE);
1947 }
1948 else
1949 {
1950 SetZCompare(gRDP.otherMode.z_cmp);
1951 SetZUpdate(gRDP.otherMode.z_upd);
1952 }
1953
1954 /*
1955 if( options.enableHackForGames == HACK_FOR_SOUTH_PARK_RALLY && m_Mux == 0x00121824ff33ffff &&
1956 gRSP.bCullFront && gRDP.otherMode.z_cmp && gRDP.otherMode.z_upd )//&& gRDP.otherMode.aa_en )
1957 {
1958 SetZCompare(FALSE);
1959 SetZUpdate(FALSE);
1960 }
1961 */
1962}
1963
1964
1965void CRender::SetTextureFilter(uint32 dwFilter)
1966{
1967 if( options.forceTextureFilter == FORCE_DEFAULT_FILTER )
1968 {
1969 switch(dwFilter)
1970 {
1971 case RDP_TFILTER_AVERAGE: //?
1972 case RDP_TFILTER_BILERP:
1973 m_dwMinFilter = m_dwMagFilter = FILTER_LINEAR;
1974 break;
1975 default:
1976 m_dwMinFilter = m_dwMagFilter = FILTER_POINT;
1977 break;
1978 }
1979 }
1980 else
1981 {
1982 switch( options.forceTextureFilter )
1983 {
1984 case FORCE_POINT_FILTER:
1985 m_dwMinFilter = m_dwMagFilter = FILTER_POINT;
1986 break;
1987 case FORCE_LINEAR_FILTER:
1988 m_dwMinFilter = m_dwMagFilter = FILTER_LINEAR;
1989 break;
1990 }
1991 }
1992
1993 ApplyTextureFilter();
1994}
1995
1996bool SaveRGBBufferToFile(char *filename, unsigned char *buf, int width, int height, int pitch)
1997{
1998 if( pitch == -1 )
1999 pitch = width*3;
2000
2001 if( strcasecmp(right(filename,3),"bmp") == 0 )
2002 {
2003 BITMAPFILEHEADER fileHeader;
2004 BITMAPINFOHEADER infoHeader;
2005
2006 infoHeader.biSize = sizeof( BITMAPINFOHEADER );
2007 infoHeader.biWidth = width;
2008 infoHeader.biHeight = height;
2009 infoHeader.biPlanes = 1;
2010 infoHeader.biBitCount = 24;
2011 infoHeader.biCompression = BI_RGB;
2012 infoHeader.biSizeImage = width * height * 3;
2013 infoHeader.biXPelsPerMeter = 0;
2014 infoHeader.biYPelsPerMeter = 0;
2015 infoHeader.biClrUsed = 0;
2016 infoHeader.biClrImportant = 0;
2017
2018 fileHeader.bfType = 19778;
2019 fileHeader.bfSize = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + infoHeader.biSizeImage;
2020 fileHeader.bfReserved1 = fileHeader.bfReserved2 = 0;
2021 fileHeader.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );
2022
2023
2024 FILE *f;
2025 f = fopen(filename, "wb");
2026 if(f != NULL)
2027 {
2028 if (fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 ||
2029 fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1 ||
2030 fwrite(buf, infoHeader.biSizeImage, 1, f) != 1)
2031 printf("failed to write out texture data to image file '%s'", filename);
2032
2033 fclose(f);
2034 return true;
2035 }
2036 else
2037 {
2038 // Do something
2039 TRACE1("Fail to create file %s", filename);
2040 return false;
2041 }
2042 }
2043 else
2044 {
2045 if( strcasecmp(right(filename,4),".png") != 0 ) strcat(filename,".png");
2046
2047 struct BMGImageStruct img;
2048 memset(&img, 0, sizeof(BMGImageStruct));
2049 InitBMGImage(&img);
2050 img.bits = buf;
2051 img.bits_per_pixel = 24;
2052 img.height = height;
2053 img.width = width;
2054 img.scan_width = pitch;
2055 BMG_Error code = WritePNG(filename, img);
2056
2057 if( code == BMG_OK )
2058 return true;
2059 else
2060 return false;
2061 }
2062}
2063
2064bool SaveRGBABufferToPNGFile(char *filename, unsigned char *buf, int width, int height, int pitch)
2065{
2066 if( pitch == -1 )
2067 pitch = width*4;
2068
2069 if( strcasecmp(right(filename,4),".png") != 0 ) strcat(filename,".png");
2070
2071 struct BMGImageStruct img;
2072 memset(&img, 0, sizeof(BMGImageStruct));
2073 InitBMGImage(&img);
2074 img.bits = buf;
2075 img.bits_per_pixel = 32;
2076 img.height = height;
2077 img.width = width;
2078 img.scan_width = pitch;
2079 BMG_Error code = WritePNG(filename, img);
2080
2081 if( code == BMG_OK )
2082 return true;
2083 else
2084 return false;
2085}
2086