Package ca.eandb.jmist.framework.shader.ray

Source Code of ca.eandb.jmist.framework.shader.ray.SceneRayShader

/**
* Java Modular Image Synthesis Toolkit (JMIST)
* Copyright (C) 2008-2013 Bradley W. Kimmel
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package ca.eandb.jmist.framework.shader.ray;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Stack;

import ca.eandb.jmist.framework.Illuminable;
import ca.eandb.jmist.framework.Intersection;
import ca.eandb.jmist.framework.Light;
import ca.eandb.jmist.framework.LightSample;
import ca.eandb.jmist.framework.Material;
import ca.eandb.jmist.framework.Medium;
import ca.eandb.jmist.framework.Modifier;
import ca.eandb.jmist.framework.NearestIntersectionRecorder;
import ca.eandb.jmist.framework.Random;
import ca.eandb.jmist.framework.RayShader;
import ca.eandb.jmist.framework.ScatteredRay;
import ca.eandb.jmist.framework.Scene;
import ca.eandb.jmist.framework.SceneElement;
import ca.eandb.jmist.framework.Shader;
import ca.eandb.jmist.framework.ShadingContext;
import ca.eandb.jmist.framework.ScatteredRay.Type;
import ca.eandb.jmist.framework.color.Color;
import ca.eandb.jmist.framework.color.ColorModel;
import ca.eandb.jmist.framework.color.WavelengthPacket;
import ca.eandb.jmist.framework.random.SimpleRandom;
import ca.eandb.jmist.framework.random.ThreadLocalRandom;
import ca.eandb.jmist.math.Basis3;
import ca.eandb.jmist.math.Point2;
import ca.eandb.jmist.math.Point3;
import ca.eandb.jmist.math.Ray3;
import ca.eandb.jmist.math.Vector3;

/**
* A <code>RayShader</code> that shades a ray using a scene description.
* @author Brad Kimmel
*/
public final class SceneRayShader implements RayShader {

  /**
   * Serialization version ID.
   */
  private static final long serialVersionUID = -1662354927701351486L;

  private final Light light;

  private final SceneElement root;

  private final RayShader background;

  private final Random rng;

  public SceneRayShader(SceneElement root, Light light, RayShader background, Random rng) {
    this.root = root;
    this.light = light;
    this.background = background;
    this.rng = rng;
  }

  public SceneRayShader(SceneElement root, Light light, RayShader background) {
    this(root, light, background, new ThreadLocalRandom(new SimpleRandom()));
  }

  public SceneRayShader(Scene scene, RayShader background) {
    this(scene.getRoot(), scene.getLight(), background, new ThreadLocalRandom(new SimpleRandom()));
  }

