Package ca.eandb.jmist.framework.scene

Source Code of ca.eandb.jmist.framework.scene.HairSceneElement$Strand

/**
* 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.scene;

import java.util.Random;

import ca.eandb.jmist.framework.Bounded3;
import ca.eandb.jmist.framework.BoundingBoxBuilder3;
import ca.eandb.jmist.framework.Intersection;
import ca.eandb.jmist.framework.IntersectionRecorder;
import ca.eandb.jmist.framework.Light;
import ca.eandb.jmist.framework.Material;
import ca.eandb.jmist.framework.SceneElement;
import ca.eandb.jmist.framework.ShadingContext;
import ca.eandb.jmist.framework.SurfacePoint;
import ca.eandb.jmist.framework.random.RandomAdapter;
import ca.eandb.jmist.framework.random.RandomUtil;
import ca.eandb.jmist.framework.shader.MinimalShadingContext;
import ca.eandb.jmist.math.Basis3;
import ca.eandb.jmist.math.Box3;
import ca.eandb.jmist.math.GeometryUtil;
import ca.eandb.jmist.math.MathUtil;
import ca.eandb.jmist.math.Plane3;
import ca.eandb.jmist.math.Point3;
import ca.eandb.jmist.math.Ray3;
import ca.eandb.jmist.math.Sphere;
import ca.eandb.jmist.math.Vector3;
import ca.eandb.util.UnimplementedException;

/**
* A <code>SceneElement</code> decorator that adds hair to the underlying
* geometry.
* @author Brad Kimmel
*/
public final class HairSceneElement implements SceneElement {

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

  private final SceneElement emitter;

  private final Material hairMaterial;

  private final int base;

  private final int amount = 1000;//500000;

  private final int segments = 4;

  private final Vector3 meanInitialVelocity = new Vector3(0.0, 0.0, 0.2);//0.05);

  private final double randomInitialVelocity = 0.02;

  private final double roughness = 0.05;//0.01;

  private final boolean renderEmitter = true;

  private final double baseWidth = 0.01;

  private final double tipWidth = 0.01;

  private class Strand implements Bounded3 {

    private Point3[] vertices;

    private ShadingContext emitterContext;

    private int strandIndex;

    public Box3 boundingBox() {
      BoundingBoxBuilder3 bound = new BoundingBoxBuilder3();
      for (Point3 vertex : vertices) {
        bound.add(vertex);
      }
      return bound.getBoundingBox();
    }

    public Sphere boundingSphere() {
      Box3 box = boundingBox();
      return new Sphere(box.center(), box.diagonal() / 2.0);
    }

    public void intersect(final Ray3 ray, IntersectionRecorder recorder) {
      for (int i = 0; i < vertices.length - 3; i++) {
        final double t = GeometryUtil.rayIntersectTriangle(ray, vertices[i], vertices[i+1], vertices[i+2]);
        final int vertexIndex = i;
        if (!Double.isNaN(t)) {
          recorder.record(new Intersection() {
            public double getDistance() {
              return t;
            }
            public double getTolerance() {
              return MathUtil.SMALL_EPSILON;
            }
            public boolean isFront() {
              return true;
            }
            public void prepareShadingContext(ShadingContext context) {
              Plane3 plane = Plane3.throughPoints(vertices[vertexIndex], vertices[vertexIndex+1], vertices[vertexIndex+2]);
              Vector3 n = plane.normal();
              Vector3 v = ray.direction();
              context.setPosition(ray.pointAt(t));
              context.setNormal(v.dot(n) > 0.0 ? n.opposite() : n);
              context.setMaterial(hairMaterial != null ? hairMaterial : emitterContext.getMaterial());
              context.setModifier(emitterContext.getModifier());
              context.setPrimitiveIndex(base + strandIndex);
              context.setShader(emitterContext.getShader());
              context.setUV(emitterContext.getUV());
              context.setAmbientMedium(emitterContext.getAmbientMedium());
            }

          });
        }
      }
    }

    public boolean visibility(Ray3 ray) {
      for (int i = 0; i < vertices.length - 3; i++) {
        final double t = GeometryUtil.rayIntersectTriangle(ray, vertices[i], vertices[i+1], vertices[i+1]);
        if (!Double.isNaN(t) && t > MathUtil.SMALL_EPSILON) {
          return false;
        }
      }
      return true;
    }

    public boolean intersects(Box3 box) {
      throw new UnimplementedException();
    }

  };

  public HairSceneElement(SceneElement emitter) {
    this(null, emitter);
  }

  public HairSceneElement(Material hairMaterial, SceneElement emitter) {
    this.hairMaterial = hairMaterial;
    this.emitter = emitter;
    this.base = renderEmitter ? emitter.getNumPrimitives() : 0;
  }

