// Câmera e luz para exercicio de energia solar
// Last edited on 2023-12-31 09:04:29 by stolfi

#macro camsol(ctr,rad,vis,nor,sol,lux)
  // {ctr} = center of interest in scene.
  // {rad} = approx radius of scene.
  // {vis} = 0 for observer's view, 1 for sun's view.
  // {nor} = vector pointing from {ctr} to celestial nort; only its direction matters.
  // {sol} = vector pointing from {ctr} to sun; only its direction matters.
  // {lux} = a scaling factor for the intensity of standard light sources.

  // {cav} = vector pointing from {ctr} to camera; only its direction matters.
  // {dst} = distance from {ctr} to camera.
  // {upp} = vector pointing 'up' for camera roll; only direction matters.

  #local nor_dir = vnormalize(nor);
  #local sol_dir = vnormalize(sol);

  #if (vista = 0)
    // Flying observer's view: 
    #local cam_dir = vnormalize(< -7, 3, 5 >);
    #local cam_dst = 6*(rad);
    #local upp = z;
  #elseif (vista = 1)
    // Sun's view:
    #local cam_dir = sol_dir;
    #local cam_dst = 100*(rad);
    #local upp = nor_dir;
  #elseif (vista = 2)
    // View from X-axis
    #local cam_dir = x;
    #local cam_dst = 100*(rad);
    #local upp = z;
  #elseif (vista = 3)
    // View from Y-axis
    #local cam_dir = -y;
    #local cam_dst = 100*(rad);
    #local upp = z;
  #end

  #if (vista = 0)
    #local nor_tip = 0.65*(rad)*nor_dir;
    #local font_size = 0.07*(rad);
    union{
      object{ eixo(nor_tip,<1,1,1>,"norte") }
      text{ 
        ttf "tt-fonts/arial.ttf" "N" 0.2, 0 
        texture{ pigment{ color rgb <1,1,1>} finish{ diffuse 0.6 ambient 0.4 } }
        scale font_size
        rotate 90*x rotate -90*z
        translate -0.45*font_size*z
        translate 1.1*nor_tip
      }
      translate ctr
      no_shadow
    }
  #end

  #local swh = sqrt(image_width/image_height);
  #local ape = 1.41*(rad)/cam_dst;  // Camera aperture.
  camera {
    location (ctr) + cam_dst*cam_dir
    right  -swh*ape*x
    up     1.0/swh*ape*y
    sky    (upp)
    look_at (ctr)
  }
  #if (lux > 0)
    #local sol_elev = -degrees(atan2(sol_dir.z,sqrt(sol_dir.x*sol_dir.x + sol_dir.y*sol_dir.y)));
    #local sol_azim = degrees(atan2(sol_dir.y,sol_dir.x));
    #local nrings = 2;
    #local angrad = 2;
    object{ lamp_array(nrings,angrad,lux)
      rotate 360*(sqrt(5)-1)/2*x
      scale 2000*rad
      rotate sol_elev*y 
      rotate sol_azim*z
      translate ctr 
    }
  #end
#end
    
#macro lamp_array(nrings,angrad,lux) 
  // A round lamp array with {nrings} rings of lamps,
  // total angular radius {angrad} (degrees), and total strength {lux}.
  // The array is centered on the X axis at distance 1 
  // from the origin.

  // Compute normalization factor {st_norm} for ring strengths:
  #local k = 0;  // Light ring index.
  #local st_norm = 0;
  #while (k < nrings)
    #local st = camlight_rel_ring_strength(k,nrings);
    #local st_norm = st_norm + st; 
    #local k = k + 1;
  #end
  
  #local coin = seed(4615);
  
  union{
    #local k = 0;  // Light ring index.
    #while (k < nrings)
      // Number of lamps {np} in ring {k} and initial phase {phas}:
      #local np = (k = 0 ? 1 : 12*pow(2,k-1));      // Number of lamps in ring.
      #local phas = (k = 0 ? 0 : 0.6180/np);       // Rel. position of first lamp
      // Total relative strength {st} of lamps in ring:
      #local st = camlight_rel_ring_strength(k,nrings);
      // Generate the lamps in the ring:
      #local p = 0; // Lamp index.
      #while (p < np)
        // Jittering terms in {k,p}:
        #local dk = (k = 0 ? 0 : 0.20*(2*rand(coin)-1));
        #local dp = (k = 0 ? 0 : 0.20*(2*rand(coin)-1));
        // Angular radius of ring:
        #local tau = camlight_rel_ring_radius(k+dk,nrings); // Relative radius of ring
        #local phi = (k = 0 ? 0 : angrad*tau);  // Angular radius of ring.
        #local tht = 360*(phas + (p+dp)/np); 
        light_source {
          <1,0,0>
          color rgb (st/st_norm/np)*lux*<1.0,1.0,1.0>
          rotate phi*z
          rotate tht*x
        }
        // #warning concat("light at phi = ", str(phi,6,1), " tht = ", str(tht,6,1), "\n")
        #local p = p + 1;
      #end
      #local k = k + 1;
    #end
  }
#end

#macro camlight_rel_ring_strength(k,nrings)
  // Relative total strength (unnormalized) of lights in ring {k} of {nrings} rings.
  #local st = (k = 0 ? 0.5*0.5 : 2*k)*((nrings-0.5)*(nrings-0.5) - k*k);
  st
#end

#macro camlight_rel_ring_radius(k,nrings)
  // Relative radius of light ring {k} in a lamp cluster with {nrings} rings.
  #local rr = (k = 0 ? 0 : pow(k/(nrings - 0.5),1.5));
  rr
#end