  public SceneRayShader(Scene scene) {
    this(scene.getRoot(), scene.getLight(), RayShader.BLACK, new ThreadLocalRandom(new SimpleRandom()));
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.RayShader#shadeRay(ca.eandb.jmist.math.Ray3, ca.eandb.jmist.framework.color.WavelengthPacket)
   */
  public Color shadeRay(Ray3 ray, WavelengthPacket lambda) {
    Context context = new Context();
    return context.castPrimaryRay(ray, lambda);
  }

  private final class LocalContext {

    public double distance;
    public Color importance;
    public Ray3 ray;
    public List<LightSample> samples;
    public Modifier modifier;
    public ScatteredRay scatteredRay;
    public Shader shader;
    public boolean front;
    public Basis3 basis;
    public Basis3 shadingBasis;
    public Point3 position;
    public Point2 uv;
    public Medium medium;
    public Material material;
    public int primitiveIndex;

  }

  private class Context implements ShadingContext, Illuminable {

    private final Stack<LocalContext> stack = new Stack<LocalContext>();
    private final EnumMap<ScatteredRay.Type, Integer> depth = new EnumMap<ScatteredRay.Type, Integer>(ScatteredRay.Type.class);
    private int totalDepth = 0;
    private final Stack<Medium> media = new Stack<Medium>();

    public Color castPrimaryRay(Ray3 ray, WavelengthPacket lambda) {
      Intersection x = NearestIntersectionRecorder.computeNearestIntersection(ray, root);

      if (x != null) {
        LocalContext local = new LocalContext();
        local.ray = ray;
        local.distance = x.getDistance();
        local.front = x.isFront();
        local.medium = Medium.VACUUM;
        local.importance = lambda.getColorModel().getWhite(lambda);

        stack.push(local);
        x.prepareShadingContext(this);

        Color color = shade();

        stack.pop();
        return color;
      } else {
        return background.shadeRay(ray, lambda);
      }
    }

    public Color castRay(ScatteredRay sr) {
      ScatteredRay.Type type = sr.getType();
      Ray3 ray = sr.getRay();
      Intersection x = NearestIntersectionRecorder.computeNearestIntersection(ray, root);

      if (x != null) {
        totalDepth++;
        depth.put(type, getPathDepthByType(type) + 1);

        boolean pop = false;
        Medium popped = null;
        if (sr.isTransmitted()) {
          if (isFront()) {
            media.push(getMaterial());
            pop = true;
          } else if (!media.isEmpty()) {
            popped = media.pop();
          }
        }

        Medium ambientMedium;
        Medium medium = media.isEmpty() ? Medium.VACUUM : media.peek();
        if (x.isFront()) {
          ambientMedium = medium;
        } else {
          ambientMedium = media.size() > 1 ? media.elementAt(media.size() - 2) : Medium.VACUUM;
        }

        LocalContext local = new LocalContext();
        local.ray = ray;
        local.distance = x.getDistance();
        local.front = x.isFront();
        local.medium = ambientMedium;
        local.importance = sr.getColor().times(stack.peek().importance);

        stack.push(local);
        x.prepareShadingContext(this);

        Color color = shade();
        color = color.times(medium.transmittance(local.ray,
            local.distance, color.getWavelengthPacket()));

        if (popped != null) {
          media.push(popped);
        }

        if (pop) {
          media.pop();
        }

        stack.pop();
        depth.put(type, depth.get(type) - 1);
        totalDepth--;
        return color;
      } else {
        return background.shadeRay(ray, getWavelengthPacket());
      }
    }

    public Color getAmbientLight() {
      // TODO add ambient illumination from scene
      return getColorModel().getBlack(getWavelengthPacket());
    }

    public ColorModel getColorModel() {
      return getImportance().getColorModel();
    }

    public WavelengthPacket getWavelengthPacket() {
      return getImportance().getWavelengthPacket();
    }

    public Color getImportance() {
      return stack.peek().importance;
    }

    public Iterable<LightSample> getLightSamples() {
      List<LightSample> samples = stack.peek().samples;
      if (samples == null) {
        samples = new ArrayList<LightSample>();
        stack.peek().samples = samples;
        light.illuminate(this, getWavelengthPacket(), rng, this);
      }
      return samples;
    }

    public int getPathDepth() {
      return totalDepth;
    }

    public int getPathDepthByType(Type type) {
      Integer d = depth.get(type);
      return (d != null ? d.intValue() : 0);
    }

    public ScatteredRay getScatteredRay() {
      if (stack.peek().scatteredRay == null) {
        stack.peek().scatteredRay = stack.peek().material.scatter(this,
            getIncident(), true, getWavelengthPacket(), rng.next(),
            rng.next(), rng.next());
      }
      return stack.peek().scatteredRay;
    }

    public Color shade() {
      return getShader().shade(this);
    }

    public double getDistance() {
      return stack.peek().distance;
    }

    public Vector3 getIncident() {
      return stack.peek().ray.direction();
    }

    public boolean isFront() {
      return stack.peek().front;
    }

    public Basis3 getBasis() {
      return stack.peek().basis;
    }

    public Vector3 getNormal() {
      return stack.peek().basis.w();
    }

    public Point3 getPosition() {
      return stack.peek().position;
    }

    public Basis3 getShadingBasis() {
      Basis3 basis = stack.peek().shadingBasis;
      return basis != null ? basis : stack.peek().basis;
    }

    public Vector3 getShadingNormal() {
      return getShadingBasis().w();
    }

    public Vector3 getTangent() {
      return stack.peek().basis.u();
    }

    public Point2 getUV() {
      return stack.peek().uv;
    }

    public boolean visibility(Ray3 ray) {
      return root.visibility(ray);
    }

    public void addLightSample(LightSample sample) {
      List<LightSample> samples = stack.peek().samples;
      assert(samples != null);
      samples.add(sample);
    }

    public Modifier getModifier() {
      return stack.peek().modifier;
    }

    public Random getRandom() {
      return rng;
    }

    public Ray3 getRay() {
      return stack.peek().ray;
    }

    public Shader getShader() {
      return stack.peek().shader;
    }

    public void setAmbientMedium(Medium medium) {
      stack.peek().medium = medium;
    }

    public void setBasis(Basis3 basis) {
      stack.peek().basis = basis;
    }

    public void setMaterial(Material material) {
      stack.peek().material = material;
    }

    public void setModifier(Modifier modifier) {
      stack.peek().modifier = modifier;
    }

    public void setNormal(Vector3 normal) {
      stack.peek().basis = Basis3.fromW(normal);
    }

    public void setPosition(Point3 position) {
      stack.peek().position = position;
    }

    public void setPrimitiveIndex(int index) {
      stack.peek().primitiveIndex = index;
    }

    public void setShader(Shader shader) {
      stack.peek().shader = shader;
    }

    public void setShadingBasis(Basis3 basis) {
      stack.peek().shadingBasis = basis;
    }

    public void setShadingNormal(Vector3 normal) {
      stack.peek().shadingBasis = Basis3.fromW(normal);
    }

    public void setUV(Point2 uv) {
      stack.peek().uv = uv;
    }

    public Medium getAmbientMedium() {
      return stack.peek().medium;
    }

    public Material getMaterial() {
      return stack.peek().material;
    }

    public int getPrimitiveIndex() {
      return stack.peek().primitiveIndex;
    }

  }

}
TOP

Related Classes of ca.eandb.jmist.framework.shader.ray.SceneRayShader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.