1 #include <config.h>
2
3 #include <stdio.h>
4 #include <gettext.h>
5
6 #include "G-Force_Proj.h"
7 #include "G-Force.h"
8 #include "XFloatList.h"
9
10 #ifdef UNIX_X
11
12 #define __defaultTTFormat ""
13 #define __defaultFont ""
14
15 #define __drawText( x, y, str ) fprintf(stderr,str->getCStr());
16
17
18 #include "RectUtils.h"
19 #define __setupPort
20
21 #define __restorePort
22
23 #endif
24
25
26 #include "CEgFileSpec.h"
27 #include "EgOSUtils.h"
28 #include <math.h>
29 #include <stdlib.h>
30 #include "ConfigFile.h"
31 #include "Expression.h"
32 #include "Hashtable.h"
33 #include "ParticleGroup.h"
34
GForce(void * inRefCon)35 GForce::GForce( void* inRefCon ) :
36 #if defined(UNIX_X)
37 mPrefs( ".G-Force", true ),
38 #endif
39 mWave1( &mT ),
40 mWave2( &mT ),
41 mPal1( &mT, &mIntensityParam ),
42 mPal2( &mT, &mIntensityParam ),
43 mConsoleLines( cDuplicatesAllowed, cOrderImportant ),
44 mLineExpireTimes( cOrderImportant ),
45 mParticles ( cNoDuplicates_CaseInsensitive, cSortLowToHigh ),
46 mColorMaps ( cNoDuplicates_CaseInsensitive, cSortLowToHigh ),
47 mDeltaFields ( cNoDuplicates_CaseInsensitive, cSortLowToHigh ),
48 mWaveShapes ( cNoDuplicates_CaseInsensitive, cSortLowToHigh ) {
49
50 // Do initting...
51 mWind = 0;
52 mOutPort = 0;
53 mRefCon = inRefCon;
54 mFrameCount = 0;
55 mT_MS_Base = EgOSUtils::CurTimeMS();
56 mConsoleExpireTime =
57 mLastCursorUpdate =
58 mLastGetKeys =
59 mT_MS = 0;
60 mFrameCountStart =
61 mNextPaletteUpdate =
62 mT = 0;
63 mNextShapeChange = mT + 10;
64 mNextFieldChange = mT + 10;
65 mNextColorChange = mT + 10;
66 mLastSongStart = mT - 10000;
67 mLastKeyPollTime = mT;
68 mLastActiveTime = mT;
69 mDoingSetPortWin = false;
70 mNeedsPaneErased = true;
71 mShapeSlideShow =
72 mColorSlideShow =
73 mFieldSlideShow = true;
74 mAtFullScreen = false;
75 mMouseWillAwaken = false;
76 mTrackTextDur = 0;
77
78 mPrefs.Load();
79 if ( mPrefs.GetPref( 'Vers' ) != GFORCE_COMPAT_VERSION ) {
80 mConsoleDelay = 8;
81 mConsoleLineDur = 14;
82 mMagScale = 1;
83 mTransitionLo = 4;
84 mTransitionHi = 18;
85 mScrnSaverDelay = -1 * 60; // Factory: screen saver mode disabled
86 mPrefs.SetPref( 'Vers', GFORCE_COMPAT_VERSION );
87 mBorderlessWind = 0;
88 mHandleKeys = 1;
89 mNum_S_Steps = 200;
90 mFullscreenSize.h = 640;
91 mFullscreenSize.v = 480;
92 mFullscreenDepth = 8;
93 mFullscreenDevice = 0;
94 mMaxSize.h = 30000;
95 mMaxSize.v = 360;
96 mTrackTextPosMode = 5;
97 mTrackTextSize = 18;
98 mNormalizeInput = false;
99 mNewConfigNotify = false;
100 mParticlesOn = true;
101 mKeyMap .Assign( "TLRY`SNGFZXCQWE,.M[]{}P******!@#$%^&*()1234567890" );
102 mFieldIntervalStr .Assign( "18 + rnd( 15 )" );
103 mColorIntervalStr .Assign( "10 + rnd( 15 )" );
104 mShapeIntervalStr .Assign( "10 + rnd( 15 )" );
105 mTrackFont .Assign( __defaultFont );
106 mTrackTextStartStr .Assign( "4 + LAST_SONG_START - t" );
107 mTrackTextDurationStr .Assign( "5" );
108 mTrackMetaText .Assign( __defaultTTFormat );
109 mParticleDuration .Assign( "8 + rnd( 15 )" );
110 mParticleProbability .Assign( ".09/((NUM_PARTICLES+1)^1.66)" );
111
112
113 // Show the welcome msg for a pref rewrite...
114 Println( GFORCE_VERS_STR );
115 Println( "Press '?' for help" );
116 }
117 else {
118 mConsoleDelay = mPrefs.GetPref( 'CDur' );
119 mConsoleLineDur = mPrefs.GetPref( 'CLin' );
120 mMagScale = mPrefs.GetPref( 'MScl' ) / 1000.0;
121 mTransitionLo = mPrefs.GetPref( 'TrLo' );
122 mTransitionHi = mPrefs.GetPref( 'TrHi' );
123 mScrnSaverDelay = mPrefs.GetPref( 'SSvr' ) * 60.0;
124 mBorderlessWind = mPrefs.GetPref( 'NoBo' );
125 mHandleKeys = mPrefs.GetPref( 'Kybd' );
126 mNum_S_Steps = mPrefs.GetPref( 'Stps' );
127 mFullscreenSize.h = mPrefs.GetPref( 'FS_X' );
128 mFullscreenSize.v = mPrefs.GetPref( 'FS_Y' );
129 mFullscreenDepth = mPrefs.GetPref( 'FS_D' );
130 mFullscreenDevice = mPrefs.GetPref( 'FS_#' );
131 mParticlesOn = mPrefs.GetPref( 'P_On' );
132 mNormalizeInput = mPrefs.GetPref( 'Norm' );
133 mMaxSize.v = mPrefs.GetPref( 'MaxY' );
134 mMaxSize.h = mPrefs.GetPref( 'MaxX' );
135 mNewConfigNotify = mPrefs.GetPref( 'ShwT' );
136 mTrackTextPosMode = mPrefs.GetPref( 'TPos' );
137 mTrackTextSize = mPrefs.GetPref( 'TSze' );
138 mPrefs.GetPref( 'WInt', mShapeIntervalStr );
139 mPrefs.GetPref( 'DInt', mFieldIntervalStr );
140 mPrefs.GetPref( 'CInt', mColorIntervalStr );
141 mPrefs.GetPref( 'T?', mTrackTextStartStr );
142 mPrefs.GetPref( 'TDur', mTrackTextDurationStr );
143 mPrefs.GetPref( 'TFnt', mTrackFont );
144 mPrefs.GetPref( 'TStr', mTrackMetaText );
145 mPrefs.GetPref( 'PDur', mParticleDuration );
146 mPrefs.GetPref( 'PPrb', mParticleProbability );
147 mPrefs.GetPref( 'KMap', mKeyMap );
148 }
149
150 mPortA.SetTrackTextFont( mTrackFont, mTrackTextSize );
151 mPortB.SetTrackTextFont( mTrackFont, mTrackTextSize );
152
153 // Catch any bad values for mNumSampleBins
154 if ( mNum_S_Steps < 1 || mNum_S_Steps > 10000 )
155 mNum_S_Steps = 320;
156
157
158 mNum_FFT_Steps = 256;
159
160 // Alloc/setup the data we'll have our virtual machines accessing...
161 SetNumSampleBins( mNum_S_Steps );
162 SetNumFFTBins( mNum_FFT_Steps );
163
164 // Setup waveshape members
165 mWave1.SetMagFcn( (ExprUserFcn**) &mSampleFcn );
166 mWave2.SetMagFcn( (ExprUserFcn**) &mSampleFcn );
167
168 // FIXME, use FFT data here
169 mWave1.SetFFTFcn( (ExprUserFcn**) &mFFTFcn );
170 mWave2.SetFFTFcn( (ExprUserFcn**) &mFFTFcn );
171
172 // Init particle stuff
173 mDict.AddVar( "T", &mT );
174 mDict.AddVar( "LAST_PARTICLE_START", &mLastParticleStart );
175 mDict.AddVar( "NUM_PARTICLES", &mNumRunningParticles );
176 mNumRunningParticles = 0;
177 mNextParticleCheck = mT + 1;
178 mParticleProbabilityFcn.Compile( mParticleProbability, mDict );
179 mParticleDurationFcn.Compile( mParticleDuration, mDict );
180 mShapeInterval.Compile( mShapeIntervalStr, mDict );
181 mColorInterval.Compile( mColorIntervalStr, mDict );
182 mFieldInterval.Compile( mFieldIntervalStr, mDict );
183
184 // Track Text stuff
185 mDict.AddVar( "LAST_SONG_START", &mLastSongStart );
186 mTrackTextStartFcn.Compile( mTrackTextStartStr, mDict );
187 mTrackTextDurFcn.Compile( mTrackTextDurationStr, mDict );
188
189 // Transition bookkeeping
190 mColorTransTime = -1;
191 mShapeTransTime = -1;
192 mGF_Palette = 0;
193 mWave = 0;
194
195
196 // Look in G-Force's support folders and see what we have to select from...
197 BuildConfigLists();
198
199 mField = &mField1;
200 mNextField = &mField2;
201
202 for ( int i = 0; i < 4; i++ )
203 mCurKeys[ i ] = 0;
204 }
205
206
207
208
~GForce()209 GForce::~GForce() {
210
211
212 // Rewrite the prefs to disk...
213 mPrefs.SetPref( 'SSvr', mScrnSaverDelay / 60.0 );
214 mPrefs.SetPref( 'TrHi', mTransitionHi );
215 mPrefs.SetPref( 'TrLo', mTransitionLo );
216 mPrefs.SetPref( 'MScl', mMagScale * 1000 );
217 mPrefs.SetPref( 'CInt', mColorIntervalStr );
218 mPrefs.SetPref( 'WInt', mShapeIntervalStr );
219 mPrefs.SetPref( 'DInt', mFieldIntervalStr );
220 mPrefs.SetPref( 'Stps', mNum_S_Steps );
221 mPrefs.SetPref( 'PDur', mParticleDuration );
222 mPrefs.SetPref( 'PPrb', mParticleProbability );
223 mPrefs.SetPref( 'NoBo', mBorderlessWind );
224 mPrefs.SetPref( 'Kybd', mHandleKeys );
225 mPrefs.SetPref( 'FS_X', mFullscreenSize.h );
226 mPrefs.SetPref( 'FS_Y', mFullscreenSize.v );
227 mPrefs.SetPref( 'FS_D', mFullscreenDepth );
228 mPrefs.SetPref( 'FS_#', mFullscreenDevice );
229 mPrefs.SetPref( 'Norm', mNormalizeInput );
230 mPrefs.SetPref( 'MaxY', mMaxSize.v );
231 mPrefs.SetPref( 'MaxX', mMaxSize.h );
232 mPrefs.SetPref( 'TPos', mTrackTextPosMode );
233 mPrefs.SetPref( 'TSze', mTrackTextSize );
234 mPrefs.SetPref( 'TFnt', mTrackFont );
235 mPrefs.SetPref( 'TStr', mTrackMetaText );
236 mPrefs.SetPref( 'T?', mTrackTextStartStr );
237 mPrefs.SetPref( 'TDur', mTrackTextDurationStr );
238 mPrefs.SetPref( 'KMap', mKeyMap );
239 mPrefs.SetPref( 'P_On', mParticlesOn ? 1 : 0 );
240 mPrefs.SetPref( 'ShwT', mNewConfigNotify ? 1 : 0 );
241 mPrefs.SetPref( 'CDur', mConsoleDelay );
242 mPrefs.SetPref( 'CLin', mConsoleLineDur );
243
244 // Init the track text info
245 NewSong();
246
247 mPrefs.Store();
248
249 }
250
251
252
253
254
SetNumSampleBins(long inNumBins)255 void GForce::SetNumSampleBins( long inNumBins ) {
256 float k;
257
258 if ( inNumBins > 0 && inNumBins < 10000 ) {
259 mSampleFcn = (ExprUserFcn*) mSamplesBuf.Dim( sizeof( float ) * inNumBins + sizeof( ExprUserFcn ) + 32 );
260 mNum_S_Steps = inNumBins;
261 mSampleFcn -> mNumFcnBins = inNumBins;
262
263 // A fast lookup table for a sine wave
264 mSine = (float*) mSineBuf.Dim( sizeof( float ) * inNumBins );
265 k = 6.2831853071795 / ( (float) inNumBins );
266
267 for ( int i = 0; i < inNumBins; i++ ) {
268 mSampleFcn -> mFcn[ i ] = 0;
269 mSine[ i ] = sin( k * ( (float) i ) );
270 }
271 }
272 }
273
274
275
276
277
SetNumFFTBins(long inNumBins)278 void GForce::SetNumFFTBins( long inNumBins ) {
279
280 if ( inNumBins > 0 && inNumBins < 1000 ) {
281 mFFTFcn = (ExprUserFcn*) mFFTBuf.Dim( sizeof( float ) * inNumBins + sizeof( ExprUserFcn ) + 32);
282 mNum_FFT_Steps = inNumBins;
283 mFFTFcn -> mNumFcnBins = inNumBins;
284 }
285 }
286
287
288
289 #define __setChar( n, ID ) s.setChar( n, mKeyMap.getChar( ID ) )
290
291
ShowHelp()292 void GForce::ShowHelp() {
293 UtilStr s;
294
295 s.Assign( "X - Display track title" ); __setChar( 1, cDispTrackTitle ); Println( &s );
296 s.Assign( "X - List configs" ); __setChar( 1, cGetConfigInfo ); Println( &s );
297 s.Assign( "X - Frame Rate" ); __setChar( 1, cFrameRate ); Println( &s );
298 s.Assign( "X - Particles on/off" ); __setChar( 1, cToggleParticles ); Println( &s );
299 s.Assign( "X - Spawn new particle" ); __setChar( 1, cSpawnNewParticle ); Println( &s );
300 s.Assign( "X - Fullscreen on/off" ); __setChar( 1, cToggleFullsceen ); Println( &s );
301 s.Assign( "X - Show config titles on/off" ); __setChar( 1, cToggleConfigName ); Println( &s );
302 s.Assign( "X - Normalize input on/off" ); __setChar( 1, cToggleNormalize ); Println( &s );
303
304 s.Assign( "X X - Freeze/Continue slideshow" ); __setChar( 1, cStopSlideshowAll ); __setChar( 3, cStartSlideshowAll ); Println( &s );
305 s.Assign( "X X - +/- sound amplitude (See Extras docs)" ); __setChar( 1, cDecMagScale ); __setChar( 3, cIncMagScale ); Println( &s );
306 s.Assign( "X X - +/- num of bins (Stps) (See Extras docs)" ); __setChar( 1, cDecNumSSteps ); __setChar( 3, cIncNumSSteps ); Println( &s );
307 s.Assign( "X X X - Prev/Next/Hold DeltaField" ); __setChar( 1, cPrevDeltaField ); __setChar( 3, cNextDeltaField ); __setChar( 5, cToggleFieldShow ); Println( &s );
308 s.Assign( "X X X - Prev/Next/Hold ColorMap" ); __setChar( 1, cPrevColorMap ); __setChar( 3, cNextColorMap ); __setChar( 5, cToggleColorShow ); Println( &s );
309 s.Assign( "X X X - Prev/Next/Hold WaveShape" ); __setChar( 1, cPrevWaveShape ); __setChar( 3, cNextWaveShape ); __setChar( 5, cToggleShapeShow ); Println( &s );
310
311 Println( "" );
312 Println( "Press SHIFT and a number to store the current ColorMap, Waveshape, and" );
313 Println( " DeltaField, and press just the number to recall them." );
314
315 // Give the user more time than usual since this is help info
316 mConsoleExpireTime += 8000;
317 }
318
HandleKey(long inChar)319 bool GForce::HandleKey( long inChar ) {
320 bool handled = true;
321 int n;
322
323 // See if this keystroke is to be ignored
324 if ( ! mHandleKeys )
325 return false;
326
327
328 if ( inChar >= 'a' && inChar <= 'z' )
329 inChar = 'A' + ( inChar - 'a' );
330
331 if ( inChar == '/' || inChar == '?' )
332 ShowHelp();
333 else if ( inChar >= ' ' && inChar < 129 ) {
334
335 inChar = mKeyMap.FindNextInstanceOf( 0, inChar );
336
337 switch ( inChar ) {
338
339 case cDispTrackTitle:
340 StartTrackText();
341 break;
342
343 case cGetConfigInfo:
344 {
345 Print(_("WaveShape: "));
346 Println( &mWaveShapeName );
347 Print(_("ColorMap: "));
348 Println( &mColorMapName );
349 Print(_("DeltaField: "));
350 Println( mField -> GetName() );
351 ParticleGroup* particle = (ParticleGroup*) mRunningParticlePool.GetHead();
352 if ( particle ) {
353 Print(_("Particles: "));
354
355 while ( particle ) {
356 Print( &particle -> mTitle );
357 particle = (ParticleGroup*) particle -> GetNext();
358 if ( particle )
359 Print( ", " );
360 }
361 Println( "" );
362 }
363 break;
364 }
365 case cFrameRate:
366 mTemp.SetFloatValue( ( (float) mCurFrameRate ) / 10.0 );
367 mTemp.Append(_(" frames/sec"));
368 Println( &mTemp );
369 break;
370
371 case cDecMagScale:
372 mMagScale /= 1.2;
373 mTemp.SetFloatValue( mMagScale );
374 Print(_("Amplitude scale: "));
375 Println( &mTemp );
376 break;
377
378 case cIncMagScale:
379 mMagScale *= 1.2;
380 mTemp.SetFloatValue( mMagScale );
381 Print(_("Amplitude scale: "));
382 Println( &mTemp );
383 break;
384
385 case cToggleParticles:
386 mParticlesOn = ! mParticlesOn;
387 if ( mParticlesOn )
388 Println(_("Particles ON"));
389 else
390 Println(_("Particles OFF"));
391 break;
392
393 case cSpawnNewParticle:
394 SpawnNewParticle();
395 break;
396
397 case cDecNumSSteps:
398 case cIncNumSSteps:
399 if ( inChar == cDecNumSSteps )
400 n = - 4;
401 else
402 n = + 4;
403 SetNumSampleBins( mNum_S_Steps + n );
404 mTemp.Assign(_("Number s steps: "));
405 mTemp.Append( mNum_S_Steps );
406 Println( &mTemp );
407 break;
408
409 case cToggleConfigName:
410 mNewConfigNotify = ! mNewConfigNotify;
411 if ( mNewConfigNotify )
412 Println(_("Show names ON"));
413 else
414 Println(_("Show names OFF"));
415 break;
416
417 case cToggleNormalize:
418 mNormalizeInput = ! mNormalizeInput;
419 if ( mNormalizeInput )
420 Println(_("Normalize ON"));
421 else
422 Println(_("Normalize OFF"));
423 break;
424
425 case cPrevDeltaField:
426 case cNextDeltaField:
427 n = mFieldPlayList.FindIndexOf( mCurFieldNum );
428 if ( inChar == cPrevDeltaField )
429 n = n + mFieldPlayList.Count() - 2;
430
431 loadDeltaField( mFieldPlayList.Fetch( 1 + n % mFieldPlayList.Count() ) );
432
433 // If the pref says so, display that we're loading a new config
434 if ( mNewConfigNotify ) {
435 Print(_("Loading DeltaField: "));
436 Println( mField -> GetName() );
437 }
438
439 // Turn field slide show off when we change deltafields manually
440 if ( ! mFieldSlideShow )
441 break;
442 case cToggleFieldShow:
443 mFieldSlideShow = ! mFieldSlideShow;
444 mNextFieldChange = mT;
445 if ( mFieldSlideShow ) {
446 Println(_("DeltaField slideshow ON"));
447 mFieldPlayList.Randomize(); }
448 else
449 Println(_("DeltaField slideshow OFF"));
450 break;
451
452
453 case cStartSlideshowAll:
454 mFieldSlideShow = true; mNextFieldChange = mT;
455 mColorSlideShow = true; mNextColorChange = mT;
456 mShapeSlideShow = true; mNextShapeChange = mT;
457 Println(_("All slideshows ON"));
458 break;
459
460 case cStopSlideshowAll:
461 mFieldSlideShow = false;
462 mColorSlideShow = false;
463 mShapeSlideShow = false;
464 Println(_("All slideshows OFF"));
465 break;
466
467
468 case cPrevColorMap:
469 case cNextColorMap:
470 n = mColorPlayList.FindIndexOf( mCurColorMapNum );
471 if ( inChar == cPrevColorMap )
472 n = n + mColorPlayList.Count() - 2;
473
474 loadColorMap( mColorPlayList.Fetch( 1 + n % mColorPlayList.Count() ), false );
475
476 // Turn slide show off when we change colormaps manually
477 if ( ! mColorSlideShow )
478 break;
479 case cToggleColorShow:
480 mColorSlideShow = ! mColorSlideShow;
481 mNextColorChange = mT;
482 if ( mColorSlideShow ) {
483 Println(_("ColorMap slideshow ON"));
484 mColorPlayList.Randomize(); }
485 else
486 Println(_("ColorMap slideshow OFF"));
487 break;
488
489 case cPrevWaveShape:
490 case cNextWaveShape:
491 n = mShapePlayList.FindIndexOf( mCurShapeNum );
492 if ( inChar == cPrevWaveShape )
493 n = n + mShapePlayList.Count() - 2;
494
495 loadWaveShape( mShapePlayList.Fetch( 1 + n % mShapePlayList.Count() ), false );
496
497 // Turn slide show off when we change shapes manually
498 if ( ! mShapeSlideShow )
499 break;
500 case cToggleShapeShow:
501 mShapeSlideShow = ! mShapeSlideShow;
502 mNextShapeChange = mT;
503 if ( mShapeSlideShow ) {
504 Println(_("WaveShape slideshow ON"));
505 mShapePlayList.Randomize(); }
506 else
507 Println(_("WaveShape slideshow OFF"));
508 break;
509
510 case cSetPreset0: StoreConfigState( 'SET0' ); break;
511 case cSetPreset1: StoreConfigState( 'SET1' ); break;
512 case cSetPreset2: StoreConfigState( 'SET2' ); break;
513 case cSetPreset3: StoreConfigState( 'SET3' ); break;
514 case cSetPreset4: StoreConfigState( 'SET4' ); break;
515 case cSetPreset5: StoreConfigState( 'SET5' ); break;
516 case cSetPreset6: StoreConfigState( 'SET6' ); break;
517 case cSetPreset7: StoreConfigState( 'SET7' ); break;
518 case cSetPreset8: StoreConfigState( 'SET8' ); break;
519 case cSetPreset9: StoreConfigState( 'SET9' ); break;
520
521
522 case cPreset0: handled = RestoreConfigState( 'SET0' ); break;
523 case cPreset1: handled = RestoreConfigState( 'SET1' ); break;
524 case cPreset2: handled = RestoreConfigState( 'SET2' ); break;
525 case cPreset3: handled = RestoreConfigState( 'SET3' ); break;
526 case cPreset4: handled = RestoreConfigState( 'SET4' ); break;
527 case cPreset5: handled = RestoreConfigState( 'SET5' ); break;
528 case cPreset6: handled = RestoreConfigState( 'SET6' ); break;
529 case cPreset7: handled = RestoreConfigState( 'SET7' ); break;
530 case cPreset8: handled = RestoreConfigState( 'SET8' ); break;
531 case cPreset9: handled = RestoreConfigState( 'SET9' ); break;
532
533 default:
534 handled = false;
535 } }
536 else
537 handled = false;
538
539 return handled;
540 }
541
542
543
StoreConfigState(long inParamName)544 void GForce::StoreConfigState( long inParamName ) {
545 UtilStr str;
546
547 str.Assign( mWaveShapeName ); str.Append( ',' );
548 str.Append( mColorMapName ); str.Append( ',' );
549 str.Append( mField -> GetName() ); str.Append( ',' );
550
551 mPrefs.SetPref( inParamName, str );
552 Println(_("State stored."));
553 }
554
555
RestoreConfigState(long inParamName)556 bool GForce::RestoreConfigState( long inParamName ) {
557 UtilStr str, configName;
558 long pos, n, found = false;
559
560 if ( mPrefs.GetPref( inParamName, str ) ) {
561
562 // Parse the waveshape config name
563 pos = str.FindNextInstanceOf( 0, ',' );
564 configName.Assign( str.getCStr(), pos - 1 );
565 n = mWaveShapes.FetchBestMatch( configName );
566 loadWaveShape( n, false );
567 mShapeSlideShow = false;
568
569 // Parse the colormap config name
570 str.Trunc( pos, false );
571 pos = str.FindNextInstanceOf( 0, ',' );
572 configName.Assign( str.getCStr(), pos - 1 );
573 n = mColorMaps.FetchBestMatch( configName );
574 loadColorMap( n, false );
575 mColorSlideShow = false;
576
577 // Parse the colormap config name
578 str.Trunc( pos, false );
579 n = mDeltaFields.FetchBestMatch( str );
580 loadDeltaField( n );
581 mFieldSlideShow = false;
582
583 found = true;
584 }
585
586 return found;
587 }
588
589
590
ManageColorChanges()591 void GForce::ManageColorChanges() {
592 int i;
593
594 // If in a ColorMap transition/morph
595 if ( mColorTransTime > 0 ) {
596
597 // If we've the ColorMap transition is over, end it
598 if ( mT_MS > mColorTransEnd ) {
599 GF_Palette* temp = mGF_Palette;
600 mGF_Palette = mNextPal;
601 mNextPal = temp;
602 mColorTransTime = -1;
603 mNextColorChange = mT + mColorInterval.Evaluate();
604 }
605 }
606
607 // Time for a color map change?
608 else if ( mT > mNextColorChange && mColorSlideShow ) {
609
610 // Load the next config in the (randomized) config list...
611 i = mColorPlayList.FindIndexOf( mCurColorMapNum );
612
613 // Make a new play list if we've reached the end of the list...
614 if ( i >= mColorPlayList.Count() ) {
615 mColorPlayList.Randomize();
616 i = 0;
617 }
618 loadColorMap( mColorPlayList.Fetch( i + 1 ), true );
619 }
620
621 // Update the screen palette if it's time
622 if ( mT > mNextPaletteUpdate ) {
623
624 // If in a ColorMap transition/morph then we must set mColorTrans, for it's linked into mGF_Palette
625 if ( mColorTransTime > 0 ) {
626 float t = (float) ( mColorTransEnd - mT_MS ) / ( (float) mColorTransTime );
627 mColorTrans = pow( t, TRANSITION_ALPHA );
628 }
629
630 // Evaluate the palette at this time
631 mGF_Palette -> Evaluate( mPalette );
632
633 // Set our offscreen ports to the right palette...
634 mPortA.SetPalette( mPalette );
635 mPortB.SetPalette( mPalette );
636
637 // If we're at fullsceen, the screen device may need the current palette too
638 if ( mAtFullScreen && mFullscreenDepth == 8 ) {
639 mScreen.SetPalette( mPalette );
640 mPortA.PreventActivate( mOutPort );
641 mPortB.PreventActivate( mOutPort );
642 }
643
644 // Reevaluate the palette a short time from now
645 mNextPaletteUpdate = mT + .1;
646 }
647 }
648
649
650
ManageShapeChanges()651 void GForce::ManageShapeChanges() {
652 int i;
653
654 // If in a WaveShape transition/morph
655 if ( mShapeTransTime > 0 ) {
656
657 // If we've the ColorMap transition is over, end it
658 if ( mT_MS > mShapeTransEnd ) {
659 WaveShape* temp = mWave;
660 mWave = mNextWave;
661 mNextWave = temp;
662 mShapeTransTime = -1;
663 mNextShapeChange = mT + mShapeInterval.Evaluate();
664 } }
665
666 // Time for a wave shape change?
667 else if ( mT > mNextShapeChange && mShapeSlideShow ) {
668
669 // Load the next config in the (randomized) config list...
670 i = mShapePlayList.FindIndexOf( mCurShapeNum );
671
672 // Make a new play list if we've reached the end of the list...
673 if ( i >= mShapePlayList.Count() ) {
674 mShapePlayList.Randomize();
675 i = 0;
676 }
677 loadWaveShape( mShapePlayList.Fetch( i + 1 ), true );
678 }
679
680 }
681
682
ManageFieldChanges()683 void GForce::ManageFieldChanges() {
684 long i;
685
686 // If we have have a delta field in mid-calculation, chip away at it...
687 if ( ! mNextField -> IsCalculated() )
688 mNextField -> CalcSome();
689
690 if ( mT > mNextFieldChange && mNextField -> IsCalculated() && mFieldSlideShow ) {
691
692 // Load the next field in the (randomized) field list...
693 i = mFieldPlayList.FindIndexOf( mCurFieldNum );
694
695 // Make a new play list if we've reached the end of the list...
696 if ( i >= mFieldPlayList.Count() ) {
697 mFieldPlayList.Randomize();
698 i = 0;
699 }
700
701 // loadGradField() will initiate computation on mField with a new grad field...
702 loadDeltaField( mFieldPlayList.Fetch( i + 1 ) );
703 DeltaField* temp = mField;
704 mField = mNextField;
705 mNextField = temp;
706
707 // If the pref says so, display that we're loading a new config
708 if ( mNewConfigNotify ) {
709 Print( "Loaded DeltaField: " );
710 Println( mField -> GetName() );
711 }
712 }
713 }
714
715
716
ManageParticleChanges()717 void GForce::ManageParticleChanges() {
718 float rndVar;
719
720 if ( mT > mNextParticleCheck && mParticlesOn ) {
721
722 // Generate a random probability value.
723 rndVar = ( (float) rand() ) / ( (float) RAND_MAX );
724
725 // Comparing that to the evalated probability of a new particle being spawned determines if a new one *should* be spawned
726 if ( rndVar < mParticleProbabilityFcn.Evaluate() ) {
727
728 SpawnNewParticle();
729 }
730
731 // Check to make a new particle one second from now
732 mNextParticleCheck = mT + 1;
733 }
734 }
735
736
737
DrawParticles(PixPort & inPort)738 void GForce::DrawParticles( PixPort& inPort ) {
739
740 // Draw all the particles
741 ParticleGroup* particle, *next;
742 particle = (ParticleGroup*) mRunningParticlePool.GetHead();
743 while ( particle ) {
744 next = (ParticleGroup*) particle -> GetNext();
745
746 // When particles stop, move them to a holding/stopped list
747 if ( ! particle -> IsExpired() )
748 particle -> DrawGroup( inPort );
749 else {
750 mStoppedParticlePool.addToHead( particle );
751
752 // Update the var that holds how many particles are running (and is accessible in the PPrb expr)
753 mNumRunningParticles = mRunningParticlePool.shallowCount();
754 }
755
756 particle = next;
757 }
758
759 }
760
761
762
763
RecordZeroSample(long inCurTime)764 void GForce::RecordZeroSample( long inCurTime ) {
765 int i;
766
767 for ( i = 0; i < mNum_S_Steps; i++ )
768 mSampleFcn -> mFcn[ i ] = 0;
769
770 RecordSample( inCurTime );
771 }
772
773
774
775 /*
776 void GForce::RecordSample( long inCurTime, float* inFourier, long inNumBins ) {
777 long w, s, n;
778 float sample;
779 ExprUserFcn* fcn;
780
781 // Now write the sample to memory, adjusted for amplitude...
782 fcn = (ExprUserFcn*) mSampleFcn;
783 fcn -> mNumFcnBins = mNum_S_Steps;
784
785 for ( s = 0; s < mNum_S_Steps; s++ ) {
786
787 sample = 0;
788 for ( w = 0; w < inNumBins; w++ ) {
789 n = ( 2.42322211 * w + 1.9 ) * ((float) s) + 1.23231121211 * w;
790 sample += inFourier[ w ] * mSine[ n % mNum_S_Steps ];
791 }
792
793 fcn -> mFcn[ s ] = sample;
794 }
795
796 RecordSample( inCurTime );
797 }
798 */
799
800
IdleMonitor()801 void GForce::IdleMonitor() {
802 bool kybdPress = false;
803 float pollDelay;
804 float secsUntilSleep = mScrnSaverDelay - ( mT - mLastActiveTime );
805 Point pt;
806
807 // Calc time till next kybd poll (Don't waste time checking the kybd unless we've been idle a while)
808 if ( IsFullscreen() )
809 pollDelay = .6;
810
811 // Don't bother rapildly checking the kybd until we're really close to going into screen saver mode
812 else if ( secsUntilSleep < 90 )
813 pollDelay = secsUntilSleep / 120.0;
814 else
815 pollDelay = 10;
816
817 // If it's time to poll for activity...
818 if ( mT > mLastKeyPollTime + pollDelay ) {
819
820 mLastKeyPollTime = mT;
821
822 // Check the mouse pos and record it as active if its been moved.
823 EgOSUtils::GetMouse( pt );
824 if ( pt.h != mLastMousePt.h || pt.v != mLastMousePt.v || kybdPress ) {
825 mLastMousePt = pt;
826 mLastActiveTime = mT;
827 }
828
829 // If we're elligible to enter fullscreen then do it
830 if ( ! mAtFullScreen && mT - mLastActiveTime > mScrnSaverDelay ) {
831 mMouseWillAwaken = true;
832 }
833 }
834 }
835
836
837
RecordSample(long inCurTime,float * inSound,float inScale,long inNumBins,float * inFFT,float inFFTScale,long inFFTNumBins)838 void GForce::RecordSample( long inCurTime, float* inSound, float inScale, long inNumBins, float* inFFT, float inFFTScale, long inFFTNumBins) {
839 float mag, sum;
840 int i, n;
841 ExprUserFcn* fcn;
842
843 // Only use/process bins we'll actually use
844 if ( inNumBins > mNum_S_Steps )
845 inNumBins = mNum_S_Steps;
846
847
848
849 // Calc a 1/RMS avg value...
850 if ( mNormalizeInput ) {
851
852 // Find an RMS amplitude for the sample
853 for ( sum = 0.0001, i = 0; i < inNumBins; i++ ) {
854 mag = inSound[ i ];
855 sum += mag * mag;
856 }
857 inScale = mMagScale * .009 * ( (float) inNumBins ) / ( sqrt( sum ) ); }
858 else
859 inScale *= mMagScale;
860
861
862 // Now write the sample to memory, adjusted for amplitude...
863 fcn = (ExprUserFcn*) mSampleFcn;
864 fcn -> mNumFcnBins = inNumBins;
865 for ( i = 0; i < inNumBins; i++ ) {
866 mag = inSound[ i ];
867 fcn -> mFcn[ i ] = inSound[ i ] * inScale;
868 }
869
870 XFloatList::GaussSmooth( 1.3, inNumBins, fcn -> mFcn );
871
872 // Flatten the ends of the sample...
873 n = inNumBins / 20 + 1;
874 if ( n <= inNumBins ) {
875 for ( i = 0; i < n; i++ ) {
876 mag = sin( .5 * 3.1 * i / n );
877 fcn -> mFcn[ i ] *= mag;
878 fcn -> mFcn[ inNumBins - i - 1 ] *= mag;
879 }
880 }
881
882 // Now write the FFT data to memory, adjusted for amplitude...
883 fcn = (ExprUserFcn*) mFFTFcn;
884 fcn -> mNumFcnBins = inFFTNumBins;
885
886 for ( i = 0; i < inFFTNumBins; i++ ) {
887 fcn -> mFcn[ i ] = inFFT[ i ] * inFFTScale;
888 }
889
890
891 RecordSample( inCurTime );
892 }
893
894
895
896
897
RecordSample(long inCurTime)898 void GForce::RecordSample( long inCurTime ) {
899 bool drewTitleText = false;
900 long intensity;
901 float t;
902
903 if ( &mPortA == mCurPort )
904 mCurPort = &mPortB;
905 else
906 mCurPort = &mPortA;
907
908 // All the waveshape virtual machines are linked to our time index
909 mT_MS = inCurTime - mT_MS_Base;
910 mT = ( (float) inCurTime ) / 1000.0;
911
912 // Don't bother doing mouse or kybd poll if sceeen saver mode is disabled
913 if ( mScrnSaverDelay > 0 )
914 IdleMonitor();
915
916 ManageColorChanges();
917 ManageShapeChanges();
918 ManageFieldChanges();
919 ManageParticleChanges();
920
921 // Do the blur operation, a fcn of what's oqn the screen, and the current delta field
922
923 if ( mCurPort == &mPortA )
924 mPortB.Fade( mPortA, mField -> GetField() );
925 else
926 mPortA.Fade( mPortB, mField -> GetField() );
927
928 /* This redraws the image */
929
930 // Draw all the current particles
931 DrawParticles( *mCurPort );
932
933 // Draw the current wave shape for the current music sample playing
934 // If there's a morph going, drawing is a mix of both waves
935 if ( mShapeTransTime > 0 ) {
936 float morphPct = (float) ( mShapeTransEnd - mT_MS ) / ( (float) mShapeTransTime );
937 mWave -> Draw( mNum_S_Steps, *mCurPort, 1, mNextWave, morphPct ); }
938 else
939 mWave -> Draw( mNum_S_Steps, *mCurPort, 1, 0, 0 );
940
941
942 // If we're not currently drawing track text, check to see if we start new text
943 if ( mTrackTextDur == 0 && mTrackTextPosMode ) {
944 if ( mTrackTextStartFcn.Evaluate() > 0 )
945 StartTrackText();
946 }
947
948 // If we already have a t.t. draw in progress, draw the text in the (full) foreground color
949 if ( mTrackTextDur > 0 ) {
950
951 // From 0 to 1, how far are we into the text display interval?
952 t = ( mT - mTrackTextStartTime ) / mTrackTextDur;
953
954 // Decrease the text intensity thru time
955 intensity = 255 * ( 1.2 - .3*t );
956 if ( intensity > 255 )
957 intensity = 255;
958
959 mCurPort -> SetTextColor( mPalette[ intensity ] );
960 mCurPort -> SetTrackTextFont();
961 mCurPort -> DrawText( mTrackTextPos.h, mTrackTextPos.v, mTrackText );
962 }
963
964 // Draw the console text to the offscreen image. Then copy the image to the OS out port
965 if ( mT_MS < mConsoleExpireTime ) {
966
967 // To ensure the console text is readable, we erase it when we're done
968 mCurPort -> SetTextMode( SRC_XOR );
969 mCurPort -> SetTextColor( mPalette[ 255 ] );
970 mCurPort -> SetConsoleFont();
971 DrawConsole();
972 DrawFrame();
973 mCurPort -> SetTextColor( mPalette[ 0 ] );
974 DrawConsole();
975 mCurPort -> SetTextMode( SRC_OR ); }
976 else
977 DrawFrame();
978
979 // We need to avoid text all bluring together so we overwrite the foreground text we just drew
980 // with text of a lower intensity...
981 if ( mTrackTextDur > 0 ) {
982
983 // Is the text is about to expire? if not, continue drawing.
984 if ( t <= 1 ) {
985
986 intensity = 255.5 * pow( t, 1.5 );
987 mCurPort -> SetTextColor( mPalette[ intensity ] );
988 mCurPort -> SetTrackTextFont();
989 mCurPort -> DrawText( mTrackTextPos.h, mTrackTextPos.v, mTrackText ); }
990 else {
991
992 // The text's duration is up so turn the draw flag off
993 mTrackTextDur = 0;
994 }
995 }
996
997
998 // Maintain the frame rate
999 mFrameCount++;
1000 if ( mT_MS - mFrameCountStart >= 1500 ) {
1001 mCurFrameRate = 10000 * mFrameCount / ( mT_MS - mFrameCountStart );
1002 mFrameCountStart = mT_MS;
1003 mFrameCount = 0;
1004 }
1005
1006 if ( mT_MS - mLastCursorUpdate > 3000 ) {
1007 mLastCursorUpdate = mT_MS;
1008 if ( IsFullscreen() )
1009 EgOSUtils::HideCursor();
1010 }
1011 }
1012
1013
StartTrackText()1014 void GForce::StartTrackText() {
1015
1016 if ( mTrackTextPosMode ) {
1017 CalcTrackTextPos();
1018 mTrackTextDur = mTrackTextDurFcn.Evaluate();
1019 mTrackTextStartTime = mT;
1020 }
1021 }
1022
DrawFrame()1023 void GForce::DrawFrame() {
1024
1025
1026 __setupPort
1027
1028 // If we're fullscreen, follow the API (the screen may need to do something to finish)
1029 if ( mScreen.IsFullscreen() )
1030 mOutPort = mScreen.BeginFrame();
1031
1032 // Someone may have asked to clear the GF window/pane
1033 if ( mNeedsPaneErased ) {
1034 ErasePane();
1035 mNeedsPaneErased = false;
1036 }
1037
1038 // Blt our offscreen world to the output device
1039 Rect r;
1040 r.left = r.top = 0;
1041 r.right = mDispRect.right - mDispRect.left;
1042 r.bottom = mDispRect.bottom - mDispRect.top;
1043 // mCurPort -> CopyBits( mOutPort, &r, &mDispRect );
1044 mCurPort -> CopyBits( mVideoBuffer, &r, &mDispRect );
1045
1046 // If we're fullscreen, follow the API (the screen may need to do something to finish)
1047 if ( mScreen.IsFullscreen() )
1048 mScreen.EndFrame();
1049
1050 __restorePort
1051 }
1052
SetOutVideoBuffer(unsigned char * inVideoBuffer)1053 void GForce::SetOutVideoBuffer( unsigned char *inVideoBuffer ) {
1054 mVideoBuffer = inVideoBuffer;
1055 }
1056
1057 #define __loadFolder( folderName, specList, playList ) \
1058 startOver = true; \
1059 folder.AssignFolder( folderName ); \
1060 while ( EgOSUtils::GetNextFile( folder, spec, startOver, false ) ) { \
1061 specList.AddCopy( spec ); \
1062 startOver = false; \
1063 } \
1064 /* Build a 'play' list */ \
1065 playList.RemoveAll(); \
1066 for ( i = 1; i <= specList.Count(); i++ ) { \
1067 playList.Add( i ); \
1068 } \
1069 playList.Randomize();
1070
1071
BuildConfigLists()1072 void GForce::BuildConfigLists() {
1073 CEgFileSpec folder, spec;
1074 int i;
1075 bool startOver;
1076
1077 __loadFolder( DATADIR "/GForceDeltaFields", mDeltaFields, mFieldPlayList )
1078
1079 __loadFolder( DATADIR "/GForceWaveShapes", mWaveShapes, mShapePlayList )
1080
1081 __loadFolder( DATADIR "/GForceColorMaps", mColorMaps, mColorPlayList )
1082
1083 __loadFolder( DATADIR "/GForceParticles", mParticles, mParticlePlayList )
1084 }
1085
1086
1087 // A linear spread is the default field
1088 #define __FIELD_FACTORY "\
1089 Aspc=0,\
1090 srcX=\"x * .9\",\
1091 srcY=\"y * .9\",\
1092 Vers=100\
1093 "
1094
1095 // A centered circle is the default shape
1096 #define __SHAPE_FACTORY "\
1097 Stps=-1,\
1098 B0=\"t * 0.0003\",\
1099 Aspc=1,\
1100 C0=\"abs( mag( s ) ) * 0.15 + .3\",\
1101 C1=\"s * 6.28318530 + b0\",\
1102 X0=\"c0 * cos( c1 )\",\
1103 Y0=\"c0 * sin( c1 )\",\
1104 Vers=100\
1105 "
1106
1107 // A single color is the defaut color
1108 #define __COLOR_FACTORY "\
1109 H=\".9\",\
1110 S=\".8\",\
1111 V=\"i\",\
1112 Vers=100\
1113 "
1114
1115
1116
1117
loadColorMap(long inColorMapNum,bool inAllowMorph)1118 void GForce::loadColorMap( long inColorMapNum, bool inAllowMorph ) {
1119 const CEgFileSpec* spec;
1120 int ok = false, vers;
1121 ArgList args;
1122
1123 // Fetch the spec for our config file or folder
1124 spec = mColorMaps.FetchSpec( inColorMapNum );
1125
1126 if ( spec ) {
1127 mCurColorMapNum = inColorMapNum;
1128
1129 ok = ConfigFile::Load( spec, args );
1130 if ( ok ) {
1131 vers = args.GetArg( 'Vers' );
1132 ok = vers >= 100 && vers < 110;
1133 spec -> GetFileName( mColorMapName );
1134 }
1135 }
1136
1137
1138 if ( ! ok ) {
1139 args.SetArgs( __COLOR_FACTORY );
1140 mColorMapName.Assign( "<Factory Default>" );
1141 }
1142
1143 // If the pref says so, display that we're loading a new config
1144 if ( mNewConfigNotify ) {
1145 Print( "Loaded ColorMap: " );
1146 Println( &mColorMapName );
1147 }
1148
1149
1150 // If first time load, don't do any transition/morph, otherwise set up the morph
1151 if ( mGF_Palette == 0 || ! inAllowMorph ) {
1152 mGF_Palette = &mPal1;
1153 mNextPal = &mPal2;
1154 mGF_Palette -> Assign( args );
1155 mColorTransTime = -1;
1156 mNextColorChange = mT + mColorInterval.Evaluate(); }
1157 else {
1158 mNextPal -> Assign( args );
1159 mGF_Palette -> SetupTransition( mNextPal, &mColorTrans );
1160
1161 // Calculate how long this transition/morph will be
1162 mColorTransTime = EgOSUtils::Rnd( mTransitionLo * 1000, mTransitionHi * 1000 );
1163 mColorTransEnd = mT_MS + mColorTransTime;
1164 }
1165 }
1166
1167
1168
1169
1170
1171 #define DEC_SIZE 6
1172
loadDeltaField(long inFieldNum)1173 void GForce::loadDeltaField( long inFieldNum ) {
1174 const CEgFileSpec* spec;
1175 int ok = false, vers;
1176 ArgList args;
1177 UtilStr name;
1178
1179 // Fetch the spec for our config file or folder
1180 spec = mDeltaFields.FetchSpec( inFieldNum );
1181
1182 if ( spec ) {
1183
1184 // Know what to put a check mark next to in the popup menu
1185 mCurFieldNum = inFieldNum;
1186
1187 ok = ConfigFile::Load( spec, args );
1188 if ( ok ) {
1189 vers = args.GetArg( 'Vers' );
1190 ok = vers >= 100 && vers < 110;
1191 spec -> GetFileName( name );
1192 }
1193 }
1194
1195 if ( ! ok ) {
1196 args.SetArgs( __FIELD_FACTORY );
1197 name.Assign( "<Factory Default>" );
1198 }
1199
1200 // Initiate recomputation of mField
1201 mField -> Assign( args, name );
1202 mNextFieldChange = mT + mFieldInterval.Evaluate();
1203 }
1204
1205
1206
1207
1208
loadWaveShape(long inShapeNum,bool inAllowMorph)1209 void GForce::loadWaveShape( long inShapeNum, bool inAllowMorph ) {
1210 const CEgFileSpec* spec;
1211 int ok = false, vers;
1212 ArgList args;
1213
1214 // Fetch the spec for our config file or folder
1215 spec = mWaveShapes.FetchSpec( inShapeNum );
1216
1217 if ( spec ) {
1218 // Know what to put a check mark next to in the popup menu
1219 mCurShapeNum = inShapeNum;
1220
1221 ok = ConfigFile::Load( spec, args );
1222
1223 if ( ok ) {
1224 vers = args.GetArg( 'Vers' );
1225 ok = vers >= 100 && vers < 110;
1226 spec -> GetFileName( mWaveShapeName );
1227 }
1228 }
1229
1230
1231 if ( ! ok ) {
1232 args.SetArgs( __SHAPE_FACTORY );
1233 mWaveShapeName.Assign( "<Factory Default>" );
1234 }
1235
1236 // If the pref says so, display that we're loading a new config
1237 if ( mNewConfigNotify ) {
1238 Print( "Loaded WaveShape: " );
1239 Println( &mWaveShapeName );
1240 }
1241
1242
1243 // If first time load, don't do any transition/morph, otherwise set up the morph
1244 if ( mWave == 0 || ! inAllowMorph ) {
1245 mWave = &mWave1;
1246 mNextWave = &mWave2;
1247 mWave -> Load( args, mNum_S_Steps );
1248 mNextShapeChange = mT + mShapeInterval.Evaluate();
1249 mShapeTransTime = -1; }
1250 else {
1251 mNextWave -> Load( args, mNum_S_Steps );
1252 mWave -> SetupTransition( mNextWave );
1253
1254 // Calculate how long this transition/morph will take
1255 mShapeTransTime = EgOSUtils::Rnd( mTransitionLo * 1000, mTransitionHi * 1000 );
1256 mShapeTransEnd = mT_MS + mShapeTransTime;
1257 }
1258
1259 }
1260
1261
1262
1263
1264
1265
loadParticle(long inParticleNum)1266 void GForce::loadParticle( long inParticleNum ) {
1267 const CEgFileSpec* spec;
1268 int ok = false, vers;
1269 ArgList args;
1270 ParticleGroup* newParticle;
1271 UtilStr name;
1272
1273 // Fetch the spec for our config file or folder
1274 spec = mParticles.FetchSpec( inParticleNum );
1275
1276 if ( spec ) {
1277
1278 mCurParticleNum = inParticleNum;
1279 ok = ConfigFile::Load( spec, args );
1280 if ( ok ) {
1281 vers = args.GetArg( 'Vers' );
1282 ok = vers >= 100 && vers < 110;
1283
1284 spec -> GetFileName( name );
1285
1286 // If the pref says so, display that we're loading a new config
1287 if ( mNewConfigNotify ) {
1288 Print( "Loaded Particle: " );
1289 Println( &name );
1290 }
1291 }
1292 }
1293
1294
1295 if ( ok ) {
1296
1297 // Avoid having to reallocate mem...
1298 newParticle = (ParticleGroup*) mStoppedParticlePool.GetHead();
1299
1300 // If there weren'y any particles already expired, make a new instance
1301 if ( ! newParticle )
1302 newParticle = new ParticleGroup( &mT, (ExprUserFcn**) &mSampleFcn );
1303
1304 // Add the new particle to the group that gets executed each frame
1305 newParticle -> mTitle.Assign( name );
1306 mRunningParticlePool.addToHead( newParticle );
1307
1308 // The GF particle probability fcn has access to these variables
1309 mNumRunningParticles = mRunningParticlePool.shallowCount();
1310 mLastParticleStart = mT;
1311
1312 // Determine how long this particle will be around
1313 newParticle -> SetDuration( mParticleDurationFcn.Evaluate() );
1314
1315 // Tell the particle to compile it's config text
1316 newParticle -> Load( args );
1317 }
1318 }
1319
1320
NewSong()1321 void GForce::NewSong() {
1322
1323 mTrackText.Assign( mTrackMetaText );
1324 mTrackText.Replace( "\\r", "\r" );
1325 mTrackText.Replace( "#ARTIST#", mArtist.getCStr(), false );
1326 mTrackText.Replace( "#ALBUM#", mAlbum.getCStr(), false );
1327 mTrackText.Replace( "#TITLE#", mSongTitle.getCStr(), false );
1328
1329 CalcTrackTextPos();
1330
1331 mLastSongStart = mT;
1332 }
1333
1334
1335
CalcTrackTextPos()1336 void GForce::CalcTrackTextPos() {
1337 long height, width;
1338 long x = mCurPort -> GetX();
1339 long y = mCurPort -> GetY();
1340
1341 mCurPort -> TextRect( mTrackText.getCStr(), width, height );
1342
1343 switch ( mTrackTextPosMode ) {
1344
1345 case 1: // Upper-left corner
1346 mTrackTextPos.h = 5;
1347 mTrackTextPos.v = mTrackTextSize + 5;
1348 break;
1349
1350 case 2: // Bottom-left corner
1351 mTrackTextPos.h = 5;
1352 mTrackTextPos.v = y - height - 3;
1353 break;
1354
1355 case 3: // Centered
1356 mTrackTextPos.h = ( x - width ) / 2;
1357 mTrackTextPos.v = ( y - height ) / 2;
1358 break;
1359
1360 default: // Random Position
1361 mTrackTextPos.h = EgOSUtils::Rnd( 5, x - width );
1362 mTrackTextPos.v = EgOSUtils::Rnd( mTrackTextSize + 5, y - height );
1363 break;
1364 }
1365 }
1366
1367
1368
SpawnNewParticle()1369 void GForce::SpawnNewParticle() {
1370 int i;
1371
1372 // Load the next particle in the (randomized) play list...
1373 i = mParticlePlayList.FindIndexOf( mCurParticleNum );
1374
1375 // Make a new play list if we've reached the end of the list...
1376 if ( i >= mParticlePlayList.Count() ) {
1377 mParticlePlayList.Randomize();
1378 i = 0;
1379 }
1380
1381 // loadGradField() will initiate computation on mField with a new grad field...
1382 loadParticle( mParticlePlayList.Fetch( i + 1 ) );
1383 }
1384
1385
1386
1387
1388
Print(char * inStr)1389 void GForce::Print( char* inStr ) {
1390 long num = mConsoleLines.Count();
1391 UtilStr* lastLine = mConsoleLines.Fetch( num );
1392
1393 // Append the text to the console text..
1394 if ( lastLine )
1395 lastLine -> Append( inStr );
1396 else {
1397 mConsoleLines.Add( inStr );
1398 num = 1;
1399 }
1400
1401 // Setup when this line will be deleted
1402 mLineExpireTimes[ num - 1 ] = mT_MS + mConsoleLineDur * 1000;
1403
1404 // Make the console visible for the next few seconds
1405 mConsoleExpireTime = mT_MS + mConsoleDelay * 1000;
1406 }
1407
1408
Println(char * inStr)1409 void GForce::Println( char* inStr ) {
1410 Print( inStr );
1411
1412 mConsoleLines.Add( "" );
1413 }
1414
1415 // This is stupid!
1416 #define PIX_PER_LINE 10
1417
DrawConsole()1418 void GForce::DrawConsole() {
1419 long i, start, num = mConsoleLines.Count();
1420 long x = mDispRect.left + 5;
1421 long top = PIX_PER_LINE + 3;
1422 UtilStr* theLine;
1423
1424 if ( mConsoleLines.Count() == 0 )
1425 return;
1426
1427 // Delete console lines that are too old...
1428 while ( mLineExpireTimes.Fetch( 1 ) < mT_MS && num > 0 ) {
1429 mConsoleLines.Remove( 1 );
1430 mLineExpireTimes.RemoveElement( 1 );
1431 num--;
1432 }
1433
1434 // Check if console runs off the display rect...
1435 if ( num * PIX_PER_LINE > mDispRect.bottom - mDispRect.top - top)
1436 start = num - ( mDispRect.bottom - mDispRect.top - top) / PIX_PER_LINE;
1437 else
1438 start = 1;
1439
1440 // Draw each line of the console...
1441 for ( i = start; i <= num; i++ ) {
1442 theLine = mConsoleLines.Fetch( i );
1443 mCurPort -> DrawText( x, top + (i-start) * PIX_PER_LINE, *theLine );
1444 }
1445 }
1446
1447
1448
1449
1450
1451
ErasePane()1452 void GForce::ErasePane() {
1453
1454 }
1455
1456
1457
SetWinPort(WindowPtr inWin,const Rect * inRect)1458 void GForce::SetWinPort( WindowPtr inWin, const Rect* inRect ) {
1459 Rect r;
1460
1461 // mDoingSetPortWin == true is a signal that another thread is in SetWinPort()
1462 if ( mDoingSetPortWin )
1463 return;
1464 mDoingSetPortWin = true;
1465 mWind = inWin;
1466
1467 if ( inRect )
1468 r = *inRect;
1469
1470 long x = r.right - r.left;
1471 long y = r.bottom - r.top;
1472
1473 SetPort( 0, r, false );
1474
1475 // Signal that this thread is done with SetPortWin()
1476 mDoingSetPortWin = false;
1477 }
1478
1479
1480
1481
SetPort(GrafPtr inPort,const Rect & inRect,bool inFullScreen)1482 void GForce::SetPort( GrafPtr inPort, const Rect& inRect, bool inFullScreen ) {
1483 long x = inRect.right - inRect.left;
1484 long y = inRect.bottom - inRect.top;
1485
1486 mOutPort = inPort;
1487 mAtFullScreen = inFullScreen;
1488
1489
1490 // The pane rect is the rect within inPort th plugin frame occupies
1491 mPaneRect = inRect;
1492
1493 // mDispRect is the rect within inPort G-Force is drawing in (ex, the letterbox)
1494 // Change the disp rect if the desired size exceeds the pixel ceiling
1495 mDispRect = inRect;
1496
1497 // Setup the offscreen port
1498 mPortA.Init( x, y, 8 );
1499 mPortB.Init( x, y, 8 );
1500 mCurPort = &mPortA;
1501
1502 // Erase/init our output window
1503 mNeedsPaneErased = true;
1504
1505 // If setting port for the first time...
1506 if ( mWave == 0 ) {
1507 loadWaveShape( mShapePlayList.Fetch( 1 ), false );
1508 loadColorMap( mColorPlayList.Fetch( 1 ), false );
1509
1510 // loadGradField() will initiate computation on mField with a new grad field...
1511 loadDeltaField( mFieldPlayList.Fetch( 1 ) );
1512 DeltaField* temp = mField;
1513 mField = mNextField;
1514 mNextField = temp;
1515 loadDeltaField( mFieldPlayList.Fetch( 2 ) );
1516 }
1517
1518 // The grad fields have to know the pixel dimentions
1519 mField1.SetSize( x, y, mPortA.GetRowSize() );
1520 mField2.SetSize( x, y, mPortA.GetRowSize() );
1521
1522 // The track text may depend on the port size
1523 CalcTrackTextPos();
1524
1525 // Changing the port (and the resolution) may change the mouse cords
1526 EgOSUtils::GetMouse( mLastMousePt );
1527
1528 }
1529
GetWinRect(Rect & outRect)1530 void GForce::GetWinRect( Rect& outRect ) {
1531 SetRect( &outRect, 0, 0, 0, 0 );
1532 }
1533
1534