1 /*---------------------------------------------------------------------------*
2   Project:  Dolphin/Revolution gx demo
3   File:     paraboloidmap.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 #include <demo.h>
15 #include <math.h>
16 
17 //
18 //  Functions to convert a cube map into a dual paraboloid  map
19 //
20 
21 /*>*******************************(*)*******************************<*/
22 
23 // can be used by the app to show only certain cube faces
24 u8 CubeFaceStart = 0;
25 u8 CubeFaceEnd   = 5;
26 u8 CubeTevMode   = 0;
27 
28 // cube map selector
29 static u8 remap_map[2][5] = {{2, 0, 3, 5, 4},  // rt, lf, bk, tp, bt
30                              {0, 1, 2, 4, 5}}; // rt, fr, lf, tp, bt
31 
32 // axis1,angle1 selector
33 static u8 remap_a1[2][5] = {{2, 0, 3, 4, 5},  // back
34                             {2, 1, 0, 4, 5}}; // front
35 
36 // angle 2 selector
37 static u8 remap_a2[2][5] = {{0, 0, 0, 0, 0},  // back
38                             {0, 0, 0, 1, 1}}; // front
39 
40 // light dir selector
41 static u8 remap_lit[2][5] = {{2, 0, 1, 3, 4},  // back
42                              {2, 1, 0, 4, 3}}; // front
43 
44 //
45 //  used to rotate the projection for each face of the cube
46 //
47 static f32 angle1[6] = {90.0F, 180.0F, 270.0F, 180.0F, -90.0F, 90.0F};
48 static char axis1[6] = { 'y',    'y',    'y',    'y',    'x',   'x' };
49 //
50 //  used for the top and bottom faces
51 //
52 static
53 f32 angle2[2]   = {0.0F, 180.0F}; // around y axis
54 
55 
56 /*>*******************************(*)*******************************<*/
57 void genMapSphere        ( void**    display_list,
58                            u32*      size,
59                            u16       tess,
60                            GXVtxFmt  fmt );
61 
62 void drawParaboloidMap   ( GXTexObj* cubemap,
63                            GXTexObj* spheremap,
64                            void*     dl,
65                            u32       dlsz,
66                            GXBool    front );
67 /*>*******************************(*)*******************************<*/
68 
69 
70 /*---------------------------------------------------------------------------*
71     Name:           genMapSphere
72 
73     Description:    Initialize hemisphere geometry display list.
74 
75                     This function only generates geometry for a hemisphere
76                     since it is always facing the viewpoint (viewed from the
77                     north pole).  Computes a position on the hemisphere
78                     and a normal that is a modified reflection vector.
79 
80                     The reflection vector, assuming the eye point is
81                     (0, 0, 1) is related to the normal by:
82                     Rx = 2NxNz, Ry = 2NyNz, Rz = 2NzNz - 1
83 
84                     The s,t coordinates for a parabolic reflection are
85                     generated using:
86 
87                     s = -Rx / (1 + Rz), t = -Ry / (1 + Rz)
88 
89                     So the 'normal' computed here is:
90 
91                     nx = -Rx, ny = -Ry, nz = (1 + Rz)
92 
93                     The reflection vectors will be projected onto
94                     each face of a cube-map to generate texture coordinates.
95                     See drawParaboloidMap for further details.
96 
97                     This function creates a display list which will be called
98                     multiple times by drawParaboloidMap.  The Mac emulator
99                     version calls the immediate mode code repeatedly since
100                     the display list functionality is not fully emulated.
101 
102                     amount of buffer required?
103                         ((tess+1) + (tess-2)*(tess+1)*2) * 6 * sizeof()
104                         (rounded up to 32B).
105 
106     Arguments:      tess = amount of tessellation desired
107                     fmt  = vertex format to use for geometry
108 
109     Returns:        display_list = pointer to the display list
110                     size         = size of the display list
111  *---------------------------------------------------------------------------*/
112 #define M_PI  3.14159265F
113 
genMapSphere(void ** display_list,u32 * size,u16 tess,GXVtxFmt fmt)114 void genMapSphere( void**    display_list,
115                    u32*      size,
116                    u16       tess,
117                    GXVtxFmt  fmt )
118 {
119     f32 r = 1.0f, r1, r2, z1, z2;
120     f32 n1x, n1y, n1z;
121     f32 n2x, n2y, n2z;
122     f32 theta, phi;
123     u16 nlon = tess, nlat = tess;
124     s32 i, j;
125     u32 dl_sz = ((tess+1) + (tess-2)*(tess+1)*2) * 6 * sizeof(f32); // bytes
126 
127     dl_sz = OSRoundUp32B(dl_sz);
128     *display_list = (void *)NULL;
129 
130     *display_list = (void *)MEMAllocFromAllocator(&DemoAllocator1, dl_sz);
131     GXBeginDisplayList(*display_list, dl_sz);
132 
133     GXClearVtxDesc();
134     GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
135     GXSetVtxDesc(GX_VA_NRM, GX_DIRECT);
136 
137     // could maybe get away with 8-bits?
138     GXSetVtxAttrFmt(fmt, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
139     GXSetVtxAttrFmt(fmt, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);
140 
141     //
142     //  Draw a fan for the pole
143     //
144     GXBegin(GX_TRIANGLEFAN, GX_VTXFMT7, (u16)(nlon+2));
145         theta = M_PI*1.0f/nlat;
146         r2 = r*sinf(theta);
147         z2 = r*cosf(theta);
148         n2z = 2*z2*z2;
149         n1z = 2*r*r;
150 
151         GXPosition3f32(0.0F, 0.0F, 1.0F);
152         GXNormal3f32(0.0F, 0.0F, n1z); // refl vector
153         for (j = 0, phi = 0.0f; j <= nlon; j++, phi = -2*M_PI*j/nlon) {
154             n2x = r2*cosf(phi);
155             n2y = r2*sinf(phi);
156             GXPosition3f32(n2x, n2y, z2);
157             GXNormal3f32(-2*n2x*z2, -2*n2y*z2, n2z); // refl vector
158         }
159     GXEnd();
160 
161     //
162     //  Draw circular strips down to the equator
163     //
164     for (i = 2; i < nlat; i++)
165     {
166         theta = M_PI*i/nlat;
167         r1 = r*sinf(M_PI*(i-1)/nlat);
168         z1 = r*cosf(M_PI*(i-1)/nlat);
169         r2 = r*sinf(theta);
170         z2 = r*cosf(theta);
171 
172         n1z = 2*z1*z1; // parabolic reflection vector z
173         n2z = 2*z2*z2; // parabolic reflection vector z
174 
175         // only render hemisphere, quit at equator
176         if (fabs(z1) < 0.01f || fabs(z2) < 0.01f)
177         {
178             break;
179         }
180 
181         GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT7, (u16)((nlon+1)*2));
182             for (j = 0, phi = 0.0f; j <= nlon; j++, phi = 2*M_PI*j/nlon)
183             {
184                 n2x = r2*cosf(phi);
185                 n2y = r2*sinf(phi);
186                 GXPosition3f32(n2x, n2y, z2);
187                 GXNormal3f32(-2*n2x*z2, -2*n2y*z2, n2z); // refl vector
188                 n1x = r1*cosf(phi);
189                 n1y = r1*sinf(phi);
190                 GXPosition3f32(n1x, n1y, z1);
191                 GXNormal3f32(-2*n1x*z1, -2*n1y*z1, n1z); // refl vector
192             }
193         GXEnd();
194     }
195 
196     *size = GXEndDisplayList();
197 
198     if (*size > dl_sz)
199     {
200         OSReport("Error allocating display list (%d, %d)\n", dl_sz, *size);
201         OSHalt("Exiting");
202     }
203 }
204 
205 
206 /*---------------------------------------------------------------------------*
207     Name:           drawSphereMap
208 
209     Description:    Creates a sphere map from a cube map.
210 
211                     Projects the reflection vectors of a hemisphere onto
212                     each face of the cube map.  The projection is clipped
213                     using vertex lighting so that only the forward projection
214                     is visible.  Each cube-face texture should have a border
215                     of alpha=0 to avoid streaking outside the desired
216                     projection.
217 
218                     The hemisphere geometry is created using genMapSphere and
219                     is in form of a display list that gets called once for each
220                     cube face.  The display list only needs to be created once.
221 
222                     This function trashes some state, like viewport, light 0,
223                     texmap 0, tev stage 0, ...
224 
225     Arguments:      cubemap - array of six GXTexObj's which describe the
226                               cube map.  Each can be a different size if
227                               desired.
228 
229                     spheremap - texture object that describes the spheremap.
230                                 This function will query the width, height,
231                                 and format to create the spheremap.  The
232                                 calling function should allocate the texture
233                                 image memory before calling this function.
234 
235                     dl - display list of sphere geometry.
236 
237                     dlsz - display list size in bytes.
238 
239     Returns:        none
240  *---------------------------------------------------------------------------*/
drawParaboloidMap(GXTexObj * cubemap,GXTexObj * spheremap,void * dl,u32 dlsz,GXBool front)241 void drawParaboloidMap( GXTexObj *cubemap,   // array of six textures
242                         GXTexObj *spheremap, // output texture
243                         void*     dl,        // display list of sphere geometry
244                         u32       dlsz,      // size of display list
245                         GXBool    front )    // draw front or back map?
246 {
247     s32        i;
248     GXColor    color;
249     u16        width, height;
250     GXTexFmt   fmt;
251     void*      data;      // sphere map texture data
252     GXLightObj ClipLight; // to clip q
253     Mtx44      p;
254     Mtx        v;
255     Mtx        tm, tc, tmp;
256     Vec camLoc = {0.0F, 0.0F, 6.0F};
257     Vec up     = {0.0F, 1.0F, 0.0F};
258     Vec objPt  = {0.0F, 0.0F, 0.0F};
259 
260     u8  map, a1, a2, lit;
261 
262     u8* remap    = remap_map[front];
263     u8* remapa1  = remap_a1[front];
264     u8* remapa2  = remap_a2[front];
265     u8* remaplit = remap_lit[front];
266 
267 
268     // get sphere map size, format, and data pointer
269     width  = GXGetTexObjWidth(spheremap);
270     height = GXGetTexObjHeight(spheremap);
271     fmt    = GXGetTexObjFmt(spheremap);
272     data   = GXGetTexObjData(spheremap);
273 
274     // set projection
275     MTXOrtho(p, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 100.0f);
276     GXSetProjection(p, GX_ORTHOGRAPHIC);
277 
278     // set modelview matrix
279     MTXLookAt(v, &camLoc, &up, &objPt);
280     GXLoadPosMtxImm(v, GX_PNMTX0);
281     GXSetCullMode(GX_CULL_BACK);
282 
283     // size the sphere map
284     GXSetViewport(0.0F, 0.0F,
285                   (f32)width, (f32)height,
286                   0.0F, 1.0F);
287 
288     // set copy parameters
289     GXSetTexCopySrc(0, 0, width, height);
290     GXSetTexCopyDst(width, height, fmt, GX_FALSE);
291 
292     // Generate alpha=1 wherever q > 0.  This alpha is used for
293     // clipping the rear projected image.  The tex alpha is used to keep the
294     // texture from smearing outside the front projected image.  Multiply
295     // these two together to merge.
296     GXSetChanCtrl(GX_COLOR0A0,
297             GX_ENABLE,
298             GX_SRC_REG,
299             GX_SRC_REG,
300             GX_LIGHT0,
301             GX_DF_NONE,
302             GX_AF_SPEC);
303 
304     // pass angle attn through
305     color.r = color.g = color.b = color.a = 0xff;
306     GXInitLightColor(&ClipLight, color);
307     GXSetChanMatColor(GX_COLOR0A0, color);
308 
309     color.r = color.g = color.b = color.a = 0x0;
310     GXSetChanAmbColor(GX_COLOR0A0, color);
311 
312     //
313     // The specular lighting equation is used to clip the rear projection
314     // where q > 0.  The angle and distance attenuation are computed as
315     // datt = aatt = N*L > 0 ? N*H : 0
316     // We set L = (0,0,-1) to select Nz which is the q component used
317     // for texgen (we use the same matrix for normal and texgen).
318     //
319     GXInitLightAttnA(&ClipLight, 0.0F, 2.0F, 0.0F);  // saturate on any value
320     GXInitLightAttnK(&ClipLight, 0.0F, 1.0F, 0.0F);  //   of Nz
321     GXInitLightPos(&ClipLight, 0.0F, 0.0F,-1.0F); // direction for spec lit
322     GXInitLightDir(&ClipLight, 0.0F, 0.0F,-1.0F); // half-angle for spec lit
323     GXLoadLightObjImm(&ClipLight, GX_LIGHT0);
324 
325     // the texgen (projection) matrix is computed in the loop based
326     // on the cube face being rendered. The same matrix is used for normal
327     // transform.
328     GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_NRM, GX_TEXMTX0);
329 
330     //
331     //  texc*1.0, texa*rasa, a one-texel border of alpha=0 is required
332     //
333     switch (CubeTevMode)
334     {
335         case 0:  // final result
336         GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
337 
338         // texa*rasa must be > zero or pixel gets clipped
339 
340         // output tex color
341         GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
342         GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
343         // output ras alpha * tex alpha
344         GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO);
345         GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
346 
347         GXSetNumTevStages(1);
348         break;
349 
350 
351         case 1:  // unclipped texture
352         GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
353 
354         // output tex color
355         GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
356         GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
357         // output ras alpha
358         GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ONE);
359         GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
360 
361         GXSetNumTevStages(1);
362         break;
363 
364 
365         case 2: // texture alpha
366         GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
367 
368         // output = 1.0
369         GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ONE);
370         GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
371         // output tex alpha
372         GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA);
373         GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
374 
375         GXSetNumTevStages(1);
376         break;
377 
378 
379         case 3:  // raster alpha
380         GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
381 
382         // out = 1.0
383         GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ONE);
384         GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
385         // output ras alpha
386         GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA);
387         GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
388 
389         GXSetNumTevStages(1);
390         break;
391 
392 
393         case 4: // ras alpha * tex alpha
394         GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
395 
396         // texa*rasa must be > zero or pixel gets clipped
397 
398         // output 1.0
399         GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ONE);
400         GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
401         // output ras alpha * tex alpha
402         GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO);
403         GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
404 
405         GXSetNumTevStages(1);
406         break;
407 
408     }
409 
410     GXSetNumTexGens(1);
411     GXSetNumChans(1);
412 
413     // compute constant part of the texture matrix
414     // scale/translate texture coordinates so range is moved
415     // from -1,1 to 0,1.  Flip frustum for reflected image.
416     MTXLightFrustum(tc,  1.02F,-1.02F, 1.02F,-1.02F,   // t, b, l, r
417                          1.0F,                         // near
418                          0.5F,  0.5F,   0.5F,   0.5F); // ss,st,ts,tt
419 
420     GXSetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
421     GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
422 
423     //
424     // Since we draw a background sphere for visualization purposes
425     // we don't want to clear the background here, but a real app might.
426     //
427 
428     //
429     //  Loop over each cube face texture.  Compute the projection matrix
430     //  for each face orientation and direct the light to properly clip the
431     //  the rear projected image.
432     //
433     for (i = CubeFaceStart; i < CubeFaceEnd; i++)
434     {
435         // remap indices for front/back map
436         map = remap[i];
437         a1  = remapa1[i];
438         a2  = remapa2[i];
439         lit = remaplit[i];
440 
441         // load each face of the cube map
442         GXLoadTexObj(&cubemap[map], GX_TEXMAP0);
443 
444         // rotate (texture matrix) projection to a face of the cube.
445         // when capturing the cube-face images, the rotations must match.
446         MTXIdentity(tm);
447 
448         if (angle2[a2])
449         {
450             // used for top/bottom faces
451             MTXRotDeg(tmp, 'y', angle2[a2]);
452             MTXConcat(tm, tmp, tm);
453         }
454         MTXRotDeg(tmp, axis1[a1], angle1[a1]);
455         MTXConcat(tm, tmp, tm);
456 
457         MTXConcat(tc, tm, tm);
458         GXLoadTexMtxImm(tm, GX_TEXMTX0, GX_MTX3x4);
459 
460         // Use same matrix for the normal transform for lighting
461         GXLoadNrmMtxImm(tm, GX_PNMTX0);
462 
463         GXCallDisplayList(dl, dlsz);
464     }
465 
466     //
467     //  Copy texture
468     //
469     GXCopyTex(data, GX_TRUE); // clear old texture
470     GXPixModeSync(); // prevent data from being used until copy completes
471 }
472