1 /*---------------------------------------------------------------------------*
2   Project:     SwingDemo
3   File:        swing.c
4   Programmer:  Keizo Ohta
5 
6   Copyright 2005-2007 Nintendo. All rights reserved.
7 
8   These coded instructions, statements, and computer programs contain
9   proprietary information of Nintendo of America Inc. and/or Nintendo
10   Company Ltd., and are protected by Federal copyright law.  They may
11   not be disclosed to third parties or copied or duplicated in any form,
12   in whole or in part, without the prior written consent of Nintendo.
13  *---------------------------------------------------------------------------*/
14 
15 #include    <math.h>
16 
17 #include    <revolution.h>
18 #include    <revolution/kpad.h>
19 #include    <revolution/mem.h>
20 
21 #include    "kfont.h"
22 #include    "graphic.h"
23 
24 
25 /***************************************************************
26     GXFIFO
27  ***************************************************************/
28 #define GX_FIFO_SIZE    ( 1 * 1024*1024 )   // Large enough (The smaller the FIFO, the larger the chance of waiting for space to open when a command is issued.)
29 
30 static void      *gx_fifo_p ;       // Pointer to buffer
31 static GXFifoObj *gx_fifo_obj ;     // Management structure
32 
33 
34 /***************************************************************
35     Frame buffer
36  ***************************************************************/
37 GXRenderModeObj     *rmode_p ;      // Pointer to drawing mode
38 static u8       vfilter[7] = { 0,8, 16,16,16, 8,0 } ;
39 
40 static void     *xfb_p[ 2 ] ;       // Pointer to double buffer
41 static s32      draw_xfb_idx ;      // XFB being drawn by GX
42 static s32      disp_xfb_idx ;      // XFB being displayed by VI
43 
44 
45 /***************************************************************
46     Controller
47  ***************************************************************/
48 KPADStatus  kpads[ WPAD_MAX_CONTROLLERS ][ KPAD_MAX_READ_BUFS ] ;
49 s32     kpad_reads ;
50 
51 
52 /*******************************************************************************
53     Memory initialization
54  *******************************************************************************/
init_memory(void)55 static void init_memory( void )
56 {
57     void         *arenaLo, *arenaHi ;
58     OSHeapHandle  heap ;
59 
60 
61     //----- Check available memory
62     arenaLo = OSGetArenaLo() ;
63     arenaHi = OSGetArenaHi() ;
64 
65     //----- Declare the creation of one heap that takes up all available memory
66     arenaLo = OSInitAlloc( arenaLo, arenaHi, 1 ) ;
67     OSSetArenaLo( arenaLo ) ;   // Reset because the available memory changes
68 
69     //----- Create a heap which takes up all available memory and set it as the current heap
70     heap = OSCreateHeap( arenaLo, arenaHi ) ;
71     (void)OSSetCurrentHeap( heap ) ;
72 
73     //----- Make it clear ahead of time that available memory has been utilized
74     arenaLo = arenaHi ;
75     OSSetArenaLo( arenaLo ) ;
76 }
77 
78 
79 /*******************************************************************************
80     Initialize memory (MEM2)
81  *******************************************************************************/
82 static MEMAllocator s_mem2Allocator;
83 static MEMHeapHandle s_handle;
84 
init_memory2(void)85 static void init_memory2(void)
86 {
87     void *lo = OSGetMEM2ArenaLo();
88     void *hi = OSGetMEM2ArenaHi();
89     s_handle = MEMCreateFrmHeap(lo, (u32)hi - (u32)lo);
90     if ( s_handle == MEM_HEAP_INVALID_HANDLE )
91     {
92         OSHalt("MEM2 heap allocation error.\n");
93     }
94     else
95     {
96         OSSetMEM2ArenaLo(hi);
97         MEMInitAllocatorForFrmHeap(&s_mem2Allocator, s_handle, 32);   // Buffer requires 32-byte alignment.
98     }
99 }
100 
alloc32(u32 size)101 static void* alloc32(u32 size)
102 {
103     return MEMAllocFromAllocator(&s_mem2Allocator, size);
104 
105 }
106 
free32(void * addr)107 static u8 free32(void *addr)
108 {
109     MEMFreeToAllocator(&s_mem2Allocator, addr);
110     return 1;
111 }
112 
113 
114 /*******************************************************************************
115     Initialize relationship with display device
116  *******************************************************************************/
init_display(void)117 static void init_display( void )
118 {
119     u32         xfb_size ;
120 
121 
122     //----- Select drawing mode
123     rmode_p = &GXNtsc480IntDf ;
124 
125     //----- Allocate frame buffer
126     xfb_size = VIPadFrameBufferWidth(rmode_p->fbWidth) * rmode_p->xfbHeight * (u32)VI_DISPLAY_PIX_SZ ;
127     xfb_p[0] = OSAlloc( xfb_size ) ;
128     xfb_p[1] = OSAlloc( xfb_size ) ;
129 
130     //----- Select a drawing buffer and display buffer
131     draw_xfb_idx = 0 ;
132     disp_xfb_idx = 1 ;
133 
134     //----- Initialize VI
135     VIConfigure( rmode_p ) ;
136     VISetNextFrameBuffer( xfb_p[ disp_xfb_idx ] ) ;
137     VIFlush() ;
138 
139     VIWaitForRetrace() ;    // In order to enable Configure, you need to wait two times.
140     VIWaitForRetrace() ;    //
141 }
142 
143 
144 /*******************************************************************************
145     Initialize GX relationship
146  *******************************************************************************/
init_gx(void)147 static void init_gx( void )
148 {
149     GXColor     clear_clr = { 0,0,0, 0 } ;
150 
151 
152     //----- Create GXFIFO
153     gx_fifo_p   = OSAlloc( GX_FIFO_SIZE ) ;
154     gx_fifo_obj = GXInit( gx_fifo_p, GX_FIFO_SIZE ) ;
155 
156     //----- Select pixel format
157     GXSetPixelFmt( GX_PF_RGB8_Z24, GX_ZC_LINEAR ) ;
158     GXSetDither( GX_DISABLE ) ;
159 
160     //----- Initialize settings for copying from EFB to XFB
161     GXSetDispCopySrc( 0, 0, rmode_p->fbWidth, rmode_p->efbHeight ) ;
162     GXSetDispCopyDst( rmode_p->fbWidth, rmode_p->xfbHeight ) ;
163     (void)GXSetDispCopyYScale( (f32)(rmode_p->xfbHeight) / (f32)(rmode_p->efbHeight) ) ;
164     GXSetDispCopyGamma( GX_GM_1_0 ) ;
165 
166     GXSetCopyFilter( rmode_p->aa, rmode_p->sample_pattern, GX_ENABLE, vfilter ) ;
167     GXSetCopyClear( clear_clr, 0x00FFFFFF ) ;
168 
169     //----- Initialize EFB drawing area
170     GXSetViewport( 0.0f, 0.0f, rmode_p->fbWidth, rmode_p->efbHeight, 0.0f, 1.0f ) ;
171     GXSetScissor( 0, 0, (u32)rmode_p->fbWidth, (u32)rmode_p->efbHeight ) ;
172 
173     //----- Clear EFB and XFB
174     GXCopyDisp( xfb_p[0], GX_ENABLE ) ;     // The EFB is cleared and garbage is placed in XFB[0]
175     GXCopyDisp( xfb_p[0], GX_DISABLE ) ;    // The cleared EFB is placed in XFB[0]
176     GXCopyDisp( xfb_p[1], GX_DISABLE ) ;    // The cleared EFB is placed in XFB[1]
177     GXDrawDone() ;
178 }
179 
180 
181 /***************************************************************
182     Adjustment item
183  ***************************************************************/
184 #define ACC_PLAY_RADIUS 0.0f    // Tolerance radius of KPAD acceleration
185 #define ACC_SENSITIVITY 1.0f    // Tracking of KPAD acceleration
186 #define ACC_MAX     3.3999f     // Maximum acceleration range
187 #define ACC_DAMP    0.6f        // Attenuation rate of acceleration changes outside the range
188 
189 #define DIR_VEC_DAMP    0.9f    // Attenuation rate of the speed vector (0-1)
190 #define DIR_SPEED_MIN   1.9f    // Minimum speed required for recognition
191 #define DIR_SPEED_DAMP  0.98f   // Attenuation rate of the speed record
192 
193 /*----- Methods of adjustment
194 ACC_XXX is okay as it is.
195 DIR_XXX has room for adjustment depending on the circumstances of the game.
196 
197 Increasing the value of DIR_VEC_DAMP or decreasing the value of DIR_SPEED_MIN will allow even light swings to be recognized, but erroneous recognitions will also increase.
198 
199 In addition, increasing the value of DIR_SPEED_DAMP will lengthen the time from one recognition to the next, but the accuracy of the recognition may improve.
200 
201 
202 -----*/
203 
204 
205 /***************************************************************
206     Recognition data
207  ***************************************************************/
208 Vec     dir_nrm ;               // Swinging direction/length 1
209 s32     dir_nrm_pass_count ;    // Time elapsed since update; in units of 1/200 sec
210 
211 /*----- Usage Method
212 The direction in which the swinging occurred is always updated in the acceleration coordinate system in dir_nrm.
213 In a game, this would involve referencing dir_nrm the instant that dir_nrm_pass_count exceeds a certain value.
214 
215 
216 Decide how much time should pass before using it depending on the circumstances of the game.
217 Referencing it early will result in good response, but might lower the accuracy.
218 This sample changes the color of the arrow from red to yellow and then to blue, depending on the time elapsed.
219 
220 In addition, dir_nrm is a 3D vector variable, but since it has been customized for 2D in this sample, Z is always 0.
221 
222 -----*/
223 
224 
225 /***************************************************************
226     Variables for internal processing
227  ***************************************************************/
228 static Vec  last_acc ;      // Previous acceleration
229 static Vec  acc_vec ;       // Amount of change in acceleration
230 static Vec  dir_vec ;       // The aerial speed that is accelerated by the amount of change in acceleration
231 static f32  dir_speed_max ; // The record for speed
232 
233 
234 /*******************************************************************************
235     Initialization Functions
236  *******************************************************************************/
init_sample(void)237 static void init_sample( void )
238 {
239     //----- Acceleration settings for KPAD
240     KPADSetAccParam ( 0, ACC_PLAY_RADIUS, ACC_SENSITIVITY ) ;
241 
242     //----- Initialize the swing direction appropriately
243     dir_nrm.x = 1.0f ;
244     dir_nrm.y = 0.0f ;
245     dir_nrm.z = 0.0f ;
246     dir_nrm_pass_count = 0 ;
247 
248     //----- Initialize variables for other processing
249     last_acc.x = last_acc.y = last_acc.z =
250       acc_vec.x = acc_vec.y = acc_vec.z =
251         dir_vec.x = dir_vec.y = dir_vec.z =
252           dir_speed_max = 0.0f ;
253 }
254 
255 
256 /*******************************************************************************
257     CPU process function
258  *******************************************************************************/
work_sample(void)259 static void work_sample( void )
260 {
261     Vec     new_acc ;
262     f32     f1 ;
263     s32     ct ;
264 
265 
266     //----- Main recognition program (looks at the overall acceleration sampled at 200 Hz)
267     ct = kpad_reads ;
268     while ( --ct >= 0 ) {
269         //----- Measurement of the elapsed time for swing direction recognition
270         ++ dir_nrm_pass_count ;
271 
272         //----- Get the most recent acceleration
273         new_acc = kpads[0][ ct ].acc ;
274         // If getting the acceleration results in an error, it is best to do the same calculation as when the limit of acceleration measurement is exceeded as follows.
275         //
276 
277         //----- Get the amount of change in acceleration from the previous sample, with predicting calculation
278         if ( new_acc.x < -ACC_MAX || new_acc.x > ACC_MAX ) {
279             new_acc.x = ( acc_vec.x *= ACC_DAMP ) + last_acc.x ;
280         } else {
281             acc_vec.x = new_acc.x - last_acc.x ;
282         }
283         if ( new_acc.y < -ACC_MAX || new_acc.y > ACC_MAX ) {
284             new_acc.y = ( acc_vec.y *= ACC_DAMP ) + last_acc.y ;
285         } else {
286             acc_vec.y = new_acc.y - last_acc.y ;
287         }
288         if ( new_acc.z < -ACC_MAX || new_acc.z > ACC_MAX ) {
289             new_acc.z = ( acc_vec.z *= ACC_DAMP ) + last_acc.z ;
290         } else {
291             acc_vec.z = new_acc.z - last_acc.z ;
292         }
293 
294         //----- Save the last acceleration
295         last_acc = new_acc ;
296 
297         //----- Attenuate the speed that is accelerated by the amount of change
298         dir_vec.x *= DIR_VEC_DAMP ;
299         dir_vec.y *= DIR_VEC_DAMP ;
300         dir_vec.z *= DIR_VEC_DAMP ;
301 
302         //----- The further the Z change amount is from zero, the more to increase the X speed and Y speed; always set the Z speed to zero.
303         f1 = acc_vec.z ;
304         if ( f1 < 0.0f ) f1 = -f1 ;
305         f1 = 1.0f - f1 * f1 ;
306         if ( f1 > 0.0f ) {
307             dir_vec.x += f1 * acc_vec.x ;
308             dir_vec.y += f1 * acc_vec.y ;
309         }
310         dir_vec.z = 0.0f ;
311         // If acceleration is done straightforwardly for all three axes, it is possible to recognize in three dimensions.
312 
313         //---- Dampen the speed record
314         dir_speed_max *= DIR_SPEED_DAMP ;
315 
316         //----- Update check for the speed record; a minimum amount of speed is required
317         f1 = sqrtf( dir_vec.x * dir_vec.x + dir_vec.y * dir_vec.y + dir_vec.z * dir_vec.z ) ;
318         if ( f1 >= DIR_SPEED_MIN && f1 > dir_speed_max ) {
319 
320             //----- Update the swing direction!!!
321             dir_speed_max = f1 ;
322             f1 = 1.0f / f1 ;
323             dir_nrm.x = f1 * dir_vec.x ;
324             dir_nrm.y = f1 * dir_vec.y ;
325             dir_nrm.z = f1 * dir_vec.z ;
326             dir_nrm_pass_count = 0 ;
327         }
328     }
329 }
330 
331 
332 /*******************************************************************************
333     Drawing function
334  *******************************************************************************/
draw_sample(void)335 static void draw_sample( void )
336 {
337     f32     r = 200.0f ;    // Length of the arrow
338     GXColor     clr ;
339 
340 
341     if ( dir_nrm_pass_count < 10 )      clr = red_clr ;     // The color just after recognition
342     else if ( dir_nrm_pass_count < 20 ) clr = yellow_clr ;  // The color after some time has passed
343     else                                clr = blue_clr ;    // The color after even more time has passed
344 
345     init_draw_graphic( rmode_p->fbWidth, rmode_p->efbHeight ) ;
346     draw_arrow( r*dir_nrm.x, r*dir_nrm.y, -r*dir_nrm.x, -r*dir_nrm.y, clr, 10 ) ;
347     // The acceleration coordinate system of KPAD is in the opposite direction as the rendering coordinate system.
348 }
349 
350 
351 
352 /*******************************************************************************
353     Main
354  *******************************************************************************/
main(void)355 void main( void )
356 {
357     /***********************************************************************
358         Initialization processing
359      ***********************************************************************/
360     //----- Highest-priority hardware initialization
361     VIInit() ;
362 
363     //----- Other priority initializations
364     init_memory() ;     // Enable allocation and release of memory
365     init_memory2();
366     init_display() ;    // Display device-related
367     init_gx() ;         // GX-related
368     WPADRegisterAllocator(alloc32, free32);
369     KPADInit() ;        // Controller
370 
371 
372 
373     //----- Initialize application
374     init_kfont_texture() ;
375     init_sample() ;
376 
377     //----- Screen display ON
378     VISetBlack( FALSE ) ;
379     VIFlush() ;
380 
381 
382     /***********************************************************************
383         Main loop
384      ***********************************************************************/
385     while (1) {
386         /***************************************************************
387             First, perform calculations by the CPU
388             Then find coordinates based on controller input,
389             And prepare the data necessary to issue a GX command
390          ***************************************************************/
391         //----- Load controller
392         kpad_reads = KPADRead( 0, &kpads[0][0], KPAD_MAX_READ_BUFS ) ;
393 
394         //----- Various calculations
395         work_sample() ;
396 
397 
398         /***************************************************************
399             Issue GX command and go on drawing to EFB
400          ***************************************************************/
401         //----- Cast spell to start issuing the command
402         GXInvalidateVtxCache() ;
403         GXInvalidateTexAll() ;
404 
405         //----- Issue various commands
406         draw_sample() ;
407 
408         //----- Finally, copy from EFB to XFB
409         GXCopyDisp( xfb_p[ draw_xfb_idx ], GX_ENABLE ) ;
410         GXDrawDone() ;
411 
412 
413         /***************************************************************
414             The XFB copied here can be selected for display on the TV
415          ***************************************************************/
416         disp_xfb_idx = draw_xfb_idx ;
417         VISetNextFrameBuffer( xfb_p[ disp_xfb_idx ] ) ;
418         VIFlush() ;
419 
420         //----- Wait for the display to actually switch
421         VIWaitForRetrace() ;
422 
423         //----- Switch to the no longer displayed XFB ahead of time for the next drawing
424         draw_xfb_idx ^= 1 ;
425     }
426 }
427