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;
    }
    @Override
    public void run() {
      // Always tracing from the origin here.
      Double3D origin = new Double3D(0.0, 0.0, 0.0);

      black.ka = new DoubleColor(0, 0, 0, 1);
      black.kd = new DoubleColor(0, 0, 0, 1);
      black.ks = new DoubleColor(0, 0, 0, 1);
      black.shiny = 0;
      black.refractiveIndex = 1;
      black.reflectivity = new DoubleColor(0, 0, 0, 1);
      black.refractivity = new DoubleColor(0, 0, 0, 1);

      white.ka = new DoubleColor(1, 1, 1, 1);
      white.kd = new DoubleColor(0, 0, 0, 1);
      white.ks = new DoubleColor(0, 0, 0, 1);
      white.shiny = 0;
      white.refractiveIndex = 1;
      white.reflectivity = new DoubleColor(0, 0, 0, 1);
      white.refractivity = new DoubleColor(0, 0, 0, 1);

      // Work though viewport (pixel) coordinates
      // Start in the middle of pixel 0
      double worldY = scene.camera.windowBottom + (yMin + startLine) * heightRatio;
      for (curY = (int) yMin + startLine; curY < yMax; curY = curY + THREADS) {
        worldY += THREADS * heightRatio;
        // Start in the middle of pixel 0
        double worldX = scene.camera.windowLeft + (xMin) * widthRatio;

        for (curX = (int) xMin; curX < xMax; curX++) {
          worldX += widthRatio;
          Double3D dir = new Double3D(worldX, worldY, -scene.camera.near);
          Ray ray = new Ray(origin, dir.getUnit());

          viewPort[curX][curY] = trace(ray);

          // We haven't sampled this pixel yet.
          if (!scene.antiAliasing) sampled = false;
        } // for y
      } // for x

      System.out.println("Thread " + startLine + " finished.");
    }
    // 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;
    }
    // All rays we deal with here are in world coordinates.
    // Should take the refractive index of the material it is currently in.
    DoubleColor trace(Ray ray) {
      DoubleColor color = new DoubleColor(0.0, 0.0, 0.0, 1.0);
      HitRecord hit = new HitRecord();

      if (depth > Math.max(DEBUG_recursion, scene.maxRecursiveDepth)) return color;

      double tMin = 0.0001;
      double tMax = 10000000;

      // Spheres only for now
      for (int i = 0; i < numObjects; i++)
        // Did I hit the bounding sphere for an object?
        if (spheres[i].hit(ray, tMin, tMax, 0, hit))
          if (scene.spheresOnly) {
            for (PMesh.SurfCell s = shapes[i].surfHead; s != null; s = s.next)
              for (PMesh.PolyCell poly = s.polyHead; poly != null; poly = poly.next)
                // Triangles only for now
                if (poly.numVerts == 3) {
                  Double3D v[] = new Double3D[3];
                  int j = 0;
                  for (PMesh.VertListCell vert = poly.vert; vert != null; vert = vert.next)
                    v[j++] = shapes[i].vertArray.get(vert.vert).viewPos;
                  // Increment j in the line post access

                  // Check for a hit on this polygon
                  if (Triangle.hit(v[0], v[1], v[2], ray, tMin, tMax, 0, hit)) {
                    tMax = hit.t;

                    hit.normal = poly.viewNorm;
                    hit.matIndex = s.material;
                    hit.index = i;
                  }
                } else
                  System.out.println(
                      "Need to intersect polygon with " + poly.numVerts + " vertices.");
          } else {
            tMax = hit.t;
            hit.matIndex = i; // May cause an error if object 10 and it only has 3 materials.
            hit.index = i;
          }

      if (hit.index >= 0) // If it intersects then multi-sample
      {
        if (!sampled && depth == 0) {
          // Only sample once per ray from the main loop
          sampled = true;

          Double3D dir = ray.dir;
          DoubleColor antiAlias = trace(ray);

          for (int i = 0; i < samples; i++) {
            // Double3D sample = new Double3D(dir.x + imageSamples[i].x, dir.y + imageSamples[i].y,
            // dir.z).getUnit();
            // ray.dir = sample;
            ray.dir.x = dir.x + imageSamples[i].x;
            ray.dir.y = dir.y + imageSamples[i].y;

            antiAlias.plus(trace(ray));
          }
          antiAlias.scale(1.0 / (samples + 1.0));

          color.plus(antiAlias);
        } else if (hit.matIndex < shapes[hit.index].materials.length)
          color = shade(ray, hit, shapes[hit.index].materials[hit.matIndex], false);
        else
          color =
              shade(
                  ray,
                  hit,
                  shapes[hit.index].materials[shapes[hit.index].materials.length - 1],
                  false);
      } else // We hit nothing check for intersection with the far clip plane for checker board
      // pattern.
      if (scene.checkerBackground) color = shade(ray, hit, checkerBackgroundHit(ray, hit), true);

      return color;
    }