  private Strand createStrand(int index) {
    Random tempRnd = new Random(index);
    Random rnd = new Random(tempRnd.nextLong());
    RandomAdapter adapter = new RandomAdapter(rnd);
    MinimalShadingContext context = new MinimalShadingContext(adapter);
    emitter.generateRandomSurfacePoint(context, rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble());
    Strand strand = new Strand();
    strand.vertices = new Point3[2 * (segments + 1)];
    strand.emitterContext = context;
    strand.strandIndex = index;

    Point3 pos = context.getPosition();
    Vector3 vel = context.getBasis().toStandard(meanInitialVelocity).plus(
        RandomUtil.uniformInsideSphere(randomInitialVelocity, adapter)
            .toCartesian());
    double dt = 1.0 / (double) segments;
    double orientation = 2.0 * Math.PI * rnd.nextDouble();
    double co = Math.cos(orientation);
    double so = Math.sin(orientation);
    Basis3 basis = Basis3.fromWU(vel, context.getTangent());

    int segment = 0;
    int i = 0;
    while (true) {
      double t = (double) segment / (double) segments;
      double width = MathUtil.interpolate(baseWidth, tipWidth, t);

      strand.vertices[i++] = pos.plus(basis.toStandard(-0.5 * width * co, -0.5 * width * so, 0.0));
      strand.vertices[i++] = pos.plus(basis.toStandard(0.5 * width * co, 0.5 * width * so, 0.0));

      if (++segment > segments) {
        break;
      }

      pos = pos.plus(vel.times(dt));
      pos = pos.plus(RandomUtil.uniformInsideSphere(roughness, adapter).toCartesian());
    }

    return strand;
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#createLight()
   */
  public Light createLight() {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#generateImportanceSampledSurfacePoint(int, ca.eandb.jmist.framework.SurfacePoint, ca.eandb.jmist.framework.ShadingContext, double, double, double)
   */
  @Override
  public double generateImportanceSampledSurfacePoint(int index,
      SurfacePoint x, ShadingContext context, double ru, double rv,
      double rj) {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#generateImportanceSampledSurfacePoint(ca.eandb.jmist.framework.SurfacePoint, ca.eandb.jmist.framework.ShadingContext, double, double, double)
   */
  @Override
  public double generateImportanceSampledSurfacePoint(SurfacePoint x,
      ShadingContext context, double ru, double rv, double rj) {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#generateRandomSurfacePoint(int, ca.eandb.jmist.framework.ShadingContext, double, double, double)
   */
  @Override
  public void generateRandomSurfacePoint(int index, ShadingContext context,
      double ru, double rv, double rj) {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#generateRandomSurfacePoint(ca.eandb.jmist.framework.ShadingContext, double, double, double)
   */
  @Override
  public void generateRandomSurfacePoint(ShadingContext context, double ru,
      double rv, double rj) {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#getBoundingBox(int)
   */
  @Override
  public Box3 getBoundingBox(int index) {
    return index < base ? emitter.getBoundingBox(index) : createStrand(index - base).boundingBox();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#getBoundingSphere(int)
   */
  @Override
  public Sphere getBoundingSphere(int index) {
    return index < base ? emitter.getBoundingSphere(index) : createStrand(index - base).boundingSphere();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#getNumPrimitives()
   */
  @Override
  public int getNumPrimitives() {
    return base + amount;
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#getSurfaceArea()
   */
  @Override
  public double getSurfaceArea() {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#getSurfaceArea(int)
   */
  @Override
  public double getSurfaceArea(int index) {
    throw new UnsupportedOperationException();
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#intersect(int, ca.eandb.jmist.math.Ray3, ca.eandb.jmist.framework.IntersectionRecorder)
   */
  @Override
  public void intersect(int index, Ray3 ray, IntersectionRecorder recorder) {
    if (index < base) {
      emitter.intersect(index, ray, recorder);
    } else {
      createStrand(index - base).intersect(ray, recorder);
    }
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#intersect(ca.eandb.jmist.math.Ray3, ca.eandb.jmist.framework.IntersectionRecorder)
   */
  @Override
  public void intersect(Ray3 ray, IntersectionRecorder recorder) {
    // Warning: EXTREMELY SLOW - ALWAYS combine with an accelerator
    for (int i = 0; i < amount; i++) {
      intersect(base + i, ray, recorder);
    }
    if (renderEmitter) {
      emitter.intersect(ray, recorder);
    }
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#intersects(int, ca.eandb.jmist.math.Box3)
   */
  @Override
  public boolean intersects(int index, Box3 box) {
    return index < base ? emitter.intersects(index, box) : createStrand(
        index - base).intersects(box);
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.SceneElement#visibility(int, ca.eandb.jmist.math.Ray3)
   */
  @Override
  public boolean visibility(int index, Ray3 ray) {
    return index < base ? emitter.visibility(index, ray) : createStrand(
        index - base).visibility(ray);
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.Bounded3#boundingBox()
   */
  @Override
  public Box3 boundingBox() {
    return emitter.boundingBox().expand(this.meanInitialVelocity.length() + randomInitialVelocity + tipWidth / 2.0);
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.Bounded3#boundingSphere()
   */
  @Override
  public Sphere boundingSphere() {
    return emitter.boundingSphere().expand(this.meanInitialVelocity.length() + randomInitialVelocity + tipWidth / 2.0);
  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.VisibilityFunction3#visibility(ca.eandb.jmist.math.Ray3)
   */
  @Override
  public boolean visibility(Ray3 ray) {
    // Warning: EXTREMELY SLOW - ALWAYS combine with an accelerator
    for (int i = 0, n = amount; i < n; i++) {
      if (!visibility(base + i, ray)) {
        return false;
      }
    }
    return renderEmitter ? emitter.visibility(ray) : true;
  }



}
TOP

Related Classes of ca.eandb.jmist.framework.scene.HairSceneElement$Strand

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.