Page 1 of 1

GEuser's CGUI Lib: genWave(gW) Wave Generator

PostPosted: Sun Jan 06, 2013 1:29 am
by GEuser
GEuser's CUSTOMIZABLE GUI LIBRARY: genWave(gW) Wave Generator

Image

INTRODUCTION

Been beavering away at this for awhile, banging head against a brick wall, finally got it going (finally!!!). Sounds corny but the old addage is true: if you persevere for long enough any unsurmountable obstacle can be overcome. I'm just glad that It actually works (seemed impossible when first thought of it).

The idea was to create a single SIMPLE function that could assist in procedural generation of gradients, lighting, animated, landscaping, etc... effects without using complicated maths like calculus or some brain numbing maths intensive algorithms (well it's a try). Essentially it's a proof of concept with basic functionality that I hope more experienced programmers can take advantage of and share back with gE community.

The technique uses parameters parsed in by the user to create a waveform with an output between -1 and 1 (or 0 and 1) that can be used externally in user createded algorithms with the help of genWave to cut down on the maths. A single code to enable multiple tasks.


DEMO-TUTORIALS

Here's a list of Demo Tutorials that will give you a feeler for what it can do:-

1. STRONGLY RECCOMMEND: genWave Viewer Manual: viewtopic.php?f=8&t=12522#p89133

A gE application that helps to visualise and test output of genWave function. TRY THIS FIRST than came back here and things will be more clear about what it actually can do and how to use it effectively (their not just pretty squiggles :) ).

