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