• fullscreen
  • CachedSin.pde
  • CityStrip.pde
  • SinusoidalSeries.pde
  • alphaTexture.pde
  • //////////////////
    //lookup table implementation for sin; to speed it up.
    //number of values to hold in the lookup table - more values better, more conituous function
    // fewer values - faster implementation, less memory footprint.
    final int sinCacheSz = (int)(550*TWO_PI); 
    float sinCacheSzDivTWOPI; 
    float cachedSin[];
    boolean initialized = false;
    //cache all the values for the array
    void initCachedSin()
    {
      if(initialized)
        return;
      //we will want this value later when we're pulling values from the array
      sinCacheSzDivTWOPI = ((sinCacheSz)/TWO_PI);
      cachedSin = new float[sinCacheSz];
      for(int i = 0; i < cachedSin.length; i++)
      {
        cachedSin[i] = sin(i*TWO_PI/cachedSin.length);
      }
      initialized = true;
    }
    
    //new implementation of sin() function
    float sn(float t)
    {  
      //reduce 't' to a number between 0 and TWO*PI
      float tmp = t%TWO_PI;
      //if it's (now) a negative number increase by two pi
      if(tmp < 0)
        tmp += TWO_PI;
      // scale up the new index
      int index = (int)(tmp * sinCacheSzDivTWOPI);
      return cachedSin[index];
    }
    //////////////////////////
    
    
    class CityStrip
    {
      float[][] lineVerts;
      private float[][][] triVerts;
      
      private float texSpeed = .00001;
      private float stripWidth = 250;
      public CityStrip(float[][] lineVerticies)
      {
        lineVerts = lineVerticies;
        generateTriVerts();
      }
      
      private float getLineLength(float[][] lineVerts)
      {
        float lineLen = 0;
        for(int i = 1; i < lineVerts.length; i++)
        {
          float a = lineVerts[i][0]-lineVerts[i-1][0];
          float b = lineVerts[i][1]-lineVerts[i-1][1];
          float c = lineVerts[i][2]-lineVerts[i-1][2];
          lineLen += sqrt(a*a+b*b+c*c);
        }
        return lineLen;
      }
      
      private void generateTriVerts()
      {
        triVerts = new float[lineVerts.length*2][3][6];
    
        // up dir is parallel to breadth of the ribbon
        //up dir should be normalized
        float[] upDir = new float[]{0,1,0};
        upDir[0]*=stripWidth;
        upDir[1]*=stripWidth;
        upDir[2]*=stripWidth;
        
        //each element in the vert array is stored as follows
        //index  description
        //    0  x - pos
        //    1  y - pos
        //    2  z - pos
        //    3  ux texture coord
        //    4  uy texture coord
        //    5  vert sorting value, ie depth
        for(int i = 0; i < lineVerts.length-1; i++)
        {
          //firstTri
          triVerts[i*2] = new float[][]{new float[]{lineVerts[i][0]-upDir[0],
                                                    lineVerts[i][1]-upDir[1],
                                                    lineVerts[i][2]-upDir[2],
                                                    i*1.f/lineVerts.length,0,0},
                                      new float[]{lineVerts[i][0]+upDir[0],
                                                  lineVerts[i][1]+upDir[1],
                                                  lineVerts[i][2]+upDir[2],
                                                  i*1.f/lineVerts.length,1,0},
                                      new float[]{lineVerts[i+1][0]-upDir[0],
                                                  lineVerts[i+1][1]-upDir[1],
                                                  lineVerts[i+1][2]-upDir[2],
                                                  (i+1)*1.f/lineVerts.length,0,0}
                                                };
    
          triVerts[i*2+1] = new float[][]{
                                          new float[]{lineVerts[i][0]+upDir[0],
                                                      lineVerts[i][1]+upDir[1],
                                                      lineVerts[i][2]+upDir[2],
                                                      i*1.f/lineVerts.length,1,0},
                                          new float[]{lineVerts[i+1][0]+upDir[0],
                                                      lineVerts[i+1][1]+upDir[1],
                                                      lineVerts[i+1][2]+upDir[2],
                                                      (i+1)*1.f/lineVerts.length,1,0},
                                          new float[]{lineVerts[i+1][0]-upDir[0],
                                                      lineVerts[i+1][1]-upDir[1],
                                                      lineVerts[i+1][2]-upDir[2],
                                                      (i+1)*1.f/lineVerts.length,0,0}};
        }
      }
      
      public void draw(PImage tex)
      {
        stroke(255);
        strokeWeight(2);
        textureMode(NORMAL);
        beginShape(TRIANGLES);
        texture(tex);
        int cutoff = mouseX*triVerts.length/width;
        //iterate through all trangles and draw them
        //note that this is limited by the horizontal positioning of the mouse
        for(int i = 0; i < cutoff; i++)
        {
          float texPos = 0;
          
          for(int j = 0; j < triVerts[i].length; j++)    
          {
            float tmp = ((triVerts[i][j][3]*80)%1.f);
            if(tmp < texPos)
              texPos = tmp +1;
            else
              texPos = tmp;
            
              vertex(triVerts[i][j][0],triVerts[i][j][1],triVerts[i][j][2],
                     texPos,triVerts[i][j][4]);             
          }
        }
        for(int i = cutoff; i < triVerts.length; i++)
        {
          for(int j = 0; j < triVerts[i].length; j++)    
          {
            vertex(triVerts[i][j][0],triVerts[i][j][1],triVerts[i][j][2]);
          }
        }
        endShape();
      }
      
      
      //reorders the triangles forcing them to rener back to front so
      //processing handles the alpha better  
      public void sortTrianglesByDepth(float[] camerapos)
      {    
        //calculate a 'depth' for each triangle and sort based on that
        for(int i = 0; i < triVerts.length; i++)
        {
          //add all three verts together and divide...  could use another determination 
          //of the 'depth' of the triangle such as min or max vert dist...
          float[] pos = new float[]{(triVerts[i][0][0] + triVerts[i][1][0] + triVerts[i][2][0]) /3, 
                                    (triVerts[i][0][1] + triVerts[i][1][1] + triVerts[i][2][1]) /3,
                                    (triVerts[i][0][2] + triVerts[i][1][2] + triVerts[i][2][2]) /3};
    
          //store whatever you want to sort with in index 5 of the array                                
          triVerts[i][0][5] = dist(camerapos[0],camerapos[1],camerapos[2],
                                   pos[0],pos[1],pos[2]);      
        }    
        quickSortTris(0,triVerts.length-1);
      }
      
      // an 'in-place' implementation of quick I whipped together late at night
      // based off of the algorithm found on wikipedia: http://en.wikipedia.org/wiki/Quicksort
      private void quickSortTris(int leftI, int rightI)
      {
        if(leftI < rightI)
        {
          int pivotIndex = (leftI + rightI)/2;
          int newPivotIndex = partition(leftI,rightI,pivotIndex);
          quickSortTris(leftI, newPivotIndex-1);
          quickSortTris(newPivotIndex+1, rightI);
        }
      }
      
      //part of quicksort
      private int partition(int leftIndex, int rightIndex, int pivotIndex)
      {
        float pivotVal = triVerts[pivotIndex][0][5];
        swapTris(pivotIndex,rightIndex);
        int storeIndex = leftIndex;
        for(int i = leftIndex; i < rightIndex; i++)
        {
          if(triVerts[i][0][5] > pivotVal)
          {
            swapTris(i,storeIndex);
            storeIndex++;
          }      
        }
        swapTris(rightIndex,storeIndex);
        return storeIndex;
      }
      
      //part of quicksort
      private void swapTris(int a, int b)
      {
        float[][] tmp = triVerts[a];
        triVerts[a] = triVerts[b];
        triVerts[b] = tmp;
      }
    }
    
    //uses a combination of cosine functions to output a continuous number along 't' that 
    // is always between 0 and maxAmp and varies no more than maxfreq
    class SinusoidalSeries
    {
      public SinusoidalSeries(int coefs, float maxFreq, /*float maxOffset,*/ float maxAmp)
      {
        initCachedSin();
        frequency = new float[coefs];
        offset = new float[coefs];
        amplitude = new float[coefs];
        
        // secondary arrays don't affect the max and mins and have slightly different rules
        secondaryFrequency = new float[coefs];  
        secondaryOffset = new float[coefs];
        
        float ampD = 0;
        float freqD = 0;
        float freqD2 = 0;
        for(int i = 0; i< coefs; i++)
        {      
          frequency[i] = maxFreq-random(2*maxFreq);
          secondaryFrequency[i] = maxFreq-random(2*maxFreq);
          amplitude[i] = random(maxAmp);  
          ampD += amplitude[i];//*amplitude[i];
          freqD += frequency[i]*frequency[i];
          freqD2 += secondaryFrequency[i]*secondaryFrequency[i];
    
          offset[i] = random(TWO_PI);             
          secondaryOffset[i] = random(TWO_PI);
        }    
    
        freqD = sqrt(freqD);
        freqD2 = sqrt(freqD2);
        for(int i = 0; i< coefs; i++)
        {
          amplitude[i] *= maxAmp/ampD;
          frequency[i] *= maxFreq/freqD;
          secondaryFrequency[i] *= maxFreq/freqD2;
        }
      }
      
      public float getCurrentValue(float t)
      {
        //calc the crazy sinusoidal biz
        //iterate through and combine all sine wavs
        float tmp = 0.0f;
        for(int i = 0; i < frequency.length; i++)
        {      
          tmp += amplitude[i] * (sn(frequency[i] * (t + offset[i]) ) * sn(secondaryFrequency[i] * (t + secondaryOffset[i])) +.0f)/1.0f ;           
       
        }
        return tmp;
      }
      
      //sinusoidal stuff
      private float[] frequency;
      private float[] secondaryFrequency;  
      private float[] amplitude;
      private float[] secondaryOffset;
      private float[] offset;
    
    }
    
    import processing.opengl.*;
    
    PImage textureImg;
    CityStrip strip1;
    float[] cameraPos;
    SinusoidalSeries camSinsPos = new SinusoidalSeries(2, .8, 850);
    float[][] pts;
    
    void setup()
    {
      size(800,500,P3D);
    
      textureImg = loadImage("spots.png");
      genStrip1();  
      setCameraPos(new float[]{0, 1000, 2000});
    }
    
    void genStrip1()
    {
      //make some fancy curves for us!
      SinusoidalSeries sinS = new SinusoidalSeries(6, 6, 4.f);
      SinusoidalSeries sinS2 = new SinusoidalSeries(8, 9, 4.f);
      SinusoidalSeries sinS3 = new SinusoidalSeries(8, 9, 2.f);  
      pts = new float[1000][3];
      float helixW = 350;
      for(int i = 0; i < pts.length; i++)
      {
        float t = PI+i*TWO_PI*3/ pts.length;
        pts[i] = new float[]{(helixW)*sinS.getCurrentValue(t),
                             -1000+(pts.length-i)/.29,
                             (helixW)*sinS2.getCurrentValue(t)};
      }
      strip1 = new CityStrip(pts); 
    }
    
    float[] getLinePos(float pct)
    {
      int index = (int)(pct*pts.length%(pts.length*1.f));
      float leftover = (pct*pts.length%(pts.length*1.f)) - index;
    
      float[] dir = new float[]{pts[index+1][0] - pts[index][0],
                                pts[index+1][1] - pts[index][1],
                                pts[index+1][2] - pts[index][2]};
      float len = dist(0,0,0,dir[0],dir[1],dir[2]);
      dir[0]*= leftover; dir[1]*= leftover; dir[2]*= leftover;
      dir[0]+=pts[index][0]; dir[1]+=pts[index][1]; dir[2]+=pts[index][2];
      return dir;
    }
    
    void draw()
    {
      //this helps decrease the minimum triangle culling plane, I think...
      perspective(95,768.f/1024,1,5000);
      
      //finds an interpolated position along the triangle strip 
      float[] linePos = getLinePos(millis()/1000000.f);
      
      //produce slo-moving camera position.
      //sets the camera position and sorts all of the triangles
      setCameraPos(new float[]{linePos[0]+camSinsPos.getCurrentValue(millis()/5000.f), 
                               linePos[1]+camSinsPos.getCurrentValue(700+millis()/5000.f), 
                               linePos[2]+camSinsPos.getCurrentValue(-500+millis()/5000.f)});
      camera(cameraPos[0],cameraPos[1],cameraPos[2], //eyepos
             linePos[0], linePos[1],linePos[2],//target
             0.0, 1.0, 0.0); //up
      
      background(0,0,0);
      
      strip1.draw(textureImg);
      println("alpha-frameRate: " + frameRate);
    }
    
    void setCameraPos(float[] pos)
    {
      cameraPos = pos;
      //re sort the triangles
      strip1.sortTrianglesByDepth(cameraPos);
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    ben van citters

    alphaTexture

    Add to Faves Me Likey@!
    You must login/register to add this sketch to your favorites.

    Illustrates a triangle strip, sorted back to front and the associated problems with rendering with partially transparent png textures

    You need to login/register to comment.