2. Gradients: viewtopic.php?f=27&t=12524#p89138
3. Simple Procedural Landscaper / Scroller: LINK? (comming soon)
4. Orbiting Actors viewtopic.php?f=27&t=12524#p89262
5. Simple lighting effects: LINK? (comming soon
6. Simple animated effects: LINK? (comming soon)


BASIC INFORMATION

Image
Image
Image
Image

CODE USE
1. Access game editor's script editor.
2. Copy/Paste Global Code from below into script editor (ALTERNATIVE METHOD: Copy/paste code into a simple text editor and save as a .txt file than use Script Editors FILE=>LOAD option to import this text file into gE).
3. Add this imported code as: genWave

The genWave function should now be globally accessible from all other events.

GLOBAL CODE

Code: Select all
double genWave(int wMode, int NegON, int invertY, double wStep,double wStart, double wEnd, double vShift, double wAmp1, double wFreq1, double wPhase1,double wAmp2, double wFreq2, double wPhase2,double wAmp3, double wFreq3, double wPhase3)
{
   double wave1=0;       // stores reference wave 1
   double wave2=0;       // stores reference wave 2
   double wave3=0;       // stores reference wave 3
   double waveResult=0;  // stores generated output wave
   double unionAmp=0;    // stores multiplier for expanding/shrinking amplitude to 1 unit.
                         // NOTE: in maths union can mean 1, unity, etc...
                         // so I've coined the term unionAmp to mean amplitude of 1 always.
 
   // check for valid parameters and make adjustments
   if((wStep==0)||(wStep<0.001))
   {
       wStep=0.1;
   }
 
   if(wStart<0)
   {
       wStart=0.0;
   }

   if(wEnd>360)
   {
       wEnd=360.0;
   }
 
   if(wAmp1==0)
   {
       wAmp1=0.01;
   }
 
   if(wFreq1==0)
   {
       wFreq1=0.01;
   }
   if(wAmp2==0)
   {
       wAmp2=0.01;
   }
 
   if(wFreq2==0)
   {
       wFreq2=0.01;
   }
 
   if(wAmp2==0)
   {
       wAmp2=0.01;
   }
 
   if(wFreq3==0)
   {
       wFreq3=0.01;
   }
 
   if(vShift>1)
   {
       vShift=1.0;
   }
 
   if(vShift<-1)
   {
       vShift=-1.0;
   }
 
   // continue operations if within selection range (i.e. haven't yet reached wEnd )
   if((wStart+wStep)<=wEnd)
   {
       // calculate reference waves from parameters & store
       wave1=wAmp1*sin(degtorad((wStart+wStep+((360-invertY*180)/wFreq1)+wPhase1)*wFreq1));
       wave2=wAmp2*sin(degtorad((wStart+wStep+((360-invertY*180)/wFreq2)+wPhase2)*wFreq2));
       wave3=wAmp3*sin(degtorad((wStart+wStep+((360-invertY*180)/wFreq3)+wPhase3)*wFreq3));

       // detect arithmetic mode & generate output wave
       switch(wMode)
       {
           case 0:
                   waveResult=0;
                   break;

           case 1:
                   unionAmp=1/wAmp1;
                   waveResult=unionAmp*wave1;
                   break;

           case 2:
                   unionAmp=1/wAmp2;
                   waveResult=unionAmp*wave2;
                   break;

           case 3:
                   unionAmp=1/wAmp3;
                   waveResult=unionAmp*wave3;
                   break;

           case 4:
                   unionAmp=1/(wAmp1+wAmp2);
                   waveResult=unionAmp*wave1+unionAmp*wave2;
                   break;

           case 5:
                   unionAmp=1/(wAmp1+wAmp2);
                   waveResult=unionAmp*wave1-unionAmp*wave2;
                   break;

           case 6:
                   unionAmp=1/(wAmp1+wAmp2);
                   waveResult=unionAmp*wave2-unionAmp*wave1;
                   break;

           case 7:
                   unionAmp=1/(wAmp1+wAmp3);
                   waveResult=unionAmp*wave1+unionAmp*wave3;
                   break;

           case 8:
                   unionAmp=1/(wAmp1+wAmp3);
                   waveResult=unionAmp*wave1-unionAmp*wave3;
                   break;

           case 9:
                   unionAmp=1/(wAmp1+wAmp3);
                   waveResult=unionAmp*wave3-unionAmp*wave1;
                   break;

           case 10:
                   unionAmp=1/(wAmp2+wAmp3);
                   waveResult=unionAmp*wave3+unionAmp*wave2;
                   break;

           case 11:
                   unionAmp=1/(wAmp2+wAmp3);
                   waveResult=unionAmp*wave2-unionAmp*wave3;
                   break;

           case 12:
                   unionAmp=1/(wAmp2+wAmp3);
                   waveResult=unionAmp*wave3-unionAmp*wave2;
                   break;

           case 13:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave1+unionAmp*wave2+unionAmp*wave3;
                   break;

           case 14:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave3+unionAmp*wave2-unionAmp*wave1;
                   break;

           case 15:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave1+unionAmp*wave3-unionAmp*wave2;
                   break;

           case 16:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave3-unionAmp*wave2-unionAmp*wave1;
                   break;

           case 17:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave1+unionAmp*wave2-unionAmp*wave3;
                   break;

           case 18:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave2-unionAmp*wave1-unionAmp*wave3;
                   break;

           case 19:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=unionAmp*wave1-unionAmp*wave2-unionAmp*wave3;
                   break;

           case 20:
                   unionAmp=1/(wAmp1+wAmp2+wAmp3);
                   waveResult=0-unionAmp*wave1-unionAmp*wave2-unionAmp*wave3;
                   break;

           case 21:
                   unionAmp=1/(pow(wAmp1*wAmp2,0.5));
                   waveResult=(unionAmp*wave1)*(unionAmp*wave2);
                   break;

           case 22:
                   unionAmp=1/(pow(wAmp1*wAmp3,0.5));
                   waveResult=(unionAmp*wave1)*(unionAmp*wave3);
                   break;

           case 23:
                   unionAmp=1/(pow(wAmp2*wAmp3,0.5));
                   waveResult=(unionAmp*wave2)*(unionAmp*wave3);
                   break;

           case 24:
                   unionAmp=1/(pow(wAmp1*wAmp2*wAmp3,0.333333));
                   waveResult=(unionAmp*wave1)*(unionAmp*wave2)*(unionAmp*wave3);
                   break;


           default:
                    waveResult=0;
                    break;

       }

   }
 
   // Correctly assign negatives depending on Y-axis inversion state
   if ((waveResult>0)&&(NegON==0)&&(invertY==1)) // inversion on, adjust required
   {
       waveResult=0;
   }
   else
   {
       if ((waveResult<0)&&(NegON==0)&&(invertY==0)) //inversion off, adjustment not needed
       {
           waveResult=0;
       }
   }
 
   // Correctly assign vertical shift depending on Y-axis inversion state
   if(invertY=0)
   {
      waveResult+=vShift; // Y inversion off, addition of vShift required
   }
   else
   {
       waveResult-=vShift;  // Y inversion on, subtraction of vShift required
   }
 
   // check for overflow (due to vShift) and clip to size (make within -1 to 1)
   if(waveResult<-1) // clip values under -1
   {
       waveResult=-1;
   }
 
   if(waveResult>1) // clip values over +1
   {
       waveResult=1;
   }
 
   // output the generated waveform
   return waveResult;

}


Re: GEuser's CGUI Lib: genWave(gW) Wave Generator

PostPosted: Sat Jan 12, 2013 4:18 am
by GEuser
ampUnion?

For those curious about what this ampUnion variable is doing in the code. Here's an explanation. It's what allows the output to end up being of size 1 when waves are either multiplied or added/subtracted. I had to discover a solution after tweaking different number crunching methods. I'll try to keep it as simple as possible but I'm finding it hard to explain. There's different rules for multiplication and add/subtract. However, there was no simple way of doing this for mixed version (multiplication and addition/subtraction). Possible but too much effort and cumbersome code. Avoided division because it gives a 'Division by Zero' error. A way around this is to multiply with decimal fractons (e.g. 9/3 = 9 * 1/3, same thing, so YOU can use multiplication to do division. I don't have to provide it). Anyways, back on topic...

