1 /*---------------------------------------------------------------------------*
2   Project:  Dolphin/Revolution gx demo
3   File:     ind-pseudo-3d.c
4 
5   Copyright 1998-2006 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 
14 // Demonstrate pseudo-3D maps
15 
16 #include <demo.h>
17 #include <stdlib.h>     // for rand()
18 #include <math.h>       // for sqrtf()
19 
20 /*---------------------------------------------------------------------------*
21    Defines
22  *---------------------------------------------------------------------------*/
23 
24 #define TPL_NAME "gxTests/pseudo.tpl"
25 
26 #define PI    3.14159265358979323846f
27 
28 #define HFW 40     // height-field width
29 #define HFH 40     // height-field height (length)
30 
31 #define NMT  8     // number of tiles
32 
33 #define TIW 64     // tile width
34 #define TIH 64     // tile height
35 
36 #define IMW 128    // indirect map width
37 #define IMH 4      // indirect map height
38 
39 /*---------------------------------------------------------------------------*
40    Rendering parameters
41  *---------------------------------------------------------------------------*/
42 
43 typedef struct
44 {
45     f32 x, y, z;
46     f32 nx, ny, nz;
47     f32 s, t;
48 } coord;
49 
50 coord height[HFH][HFW];                 // height field coordinates
51 
52 void *indMap;                           // indirect map data
53 GXTexObj indMapObj, materialObj;        // texture object data
54 
55 Mtx cameraMtx, objectMtx;               // persistent matrices
56 
57 TPLPalettePtr tpl = NULL;               // tpl file data
58 
59 u32 sendNormal, sendCoords;
60 
61 /*---------------------------------------------------------------------------*
62    Forward references
63  *---------------------------------------------------------------------------*/
64 
65 void createMap(u8 *tex);
66 void adjustPoint(coord height[HFH][HFW], u16 r, u16 c, f32 h);
67 void createHeightField();
68 void sendVertex(coord *d);
69 void setupMatrices(void);
70 void commonInit(void);
71 void updateMatrices(Mtx obj, Mtx cam);
72 void textureMapSetup();
73 void textureMapSetdown();
74 void renderSetup();
75 void updateScene(Mtx cam, f32 r1, f32 r2, f32 x, f32 y, f32 z, f32 mag);
76 void drawHeightField();
77 void drawScene();
78 void main(void);
79 void printIntro( void );
80 
81 /*---------------------------------------------------------------------------*
82    Procedures
83  *---------------------------------------------------------------------------*/
84 
85 // createMap: create the indirect texture map data
86 
createMap(u8 * tex)87 void createMap(u8 *tex)
88 {
89     u16 i, j, n;
90     u32 off;
91 
92     for(i=0; i<IMW; i++)
93     {
94         for(j=0; j<IMH; j++)
95         {
96             // compute T offset base (will be multiplied in mtx by tile size)
97             n  = (u16) (i * NMT/IMW);
98 
99             // add in computed bump alpha
100             n |= (u16) (((i) & 0xf)<<4);
101 
102             // compute offset, store texel
103             off = (u32) ((i&7) | ((i>>3)<<5) | (j<<3));
104             tex[off] = (u8) n;
105         }
106     }
107     // very important: must flush data from CPU cache
108     DCFlushRange((void *) tex, IMW*IMH);
109 }
110 
111 // adjustPoint: adjust height field point vertically based upon random function
112 
adjustPoint(coord height[HFH][HFW],u16 r,u16 c,f32 h)113 void adjustPoint(coord height[HFH][HFW], u16 r, u16 c, f32 h)
114 {
115     s16 i;
116 
117     i = (s16) ((rand() % 100) - 50);
118     h += i/40.0f;
119     if (h < -10.0f || h > 10.0f)
120     {
121         h -= i/20.0f;
122     }
123     height[r][c].z = h;
124 }
125 
126 // createHeightField: Create height map coordinates
127 
createHeightField()128 void createHeightField()
129 {
130     u16 r, c;
131     f32 h;
132     f32 dx, dy;
133     f32 imag;
134 
135     srand(1);
136 
137     // initialize the field
138 
139     for(r=0; r<HFH; r++)
140     {
141         for(c=0; c<HFW; c++)
142         {
143             height[r][c].x  = (f32) c - HFW/2.0f;
144             height[r][c].y  = (f32) r - HFH/2.0f;
145             height[r][c].z  = (f32) 0;
146             height[r][c].nx = (f32) 0;
147             height[r][c].ny = (f32) 0;
148             height[r][c].nz = (f32) 1;
149             height[r][c].s  = (f32) (c & 3)/3.0f;
150             height[r][c].t  = (f32) (r & 3)/3.0f;
151         }
152     }
153 
154     // tweak the leading edge heights
155 
156     for(r=1; r<HFH; r++)
157     {
158         adjustPoint(height, r, 0, height[r-1][0].z);
159     }
160     for(c=1; c<HFW; c++)
161     {
162         adjustPoint(height, 0, c, height[0][c-1].z);
163     }
164 
165     // tweak the interior heights
166 
167     for(r=1; r<HFH; r++)
168     {
169         for(c=1; c<HFW; c++)
170         {
171             h = (height[r-1][c].z + height[r][c-1].z)/2.0f;
172             adjustPoint(height, r, c, h);
173         }
174     }
175 
176     // compute normals
177 
178     for(r=0; r<HFH; r++)
179     {
180         for(c=0; c<HFW; c++)
181         {
182             if (c < 1)
183             {
184                 dx = height[r][c+1].z - height[r][c].z;
185             }
186             else if (c < HFW-1)
187             {
188                 dx = height[r][c+1].z - height[r][c-1].z;
189             }
190             else // c == HFW-1
191             {
192                 dx = height[r][c].z - height[r][c-1].z;
193             }
194 
195             if (r < 1)
196             {
197                 dy = height[r+1][c].z - height[r][c].z;
198             }
199             else if (c < HFH-1)
200             {
201                 dy = height[r+1][c].z - height[r-1][c].z;
202             }
203             else // r == HFH-1
204             {
205                 dy = height[r][c].z - height[r-1][c].z;
206             }
207 
208             imag = 1.0f / sqrtf((dx * dx) + (dy * dy) + 1);
209 
210             height[r][c].nx =  -dx * imag;
211             height[r][c].ny =  -dy * imag;
212             height[r][c].nz = 1.0f * imag;
213         }
214     }
215 }
216 
217 // sendVertex: Send one vertex from the given coordinate structure
218 
sendVertex(coord * d)219 void sendVertex(coord *d)
220 {
221     GXPosition3f32(d->x, d->y, d->z);
222 
223     if (sendNormal)
224         GXNormal3f32(d->nx, d->ny, d->nz);
225 
226     if (sendCoords)
227         GXTexCoord2f32(d->s, d->t);
228 }
229 
230 // setupMatrices: Set up persistent matrices
231 
setupMatrices(void)232 void setupMatrices(void)
233 {
234     Point3d position = {0, -20, 5};
235     Point3d target   = {0, 0, 0};
236     Vec     up       = {0, 0, 1};
237 
238     MTXLookAt(cameraMtx, &position, &up, &target );
239 
240     MTXIdentity(objectMtx);
241 }
242 
243 // commonInit: various state initialization
244 
commonInit(void)245 void commonInit(void)
246 {
247     GXColor back = {0, 0, 128, 255};
248 
249     DEMOInit(NULL);
250     GXSetCopyClear( back, GX_MAX_Z24 );
251 
252     GXSetZMode(TRUE, GX_LEQUAL, TRUE);
253     GXSetZCompLoc(TRUE);
254     GXSetCullMode(GX_CULL_NONE);
255 
256     setupMatrices();
257 }
258 
259 // updateMatrices: Set up position/normal/texture matrices and load them
260 
updateMatrices(Mtx obj,Mtx cam)261 void updateMatrices(Mtx obj, Mtx cam)
262 {
263     Mtx vertexMtx, normalMtx, heightMtx;
264     Mtx textureMtx, tempMtx;
265 
266     // matrix for transforming vertices from object space to eye space.
267     MTXConcat(cam, obj, vertexMtx);
268 
269     // the normal mtx starts out as the inverse transpose of the vertex mtx
270     if (!MTXInverse(vertexMtx, tempMtx))
271         { ASSERTMSG(0,"Singular matrix!\n"); }
272     MTXTranspose(tempMtx, normalMtx);
273 
274     // matrix for specifying material repetitions
275     MTXScale(textureMtx, 1.0f, 1.0f, 1.0f); // no repetitions for this demo
276 
277     // matrix for mapping height to indirect index (0-6)
278     // S gets Z scaled and translated, T gets nothing
279     heightMtx[0][0] =  0.0f;
280     heightMtx[0][1] =  0.0f;
281     heightMtx[0][2] = -0.875f/20.0f;
282     heightMtx[0][3] =  7.0f/16.0f;
283     heightMtx[1][0] =  0.0f;
284     heightMtx[1][1] =  0.0f;
285     heightMtx[1][2] =  0.0f;
286     heightMtx[1][3] =  0.0f;
287 
288     GXLoadPosMtxImm(vertexMtx,  GX_PNMTX0);
289     GXLoadNrmMtxImm(normalMtx,  GX_PNMTX0);
290     GXLoadTexMtxImm(textureMtx, GX_TEXMTX0, GX_MTX2x4);
291     GXLoadTexMtxImm(heightMtx,  GX_TEXMTX1, GX_MTX2x4);
292 }
293 
294 // textureMapSetup: Initial setup for texture maps
295 
textureMapSetup(void)296 void textureMapSetup(void)
297 {
298     // Pre-existing textures are in a tpl file.
299     TPLGetPalette(&tpl, TPL_NAME);
300 
301     // Indirect Map Texture (generated)
302 
303     // Allocate texture in main memory.
304     indMap = MEMAllocFromAllocator(&DemoAllocator1, GXGetTexBufferSize(IMW, IMH, GX_TF_IA4, FALSE, 0));
305 
306     // Create the actual map
307     createMap((u8 *) indMap);
308 
309     // Create texture object.
310     GXInitTexObj(&indMapObj, indMap, IMW, IMH, GX_TF_IA4,
311                  GX_CLAMP, GX_CLAMP, FALSE);
312 
313     // Must set sampling to NEAR to avoid bad artifacts
314     GXInitTexObjLOD( &indMapObj,
315         GX_NEAR, GX_NEAR, 0.0F, 0.0F, 0.0F,
316         GX_FALSE, GX_FALSE, GX_ANISO_1 );
317 
318     GXLoadTexObj(&indMapObj, GX_TEXMAP1);   // Indirect map will be TEXMAP1.
319 
320     // Material Texture (pre-existing)
321     //
322     TPLGetGXTexObjFromPalette(tpl, &materialObj, 0);
323 
324     // Set sampling to NEAR to avoid boundary artifact
325     GXInitTexObjLOD( &materialObj,
326         GX_NEAR, GX_NEAR, 0.0F, 0.0F, 0.0F,
327         GX_FALSE, GX_FALSE, GX_ANISO_1 );
328 
329     GXLoadTexObj(&materialObj, GX_TEXMAP0); // Material will be TEXMAP0
330 
331     // Set up texgen for rendering. The matrices can be changed per-object,
332     // but the texgen needn't change.
333     GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0);
334     GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX1);
335 
336     // Treat the material texture as if it is only one tile large.
337     // Subtract one to deal with boundary artifact
338     GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_ENABLE, TIW-1, TIH-1);
339 
340     // Use position matrix 0 for geometry.
341     GXSetCurrentMtx(GX_PNMTX0);
342 }
343 
344 // textureMapSetdown: free up texture space
345 
textureMapSetdown(void)346 void textureMapSetdown(void)
347 {
348     TPLReleasePalette(&tpl);
349 
350     MEMFreeToAllocator(&DemoAllocator1, indMap);
351 }
352 
353 // renderSetup: setup for actual render
354 
renderSetup(void)355 void renderSetup(void)
356 {
357     Mtx44 pMtx;
358     f32 asp = 10.0f/7.0f;
359     GXLightObj myLight;
360     GXColor white = {255, 255, 255, 255};
361     GXColor grey  = { 63,  63,  63, 255};
362 
363     // Perspective projection
364     MTXFrustum(pMtx, 1, -1, -1*asp, 1*asp, 1, 50);
365     GXSetProjection(pMtx, GX_PERSPECTIVE);
366 
367     // Set up a light
368     GXInitLightPos(&myLight, 0.0F, 0.0F, 0.0F);
369     GXInitLightColor(&myLight, white);
370     GXLoadLightObjImm(&myLight, GX_LIGHT0);
371 
372     // Enable it in one channel
373     GXSetChanCtrl(GX_COLOR0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT0,
374                   GX_DF_CLAMP, GX_AF_NONE);
375 
376     // default mat color = white; set ambient
377     GXSetChanAmbColor(GX_COLOR0A0, grey);
378 
379     // total of 4 rendering stages per pixel
380     GXSetNumTevStages(3);
381     GXSetNumIndStages(1);
382 
383     // Two texture coordinates, one rasterized color.
384     GXSetNumTexGens(2);
385     GXSetNumChans(1);
386 
387     // Indirect Stage 0 -- Sample pseudo-3D map
388     GXSetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD1, GX_TEXMAP1);
389     GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_1, GX_ITS_1);
390 
391     // TEV Stage 0 -- Apply first layer of pseudo-3D
392     //
393     // TEVPREV = TEXC/TEXA
394     //
395     GXSetTevIndTile(GX_TEVSTAGE0,       // tev stage
396                     GX_INDTEXSTAGE0,    // ind stage
397                     TIW,                // tile size S
398                     TIH,                // tile size T
399                     0,                  // tile spacing S
400                     TIH,                // tile spacing T
401                     GX_ITF_4,           // ind tex format
402                     GX_ITM_0,           // ind matrix select
403                     GX_ITB_NONE,        // bias select
404                     GX_ITBA_OFF);       // bump alpha select
405 
406     GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
407     GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
408 
409     // TEV Stage 1 -- Apply second layer of pseudo 3D
410     //                Use bump alpha (normalized) to blend with first layer
411     //                The second layer is "lower" than the first.
412     //
413     //              A        1-C       B        C      D
414     // TEVPREVC = PREVC * (1-BUMPA) + TEXC * (BUMPA) + 0
415     // TEVPREVA = PREVA
416     //
417     GXSetTevIndTile(GX_TEVSTAGE1,       // tev stage
418                     GX_INDTEXSTAGE0,    // ind stage
419                     TIW,                // tile size S
420                     TIH,                // tile size T
421                     0,                  // tile spacing S
422                     TIH,                // tile spacing T
423                     GX_ITF_4,           // ind tex format
424                     GX_ITM_0,           // ind matrix select
425                     GX_ITB_T,           // bias select
426                     GX_ITBA_S);         // bump alpha select
427 
428     GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_ALPHA_BUMP);
429     GXSetTevColorIn(GX_TEVSTAGE1,
430                     GX_CC_CPREV, GX_CC_TEXC, GX_CC_RASA, GX_CC_ZERO);
431     GXSetTevAlphaIn(GX_TEVSTAGE1,
432                     GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
433     GXSetTevColorOp(GX_TEVSTAGE1,
434                     GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
435     GXSetTevAlphaOp(GX_TEVSTAGE1,
436                     GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
437 
438     // TEV Stage 2 -- Apply lighting
439     //
440     // TEVPREVC = PREVC * RASC
441     //
442     GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
443     GXSetTevColorIn(GX_TEVSTAGE2,
444                     GX_CC_ZERO, GX_CC_CPREV, GX_CC_RASC, GX_CC_ZERO);
445     GXSetTevAlphaIn(GX_TEVSTAGE2,
446                     GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV);
447     GXSetTevColorOp(GX_TEVSTAGE2,
448                     GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
449     GXSetTevAlphaOp(GX_TEVSTAGE2,
450                     GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
451 
452     // Vertex packet specification -- Position, normal, and
453     //                                one pair of texture coordinates.
454     GXClearVtxDesc();
455     GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
456     GXSetVtxDesc(GX_VA_NRM, GX_DIRECT);
457     GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
458     GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
459     GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);
460     GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
461 
462     sendNormal = TRUE;
463     sendCoords = TRUE;
464 }
465 
466 // updateScene: Prepare for scene drawing.
467 
updateScene(Mtx cam,f32 r1,f32 r2,f32 x,f32 y,f32 z,f32 mag)468 void updateScene(Mtx cam, f32 r1, f32 r2, f32 x, f32 y, f32 z, f32 mag)
469 {
470     Mtx myobjMtx, tempMtx;
471 
472     // update object matrix
473     MTXRotDeg(tempMtx, 'x', -r2);
474     MTXConcat(tempMtx, objectMtx, objectMtx);
475     MTXRotDeg(tempMtx, 'z', r1);
476     MTXConcat(tempMtx, objectMtx, objectMtx);
477     MTXTrans(tempMtx, x, y, z);
478     MTXConcat(tempMtx, objectMtx, myobjMtx);
479     MTXScale(tempMtx, mag, mag, mag);
480     MTXConcat(tempMtx, myobjMtx, myobjMtx);
481 
482     // Create all other matrices derived from this object matrix.
483     updateMatrices(myobjMtx, cam);
484 }
485 
486 // drawHeightField: Draw the height field using T-strips
487 
drawHeightField()488 void drawHeightField()
489 {
490     u16 r, c;
491 
492     for(r=0; r<HFH-1; r++)
493     {
494         GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT0, HFW*2 );
495 
496         for(c=0; c<HFW; c++)
497         {
498             sendVertex(&height[r][c]);
499             sendVertex(&height[r+1][c]);
500         }
501 
502         GXEnd();
503     }
504 }
505 
506 // drawScene: Draw the full scene
507 
drawScene()508 void drawScene()
509 {
510     // The scene consists only of the height field
511     drawHeightField();
512 }
513 
514 /*---------------------------------------------------------------------------*
515    Application main loop
516  *---------------------------------------------------------------------------*/
517 
main(void)518 void main(void)
519 {
520     f32 rot1;
521     f32 rot2;
522     f32 mag;
523     f32 xp, yp, zp;
524 
525     commonInit();       // Initialize the pipe
526 
527     rot1 = rot2 = 0.0f;
528     mag = 1.0f;
529     xp = yp = zp = 0.0f;
530 
531     textureMapSetup();  // Setup texture objects and all other static things
532 
533     createHeightField();
534 
535     printIntro();
536 
537     renderSetup();      // Setup for actual render
538 
539     while(!(DEMOPadGetButton(0) & PAD_BUTTON_MENU))
540     {
541         DEMOPadRead();
542 
543         rot1 = DEMOPadGetStickX(0)/10.0f;
544         rot2 = DEMOPadGetStickY(0)/10.0f;
545 
546         xp += DEMOPadGetSubStickX(0)/100.0f;
547         zp += DEMOPadGetSubStickY(0)/100.0f;
548 
549         if (DEMOPadGetButtonDown(0) & PAD_BUTTON_A)
550         mag *= 1.25f;
551 
552         if (DEMOPadGetButtonDown(0) & PAD_BUTTON_B)
553         mag *= 0.8f;
554 
555         DEMOBeforeRender();
556 
557         updateScene(cameraMtx, rot1, rot2, xp, yp, zp, mag);
558 
559         drawScene();
560 
561         DEMODoneRender();
562     }
563 
564     textureMapSetdown(); // Free memory and such.
565 
566     OSHalt("End of demo");
567 }
568 
569 /*---------------------------------------------------------------------------*
570     Name:           printIntro
571 
572     Description:    Prints the directions on how to use this demo.
573 
574     Arguments:      none
575 
576     Returns:        none
577  *---------------------------------------------------------------------------*/
printIntro(void)578 static void printIntro( void )
579 {
580     OSReport("\n\n");
581     OSReport("************************************************\n");
582     OSReport("ind-pseudo-3D: demonstrate pseudo-3D texturing\n");
583     OSReport("************************************************\n");
584     OSReport("to quit hit the start button\n");
585     OSReport("\n");
586     OSReport("  Stick X/Y    : rotate model\n");
587     OSReport("  Substick X/Y : translate model\n");
588     OSReport("  A Button     : zoom in\n");
589     OSReport("  B Button     : zoom out\n");
590     OSReport("************************************************\n\n");
591 }
592 
593