commonMain.earth.worldwind.geom.Frustum.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwind-jvm Show documentation
Show all versions of worldwind-jvm Show documentation
The WorldWind Kotlin SDK (WWK) includes the library, examples and tutorials for building multiplatform 3D virtual globe applications for Android, Web and Java.
The newest version!
package earth.worldwind.geom
/**
* Represents a six-sided view frustum in Cartesian coordinates with a corresponding viewport in screen coordinates.
*/
open class Frustum {
internal val left = Plane(1.0, 0.0, 0.0, 1.0)
internal val right = Plane(-1.0, 0.0, 0.0, 1.0)
internal val bottom = Plane(0.0, 1.0, 0.0, 1.0)
internal val top = Plane(0.0, -1.0, 0.0, 1.0)
internal val near = Plane(0.0, 0.0, -1.0, 1.0)
internal val far = Plane(0.0, 0.0, 1.0, 1.0)
internal val viewport = Viewport(0, 0, 1, 1)
internal val planes = arrayOf(near, far, left, right, top, bottom)
private val scratchMatrix = Matrix4()
/**
* Constructs a new unit frustum with each of its planes 1 meter from the center and a viewport with width and
* height both 1.
*/
constructor()
/**
* Constructs a frustum from planes.
*
* @param left the frustum's left plane
* @param right the frustum's right plane
* @param bottom the frustum's bottom plane
* @param top the frustum's top plane
* @param near the frustum's near plane
* @param far the frustum's far plane
* @param viewport the frustum's viewport
*/
constructor(left: Plane, right: Plane, bottom: Plane, top: Plane, near: Plane, far: Plane, viewport: Viewport): this() {
this.left.copy(left)
this.right.copy(right)
this.bottom.copy(bottom)
this.top.copy(top)
this.near.copy(near)
this.far.copy(far)
this.viewport.copy(viewport)
}
/**
* Sets this frustum to a unit frustum with each of its planes 1 meter from the center a viewport with width and
* height both 1.
*
* @return this frustum, set to a unit frustum
*/
fun setToUnitFrustum() = apply {
left.set(1.0, 0.0, 0.0, 1.0)
right.set(-1.0, 0.0, 0.0, 1.0)
bottom.set(0.0, 1.0, 0.0, 1.0)
top.set(0.0, -1.0, 0.0, 1.0)
near.set(0.0, 0.0, -1.0, 1.0)
far.set(0.0, 0.0, 1.0, 1.0)
viewport.set(0, 0, 1, 1)
}
/**
* Sets this frustum to one appropriate for a modelview-projection matrix. A modelview-projection matrix's view
* frustum is a Cartesian volume that contains everything visible in a scene displayed using that
* modelview-projection matrix.
*
* This method assumes that the specified matrices represents a projection matrix and a modelview matrix
* respectively. If this is not the case the results are undefined.
*
* @param projection the projection matrix to extract the frustum from
* @param modelview the modelview matrix defining the frustum's position and orientation in Cartesian coordinates
* @param viewport the screen coordinate viewport corresponding to the projection matrix
*
* @return this frustum, with its planes set to the modelview-projection matrix's view frustum, in Cartesian
* coordinates
*/
fun setToModelviewProjection(projection: Matrix4, modelview: Matrix4, viewport: Viewport) = apply {
// Compute the transpose of the modelview matrix.
scratchMatrix.transposeMatrix(modelview)
// Get the components of the projection matrix.
val m = projection.m
// Left Plane = row 4 + row 1:
var x = m[12] + m[0]
var y = m[13] + m[1]
var z = m[14] + m[2]
var w = m[15] + m[3]
left.set(x, y, z, w) // normalizes the plane's coordinates
left.transformByMatrix(scratchMatrix)
// Right Plane = row 4 - row 1:
x = m[12] - m[0]
y = m[13] - m[1]
z = m[14] - m[2]
w = m[15] - m[3]
right.set(x, y, z, w) // normalizes the plane's coordinates
right.transformByMatrix(scratchMatrix)
// Bottom Plane = row 4 + row 2:
x = m[12] + m[4]
y = m[13] + m[5]
z = m[14] + m[6]
w = m[15] + m[7]
bottom.set(x, y, z, w) // normalizes the plane's coordinates
bottom.transformByMatrix(scratchMatrix)
// Top Plane = row 4 - row 2:
x = m[12] - m[4]
y = m[13] - m[5]
z = m[14] - m[6]
w = m[15] - m[7]
top.set(x, y, z, w) // normalizes the plane's coordinates
top.transformByMatrix(scratchMatrix)
// Near Plane = row 4 + row 3:
x = m[12] + m[8]
y = m[13] + m[9]
z = m[14] + m[10]
w = m[15] + m[11]
near.set(x, y, z, w) // normalizes the plane's coordinates
near.transformByMatrix(scratchMatrix)
// Far Plane = row 4 - row 3:
x = m[12] - m[8]
y = m[13] - m[9]
z = m[14] - m[10]
w = m[15] - m[11]
far.set(x, y, z, w) // normalizes the plane's coordinates
far.transformByMatrix(scratchMatrix)
// Copy the specified viewport.
this.viewport.copy(viewport)
}
/**
* Sets this frustum to one appropriate for a subset of a modelview-projection matrix. A modelview-projection
* matrix's view frustum is a Cartesian volume that contains everything visible in a scene displayed using that
* modelview-projection matrix. The subset is defined by the region within the original viewport that the frustum
* contains.
*
* This method assumes that the specified matrices represents a projection matrix and a modelview matrix
* respectively. If this is not the case the results are undefined.
*
* @param projection the projection matrix to extract the frustum from
* @param modelview the modelview matrix defining the frustum's position and orientation in Cartesian coordinates
* @param viewport the screen coordinate viewport corresponding to the projection matrix
* @param subViewport the screen coordinate region the frustum should contain
*
* @return this frustum, with its planes set to the modelview-projection matrix's view frustum, in Cartesian
* coordinates
*/
fun setToModelviewProjection(projection: Matrix4, modelview: Matrix4, viewport: Viewport, subViewport: Viewport) = apply {
// Compute the sub-viewport's four edges in screen coordinates.
val left = subViewport.x.toDouble()
val right = (subViewport.x + subViewport.width).toDouble()
val bottom = subViewport.y.toDouble()
val top = (subViewport.y + subViewport.height).toDouble()
// Transform the sub-viewport's four edges from screen coordinates to Cartesian coordinates.
var bln: Vec3
var blf: Vec3
var brn: Vec3
var brf: Vec3
var tln: Vec3
var tlf: Vec3
var trn: Vec3
var trf: Vec3
val mvpInv = scratchMatrix.setToMultiply(projection, modelview).invert()
mvpInv.unProject(left, bottom, viewport, Vec3().also { bln = it }, Vec3().also { blf = it })
mvpInv.unProject(right, bottom, viewport, Vec3().also { brn = it }, Vec3().also { brf = it })
mvpInv.unProject(left, top, viewport, Vec3().also { tln = it }, Vec3().also { tlf = it })
mvpInv.unProject(right, top, viewport, Vec3().also { trn = it }, Vec3().also { trf = it })
val va = Vec3(tlf.x - bln.x, tlf.y - bln.y, tlf.z - bln.z)
val vb = Vec3(tln.x - blf.x, tln.y - blf.y, tln.z - blf.z)
val nl = va.cross(vb)
this.left.set(nl.x, nl.y, nl.z, -nl.dot(bln))
va.set(trn.x - brf.x, trn.y - brf.y, trn.z - brf.z)
vb.set(trf.x - brn.x, trf.y - brn.y, trf.z - brn.z)
val nr = va.cross(vb)
this.right.set(nr.x, nr.y, nr.z, -nr.dot(brn))
va.set(brf.x - bln.x, brf.y - bln.y, brf.z - bln.z)
vb.set(blf.x - brn.x, blf.y - brn.y, blf.z - brn.z)
val nb = va.cross(vb)
this.bottom.set(nb.x, nb.y, nb.z, -nb.dot(brn))
va.set(tlf.x - trn.x, tlf.y - trn.y, tlf.z - trn.z)
vb.set(trf.x - tln.x, trf.y - tln.y, trf.z - tln.z)
val nt = va.cross(vb)
this.top.set(nt.x, nt.y, nt.z, -nt.dot(tln))
va.set(tln.x - brn.x, tln.y - brn.y, tln.z - brn.z)
vb.set(trn.x - bln.x, trn.y - bln.y, trn.z - bln.z)
val nn = va.cross(vb)
this.near.set(nn.x, nn.y, nn.z, -nn.dot(bln))
va.set(trf.x - blf.x, trf.y - blf.y, trf.z - blf.z)
vb.set(tlf.x - brf.x, tlf.y - brf.y, tlf.z - brf.z)
val nf = va.cross(vb)
this.far.set(nf.x, nf.y, nf.z, -nf.dot(blf))
// Copy the specified sub-viewport.
this.viewport.copy(subViewport)
}
/**
* See if the point is entirely within the frustum. The dot product of the point with each plane's vector
* provides a distance to each plane. If this distance is less than 0, the point is clipped by that plane and
* neither intersects nor is contained by the space enclosed by this Frustum.
*
* @param point Vector to check
*
* @return true if point contains in frustum
*/
fun containsPoint(point: Vec3) = far.dot(point) > 0 && left.dot(point) > 0 && right.dot(point) > 0
&& top.dot(point) > 0 && bottom.dot(point) > 0 && near.dot(point) > 0
/**
* Determines whether a line segment intersects this frustum.
*
* @param pointA the first line segment endpoint
* @param pointB the second line segment endpoint
*
* @return true if the segment intersects or is contained in this frustum, otherwise false
*/
fun intersectsSegment(pointA: Vec3, pointB: Vec3): Boolean {
// First do a trivial accept test.
if (containsPoint(pointA) || containsPoint(pointB)) return true
if (pointA == pointB) return false
for (i in planes.indices) {
val plane = planes[i]
// See if both points are behind the plane and therefore not in the frustum.
if (plane.onSameSide(pointA, pointB) < 0) return false
// See if the segment intersects the plane.
if (plane.clip(pointA, pointB) != null) return true
}
return false // segment does not intersect frustum
}
/**
* Determines whether a screen coordinate viewport intersects this frustum.
*
* @param viewport the viewport to test
*
* @return true if the viewport intersects or is contained in this frustum, otherwise false
*/
fun intersectsViewport(viewport: Viewport) = this.viewport.intersects(viewport)
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy