1 /*---------------------------------------------------------------------------*
2 Project: Dolphin
3 File: G2D.c (Dolphin 2D API by Paul Donnelly, Nov. 1999)
4
5 Copyright 1998, 1999 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Log: G2D.c,v $
14 Revision 1.1.1.1 2005/05/12 02:15:49 yasuh-to
15 Ported from dolphin source tree.
16
17
18 5 2000/03/24 4:08p Carl
19 Adjusted screen height for overscan
20
21 4 2000/01/18 7:54p Hirose
22 Added GXSetNumChans() and GXSetTevOrder() calls to work with
23 new GXInit() defaults
24
25 3 1999/12/14 7:58p Paul
26
27 2 1999/12/12 10:08p Paul
28
29 1 1999/12/09 12:30p Paul
30
31 *---------------------------------------------------------------------------*/
32
33 #include <math.h>
34 #include <G2D.h>
35
36 /*---------------------------------------------------------------------------*
37 Defines
38 *---------------------------------------------------------------------------*/
39
40 /*---------------------------------------------------------------------------*
41 Global variables
42 *---------------------------------------------------------------------------*/
43
44 #define nScrX 640
45 #define nScrY 448
46
47 static struct
48 {
49 u16 nViewportTlcX;
50 u16 nViewportTlcY;
51 u16 nViewportWidth;
52 u16 nViewportHeight;
53
54 G2DPosOri poCam;
55
56 f32 rWorldX;
57 f32 rWorldY;
58 f32 rHalfX;
59 f32 rHalfY;
60 }
61 glob;
62
63
64 /*---------------------------------------------------------------------------*
65 Name: G2DInitSprite
66
67 Description: Precalculates sprite texture coordinates
68
69 Arguments: G2DSprite *sprite Pointer to sprite
70
71 Returns: none
72 *---------------------------------------------------------------------------*/
G2DInitSprite(G2DSprite * sprite)73 void G2DInitSprite( G2DSprite *sprite )
74 {
75 f32 rInvWidth = 1.0F / GXGetTexObjWidth(sprite->to);
76 f32 rInvHeight = 1.0F / GXGetTexObjHeight(sprite->to);;
77
78 sprite->rS0 = ((f32)sprite->nTlcS + 0.5F) * rInvWidth;
79 sprite->rS1 = ((f32)sprite->nTlcS + sprite->nWidth - 0.5F) * rInvWidth;
80 sprite->rT0 = ((f32)sprite->nTlcT + 0.5F) * rInvHeight;
81 sprite->rT1 = ((f32)sprite->nTlcT + sprite->nHeight - 0.5F) * rInvHeight;
82 }
83
84
85 /*---------------------------------------------------------------------------*
86 Name: DrawSprite
87
88 Description: Draws sprite (Tiles with alpha)
89
90 Arguments: none
91
92 Returns: none
93 *---------------------------------------------------------------------------*/
G2DDrawSprite(G2DSprite * sprite,G2DPosOri * po)94 void G2DDrawSprite( G2DSprite *sprite, G2DPosOri *po )
95 {
96 f32 rOX, rOY;
97 f32 rWX, rWY, rHX, rHY;
98 f32 rRelX, rRelY;
99
100 GXClearVtxDesc();
101 GXLoadTexObj(sprite->to, GX_TEXMAP0);
102
103 // Set Position Params
104 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
105 GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
106
107 // Set Tex Coord Params
108 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
109 GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
110 GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
111 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
112 GXSetNumTexGens(1);
113 GXSetNumChans(0);
114
115 // Turn on alpha blending
116 GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
117
118 rOX = po->rOriX * 0.5F;
119 rOY = po->rOriY * 0.5F;
120
121 rWX = sprite->nWidth * rOX;
122 rWY = sprite->nWidth * rOY;
123 rHX = sprite->nHeight * rOX;
124 rHY = sprite->nHeight * rOY;
125
126 rRelX = po->rPosX - glob.poCam.rPosX;
127 rRelY = po->rPosY - glob.poCam.rPosY;
128
129 if (rRelX >= glob.rHalfX) rRelX -= glob.rWorldX;
130 if (rRelX < -glob.rHalfX) rRelX += glob.rWorldX;
131 if (rRelY >= glob.rHalfY) rRelY -= glob.rWorldY;
132 if (rRelY < -glob.rHalfY) rRelY += glob.rWorldY;
133
134 rRelX += glob.poCam.rPosX;
135 rRelY += glob.poCam.rPosY;
136
137 GXBegin(GX_QUADS, GX_VTXFMT0, 4);
138 {
139 GXPosition2f32( rRelX - rHX + rWY, rRelY - rHY - rWX );
140 GXTexCoord2f32( sprite->rS0, sprite->rT1 );
141
142 GXPosition2f32( rRelX + rHX + rWY, rRelY + rHY - rWX );
143 GXTexCoord2f32( sprite->rS0, sprite->rT0 );
144
145 GXPosition2f32( rRelX + rHX - rWY, rRelY + rHY + rWX );
146 GXTexCoord2f32( sprite->rS1, sprite->rT0 );
147
148 GXPosition2f32( rRelX - rHX - rWY, rRelY - rHY + rWX );
149 GXTexCoord2f32( sprite->rS1, sprite->rT1 );
150 }
151 GXEnd();
152 }
153
154
155 /*---------------------------------------------------------------------------*
156 Name: FillSection
157
158 Description: Scanline converts from a tile map to a sort buffer
159 Handles 4 cases separately: 8bit/16bit index, Wrap/No-wrap
160
161 Arguments: lots!
162 (but the function is inline so that should avoid the
163 parameter-passing overhead)
164
165 Returns: none
166 *---------------------------------------------------------------------------*/
FillSection(G2DLayer * layer,s8 * aSortBuffer,s32 * nScanLine,s32 nEvent,s16 * nIdx,s32 * nL,s32 * nR,f32 * rLeft,f32 * rRight,f32 rStep0,f32 rStep1,s32 nMapX,s32 nMapY)167 static inline void FillSection( G2DLayer *layer, s8 *aSortBuffer,
168 s32 *nScanLine, s32 nEvent, s16 *nIdx,
169 s32 *nL, s32 *nR, f32 *rLeft, f32 *rRight,
170 f32 rStep0, f32 rStep1, s32 nMapX, s32 nMapY )
171 {
172 s32 nHMask = (1<<layer->nHS)-1;
173 s32 nVMask = (1<<layer->nVS)-1;
174 s32 nI, nJ, nK, nM;
175 s16 nMaterial;
176 s16 *pAddr;
177
178 if (layer->nBPI == 1)
179 {
180 u8 nTile; // 8 bits per index
181
182 if (layer->bWrap) // Layer wraps
183 {
184 // Scan from top to bottom
185 for(; *nScanLine <= nEvent; (*nScanLine)++)
186 {
187 nJ = *nScanLine - 1;
188 nK = ((nJ + nMapY) & nVMask) << layer->nHS;
189
190 // Scan from Left to Right
191 for(nI=*nL; nI<=*nR; nI++)
192 {
193 nTile = ((u8 *)layer->map)[nK + ((nI + nMapX) & nHMask)];
194 nMaterial = layer->tileDesc[nTile].nMaterial;
195 pAddr = (s16 *)&layer->matDesc[nMaterial].nReserved;
196
197 pAddr[1]++; // Increment count
198 if (*pAddr != *nIdx) // Check continuity
199 {
200 *nIdx += 2;
201 *((s16 *)&aSortBuffer[*pAddr]) = (s16)(*pAddr - *nIdx);
202 }
203 *((u16 *)&aSortBuffer[*nIdx]) = (u16)nTile;
204 *nIdx += 2;
205 aSortBuffer[(*nIdx)++] = (s8)nI;
206 aSortBuffer[(*nIdx)++] = (s8)nJ;
207 *pAddr = *nIdx;
208 }
209 *rLeft += rStep0;
210 *rRight += rStep1;
211 *nL = (s32)floor(*rLeft);
212 *nR = (s32)floor(*rRight);
213 }
214 }
215 else // 8 bits per index, layer doesn't wrap
216 {
217 // Scan from top to bottom
218 for(; *nScanLine <= nEvent; (*nScanLine)++)
219 {
220 nJ = *nScanLine - 1 + nMapY;
221 if (nJ<0)
222 {
223 nK = 0;
224 }
225 else if (nJ>nVMask)
226 {
227 nK = nVMask << layer->nHS;
228 }
229 else
230 {
231 nK = nJ << layer->nHS;
232 }
233
234 nM = *nR+nMapX;
235 // Scan from Left to Right
236 for(nI=(*nL+nMapX); nI<=nM; nI++)
237 {
238 if (nI<0)
239 {
240 nTile = ((u8 *)layer->map)[nK];
241 }
242 else if (nI>nHMask)
243 {
244 nTile = ((u8 *)layer->map)[nK + nHMask];
245 }
246 else
247 {
248 nTile = ((u8 *)layer->map)[nK + nI];
249 }
250
251 nMaterial = layer->tileDesc[nTile].nMaterial;
252 pAddr = (s16 *)&layer->matDesc[nMaterial].nReserved;
253
254 pAddr[1]++; // Increment count
255 if (*pAddr != *nIdx) // Check continuity
256 {
257 *nIdx += 2;
258 *((s16 *)&aSortBuffer[*pAddr]) = (s16)(*pAddr - *nIdx);
259 }
260 *((u16 *)&aSortBuffer[*nIdx]) = (u16)nTile;
261 *nIdx += 2;
262 aSortBuffer[(*nIdx)++] = (s8)(nI - nMapX);
263 aSortBuffer[(*nIdx)++] = (s8)(nJ - nMapY);
264 *pAddr = *nIdx;
265 }
266 *rLeft += rStep0;
267 *rRight += rStep1;
268 *nL = (s32)floor(*rLeft);
269 *nR = (s32)floor(*rRight);
270 }
271 }
272 }
273 else
274 {
275 u16 nTile; // 16 bits per index
276
277 if (layer->bWrap) // Layer wraps
278 {
279 // Scan from top to bottom
280 for(; *nScanLine <= nEvent; (*nScanLine)++)
281 {
282 nJ = *nScanLine - 1;
283 nK = ((nJ + nMapY) & nVMask) << layer->nHS;
284
285 // Scan from Left to Right
286 for(nI=*nL; nI<=*nR; nI++)
287 {
288 nTile = ((u16 *)layer->map)[nK + ((nI + nMapX) & nHMask)];
289 nMaterial = layer->tileDesc[nTile].nMaterial;
290 pAddr = (s16 *)&layer->matDesc[nMaterial].nReserved;
291
292 pAddr[1]++; // Increment count
293 if (*pAddr != *nIdx) // Check continuity
294 {
295 *nIdx += 2;
296 *((s16 *)&aSortBuffer[*pAddr]) = (s16)(*pAddr - *nIdx);
297 }
298 *((u16 *)&aSortBuffer[*nIdx]) = (u16)nTile;
299 *nIdx += 2;
300 aSortBuffer[(*nIdx)++] = (s8)nI;
301 aSortBuffer[(*nIdx)++] = (s8)nJ;
302 *pAddr = *nIdx;
303 }
304 *rLeft += rStep0;
305 *rRight += rStep1;
306 *nL = (s32)floor(*rLeft);
307 *nR = (s32)floor(*rRight);
308 }
309 }
310 else // 16 bits per index, layer doesn't wrap
311 {
312 // Scan from top to bottom
313 for(; *nScanLine <= nEvent; (*nScanLine)++)
314 {
315 nJ = *nScanLine - 1 + nMapY;
316 if (nJ<0)
317 {
318 nK = 0;
319 }
320 else if (nJ>nVMask)
321 {
322 nK = nVMask << layer->nHS;
323 }
324 else
325 {
326 nK = nJ << layer->nHS;
327 }
328
329 nM = *nR+nMapX;
330 // Scan from Left to Right
331 for(nI=*nL+nMapX; nI<=nM; nI++)
332 {
333 if (nI<0)
334 {
335 nTile = ((u16 *)layer->map)[nK];
336 }
337 else if (nI>nHMask)
338 {
339 nTile = ((u16 *)layer->map)[nK + nHMask];
340 }
341 else
342 {
343 nTile = ((u16 *)layer->map)[nK + nI];
344 }
345
346 nMaterial = layer->tileDesc[nTile].nMaterial;
347 pAddr = (s16 *)&layer->matDesc[nMaterial].nReserved;
348
349 pAddr[1]++; // Increment count
350 if (*pAddr != *nIdx) // Check continuity
351 {
352 *nIdx += 2;
353 *((s16 *)&aSortBuffer[*pAddr]) = (s16)(*pAddr - *nIdx);
354 }
355 *((u16 *)&aSortBuffer[*nIdx]) = (u16)nTile;
356 *nIdx += 2;
357 aSortBuffer[(*nIdx)++] = (s8)(nI - nMapX);
358 aSortBuffer[(*nIdx)++] = (s8)(nJ - nMapY);
359 *pAddr = *nIdx;
360 }
361 *rLeft += rStep0;
362 *rRight += rStep1;
363 *nL = (s32)floor(*rLeft);
364 *nR = (s32)floor(*rRight);
365 }
366 }
367 }
368 }
369
370
371 /*---------------------------------------------------------------------------*
372 Name: G2DDrawLayer
373
374 Description: Draw a background layer.
375
376 Arguments: G2DLayer *layer;
377 s8 *aSortBuffer
378 Returns: none
379 *---------------------------------------------------------------------------*/
G2DDrawLayer(G2DLayer * layer,s8 * aSortBuffer)380 void G2DDrawLayer( G2DLayer *layer, s8 *aSortBuffer )
381 {
382 s16 *pAddr;
383 s16 aCount0 = 0;
384 s16 aCount1 = 0;
385 s16 aCount2 = 0;
386 // void *map, s32 nBPI, s32 nHS, s32 nVS,
387 // s32 bWrap, TileDesc tileDesc,
388
389 //s32 nWidth = 1<<layer->nHS;
390 //s32 nHeight = 1<<layer->nVS;
391
392 f32 rInvTileWidth = 1.0F / layer->nTileWidth;
393 f32 rInvTileHeight = 1.0F / layer->nTileHeight;
394
395 s16 nIdx;
396 s32 nI, nJ, nK, nL, nR;
397 f32 rX, rY;
398 f32 rTlcX, rTrcX, rBlcX, rBrcX;
399 f32 rTlcY, rTrcY, rBlcY, rBrcY;
400 s32 nScanLine;
401 f32 rLeft, rRight, rLeftY, rRightY;
402 f32 rStep0, rStep1, rMid;
403 s32 nEvent0, nEvent1, nEvent2;
404 f32 rFrcX, rFrcY;
405 s32 nMapX, nMapY;
406 s32 nLocalMapX, nLocalMapY;
407 f32 rCamOriX, rCamOriY;
408 s16 nTile, nMaterial;
409
410 // Turn on alpha blending always
411 GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
412
413 // Initialize NumTextures + 2 streams for sorting by texture/color
414 for( nI=0; nI<layer->nNumMaterials; nI++)
415 {
416 pAddr = (s16 *)&layer->matDesc[nI].nReserved;
417
418 pAddr[0] = (s16)(nI<<1); // idx
419 pAddr[1] = 0; // Count
420 }
421 nIdx = (s16)((nI-1)<<1);
422
423 {
424 rFrcX = glob.poCam.rPosX * rInvTileWidth;
425 rFrcY = glob.poCam.rPosY * rInvTileHeight;
426
427 nMapX = (s32)rFrcX;
428 nMapY = (s32)rFrcY;
429
430 rFrcX -= nMapX;
431 rFrcY -= nMapY;
432 }
433
434 // Avoid the need for special cases when the screen is axis aligned
435 // by rotating the screen slightly
436 rCamOriX = glob.poCam.rOriX;
437 rCamOriY = glob.poCam.rOriY;
438
439 if ((rCamOriX < 0.0001) && (rCamOriX > -0.0001) )
440 {
441 rCamOriX = 0.0001F; // This is an approximation to a small rotation
442 }
443 else if ((rCamOriY < 0.0001) && (rCamOriY > -0.0001) )
444 {
445 rCamOriY = 0.0001F;
446 }
447
448 rX = glob.nViewportWidth * 0.5F;
449 rY = glob.nViewportHeight * 0.5F;
450
451 // scan-line convert a rectangle for the screen to find the minimal
452 // set of tiles that need to be rendered.
453
454 rTlcX = rFrcX + (((rY * rCamOriX) + (rX * rCamOriY)) * rInvTileWidth);
455 rTlcY = rFrcY + (((rY * rCamOriY) - (rX * rCamOriX)) * rInvTileHeight);
456
457 rTrcX = rFrcX + (((rY * rCamOriX) - (rX * rCamOriY)) * rInvTileWidth);
458 rTrcY = rFrcY + (((rY * rCamOriY) + (rX * rCamOriX)) * rInvTileHeight);
459
460 rBlcX = rFrcX - (((rY * rCamOriX) - (rX * rCamOriY)) * rInvTileWidth);
461 rBlcY = rFrcY - (((rY * rCamOriY) + (rX * rCamOriX)) * rInvTileHeight);
462
463 rBrcX = rFrcX - (((rY * rCamOriX) + (rX * rCamOriY)) * rInvTileWidth);
464 rBrcY = rFrcY - (((rY * rCamOriY) - (rX * rCamOriX)) * rInvTileHeight);
465
466 // Sort the corners on Y
467
468 if (rCamOriY < 0)
469 {
470 if (rCamOriX >= 0)
471 {
472 nScanLine = 1 + (s32)floor(rTlcY);
473 rY = (nScanLine - rTlcY); // Fraction of Y
474 rLeft = rTlcX;
475 rLeftY = rBlcY;
476 rRightY = rTrcY;
477 nEvent2 = (s32)floor(rBrcY);
478
479 rStep0 = rCamOriX / rCamOriY;
480 rStep1 = -rCamOriY / rCamOriX;
481 }
482 else
483 {
484 nScanLine = 1 + (s32)floor(rTrcY);
485 rY = (nScanLine - rTrcY);
486 rLeft = rTrcX;
487 rLeftY = rTlcY;
488 rRightY = rBrcY;
489 nEvent2 = (s32)floor(rBlcY);
490
491 rStep0 = -rCamOriY / rCamOriX; // ALERT: Assumes width==height
492 rStep1 = rCamOriX / rCamOriY;
493 }
494 }
495 else
496 {
497 if (rCamOriX >= 0)
498 {
499 nScanLine = 1 + (s32)floor(rBlcY);
500 rY = (nScanLine - rBlcY); // Fraction of Y
501 rLeft = rBlcX;
502 rLeftY = rBrcY;
503 rRightY = rTlcY;
504 nEvent2 = (s32)floor(rTrcY);
505
506 rStep0 = -rCamOriY / rCamOriX; // ALERT: Assumes width==height
507 rStep1 = rCamOriX / rCamOriY;
508 }
509 else
510 {
511 nScanLine = 1 + (s32)floor(rBrcY);
512 rY = (nScanLine - rBrcY);
513 rLeft = rBrcX;
514 rLeftY = rTrcY;
515 rRightY = rBlcY;
516 nEvent2 = (s32)floor(rTlcY);
517
518 rStep0 = rCamOriX / rCamOriY; // ALERT: Assumes width==height
519 rStep1 = -rCamOriY / rCamOriX;
520 }
521 }
522
523 {
524 f32 rRatio = (f32) layer->nTileHeight / layer->nTileWidth;
525 rStep0 *= rRatio;
526 rStep1 *= rRatio;
527 }
528
529 rRight = rLeft + rY * rStep1;
530 rLeft += rY * rStep0;
531
532 // Scanline Rasterizing algorithm
533 // Top line is a special case
534 if (rLeftY < rRightY)
535 {
536 nEvent0 = (s32)floor(rLeftY);
537 nEvent1 = (s32)floor(rRightY);
538 rMid = rStep1;
539 }
540 else
541 {
542 nEvent0 = (s32)floor(rRightY);
543 nEvent1 = (s32)floor(rLeftY);
544 rMid = rStep0;
545 }
546
547 nL = (s32)floor(rLeft);
548 nR = (s32)floor(rRight);
549
550 nLocalMapX = nMapX;
551 nLocalMapY = nMapY;
552
553 if (!(layer->bWrap)) // Special care is needed when map doesn't wrap
554 {
555 f32 rInvTileWidth = 1.0F / layer->nTileWidth;
556 f32 rInvTileHeight = 1.0F / layer->nTileHeight;
557 f32 rLocalPosX = glob.poCam.rPosX;
558 f32 rLocalPosY = glob.poCam.rPosY;
559 f32 rSplitX = 0.5F * (glob.rWorldX + (layer->nTileWidth * (1<<layer->nHS)));
560 f32 rSplitY = 0.5F * (glob.rWorldY + (layer->nTileHeight * (1<<layer->nVS)));
561
562 if (rLocalPosX >= rSplitX) rLocalPosX -= glob.rWorldX;
563 if (rLocalPosY >= rSplitY) rLocalPosY -= glob.rWorldY;
564
565 rFrcX = rLocalPosX * rInvTileWidth;
566 rFrcY = rLocalPosY * rInvTileHeight;
567
568 nLocalMapX = (s32)floor(rFrcX);
569 nLocalMapY = (s32)floor(rFrcY);
570 }
571
572 FillSection(layer, aSortBuffer, &nScanLine, nEvent0, &nIdx, &nL, &nR, &rLeft, &rRight, rStep0, rStep1, nLocalMapX, nLocalMapY);
573
574 {
575 pAddr = (s16 *)&layer->matDesc[0].nReserved;
576 aCount0 = pAddr[1];
577 pAddr = (s16 *)&layer->matDesc[1].nReserved;
578 aCount1 = pAddr[1];
579 pAddr = (s16 *)&layer->matDesc[2].nReserved;
580 aCount2 = pAddr[1];
581 }
582
583 // The case where Event0 == Event1 must be handled
584 if (nScanLine > rLeftY)
585 {
586 rLeft -= (nScanLine - rLeftY) * rStep0;
587 nL = (s32)floor(rLeft);
588 rLeft += (nScanLine - rLeftY - 1) * rStep1;
589 rLeftY = 1000; // Make sure the test further down fails
590 }
591
592 if (nScanLine > rRightY)
593 {
594 rRight -= (nScanLine - rRightY) * rStep1;
595 nR = (s32)floor(rRight);
596 rRight += (nScanLine - rRightY - 1) * rStep0;
597 rRightY = 1000; // Make sure the test further down fails
598 }
599
600 FillSection(layer, aSortBuffer, &nScanLine, nEvent1, &nIdx, &nL, &nR, &rLeft, &rRight, rMid, rMid, nLocalMapX, nLocalMapY);
601
602 {
603 pAddr = (s16 *)&layer->matDesc[0].nReserved;
604 aCount0 = pAddr[1];
605 pAddr = (s16 *)&layer->matDesc[1].nReserved;
606 aCount1 = pAddr[1];
607 pAddr = (s16 *)&layer->matDesc[2].nReserved;
608 aCount2 = pAddr[1];
609 }
610
611 if (nScanLine > rLeftY)
612 {
613 rLeft -= (nScanLine - rLeftY) * rStep0;
614 nL = (s32)floor(rLeft);
615 rLeft += (nScanLine - rLeftY - 1) * rStep1;
616 }
617
618 if (nScanLine > rRightY)
619 {
620 rRight -= (nScanLine - rRightY) * rStep1;
621 nR = (s32)floor(rRight);
622 rRight += (nScanLine - rRightY - 1) * rStep0;
623 }
624
625 FillSection(layer, aSortBuffer, &nScanLine, nEvent2+1, &nIdx, &nL, &nR, &rLeft, &rRight, rStep1, rStep0, nLocalMapX, nLocalMapY);
626
627 {
628 pAddr = (s16 *)&layer->matDesc[0].nReserved;
629 aCount0 = pAddr[1];
630 pAddr = (s16 *)&layer->matDesc[1].nReserved;
631 aCount1 = pAddr[1];
632 pAddr = (s16 *)&layer->matDesc[2].nReserved;
633 aCount2 = pAddr[1];
634 }
635
636 // We now have the tiles sorted into streams by material:
637
638 for( nMaterial=0; nMaterial<layer->nNumMaterials; nMaterial++)
639 {
640 pAddr = (s16 *)&layer->matDesc[nMaterial].nReserved;
641
642 if (pAddr[1] == 0) // Count
643 {
644 continue;
645 }
646
647 switch( layer->matDesc[nMaterial].nCategory )
648 {
649 case G2D_CTG_EMPTY:
650 {
651 continue; // Empty tiles don't need to be rendered
652 }
653
654 case G2D_CTG_RGBA_INDEX8:
655 {
656 // Colored tiles using color index
657 GXClearVtxDesc();
658
659 // Set Position Params
660 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); //PositionShift);
661 GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
662
663 // Set Color for Vertex Format 0
664 GXSetNumTexGens(0);
665 GXSetVtxDesc(GX_VA_TEX0, GX_NONE);
666 GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
667 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
668 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
669 GXSetVtxDesc(GX_VA_CLR0, GX_INDEX8);
670 GXSetArray(GX_VA_CLR0, layer->matDesc[nMaterial].clut, 4);
671
672 GXSetNumChans(1);
673 GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX,
674 GX_LIGHT0, GX_DF_NONE, GX_AF_NONE);
675
676 nIdx = (s16)(nMaterial<<1);
677 GXBegin(GX_QUADS, GX_VTXFMT0, (u16)(pAddr[1]<<2));
678 for(nK = (s32)pAddr[1]; nK--;)
679 {
680 f32 rI, rJ;
681 u8 nCI;
682
683 nTile = *((s16 *)&aSortBuffer[nIdx]);
684 if (nTile < 0)
685 {
686 nIdx -= nTile;
687 nTile = *((s16 *)&aSortBuffer[nIdx]);
688 }
689 nIdx+=2;
690 nI = aSortBuffer[nIdx++];
691 nJ = aSortBuffer[nIdx++];
692
693 rI = (f32)(nI + nMapX) * layer->nTileWidth;
694 rJ = (f32)(nJ + nMapY) * layer->nTileHeight;
695
696 nCI = layer->tileDesc[nTile].nCI;
697
698 GXPosition2f32( rI + (f32)layer->nTileWidth, rJ );
699 GXColor1x8( nCI );
700
701 GXPosition2f32( rI + (f32)layer->nTileWidth, rJ + (f32)layer->nTileHeight );
702 GXColor1x8( nCI );
703
704 GXPosition2f32( rI , rJ + (f32)layer->nTileHeight );
705 GXColor1x8( nCI );
706
707 GXPosition2f32( rI , rJ );
708 GXColor1x8( nCI );
709 }
710 GXEnd();
711 break;
712 }
713
714 case G2D_CTG_RGB_DIRECT:
715 {
716 // Colored tiles using color index
717 GXClearVtxDesc();
718
719 // Set Position Params
720 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); //PositionShift);
721 GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
722
723 // Set Color for Vertex Format 0
724 GXSetNumTexGens(0);
725 GXSetVtxDesc(GX_VA_TEX0, GX_NONE);
726 GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
727 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
728 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGB, GX_RGB8, 0);
729 GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
730
731 GXSetNumChans(1);
732 GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX,
733 GX_LIGHT0, GX_DF_NONE, GX_AF_NONE);
734
735 nIdx = (s16)(nMaterial<<1);
736 GXBegin(GX_QUADS, GX_VTXFMT0, (u16)(pAddr[1]<<2));
737 for(nK = (s32)pAddr[1]; nK--;)
738 {
739 f32 rI, rJ;
740 u8 nR, nG, nB;
741
742 nTile = *((s16 *)&aSortBuffer[nIdx]);
743 if (nTile < 0)
744 {
745 nIdx -= nTile;
746 nTile = *((s16 *)&aSortBuffer[nIdx]);
747 }
748 nIdx+=2;
749 nI = aSortBuffer[nIdx++];
750 nJ = aSortBuffer[nIdx++];
751
752 rI = (f32)(nI + nMapX) * layer->nTileWidth;
753 rJ = (f32)(nJ + nMapY) * layer->nTileHeight;
754
755 nR = layer->tileDesc[nTile].nS;
756 nG = layer->tileDesc[nTile].nT;
757 nB = layer->tileDesc[nTile].nCI;
758
759 GXPosition2f32( rI + (f32)layer->nTileWidth, rJ );
760 GXColor3u8( nR, nG, nB );
761
762 GXPosition2f32( rI + (f32)layer->nTileWidth, rJ + (f32)layer->nTileHeight );
763 GXColor3u8( nR, nG, nB );
764
765 GXPosition2f32( rI , rJ + (f32)layer->nTileHeight );
766 GXColor3u8( nR, nG, nB );
767
768 GXPosition2f32( rI , rJ );
769 GXColor3u8( nR, nG, nB );
770 }
771 GXEnd();
772 break;
773 }
774
775 case G2D_CTG_TEXTURE:
776 {
777 f32 rInvTexWidth = 1.0F / GXGetTexObjWidth(layer->matDesc[nMaterial].to);
778 f32 rInvTexHeight = 1.0F / GXGetTexObjHeight(layer->matDesc[nMaterial].to);
779 f32 rWidth = layer->nTileWidth * rInvTexWidth;
780 f32 rHeight = layer->nTileHeight * rInvTexHeight;
781 f32 rS0 = 0; //0.5F * rInvTexWidth;
782 f32 rT0 = 0; //0.5F * rInvTexHeight;
783 f32 rS1 = rWidth;// - rInvTexWidth;
784 f32 rT1 = rHeight;// - rInvTexHeight;
785
786 // Second, the textured tiles
787 GXClearVtxDesc();
788 GXLoadTexObj(layer->matDesc[nMaterial].to, GX_TEXMAP0);
789
790 // Set Position Params
791 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
792 GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
793
794 // Set Tex Coord Params
795 GXSetNumTexGens(1);
796 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
797 GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
798
799 if (layer->matDesc[nMaterial].color)
800 {
801 GXSetNumChans(1);
802 GXSetChanMatColor(GX_COLOR0A0, *layer->matDesc[nMaterial].color);
803 GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_REG, GX_SRC_REG,
804 GX_LIGHT0, GX_DF_NONE, GX_AF_NONE);
805 GXSetTevOp(GX_TEVSTAGE0, GX_MODULATE);
806 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
807 }
808 else
809 {
810 GXSetNumChans(0);
811 GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
812 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
813 }
814
815 nIdx = (s16)(nMaterial<<1);
816 GXBegin(GX_QUADS, GX_VTXFMT0, (u16)(pAddr[1]<<2));
817 for(nK = (s32)pAddr[1]; nK--;)
818 {
819 f32 rI, rJ, rS, rT;
820
821 nTile = *((s16 *)&aSortBuffer[nIdx]);
822 if (nTile < 0)
823 {
824 nIdx -= nTile;
825 nTile = *((s16 *)&aSortBuffer[nIdx]);
826 }
827 nIdx+=2;
828 nI = aSortBuffer[nIdx++];
829 nJ = aSortBuffer[nIdx++];
830
831 rS = (layer->tileDesc[nTile].nS * rWidth) + rS0;
832 rT = (layer->tileDesc[nTile].nT * rHeight) + rT0;
833 rI = (f32)(nI + nMapX) * layer->nTileWidth;
834 rJ = (f32)(nJ + nMapY) * layer->nTileHeight;
835
836 GXPosition2f32( rI + (f32)layer->nTileWidth, rJ );
837 GXTexCoord2f32( rS + rS1 , rT );
838
839 GXPosition2f32( rI + (f32)layer->nTileWidth, rJ + (f32)layer->nTileHeight );
840 GXTexCoord2f32( rS + rS1 , rT + rT1 );
841
842 GXPosition2f32( rI , rJ + (f32)layer->nTileHeight );
843 GXTexCoord2f32( rS , rT + rT1 );
844
845 GXPosition2f32( rI , rJ );
846 GXTexCoord2f32( rS , rT );
847 }
848 GXEnd();
849 break;
850 }
851 }
852 }
853 }
854
855
856 /*---------------------------------------------------------------------------*
857 Name: G2DSetCamera
858
859 Description: Set up a viewing matrix corresponding to a camera
860 at the supplied position and orientation
861
862 Arguments: G2DPosOri *po Position and Orientation
863 Returns: none
864 *---------------------------------------------------------------------------*/
G2DSetCamera(G2DPosOri * po)865 void G2DSetCamera( G2DPosOri *po )
866 {
867 Mtx mView;
868 Vec vPos;
869 Vec vUp;
870 Vec vAt;
871 f32 rX, rY;
872
873 glob.poCam = *po;
874
875 // Setup vertex transform (no need to transform normals for 2D)
876
877 vUp.x = po->rOriX;
878 vUp.y = po->rOriY;
879 vUp.z = 0.0F;
880
881 rX = ((nScrX - glob.nViewportWidth)>>1) - glob.nViewportTlcX;
882 rY = ((nScrY - glob.nViewportHeight)>>1) - glob.nViewportTlcY;
883
884 vPos.x = po->rPosX - (vUp.x * rY) - (vUp.y * rX);
885 vPos.y = po->rPosY + (vUp.x * rX) - (vUp.y * rY);
886 vPos.z = -300.0F;
887
888 vAt.x = vPos.x;
889 vAt.y = vPos.y;
890 vAt.z = 0.0F;
891
892 MTXLookAt(mView, &vPos, &vUp, &vAt);
893 GXLoadPosMtxImm(mView, GX_PNMTX0);
894 }
895
896
897 /*---------------------------------------------------------------------------*
898 Name: G2DInitWorld
899
900 Description: Initialize world
901
902 Arguments: u32 nWorldX world width (in pixels)
903 u32 nWorldY world height (in pixels)
904 Returns: none
905 *---------------------------------------------------------------------------*/
G2DInitWorld(u32 nWorldX,u32 nWorldY)906 void G2DInitWorld( u32 nWorldX, u32 nWorldY )
907 {
908 Mtx44 mProjection;
909
910 // Store world dimensions in global variables
911 glob.rWorldX = (f32)nWorldX;
912 glob.rWorldY = (f32)nWorldY;
913 glob.rHalfX = (f32)(nWorldX>>1);
914 glob.rHalfY = (f32)(nWorldY>>1);
915
916 // Turn off Z buffer
917 GXSetZMode(GX_FALSE, GX_ALWAYS, GX_TRUE);
918
919 // Set up orthographic projection
920 MTXOrtho(mProjection, nScrY>>1, -(nScrY>>1), -(nScrX>>1), nScrX>>1, 100, 1000);
921 GXSetProjection(mProjection, GX_ORTHOGRAPHIC);
922 }
923
924
925 /*---------------------------------------------------------------------------*
926 Name: G2DSetViewport
927
928 Description: Set up viewport in screenspace
929
930 Arguments: u16 nLeft Leftmost X coordinate of viewport (in pixels)
931 u16 nTop Topmost Y coordinate of viewport (in pixels)
932 u16 nWidth Width of viewport (in pixels)
933 u16 nHeight Height of viewport (in pixels)
934 Returns: none
935 *---------------------------------------------------------------------------*/
G2DSetViewport(u16 nLeft,u16 nTop,u16 nWidth,u16 nHeight)936 void G2DSetViewport( u16 nLeft, u16 nTop, u16 nWidth, u16 nHeight )
937 {
938 // Set up internal global variable used by renderer
939 glob.nViewportTlcX = nLeft;
940 glob.nViewportTlcY = nTop;
941 glob.nViewportWidth = nWidth;
942 glob.nViewportHeight = nHeight;
943
944 // Use scissoring to clip to the viewport
945 GXSetScissor(nLeft, nTop, nWidth, nHeight);
946 }
947