boolean transmissionDirection(Ray ray, HitRecord hit, Ray transmission) {
      double n = transmission.r.prevR.n;
      double nt = transmission.r.n;

      Double3D N = hit.normal;
      Double3D D = ray.dir;

      double cosine = -D.dot(N);
      if (n > nt) { // We're inside, so reverse the normal
        N = N.sMult(-1);
        cosine = -D.dot(N);
      }

      double nRatio = n / nt;

      double cosinePSq = 1.0 - nRatio * nRatio * (1.0f - cosine * cosine);

      // check for total internal refraction here
      if (cosinePSq < 0.0f) return false; // total internal refraction
      else {
        // D - N(N.D)
        // Double3D pOne = D.minus( N.sMult(N.dot(D)) ).sMult(nRatio);
        double inside = nRatio * cosine - Math.sqrt(cosinePSq);
        Double3D temp = D.sMult(nRatio).plus(N.sMult(inside)).getUnit();
        transmission.dir.x = temp.x;
        transmission.dir.y = temp.y;
        transmission.dir.z = temp.z;
      }
      return true;
    }
    // iPoint is the point of intersection with the surface.
    DoubleColor shade(Ray ray, HitRecord hit, MaterialCell material, boolean background) {
      DoubleColor color = new DoubleColor(0.0, 0.0, 0.0, 1.0);

      // Add ambient light only once
      color.plus(
          new DoubleColor(
              (double) (lights[0].ambient[0] * material.ka.r),
              (double) (lights[0].ambient[1] * material.ka.g),
              (double) (lights[0].ambient[2] * material.ka.b),
              (double) (lights[0].ambient[3] * material.ka.a)));

      // Assign material color?
      // Local light or directional? If directional then we need to see if it's shining on the
      // object
      if (!background) {
        double d = 2; // L.distanceTo(hit.hitP);

        for (int i = 0; i < lights.length; i++) {
          if (lights[i].lightSwitch == 1) {
            Double3D L =
                new Double3D(
                    (double) lights[i].position[0],
                    (double) lights[i].position[1],
                    (double) lights[i].position[2]);
            L = L.minus(hit.hitP).getUnit();
            Ray shadowRay = new Ray(hit.hitP, L);
            // trace shadow ray to light source

            // Turn shadows on and shadowRay hit nothing
            if (!scene.shadows || shadowTrace(shadowRay)) {
              double LdN = Math.max(0, hit.normal.dot(L));
              if (LdN > 0) {
                // -2(-L.N)N + -L
                Double3D R = hit.normal.sMult(-2 * hit.normal.dot(L.sMult(-1))).plus(L.sMult(-1));
                double RdV = Math.max(0, -R.dot(ray.dir));

                // If the light is free add the diffuse light
                // Intensity (Kd * (LdN) + Ks *(RdV)^(shiny)/(r + k)
                color.plus(
                    new DoubleColor(
                        (double)
                                (lights[i].diffuse[0] * LdN
                                    + lights[i].specular[0] * Math.pow(RdV, material.shiny))
                            / d,
                        (double)
                                (lights[i].diffuse[1] * LdN
                                    + lights[i].specular[1] * Math.pow(RdV, material.shiny))
                            / d,
                        (double)
                                (lights[i].diffuse[2] * LdN
                                    + lights[i].specular[2] * Math.pow(RdV, material.shiny))
                            / d,
                        1.0)); // */
              } // if(LdN > 0)
            } // if(!scene.shadows || shadowTrace(shadowRay))
          } // if(lights[i].lightSwitch == 1){
        } // for

        // Shiny Phong
        // If IdN > 0 then we find a reflection
        // If IdN < 0 then we need -normal
        if (scene.reflections
            && (material.reflectivity.r > 0
                || material.reflectivity.g > 0
                || material.reflectivity.b > 0)) {
          depth++;

          // R = I - 2 * (I.N)N
          Double3D R = new Double3D();
          Double3D I = ray.dir; // .sMult(-1.0);
          Double3D N = hit.normal;
          // double IdN = I.dot(N);

          // if (IdN > 0){
          //	N = N.sMult(-1.0);
          //	IdN = -I.dot(N);
          // }//*/

          R = I.plus(N.sMult(-2.0 * I.dot(N)));

          Ray reflect = new Ray(hit.hitP, R);
          DoubleColor reflection = trace(reflect);

          // Scale by distance?
          // reflection.scale( 1 / reflect.origin().distanceTo(hit.hitP));

          reflection.r = reflection.r * material.reflectivity.r;
          reflection.g = reflection.g * material.reflectivity.g;
          reflection.b = reflection.b * material.reflectivity.b;

          color.plus(reflection);

          depth--;
        }

        if (scene.refractions
            && (material.refractivity.r > 0
                || material.refractivity.g > 0
                || material.refractivity.b > 0)) // */
        {
          depth++;

          Ray refract = new Ray(hit.hitP, ray.dir);

          if (hit.index == ray.r.objectNum) // Hit the object we're already in
          {
            // Pop the n off the stack
            refract.r = ray.r;

            // Swap the refraction indices
            double temp = refract.r.n;
            refract.r.n = refract.r.prevR.n;
            refract.r.prevR.n = temp;
          } else // Otherwise we hit a new object push this n onto the stack and get mat index
          {
            refract.r.prevR = ray.r;
            refract.r.n = material.refractiveIndex;
            refract.r.objectNum = hit.index;
          }

          if (transmissionDirection(ray, hit, refract)) {
            DoubleColor refraction = trace(refract);

            refraction.r = refraction.r * material.refractivity.r;
            refraction.g = refraction.g * material.refractivity.g;
            refraction.b = refraction.b * material.refractivity.b;

            // Scale for distance?
            color.plus(refraction);
          }

          depth--;
        }
      }
      return color;
    }