/* Copyright (c) 2013 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.math;
/**
* UV-mapped triangle
*
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class UVTriangle {
/**
* Normal vector - normal to triangle vertices
* in counterclockwise order
*/
public final Vector3 n;
private final Vector3 a;
private final Vector3 b;
private final Vector3 c;
private final Vector3 b_a;
private final Vector3 c_a;
private final Vector3 c_b;
private final Vector3 a_c;
private final Vector2 sa;
private final Vector2 sb;
private final Vector2 sc;
private final double d;
private final double rn;
/**
* Construct a new triangle.
*/
public UVTriangle(Vector3 v0, Vector3 v1, Vector3 v2, Vector2 s0,
Vector2 s1, Vector2 s2) {
a = new Vector3(v0);
b = new Vector3(v1);
c = new Vector3(v2);
this.sa = new Vector2(s0);
this.sb = new Vector2(s1);
this.sc = new Vector2(s2);
b_a = new Vector3();
c_a = new Vector3();
c_b = new Vector3();
a_c = new Vector3();
b_a.sub(b, a);
c_a.sub(c, a);
c_b.sub(c, b);
a_c.sub(a, c);
n = new Vector3();
n.cross(b_a, c_a);
rn = 1.0 / n.length();
n.normalize();
d = -n.dot(a);
}
/**
* Find intersection between the ray and this triangle.
*
* @return <code>true</code> if the ray intersects the triangle
*/
public boolean intersect(Ray ray) {
double px = ray.o.x - QuickMath.floor(ray.o.x + ray.d.x * Ray.OFFSET);
double py = ray.o.y - QuickMath.floor(ray.o.y + ray.d.y * Ray.OFFSET);
double pz = ray.o.z - QuickMath.floor(ray.o.z + ray.d.z * Ray.OFFSET);
// Test that the ray is heading toward the plane.
double denom = ray.d.dot(n);
if (denom < -Ray.EPSILON) {
// Test for intersection with the plane at origin.
double t = -(px * n.x + py * n.y + pz * n.z + d) / denom;
if (t > -Ray.EPSILON && t < ray.t) {
// Calculate plane intersection point.
px = px + ray.d.x * t;
py = py + ray.d.y * t;
pz = pz + ray.d.z * t;
// Calculate barycentric coordinates.
double nax = c_b.y * (pz - b.z) - c_b.z * (py - b.y);
double nay = c_b.z * (px - b.x) - c_b.x * (pz - b.z);
double naz = c_b.x * (py - b.y) - c_b.y * (px - b.x);
double nbx = a_c.y * (pz - c.z) - a_c.z * (py - c.y);
double nby = a_c.z * (px - c.x) - a_c.x * (pz - c.z);
double nbz = a_c.x * (py - c.y) - a_c.y * (px - c.x);
double ncx = b_a.y * (pz - a.z) - b_a.z * (py - a.y);
double ncy = b_a.z * (px - a.x) - b_a.x * (pz - a.z);
double ncz = b_a.x * (py - a.y) - b_a.y * (px - a.x);
// alpha, beta, gamma are the barycentric coordinates
double alpha = (n.x * nax + n.y * nay + n.z * naz) * rn;
double beta = (n.x * nbx + n.y * nby + n.z * nbz) * rn;
double gamma = (n.x * ncx + n.y * ncy + n.z * ncz) * rn;
if (alpha >= 0 && beta >= 0 && gamma >= 0) {
ray.tNext = t;
ray.u = alpha * sa.x + beta * sb.x + gamma * sc.x;
ray.v = alpha * sa.y + beta * sb.y + gamma * sc.y;
return true;
}
}
}
return false;
}
/**
* @return Rotated copy of this triangle
*/
public UVTriangle getYRotated() {
Transform t = Transform.NONE.rotateY();
Vector3 ar = new Vector3(a);
ar.add(-0.5, -0.5, -0.5);
t.apply(ar);
ar.add(0.5, 0.5, 0.5);
Vector3 br = new Vector3(b);
br.add(-0.5, -0.5, -0.5);
t.apply(br);
br.add(0.5, 0.5, 0.5);
Vector3 cr = new Vector3(c);
cr.add(-0.5, -0.5, -0.5);
t.apply(cr);
cr.add(0.5, 0.5, 0.5);
return new UVTriangle(ar, br, cr, sa, sb, sc);
}
}