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