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