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