/*

Sphere
------
This script generates a tileable texture with lots of spheres
randomly placed on it.

Author : Andreas Jonsson
Created: April 17th, 2005
Updated: July 28th, 2005

Texture Generator version 1.1.0

*/

randomizer rnd;

void main()
{
  int m = 1;

  image @img = @image(256*m, 256*m);

  int n = 0;
  while( n++ < 200 )
  {
    int x = int(rnd.getNumber() * float(256*m));
    int y = int(rnd.getNumber() * float(256*m));
    float r = 7.5f*float(m) + rnd.getNumber()*20.0f*float(m);
    
    sphere(@img, x, y, 20.0f);
  }
  
  light(@img, 0.4f, 0.23f, 2.0f);

  img.Show();
}

void sphere(image @img, int x, int y, float r)
{
  int w = img.width;
  int h = img.height;

  if( int(r) > w or
      int(r) > h )
      return;

  int cx = x;
  int cy = y;

  int sx = cx - int(r);
  int sy = cy - int(r);
  
  int ex = cx + int(r);
  int ey = cy + int(r);

  float fx = float(x);
  float fy = float(y);
    
  y = sy;
  while( y <= ey )
  {
    x = sx;
    while( x <= ex )
    {
      float fx = float(cx - x);
      float fy = float(cy - y);
      
      if( fx*fx + fy*fy <= r*r )
      {
        // Compute height of the sphere at this location
        float fd2 = fx*fx + fy*fy;
        float fh = sqrt(r*r - fd2);
        
        // Put this height at the pixel
        int px = x;
        int py = y;
        if( px < 0 ) px = px + w;
        if( py < 0 ) py = py + h;
        if( px >= w ) px = px - w;
        if( py >= h ) py = py - h;
        
        float old = img.Alpha(px, py);
        if( old < fh )
        {
          img.Pixel(px, py) = pixel(fh, fx/r, fy/r, fh/r);
        }
      }
    
      x++;
    }
  
    y++;
  }
}

void light(image @img, float dx, float dy, float dz)
{
  // Normalize light vector
  float l = sqrt(dx*dx + dy*dy + dz*dz);
  dx = dx/l;
  dy = dy/l;
  dz = dz/l;  

  int w = img.width;
  int h = img.height;
  
  int y = 0;
  while( y < h )
  { 
    int x = 0;
    while( x < w )
    {
      // Get surface normal
      float sx = img.Red(x,y);
      float sy = img.Green(x,y);
      float sz = img.Blue(x,y);
    
      // Normalize
      l = sqrt(sx*sx + sy*sy + sz*sz);
      if( l > 0.0 )
      {
        sx = sx/l;
        sy = sy/l;
        sz = sz/l;
    
        // Compute dot product
        float d = dx*sx + dy*sy + dz*sz;  
     
        img.Pixel(x,y) = pixel(d,d,d);
      }
      
      x++;
    }
    y++;
  }
}

image @shrink2(image @img)
{
  int w = img.width/2;
  int h = img.height/2;

  image @mip = @image(w, h);
  
  int x = 0;
  while( x < w )
  {
    int y = 0;
    while( y < h )
    {
      int c = 0;
      while( c < 3 )
      {
        float v = img.Channel(2*x, 2*y, c) +
                  img.Channel(2*x+1, 2*y, c) +
                  img.Channel(2*x, 2*y+1, c) +
                  img.Channel(2*x+1, 2*y+1, c);
        v = 0.25*v;
        mip.Channel(x,y,c) = v;      
        c++;
      }
      mip.Alpha(x,y) = 1.0;
      
      y++;
    }
    
    x++;
  }
  
  return @mip;
}