Package se.llbit.chunky.model

Source Code of se.llbit.chunky.model.WaterModel

/* Copyright (c) 2012 Jesper Öqvist <jesper@llbit.se>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package se.llbit.chunky.model;

import se.llbit.chunky.resources.Texture;
import se.llbit.math.DoubleSidedQuad;
import se.llbit.math.Quad;
import se.llbit.math.QuickMath;
import se.llbit.math.Ray;
import se.llbit.math.Triangle;
import se.llbit.math.Vector3d;
import se.llbit.math.Vector4d;

/**
* A water block. The height of the top water block is slightly
* lower than a regular block.
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class WaterModel {

  private static final double WATER_OPACITY = .42;

  private static Quad[] fullBlock = {
    // bottom
    new DoubleSidedQuad(new Vector3d(0, 0, 0), new Vector3d(1, 0, 0),
      new Vector3d(0, 0, 1), new Vector4d(0, 1, 0, 1)),
    // top
    new DoubleSidedQuad(new Vector3d(0, 1, 0), new Vector3d(1, 1, 0),
      new Vector3d(0, 1, 1), new Vector4d(0, 1, 0, 1)),
    // west
    new DoubleSidedQuad(new Vector3d(0, 0, 0), new Vector3d(0, 1, 0),
      new Vector3d(0, 0, 1), new Vector4d(0, 1, 0, 1)),
    // east
    new DoubleSidedQuad(new Vector3d(1, 0, 0), new Vector3d(1, 1, 0),
      new Vector3d(1, 0, 1), new Vector4d(0, 1, 0, 1)),
    // north
    new DoubleSidedQuad(new Vector3d(0, 1, 0), new Vector3d(1, 1, 0),
      new Vector3d(0, 0, 0), new Vector4d(0, 1, 0, 0)),
    // south
    new DoubleSidedQuad(new Vector3d(0, 1, 1), new Vector3d(1, 1, 1),
      new Vector3d(0, 0, 1), new Vector4d(0, 1, 0, 1)),
  };

  static final DoubleSidedQuad bot = new DoubleSidedQuad(
      new Vector3d(0, 0, 0), new Vector3d(1, 0, 0),
      new Vector3d(0, 0, 1), new Vector4d(0, 1, 0, 1));
  static final Triangle[][][] t012 = new Triangle[8][8][8];
  static final Triangle[][][] t230 = new Triangle[8][8][8];
  static final Triangle[][] westt = new Triangle[8][8];
  static final Triangle[] westb = new Triangle[8];
  static final Triangle[][] northt = new Triangle[8][8];
  static final Triangle[] northb = new Triangle[8];
  static final Triangle[][] eastt = new Triangle[8][8];
  static final Triangle[] eastb = new Triangle[8];
  static final Triangle[][] southt = new Triangle[8][8];
  static final Triangle[] southb = new Triangle[8];

  /**
   * Water height levels
   */
  static final double height[] = {
    14/16., 12.25/16., 10.5/16, 8.75/16, 7./16, 5.25/16, 3.5/16, 1.75/16
  };

  private static final float[][][] normalMap;
  private static final int normalMapW;

  /**
   * Block data offset for water above flag
   */
  public static final int FULL_BLOCK = 12;

  static {
    // precompute normal map
    Texture waterHeight = new Texture("water-height");
    normalMapW = waterHeight.getWidth();
    normalMap = new float[normalMapW][normalMapW][2];
    for (int u = 0; u < normalMapW; ++u) {
      for (int v = 0; v < normalMapW; ++v) {

        float hx0 = (waterHeight.getColorWrapped(u, v) & 0xFF) / 255.f;
        float hx1 = (waterHeight.getColorWrapped(u+1, v) & 0xFF) / 255.f;
        float hz0 = (waterHeight.getColorWrapped(u, v) & 0xFF) / 255.f;
        float hz1 = (waterHeight.getColorWrapped(u, v+1) & 0xFF) / 255.f;
        normalMap[u][v][0] = hx1-hx0;
        normalMap[u][v][1] = hz1-hz0;
      }
    }

    // precompute water triangles
    for (int i = 0; i < 8; ++i) {
      double c0 = height[i];
      for (int j = 0; j < 8; ++j) {
        double c1 = height[j];
        for (int k = 0; k < 8; ++k) {
          double c2 = height[k];
          t012[i][j][k] = new Triangle(
              new Vector3d(1, c1, 1), new Vector3d(1, c2, 0), new Vector3d(0, c0, 1));
        }
      }
    }
    for (int i = 0; i < 8; ++i) {
      double c2 = height[i];
      for (int j = 0; j < 8; ++j) {
        double c3 = height[j];
        for (int k = 0; k < 8; ++k) {
          double c0 = height[k];
          t230[i][j][k] = new Triangle(
              new Vector3d(0, c3, 0), new Vector3d(0, c0, 1), new Vector3d(1, c2, 0));
        }
      }
    }
    for (int i = 0; i < 8; ++i) {
      double c0 = height[i];
      for (int j = 0; j < 8; ++j) {
        double c3 = height[j];
        westt[i][j] = new Triangle(
            new Vector3d(0, c3, 0), new Vector3d(0, 0, 0), new Vector3d(0, c0, 1));
      }
    }
    for (int i = 0; i < 8; ++i) {
      double c0 = height[i];
      westb[i] = new Triangle(
          new Vector3d(0, 0, 1), new Vector3d(0, c0, 1), new Vector3d(0, 0, 0));
    }
    for (int i = 0; i < 8; ++i) {
      double c1 = height[i];
      for (int j = 0; j < 8; ++j) {
        double c2 = height[j];
        eastt[i][j] = new Triangle(
            new Vector3d(1, c2, 0), new Vector3d(1, c1, 1), new Vector3d(1, 0, 0));
      }
    }
    for (int i = 0; i < 8; ++i) {
      double c1 = height[i];
      eastb[i] = new Triangle(
          new Vector3d(1, c1, 1), new Vector3d(1, 0, 1), new Vector3d(1, 0, 0));
    }
    for (int i = 0; i < 8; ++i) {
      double c2 = height[i];
      for (int j = 0; j < 8; ++j) {
        double c3 = height[j];
        northt[i][j] = new Triangle(
            new Vector3d(0, c3, 0), new Vector3d(1, c2, 0), new Vector3d(0, 0, 0));
      }
    }
    for (int i = 0; i < 8; ++i) {
      double c2 = height[i];
      northb[i] = new Triangle(
          new Vector3d(1, 0, 0), new Vector3d(0, 0, 0), new Vector3d(1, c2, 0));
    }
    for (int i = 0; i < 8; ++i) {
      double c0 = height[i];
      for (int j = 0; j < 8; ++j) {
        double c1 = height[j];
        southt[i][j] = new Triangle(
            new Vector3d(0, c0, 1), new Vector3d(0, 0, 1), new Vector3d(1, c1, 1));
      }
    }
    for (int i = 0; i < 8; ++i) {
      double c1 = height[i];
      southb[i] = new Triangle(
          new Vector3d(1, 0, 1), new Vector3d(1, c1, 1), new Vector3d(0, 0, 1));
    }
  }

  @SuppressWarnings("javadoc")
  public static boolean intersect(Ray ray) {
    ray.t = Double.POSITIVE_INFINITY;

    int data = ray.currentMaterial;
    int isFull = (data >> FULL_BLOCK) & 1;
    //int level = data >> 8;

    if (isFull != 0) {
      boolean hit = false;
      for (Quad quad: fullBlock) {
        if (quad.intersect(ray)) {
          Texture.water.getAvgColorLinear(ray.color);
          ray.t = ray.tNear;
          ray.n.set(quad.n);
          ray.n.scale(QuickMath.signum(-ray.d.dot(quad.n)));
          hit = true;
        }
      }
      if (hit) {
        ray.distance += ray.t;
        ray.x.scaleAdd(ray.t, ray.d, ray.x);
        ray.color.w = WATER_OPACITY;

        /*if ((level&8) != 0) {
          // falling water
          if (ray.n.x != 0) {
            double sign = QuickMath.signum(ray.n.x);
            doWaterDisplacement(ray);
            ray.n.set(ray.n.y*sign, ray.n.x, ray.n.z);
          } else {
            double sign = QuickMath.signum(ray.n.z);
            doWaterDisplacement(ray);
            ray.n.set(ray.n.x, ray.n.z, ray.n.y*sign);
          }
        }*/
      }
      return hit;
    }

    // lily pad test
    if (((ray.currentMaterial >> 13) & 1) != 0) {
      if (LilyPadModel.intersect(ray))
        return true;
    }

    boolean hit = false;
    if (bot.intersect(ray)) {
      ray.n.set(bot.n);
      ray.n.scale(-QuickMath.signum(ray.d.dot(bot.n)));
      ray.t = ray.tNear;
      hit = true;
    }

    int c0 = (0xF & (data >> 16)) % 8;
    int c1 = (0xF & (data >> 20)) % 8;
    int c2 = (0xF & (data >> 24)) % 8;
    int c3 = (0xF & (data >> 28)) % 8;
    Triangle triangle = t012[c0][c1][c2];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      hit = true;
    }
    triangle = t230[c2][c3][c0];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      ray.u = 1-ray.u;
      ray.v = 1-ray.v;
      hit = true;
    }
    triangle = westt[c0][c3];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      hit = true;
    }
    triangle = westb[c0];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      ray.u = 1-ray.u;
      ray.v = 1-ray.v;
      hit = true;
    }
    triangle = eastt[c1][c2];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      hit = true;
    }
    triangle = eastb[c1];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      ray.u = 1-ray.u;
      ray.v = 1-ray.v;
      hit = true;
    }
    triangle = southt[c0][c1];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      hit = true;
    }
    triangle = southb[c1];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      ray.u = 1-ray.u;
      ray.v = 1-ray.v;
      hit = true;
    }
    triangle = northt[c2][c3];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      hit = true;
    }
    triangle = northb[c2];
    if (triangle.intersect(ray)) {
      ray.n.set(triangle.n);
      ray.n.scale(QuickMath.signum(-ray.d.dot(triangle.n)));
      ray.t = ray.tNear;
      ray.u = 1-ray.u;
      ray.v = 1-ray.v;
      hit = true;
    }
    if (hit) {
      Texture.water.getAvgColorLinear(ray.color);
      ray.color.w = WATER_OPACITY;
      ray.distance += ray.t;
      ray.x.scaleAdd(ray.t, ray.d, ray.x);
    }
    return hit;
  }

  /**
   * Displace the normal using the water displacement map.
   * @param ray
   */
  public final static void doWaterDisplacement(Ray ray) {
    int w = (1<<4);
    double x = ray.x.x/w - QuickMath.floor(ray.x.x/w);
    double z = ray.x.z/w - QuickMath.floor(ray.x.z/w);
    int u = (int) (x * normalMapW - Ray.EPSILON);
    int v = (int) ((1-z) * normalMapW - Ray.EPSILON);
    ray.n.set(normalMap[u][v][0], .15f,
        normalMap[u][v][1]);
    w = (1<<1);
    x = ray.x.x/w - QuickMath.floor(ray.x.x/w);
    z = ray.x.z/w - QuickMath.floor(ray.x.z/w);
    u = (int) (x * normalMapW - Ray.EPSILON);
    v = (int) ((1-z) * normalMapW - Ray.EPSILON);
    ray.n.x += normalMap[u][v][0]/2;
    ray.n.z += normalMap[u][v][1]/2;
    ray.n.normalize();
  }
}
TOP

Related Classes of se.llbit.chunky.model.WaterModel

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.