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