All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.llbit.math.Ray Maven / Gradle / Ivy

There is a newer version: 1.4.5
Show newest version
/* Copyright (c) 2012-2014 Jesper Öqvist 
 *
 * This file is part of Chunky.
 *
 * Chunky is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Chunky is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with Chunky.  If not, see .
 */
package se.llbit.math;

import java.util.Random;

import org.apache.commons.math3.util.FastMath;

import se.llbit.chunky.renderer.scene.Scene;
import se.llbit.chunky.world.Block;
import se.llbit.chunky.world.BlockData;
import se.llbit.chunky.world.Material;

/**
 * The ray representation used for ray tracing.
 *
 * @author Jesper Öqvist 
 */
public class Ray {

  public static final double EPSILON = 0.000005;

  public static final double OFFSET = 0.0001;

  /**
   * Ray direction.
   */
  public Vector3 d = new Vector3();

  /**
   * Intersection point.
   */
  public Vector3 o = new Vector3();

  /**
   * Intersection normal.
   */
  public Vector3 n = new Vector3();

  /**
   * Distance traveled in current medium. This is updated after all intersection
   * tests have run and the final t value has been found.
   */
  public double distance;

  /**
   * Accumulated color value.
   */
  public Vector4 color = new Vector4();

  /**
   * Emittance of previously intersected surface.
   */
  public Vector3 emittance = new Vector3();

  /**
   * Previous material.
   */
  private Material prevMaterial = Block.AIR;

  /**
   * Current material.
   */
  private Material currentMaterial = Block.AIR;

  /**
   * Previous block metadata.
   */
  private int prevData;

  /**
   * Current block metadata.
   */
  private int currentData;

  /**
   * Recursive ray depth
   */
  public int depth;

  /**
   * Distance to closest intersection.
   */
  public double t;

  /**
   * Distance to next potential intersection. The tNext value is stored by
   * subroutines when calculating a potential next hit point. This can then be
   * stored in the t variable based on further decision making.
   */
  public double tNext;

  /**
   * Texture coordinate.
   */
  public double u;

  /**
   * Texture coordinate.
   */
  public double v;

  /**
   * Is the ray specularly reflected
   */
  public boolean specular;

  /**
   * Builds an uninitialized ray.
   */
  public Ray() {
  }

  /**
   * Create a copy of the given ray
   *
   * @param other ray to copy
   */
  public Ray(Ray other) {
    set(other);
  }

  /**
   * Set default values for this ray.
   */
  public void setDefault() {
    distance = 0;
    prevMaterial = Block.AIR;
    currentMaterial = Block.AIR;
    depth = 0;
    color.set(0, 0, 0, 0);
    emittance.set(0, 0, 0);
    specular = true;
  }

  /**
   * Copy state from another ray.
   */
  public void set(Ray other) {
    prevMaterial = other.prevMaterial;
    currentMaterial = other.currentMaterial;
    depth = other.depth + 1;
    distance = 0;
    o.set(other.o);
    d.set(other.d);
    n.set(other.n);
    color.set(0, 0, 0, 0);
    emittance.set(0, 0, 0);
    specular = other.specular;
  }

  /**
   * The block data value is a 4-bit integer value describing properties of the
   * current block.
   *
   * @return current block data (sometimes called metadata).
   */
  public final int getBlockData() {
    return 0xF & (currentData >> BlockData.OFFSET);
  }

  /**
   * Initialize a ray with origin and direction.
   *
   * @param o origin
   * @param d direction
   */
  public final void set(Vector3 o, Vector3 d) {
    setDefault();
    this.o.set(o);
    this.d.set(d);
  }

  /**
   * Find the exit point from the given block for this ray. This marches the ray
   * forward - i.e. updates ray origin directly.
   *
   * @param bx block x coordinate
   * @param by block y coordinate
   * @param bz block z coordinate
   */
  public final void exitBlock(int bx, int by, int bz) {
    int nx = 0;
    int ny = 0;
    int nz = 0;
    double tNext = Double.POSITIVE_INFINITY;
    double t = (bx - o.x) / d.x;
    if (t > Ray.EPSILON) {
      tNext = t;
      nx = 1;
      ny = nz = 0;
    } else {
      t = ((bx + 1) - o.x) / d.x;
      if (t < tNext && t > Ray.EPSILON) {
        tNext = t;
        nx = -1;
        ny = nz = 0;
      }
    }

    t = (by - o.y) / d.y;
    if (t < tNext && t > Ray.EPSILON) {
      tNext = t;
      ny = 1;
      nx = nz = 0;
    } else {
      t = ((by + 1) - o.y) / d.y;
      if (t < tNext && t > Ray.EPSILON) {
        tNext = t;
        ny = -1;
        nx = nz = 0;
      }
    }

    t = (bz - o.z) / d.z;
    if (t < tNext && t > Ray.EPSILON) {
      tNext = t;
      nz = 1;
      nx = ny = 0;
    } else {
      t = ((bz + 1) - o.z) / d.z;
      if (t < tNext && t > Ray.EPSILON) {
        tNext = t;
        nz = -1;
        nx = ny = 0;
      }
    }

    o.scaleAdd(tNext, d);
    n.set(nx, ny, nz);
    distance += tNext;
  }

