1 /** 2 * Provides methods to compute rays through an arbitrary perspective transformation defined by a {@link Matrix4d}. 3 * <p> 4 * This can be used to compute the eye-rays in simple software-based raycasting/raytracing. 5 * <p> 6 * To obtain the origin of the rays call {@link #origin(Vector3d)}. 7 * Then to compute the directions of subsequent rays use {@link #dir(double, double, Vector3d)}. 8 * 9 * @author Kai Burjack 10 */ 11 module doml.frustum_ray_builder; 12 13 import Math = doml.math; 14 15 import doml.matrix_4d; 16 17 import doml.vector_3d; 18 19 /* 20 * The MIT License 21 * 22 * Copyright (c) 2015-2021 Kai Burjack 23 * 24 * Permission is hereby granted, free of charge, to any person obtaining a copy 25 * of this software and associated documentation files (the "Software"), to deal 26 * in the Software without restriction, including without limitation the rights 27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 * copies of the Software, and to permit persons to whom the Software is 29 * furnished to do so, subject to the following conditions: 30 * 31 * The above copyright notice and this permission notice shall be included in 32 * all copies or substantial portions of the Software. 33 * 34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 40 * THE SOFTWARE. 41 */ 42 43 /** 44 * Provides methods to compute rays through an arbitrary perspective transformation defined by a {@link Matrix4d}. 45 * <p> 46 * This can be used to compute the eye-rays in simple software-based raycasting/raytracing. 47 * <p> 48 * To obtain the origin of the rays call {@link #origin(Vector3d)}. 49 * Then to compute the directions of subsequent rays use {@link #dir(double, double, Vector3d)}. 50 * 51 * @author Kai Burjack 52 */ 53 struct FrustumRayBuilder { 54 55 private double nxnyX, nxnyY, nxnyZ; 56 private double pxnyX, pxnyY, pxnyZ; 57 private double pxpyX, pxpyY, pxpyZ; 58 private double nxpyX, nxpyY, nxpyZ; 59 private double cx, cy, cz; 60 61 /** 62 * Create a new {@link FrustumRayBuilder} from the given {@link Matrix4d matrix} by extracing the matrix's frustum. 63 * 64 * @param m 65 * the {@link Matrix4d} to create the frustum from 66 */ 67 this(Matrix4d m) { 68 set(m); 69 } 70 71 /** 72 * Update the stored frustum corner rays and origin of <code>this</code> {@link FrustumRayBuilder} with the given {@link Matrix4d matrix}. 73 * <p> 74 * Reference: <a href="http://gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdf"> 75 * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix</a> 76 * <p> 77 * Reference: <a href="http://geomalgorithms.com/a05-_intersect-1.html">http://geomalgorithms.com</a> 78 * 79 * @param m 80 * the {@link Matrix4d matrix} to update the frustum corner rays and origin with 81 * @return this 82 */ 83 ref public FrustumRayBuilder set(Matrix4d m) return { 84 double nxX = m.m03 + m.m00, nxY = m.m13 + m.m10, nxZ = m.m23 + m.m20, d1 = m.m33 + m.m30; 85 double pxX = m.m03 - m.m00, pxY = m.m13 - m.m10, pxZ = m.m23 - m.m20, d2 = m.m33 - m.m30; 86 double nyX = m.m03 + m.m01, nyY = m.m13 + m.m11, nyZ = m.m23 + m.m21; 87 double pyX = m.m03 - m.m01, pyY = m.m13 - m.m11, pyZ = m.m23 - m.m21, d3 = m.m33 - m.m31; 88 // bottom left 89 nxnyX = nyY * nxZ - nyZ * nxY; 90 nxnyY = nyZ * nxX - nyX * nxZ; 91 nxnyZ = nyX * nxY - nyY * nxX; 92 // bottom right 93 pxnyX = pxY * nyZ - pxZ * nyY; 94 pxnyY = pxZ * nyX - pxX * nyZ; 95 pxnyZ = pxX * nyY - pxY * nyX; 96 // top left 97 nxpyX = nxY * pyZ - nxZ * pyY; 98 nxpyY = nxZ * pyX - nxX * pyZ; 99 nxpyZ = nxX * pyY - nxY * pyX; 100 // top right 101 pxpyX = pyY * pxZ - pyZ * pxY; 102 pxpyY = pyZ * pxX - pyX * pxZ; 103 pxpyZ = pyX * pxY - pyY * pxX; 104 // compute origin 105 double pxnxX, pxnxY, pxnxZ; 106 pxnxX = pxY * nxZ - pxZ * nxY; 107 pxnxY = pxZ * nxX - pxX * nxZ; 108 pxnxZ = pxX * nxY - pxY * nxX; 109 double invDot = 1.0f / (nxX * pxpyX + nxY * pxpyY + nxZ * pxpyZ); 110 cx = (-pxpyX * d1 - nxpyX * d2 - pxnxX * d3) * invDot; 111 cy = (-pxpyY * d1 - nxpyY * d2 - pxnxY * d3) * invDot; 112 cz = (-pxpyZ * d1 - nxpyZ * d2 - pxnxZ * d3) * invDot; 113 return this; 114 } 115 116 /** 117 * Store the eye/origin of the perspective frustum in the given <code>origin</code>. 118 * 119 * @param origin 120 * will hold the perspective origin 121 * @return the <code>origin</code> vector 122 */ 123 public Vector3d origin(Vector3d origin) { 124 origin.x = cx; 125 origin.y = cy; 126 origin.z = cz; 127 return origin; 128 } 129 130 /** 131 * Obtain the normalized direction of a ray starting at the center of the coordinate system and going 132 * through the near frustum plane. 133 * <p> 134 * The parameters <code>x</code> and <code>y</code> are used to interpolate the generated ray direction 135 * from the bottom-left to the top-right frustum corners. 136 * 137 * @param x 138 * the interpolation factor along the left-to-right frustum planes, within <code>[0..1]</code> 139 * @param y 140 * the interpolation factor along the bottom-to-top frustum planes, within <code>[0..1]</code> 141 * @param dir 142 * will hold the normalized ray direction 143 * @return the <code>dir</code> vector 144 */ 145 public Vector3d dir(double x, double y, Vector3d dir) { 146 double y1x = nxnyX + (nxpyX - nxnyX) * y; 147 double y1y = nxnyY + (nxpyY - nxnyY) * y; 148 double y1z = nxnyZ + (nxpyZ - nxnyZ) * y; 149 double y2x = pxnyX + (pxpyX - pxnyX) * y; 150 double y2y = pxnyY + (pxpyY - pxnyY) * y; 151 double y2z = pxnyZ + (pxpyZ - pxnyZ) * y; 152 double dx = y1x + (y2x - y1x) * x; 153 double dy = y1y + (y2y - y1y) * x; 154 double dz = y1z + (y2z - y1z) * x; 155 // normalize the vector 156 double invLen = Math.invsqrt(dx * dx + dy * dy + dz * dz); 157 dir.x = dx * invLen; 158 dir.y = dy * invLen; 159 dir.z = dz * invLen; 160 return dir; 161 } 162 163 }