Because the amplitude of the waves is what determines the overall size of the wave (overly simplified) then I can scale these down (or up) and the rest of stuff (frequency, phase shift, etc...) will just fall in line.

NUMERICAL EXAMPLE

When adding 3 numbers togather and scaling them down to 1 without losing their proportional size to each other, I need a scaling factor that I can multiply each number with, to get them to shrink down to 1. Call this scaling factor m. Say, the numbers are 2, 4, 8. Can you see that each number is double of the previous (the first 1/4 of the last). I what to keep these proportions when shrinking down to 1. To achieve this, m (scaling factor) has to be the same for all of these numbers (in order for the numbers to keep their proportions the same).

A bit of algebra:-

Output = 2m + 4m + 8m // scaling factor, m, is applied to each number (multiplied)

We want Output of 1:

1 = 2m + 4m + 8m

simplify:
1 = m (2 + 4 + 8 ) // total of numbers is in parenthesis

Divide both sides by total:
1 / (2 + 4 + 8 ) = m

Result same as saying:

m = 1 divided by (addition) total of all the numbers.

m = 1 / 14 = 0.07143 (rounded off)

Test it out:

Output = 2m + 4m + 8m
Output = 2*0.07143 + 4*0.07143 + 8*0.07143
Output = 0.14286 + 0.28572 + 0.57144
Output = 1.00002

Not exactly 1 because I rounded off m but more than good enough. Notice that 0.14286 is half of 0.28572 which in turn is half of 0.57144, the first is 1/4 of last and they all add up to 1. We got the output we want (1) and they are all in the same proportion as original numbers we started with.

If I wanted to do that with the 3 waves amplitudes when all three waves are added I used unionAmp instead of m scaling factor:-

ampUnion = 1 / (wAmp1 + wAmp2 + wAmp3)

Output = ampUnion * wave1 + ampUnion * wave2 + ampUnion * wave3

MULTIPLICATION VERSION

For multiplication version you have to use Nth roots of number count your using (2 numbers = square root, 3 numbers=cube root, 5 numbers = 5th root, etc...), Same basic steps but you divide 1 by the Nth root of the multiplication total.

ampUnion = 1 / pow((wAmp1 * wAmp2 * wAmp3), 1/3) // note computer not comfortable with 1/3 so use rounded off 0.3333

Output = (ampUnion * wave1) * (ampUnion * wave2) * (ampUnion * wave3) // parethesis necessary


FOR YOUR OWN CODING

You can make this formula more general so that you can scale things down proportionally to any number. Simply replace the "1 divided by" part with "output wanted divided by". Example: In first addition version you might have wanted those numbers to shrink down/ magnify upto 5 instead of just 1:-

5 = 2m + 4m + 8m // wanted 5 output

m = 5 / (2 + 4 + 8 ) = 5/14 = 0.357...

Here's a summary of everything.

Image

Image

Oh, got this weird wood like texture output when messing around with genWave Viewer:

Image

genWave(10, 1, 1, 0.01, 0, 360, 0, 1, 0.5, 90, 240, 120, 90, 15, 0.3, 90);
(play with wave2 amp & freq)

Re: GEuser's CGUI Lib: genWave(gW) Wave Generator

PostPosted: Sat Jan 12, 2013 7:37 am
by Hblade
Outstanding!