  /**
   * @return foliage color for the current block
   */
  public float[] getBiomeFoliageColor(Scene scene) {
    return scene.getFoliageColor((int) (o.x + d.x * OFFSET), (int) (o.z + d.z * OFFSET));
  }

  /**
   * @return grass color for the current block
   */
  public float[] getBiomeGrassColor(Scene scene) {
    return scene.getGrassColor((int) (o.x + d.x * OFFSET), (int) (o.z + d.z * OFFSET));
  }

  /**
   * Set this ray to a random diffuse reflection of the input ray.
   */
  public final void diffuseReflection(Ray ray, Random random) {
    set(ray);

    // get random point on unit disk
    double x1 = random.nextDouble();
    double x2 = random.nextDouble();
    double r = FastMath.sqrt(x1);
    double theta = 2 * Math.PI * x2;

    // project to point on hemisphere in tangent space
    double tx = r * FastMath.cos(theta);
    double ty = r * FastMath.sin(theta);
    double tz = FastMath.sqrt(1 - x1);

    // transform from tangent space to world space
    double xx, xy, xz;
    double ux, uy, uz;
    double vx, vy, vz;

    if (QuickMath.abs(n.x) > .1) {
      xx = 0;
      xy = 1;
      xz = 0;
    } else {
      xx = 1;
      xy = 0;
      xz = 0;
    }

    ux = xy * n.z - xz * n.y;
    uy = xz * n.x - xx * n.z;
    uz = xx * n.y - xy * n.x;

    r = 1 / FastMath.sqrt(ux * ux + uy * uy + uz * uz);

    ux *= r;
    uy *= r;
    uz *= r;

    vx = uy * n.z - uz * n.y;
    vy = uz * n.x - ux * n.z;
    vz = ux * n.y - uy * n.x;

    d.x = ux * tx + vx * ty + n.x * tz;
    d.y = uy * tx + vy * ty + n.y * tz;
    d.z = uz * tx + vz * ty + n.z * tz;

    o.scaleAdd(Ray.OFFSET, d);
    currentMaterial = prevMaterial;
    specular = false;
  }

  /**
   * Set this ray to the specular reflection of the input ray.
   */
  public final void specularReflection(Ray ray) {
    set(ray);
    d.scaleAdd(-2 * ray.d.dot(ray.n), ray.n, ray.d);
    o.scaleAdd(0.00001, ray.n);
    currentMaterial = prevMaterial;
  }

  /**
   * Scatter ray normal
   *
   * @param random random number source
   */
  public final void scatterNormal(Random random) {
    // get random point on unit disk
    double x1 = random.nextDouble();
    double x2 = random.nextDouble();
    double r = FastMath.sqrt(x1);
    double theta = 2 * Math.PI * x2;

    // project to point on hemisphere in tangent space
    double tx = r * FastMath.cos(theta);
    double ty = r * FastMath.sin(theta);
    double tz = FastMath.sqrt(1 - x1);

    // transform from tangent space to world space
    double xx, xy, xz;
    double ux, uy, uz;
    double vx, vy, vz;

    if (QuickMath.abs(n.x) > .1) {
      xx = 0;
      xy = 1;
      xz = 0;
    } else {
      xx = 1;
      xy = 0;
      xz = 0;
    }

    ux = xy * n.z - xz * n.y;
    uy = xz * n.x - xx * n.z;
    uz = xx * n.y - xy * n.x;

    r = 1 / FastMath.sqrt(ux * ux + uy * uy + uz * uz);

    ux *= r;
    uy *= r;
    uz *= r;

    vx = uy * n.z - uz * n.y;
    vy = uz * n.x - ux * n.z;
    vz = ux * n.y - uy * n.x;

    n.set(ux * tx + vx * ty + n.x * tz, uy * tx + vy * ty + n.y * tz, uz * tx + vz * ty + n.z * tz);
  }

  public void setPrevMaterial(Material mat, int data) {
    this.prevMaterial = mat;
    this.prevData = data;
  }

  public void setCurrentMaterial(Material mat, int data) {
    this.currentMaterial = mat;
    this.currentData = data;
  }

  public void setMaterial(int blockId) {
    this.currentMaterial = Block.get(blockId);
    this.currentData = blockId;
  }

  public Material getPrevMaterial() {
    return prevMaterial;
  }

  public Material getCurrentMaterial() {
    return currentMaterial;
  }

  public int getPrevData() {
    return prevData;
  }

  public int getCurrentData() {
    return currentData;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy