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