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