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