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 tesselation 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 case 1: // unclipped texture
317 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
318
319 // output tex color
320 GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
321 GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
322 // output ras alpha
323 GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST);
324 GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
325
326 GXSetNumTevStages(1);
327 break;
328
329
330 case 2: // texture alpha
331 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
332
333 // output = 1.0
334 GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ONE);
335 GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
336 // output tex alpha
337 GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA);
338 GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
339
340 GXSetNumTevStages(1);
341 break;
342
343
344 case 3: // raster alpha
345 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
346
347 // out = 1.0
348 GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ONE);
349 GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
350 // output ras alpha
351 GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA);
352 GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
353
354 GXSetNumTevStages(1);
355 break;
356
357
358 case 4: // ras alpha * tex alpha
359 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
360 GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
361
362 // texa*rasa must be > zero or pixel gets clipped
363
364 // output 1.0
365 GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ONE);
366 GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
367 // output ras alpha * tex alpha
368 GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO);
369 GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
370
371 GXSetNumTevStages(1);
372 break;
373
374 }
375
376 GXSetNumTexGens(1);
377 GXSetNumChans(1);
378
379 // compute constant part of the texture matrix
380 // scale/translate texture coordinates so range is moved
381 // from -1,1 to 0,1. Flip frustum for reflected image.
382 // The 0.02 factor is to cover the seams caused by the alpha border.
383 MTXLightFrustum(tc, -1.02F, 1.02F, -1.02F, 1.02F, // t, b, l, r
384 1.0F, // near
385 0.5F, 0.5F, 0.5F, 0.5F); // ss,st,ts,tt
386
387 GXSetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
388 GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
389
390 //
391 // we don't clear the background here, but a real app might.
392 //
393
394 //
395 // Loop over each cube face texture. Compute the projection matrix
396 // for each face orientation and direct the light to properly clip the
397 // the rear projected image.
398 //
399 for (i = CubeFaceStart; i < CubeFaceEnd; i++)
400 {
401 // load each face of the cube map
402 GXLoadTexObj(&cubemap[i], GX_TEXMAP0);
403
404 // rotate (texture matrix) projection to a face of the cube.
405 // when capturing the cube-face images, the rotations must match.
406 MTXIdentity(tm);
407 if (angle2[i])
408 {
409 // used for top/bottom faces
410 MTXRotDeg(tmp, 'y', angle2[i]);
411 MTXConcat(tm, tmp, tm);
412 }
413 MTXRotDeg(tmp, axis1[i], angle1[i]);
414 MTXConcat(tm, tmp, tm);
415
416 MTXConcat(tc, tm, tm);
417 GXLoadTexMtxImm(tm, GX_TEXMTX0, GX_MTX3x4);
418
419 // Use same matrix for the normal transform for lighting
420 GXLoadNrmMtxImm(tm, GX_PNMTX0);
421
422 GXCallDisplayList(dl, dlsz);
423 }
424
425
426 //
427 // Copy texture
428 //
429 GXCopyTex(data, GX_TRUE); // clear old texture
430 GXPixModeSync(); // prevent data from being used until copy completes
431 }
432