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