All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.earth.worldwind.geom.Frustum.kt Maven / Gradle / Ivy

Go to download

The WorldWind Kotlin SDK (WWK) includes the library, examples and tutorials for building multiplatform 3D virtual globe applications for Android, Web and Java.

There is a newer version: 1.5.23
Show 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