/* Copyright (c) 2012-2015 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; import java.util.Collection; import se.llbit.chunky.world.Material; import se.llbit.math.primitive.Primitive; import se.llbit.math.primitive.TexturedTriangle; /** * A quad. * * @author Jesper Öqvist <jesper@llbit.se> */ public class Quad { protected Vector3 o = new Vector3(); protected Vector3 xv = new Vector3(); protected Vector3 yv = new Vector3(); protected Vector4 uv = new Vector4(); /** * Normal vector */ public Vector3 n = new Vector3(); protected double d, xvl, yvl; /** * Create new Quad */ protected Quad(Quad other, Transform t) { o.set(other.o); o.x -= .5; o.y -= .5; o.z -= .5; t.apply(o); o.x += .5; o.y += .5; o.z += .5; xv.set(other.xv); yv.set(other.yv); n.set(other.n); t.applyRotScale(xv); t.applyRotScale(yv); t.applyRotScale(n); xvl = other.xvl; yvl = other.yvl; d = -n.dot(o); uv.set(other.uv); } /** * Create transformed Quad */ public Quad(Quad other, Matrix3 t) { o.set(other.o); o.x -= .5; o.y -= .5; o.z -= .5; t.transform(o); o.x += .5; o.y += .5; o.z += .5; xv.set(other.xv); yv.set(other.yv); n.set(other.n); t.transform(xv); t.transform(yv); t.transform(n); xvl = other.xvl; yvl = other.yvl; d = -n.dot(o); uv.set(other.uv); } /** * Create new quad * * @param v0 Bottom left vector * @param v1 Top right vector * @param v2 Bottom right vector * @param uv Minimum and maximum U/V texture coordinates */ public Quad(Vector3 v0, Vector3 v1, Vector3 v2, Vector4 uv) { o.set(v0); xv.sub(v1, v0); xvl = 1 / xv.lengthSquared(); yv.sub(v2, v0); yvl = 1 / yv.lengthSquared(); n.cross(xv, yv); n.normalize(); d = -n.dot(o); this.uv.set(uv); this.uv.y -= uv.x; this.uv.w -= uv.z; } /** * Find intersection between the given ray and this quad * * @return <code>true</code> if the ray intersects this quad */ public boolean intersect(Ray ray) { double u, v; double ix = ray.o.x - QuickMath.floor(ray.o.x + ray.d.x * Ray.OFFSET); double iy = ray.o.y - QuickMath.floor(ray.o.y + ray.d.y * Ray.OFFSET); double iz = ray.o.z - QuickMath.floor(ray.o.z + ray.d.z * Ray.OFFSET); // Test that the ray is heading toward the plane of this quad. double denom = ray.d.dot(n); if (denom < -Ray.EPSILON) { // Test for intersection with the plane at origin. double t = -(ix * n.x + iy * n.y + iz * n.z + d) / denom; if (t > -Ray.EPSILON && t < ray.t) { // Plane intersection confirmed. // Translate to get hit point relative to the quad origin. ix = ix + ray.d.x * t - o.x; iy = iy + ray.d.y * t - o.y; iz = iz + ray.d.z * t - o.z; u = ix * xv.x + iy * xv.y + iz * xv.z; u *= xvl; v = ix * yv.x + iy * yv.y + iz * yv.z; v *= yvl; if (u >= 0 && u <= 1 && v >= 0 && v <= 1) { ray.u = uv.x + u * uv.y; ray.v = uv.z + v * uv.w; ray.tNext = t; return true; } } } return false; } /** * @return Scaled copy of this quad */ public Quad getScaled(double scale) { Matrix3 transform = new Matrix3(); transform.scale(scale); return new Quad(this, transform); } public void addTriangles(Collection<Primitive> primitives, Material material, Transform transform) { Vector3 c0 = new Vector3(o); Vector3 c1 = new Vector3(); Vector3 c2 = new Vector3(); Vector3 c3 = new Vector3(); c1.add(o, xv); c2.add(o, yv); c3.add(c1, yv); transform.apply(c0); transform.apply(c1); transform.apply(c2); transform.apply(c3); double u0 = uv.x; double u1 = uv.x + uv.y; double v0 = uv.z; double v1 = uv.z + uv.w; primitives.add(new TexturedTriangle(c0, c2, c1, new Vector2(u0, v0), new Vector2(u0, v1), new Vector2(u1, v0), material)); primitives.add(new TexturedTriangle(c1, c2, c3, new Vector2(u1, v0), new Vector2(u0, v1), new Vector2(u1, v1), material)); } /** * Build a transformed copy of this quad. */ public Quad transform(Transform transform) { return new Quad(this, transform); } }