/** {@inheritDoc} */
        public double limitZoom(View view, double value)
        {
            if (view == null)
            {
                String message = Logging.getMessage("nullValue.ViewIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            double minZoom = this.minZoom;
            double maxZoom = this.maxZoom;

            if (this.is2DGlobe(view.getGlobe())) // limit zoom to ~360 degrees of visible longitude on 2D globes
            {
                double max2DZoom = Math.PI * view.getGlobe().getEquatorialRadius() / view.getFieldOfView().tanHalfAngle();
                if (minZoom > max2DZoom)
                {
                    minZoom = max2DZoom;
                }
                if (maxZoom > max2DZoom)
                {
                    maxZoom = max2DZoom;
                }
            }

            return(WWMath.clamp(value, minZoom, maxZoom));
        }
Esempio n. 2
0
        /**
         * Returns the linear interpolation of <code>value1</code> and <code>value2</code>, treating the geographic
         * locations as simple 2D coordinate pairs, and treating the elevation values as 1D scalars.
         *
         * @param amount the interpolation factor
         * @param value1 the first position.
         * @param value2 the second position.
         *
         * @return the linear interpolation of <code>value1</code> and <code>value2</code>.
         *
         * @throws ArgumentException if either position is null.
         */
        public static Position interpolate(double amount, Position value1, Position value2)
        {
            if (value1 == null || value2 == null)
            {
                String message = Logging.getMessage("nullValue.PositionIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            if (amount < 0)
            {
                return(value1);
            }
            else if (amount > 1)
            {
                return(value2);
            }

            LatLon latLon = LatLon.interpolate(amount, value1, value2);
            // Elevation is independent of geographic interpolation method (i.e. rhumb, great-circle, linear), so we
            // interpolate elevation linearly.
            double elevation = WWMath.mix(amount, value1.getElevation(), value2.getElevation());

            return(new Position(latLon, elevation));
        }
        /** {@inheritDoc} */

        public double getProjectedArea(View view)
        {
            if (view == null)
            {
                string message = Logging.getMessage("nullValue.ViewIsNull");
                Logging.logger().severe(message);
                throw new ArgumentNullException(message);
            }

            return(WWMath.computeSphereProjectedArea(view, this.getCenter(), this.getRadius()));
        }
Esempio n. 4
0
        /** {@inheritDoc} */
        public bool intersects(Line line)
        {
            if (line == null)
            {
                String message = Logging.getMessage("nullValue.LineIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            return(WWMath.polytopeIntersect(line, this.planes) != null);
        }
Esempio n. 5
0
        /** {@inheritDoc} */
        public double getProjectedArea(View view)
        {
            if (view == null)
            {
                String message = Logging.getMessage("nullValue.ViewIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            // TODO: compute a more exact projected screen area for Cylinder.
            return(WWMath.computeSphereProjectedArea(view, this.getCenter(), this.getRadius()));
        }
Esempio n. 6
0
        protected BufferedImageRaster chooseRasterForCanvas(BufferedImageRaster canvas)
        {
            int level = this.computeMipmapLevel(
                this.getWidth(), this.getHeight(), this.getSector(),
                canvas.getWidth(), canvas.getHeight(), canvas.getSector());

            int maxLevel = this.levelRasters.length - 1;

            level = (int)WWMath.clamp(level, 0, maxLevel);

            return(this.levelRasters[level]);
        }
        protected Texture initializeTexture(DrawContext dc)
        {
            // The frame buffer can be used only during pre-rendering.
            if (!dc.isPreRenderMode())
            {
                return(null);
            }

            // Bind actually binds the source texture only if the image source is available, otherwise it initiates image
            // source retrieval. If bind returns false, the image source is not yet available.
            if (this.sourceTexture == null || !this.sourceTexture.bind(dc))
            {
                return(null);
            }

            // Ensure that the source texture size is available so that the FBO can be sized to match the source image.
            if (this.sourceTexture.getWidth(dc) < 1 || this.sourceTexture.getHeight(dc) < 1)
            {
                return(null);
            }

            int potSourceWidth  = WWMath.powerOfTwoCeiling(this.sourceTexture.getWidth(dc));
            int potSourceHeight = WWMath.powerOfTwoCeiling(this.sourceTexture.getHeight(dc));

            this.width  = Math.Min(potSourceWidth, dc.getView().getViewport().width);
            this.height = Math.Min(potSourceHeight, dc.getView().getViewport().height);

            if (!this.generateTexture(dc, this.width, this.height))
            {
                return(null);
            }

            GL gl = dc.getGL();

            TextureData td = new TextureData(gl.getGLProfile(), GL.GL_RGBA, this.width, this.height, 0, GL.GL_RGBA,
                                             GL.GL_UNSIGNED_BYTE, false, false, false, null, null);
            Texture t = TextureIO.newTexture(td);

            t.bind(gl); // must do this after generating texture because another texture is bound then

            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);

            gl.glCopyTexImage2D(GL.GL_TEXTURE_2D, 0, td.getInternalFormat(), 0, 0, td.getWidth(), td.getHeight(),
                                td.getBorder());

            dc.getTextureCache().put(this, t);

            return(t);
        }
Esempio n. 8
0
        protected int computeMipmapLevel(int sourceWidth, int sourceHeight, Sector sourceSector,
                                         int destWidth, int destHeight, Sector destSector)
        {
            double sy = ((double)sourceHeight / (double)destHeight)
                        * (destSector.getDeltaLatDegrees() / sourceSector.getDeltaLatDegrees());
            double sx = ((double)sourceWidth / (double)destWidth)
                        * (destSector.getDeltaLonDegrees() / sourceSector.getDeltaLonDegrees());
            double scale = Math.Max(sx, sy);

            if (scale < 1)
            {
                return(0);
            }

            return((int)WWMath.LogBase2(scale));
        }
        /** {@inheritDoc} */
        public Position limitCenterPosition(View view, Position position)
        {
            if (view == null)
            {
                String message = Logging.getMessage("nullValue.ViewIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            if (position == null)
            {
                String message = Logging.getMessage("nullValue.PositionIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            Sector sector = this.centerLocationLimits;
            Angle  lat    = Angle.clamp(position.latitude, sector.getMinLatitude(), sector.getMaxLatitude());
            Angle  lon    = Angle.clamp(position.longitude, sector.getMinLongitude(), sector.getMaxLongitude());
            double alt    = WWMath.clamp(position.elevation, this.minCenterElevation, this.maxCenterElevation);

            return(new Position(lat, lon, alt));
        }
Esempio n. 10
0
        /**
         * Computes a <code>Box</code> that bounds a specified buffer of points. Principal axes are computed for the points
         * and used to form a <code>Box</code>.
         * <p/>
         * The buffer must contain XYZ coordinate tuples which are either tightly packed or offset by the specified stride.
         * The stride specifies the number of buffer elements between the first coordinate of consecutive tuples. For
         * example, a stride of 3 specifies that each tuple is tightly packed as XYZXYZXYZ, whereas a stride of 5 specifies
         * that there are two elements between each tuple as XYZabXYZab (the elements "a" and "b" are ignored). The stride
         * must be at least 3. If the buffer's length is not evenly divisible into stride-sized tuples, this ignores the
         * remaining elements that follow the last complete tuple.
         *
         * @param coordinates the buffer containing the point coordinates for which to compute a bounding volume.
         * @param stride      the number of elements between the first coordinate of consecutive points. If stride is 3,
         *                    this interprets the buffer has having tightly packed XYZ coordinate tuples.
         *
         * @return the bounding volume, with axes lengths consistent with the conventions described in the <code>Box</code>
         *         class overview.
         *
         * @throws ArgumentException if the buffer is null or empty, or if the stride is less than three.
         */
        public static Box computeBoundingBox(BufferWrapper coordinates, int stride)
        {
            if (coordinates == null)
            {
                String msg = Logging.getMessage("nullValue.CoordinatesAreNull");
                Logging.logger().severe(msg);
                throw new ArgumentException(msg);
            }

            if (stride < 3)
            {
                String msg = Logging.getMessage("generic.StrideIsInvalid", stride);
                Logging.logger().severe(msg);
                throw new ArgumentException(msg);
            }

            Vec4[] axes = WWMath.computePrincipalAxes(coordinates, stride);
            if (axes == null)
            {
                String msg = Logging.getMessage("generic.ListIsEmpty");
                Logging.logger().severe(msg);
                throw new ArgumentException(msg);
            }

            Vec4 r = axes[0];
            Vec4 s = axes[1];
            Vec4 t = axes[2];

            // Find the extremes along each axis.
            double minDotR = Double.MaxValue;
            double maxDotR = -minDotR;
            double minDotS = Double.MaxValue;
            double maxDotS = -minDotS;
            double minDotT = Double.MaxValue;
            double maxDotT = -minDotT;

            for (int i = 0; i <= coordinates.length() - stride; i += stride)
            {
                double x = coordinates.getDouble(i);
                double y = coordinates.getDouble(i + 1);
                double z = coordinates.getDouble(i + 2);

                double pdr = x * r.x() + y * r.y() + z * r.z();
                if (pdr < minDotR)
                {
                    minDotR = pdr;
                }
                if (pdr > maxDotR)
                {
                    maxDotR = pdr;
                }

                double pds = x * s.x() + y * s.y() + z * s.z();
                if (pds < minDotS)
                {
                    minDotS = pds;
                }
                if (pds > maxDotS)
                {
                    maxDotS = pds;
                }

                double pdt = x * t.x() + y * t.y() + z * t.z();
                if (pdt < minDotT)
                {
                    minDotT = pdt;
                }
                if (pdt > maxDotT)
                {
                    maxDotT = pdt;
                }
            }

            if (maxDotR == minDotR)
            {
                maxDotR = minDotR + 1;
            }
            if (maxDotS == minDotS)
            {
                maxDotS = minDotS + 1;
            }
            if (maxDotT == minDotT)
            {
                maxDotT = minDotT + 1;
            }

            return(new Box(axes, minDotR, maxDotR, minDotS, maxDotS, minDotT, maxDotT));
        }
Esempio n. 11
0
        /**
         * Compute a bounding cylinder for a collection of points.
         *
         * @param points the points to compute a bounding cylinder for.
         *
         * @return a cylinder bounding all the points. The axis of the cylinder is the longest principal axis of the
         *         collection. (See {@link WWMath#computePrincipalAxes(Iterable)}.
         *
         * @throws ArgumentException if the point list is null or empty.
         * @see #computeVerticalBoundingCylinder(gov.nasa.worldwind.globes.Globe, double, Sector)
         */
        public static Cylinder computeBoundingCylinder(IEnumerable <Vec4> points)
        {
            if (points == null)
            {
                String message = Logging.getMessage("nullValue.PointListIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            Vec4[] axes = WWMath.computePrincipalAxes(points);
            if (axes == null)
            {
                String message = Logging.getMessage("generic.ListIsEmpty");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            Vec4 r = axes[0];
            Vec4 s = axes[1];

            List <Vec4> sPlanePoints = new List <Vec4>();
            double      minDotR      = Double.MaxValue;
            double      maxDotR      = -minDotR;

            foreach (Vec4 p in points)
            {
                double pdr = p.dot3(r);
                sPlanePoints.Add(p.subtract3(r.multiply3(p.dot3(r))));

                if (pdr < minDotR)
                {
                    minDotR = pdr;
                }
                if (pdr > maxDotR)
                {
                    maxDotR = pdr;
                }
            }

            Vec4   minPoint = sPlanePoints[0];
            Vec4   maxPoint = minPoint;
            double minDotS  = Double.MaxValue;
            double maxDotS  = -minDotS;

            foreach (Vec4 p in sPlanePoints)
            {
                double d = p.dot3(s);
                if (d < minDotS)
                {
                    minPoint = p;
                    minDotS  = d;
                }
                if (d > maxDotS)
                {
                    maxPoint = p;
                    maxDotS  = d;
                }
            }

            Vec4   center = minPoint.add3(maxPoint).divide3(2);
            double radius = center.distanceTo3(minPoint);

            foreach (Vec4 h in sPlanePoints)
            {
                Vec4   hq = h.subtract3(center);
                double d  = hq.getLength3();
                if (d > radius)
                {
                    Vec4 g = center.subtract3(hq.normalize3().multiply3(radius));
                    center = g.add3(h).divide3(2);
                    radius = d;
                }
            }

            Vec4 bottomCenter = center.add3(r.multiply3(minDotR));
            Vec4 topCenter    = center.add3((r.multiply3(maxDotR)));

            if (radius == 0)
            {
                radius = 1;
            }

            if (bottomCenter.Equals(topCenter))
            {
                topCenter = bottomCenter.add3(new Vec4(1, 0, 0));
            }

            return(new Cylinder(bottomCenter, topCenter, radius));
        }
Esempio n. 12
0
        /**
         * Returns a cylinder that minimally surrounds the specified height range in the sector.
         *
         * @param globe     The globe associated with the sector.
         * @param sector    the sector to return the bounding cylinder for.
         * @param minHeight the minimum height to include in the bounding cylinder.
         * @param maxHeight the maximum height to include in the bounding cylinder.
         *
         * @return The minimal bounding cylinder in Cartesian coordinates.
         *
         * @throws ArgumentException if <code>sector</code> is null
         */
        protected static Cylinder computeVerticalBoundsFromSectorQuadrilateral(Globe globe, Sector sector, double minHeight,
                                                                               double maxHeight)
        {
            if (sector == null)
            {
                String msg = Logging.getMessage("nullValue.SectorIsNull");
                Logging.logger().severe(msg);
                throw new ArgumentException(msg);
            }

            // Get three non-coincident points on the sector's quadrilateral. We choose the north or south pair that is
            // closest to the equator, then choose a third point from the opposite pair. We use maxHeight as elevation
            // because we want to bound the largest potential quadrilateral for the sector.
            Vec4 p0, p1, p2;

            if (Math.Abs(sector.getMinLatitude().degrees) <= Math.Abs(sector.getMaxLatitude().degrees))
            {
                p0 = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMaxLongitude(), maxHeight); // SE
                p1 = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), maxHeight); // SW
                p2 = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), maxHeight); // NW
            }
            else
            {
                p0 = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMinLongitude(), maxHeight); // NW
                p1 = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), maxHeight); // NE
                p2 = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(), maxHeight); // SW
            }

            // Compute the center, axis, and radius of the circle that circumscribes the three points.
            // This circle is guaranteed to circumscribe all four points of the sector's Cartesian quadrilateral.
            Vec4[]   centerOut = new Vec4[1];
            Vec4[]   axisOut   = new Vec4[1];
            double[] radiusOut = new double[1];
            if (!WWMath.computeCircleThroughPoints(p0, p1, p2, centerOut, axisOut, radiusOut))
            {
                // If the computation failed, then two of the points are coincident. Fall back to creating a bounding
                // cylinder based on the vertices of the sector. This bounding cylinder won't be as tight a fit, but
                // it will be correct.
                return(computeVerticalBoundsFromSectorVertices(globe, sector, minHeight, maxHeight));
            }
            Vec4   centerPoint = centerOut[0];
            Vec4   axis        = axisOut[0];
            double radius      = radiusOut[0];

            // Compute the sector's lowest projection along the cylinder axis. We test opposite corners of the sector
            // using minHeight. One of these will be the lowest point in the sector.
            Vec4 extremePoint = globe.computePointFromPosition(sector.getMinLatitude(), sector.getMinLongitude(),
                                                               minHeight);
            double minProj = extremePoint.subtract3(centerPoint).dot3(axis);

            extremePoint = globe.computePointFromPosition(sector.getMaxLatitude(), sector.getMaxLongitude(), minHeight);
            minProj      = Math.Min(minProj, extremePoint.subtract3(centerPoint).dot3(axis));
            // Compute the sector's highest projection along the cylinder axis. We only need to use the point at the
            // sector's centroid with maxHeight. This point is guaranteed to be the highest point in the sector.
            LatLon centroid = sector.getCentroid();

            extremePoint = globe.computePointFromPosition(centroid.getLatitude(), centroid.getLongitude(), maxHeight);
            double maxProj = extremePoint.subtract3(centerPoint).dot3(axis);

            Vec4 bottomCenterPoint = axis.multiply3(minProj).add3(centerPoint);
            Vec4 topCenterPoint    = axis.multiply3(maxProj).add3(centerPoint);

            if (radius == 0)
            {
                radius = 1;
            }

            if (bottomCenterPoint.Equals(topCenterPoint))
            {
                topCenterPoint = bottomCenterPoint.add3(new Vec4(1, 0, 0));
            }

            return(new Cylinder(bottomCenterPoint, topCenterPoint, radius));
        }
Esempio n. 13
0
 protected static int getInitialCapacity(int capacity, float loadFactor)
 {
     return(WWMath.powerOfTwoCeiling((int)Math.Ceiling(capacity / loadFactor)));
 }
Esempio n. 14
0
        /** {@inheritDoc} */
        public double getProjectedArea(View view)
        {
            if (view == null)
            {
                String message = Logging.getMessage("nullValue.ViewIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            // Implementation based on "Real-time Bounding Box Area Computation" by Dieter Schmalstieg and Robert F. Tobler,
            // Vienna University of Technology:
            // http://jgt.akpeters.com/papers/SchmalstiegTobler99/

            // Compute the exact area of this Box in the screen by determining exact area covered by this Box's projected
            // vertices. We avoid computing a 2D bounding box of the projected vertices, because it is not necessarily more
            // efficient and is is less accurate: it is an approximation of an approximation. We start by computing the 2D
            // hull of this Box's vertices in screen coordinates. We create a 6-bit index into a predetermined table of
            // possible vertex combinations by comparing the eye point to each of the six planes. The resultant index
            // determines the vertices that define the 2D hull for that viewport.
            int lookupCode = this.computeProjectionHullCode(view);

            // Index 0 indicates that the view is inside this Box. Return positive infinity, indicating that this Box does
            // not have a finite area in the viewport.
            if (lookupCode == 0)
            {
                return(Double.PositiveInfinity);
            }

            if (lookupCode < 0 || lookupCode >= ProjectionHullTable.Length)
            {
                return(0); // This should never happen, but we check anyway.
            }
            // Get the 4 or 6 vertex indices that define this Box's convex hull in screen coordinates. Each element is used
            // as an index into this Box's array of corners.
            int[] indices = ProjectionHullTable[lookupCode];
            if (indices == null || (indices.Length != 4 && indices.Length != 6))
            {
                return(0); // This should never happen, but we check anyway.
            }
            // Compute this Box's convex hull in screen coordinates, by transforming the 4 or 6 vertices that define its
            // projected outline from model coordinates into screen coordinates.
            Vec4[] vertices       = this.getCorners();
            Vec4[] screenVertices = new Vec4[indices.Length];

            // If any of this Box's vertices are behind the eye point, return positive infinity indicating that this Box
            // does not have a finite area in the viewport.
            //noinspection ForLoopReplaceableByForEach
            for (int i = 0; i < indices.Length; i++)
            {
                Vec4 eyeVertex = vertices[indices[i]].transformBy4(view.getModelviewMatrix());
                if (eyeVertex.z() >= 0)
                {
                    return(Double.PositiveInfinity);
                }
            }

            for (int i = 0; i < indices.Length; i++)
            {
                screenVertices[i] = view.project(vertices[indices[i]]);
            }

            // Compute the area of the convex hull by treating the projected vertices as a 2D polygon. If this Box's r, s,
            // and t axes define a right-handed coordinate system, the vertices have a counter-clockwise winding order on
            // screen and the area is positive. If the axes define a left-handed coordinate system, the vertices have a
            // clockwise winding order on scree and the area is negative. We return the area's absolute value to handle
            // either case.
            double area = WWMath.computePolygonAreaFromVertices(screenVertices);

            return(Math.Abs(area));
        }
Esempio n. 15
0
        /**
         * Compute a <code>Box</code> that bounds a specified list of points. Principal axes are computed for the points and
         * used to form a <code>Box</code>.
         *
         * @param points the points for which to compute a bounding volume.
         *
         * @return the bounding volume, with axes lengths consistent with the conventions described in the <code>Box</code>
         *         class overview.
         *
         * @throws ArgumentException if the point list is null or empty.
         */
        public static Box computeBoundingBox(IEnumerable <Vec4> points)
        {
            if (points == null)
            {
                String msg = Logging.getMessage("nullValue.PointListIsNull");
                Logging.logger().severe(msg);
                throw new ArgumentException(msg);
            }

            Vec4[] axes = WWMath.computePrincipalAxes(points);
            if (axes == null)
            {
                String msg = Logging.getMessage("generic.ListIsEmpty");
                Logging.logger().severe(msg);
                throw new ArgumentException(msg);
            }

            Vec4 r = axes[0];
            Vec4 s = axes[1];
            Vec4 t = axes[2];

            // Find the extremes along each axis.
            double minDotR = Double.MaxValue;
            double maxDotR = -minDotR;
            double minDotS = Double.MaxValue;
            double maxDotS = -minDotS;
            double minDotT = Double.MaxValue;
            double maxDotT = -minDotT;

            foreach (Vec4 p in points)
            {
                if (p == null)
                {
                    continue;
                }

                double pdr = p.dot3(r);
                if (pdr < minDotR)
                {
                    minDotR = pdr;
                }
                if (pdr > maxDotR)
                {
                    maxDotR = pdr;
                }

                double pds = p.dot3(s);
                if (pds < minDotS)
                {
                    minDotS = pds;
                }
                if (pds > maxDotS)
                {
                    maxDotS = pds;
                }

                double pdt = p.dot3(t);
                if (pdt < minDotT)
                {
                    minDotT = pdt;
                }
                if (pdt > maxDotT)
                {
                    maxDotT = pdt;
                }
            }

            if (maxDotR == minDotR)
            {
                maxDotR = minDotR + 1;
            }
            if (maxDotS == minDotS)
            {
                maxDotS = minDotS + 1;
            }
            if (maxDotT == minDotT)
            {
                maxDotT = minDotT + 1;
            }

            return(new Box(axes, minDotR, maxDotR, minDotS, maxDotS, minDotT, maxDotT));
        }
Esempio n. 16
0
 /** {@inheritDoc} */
 public Intersection[] intersect(Line line)
 {
     return(WWMath.polytopeIntersect(line, this.planes));
 }
Esempio n. 17
0
        /**
         * Returns an OpenGL pixel format corresponding to the specified texture internal format. This maps internal format
         * to pixel format as follows: <code> <table> <tr><th>Internal Format</th><th>Estimated Bits Per Pixel</th></tr>
         * <tr><td>GL2.GL_ALPHA</td><td>8</td></tr> <tr><td>GL2.GL_ALPHA4</td><td>4</td></tr>
         * <tr><td>GL2.GL_ALPHA8</td><td>8</td></tr> <tr><td>GL2.GL_ALPHA12</td><td>12</td></tr>
         * <tr><td>GL2.GL_ALPHA16</td><td>16</td></tr> <tr><td>GL2.GL_COMPRESSED_ALPHA</td><td>0</td></tr>
         * <tr><td>GL2.GL_COMPRESSED_LUMINANCE</td><td>0</td></tr> <tr><td>GL2.GL_COMPRESSED_LUMINANCE_ALPHA</td><td>0</td></tr>
         * <tr><td>GL2.GL_COMPRESSED_INTENSITY</td><td>0</td></tr> <tr><td>GL2.GL_COMPRESSED_RGB</td><td>0</td></tr>
         * <tr><td>GL2.GL_COMPRESSED_RGBA</td><td>0</td></tr> <tr><td>GL2.GL_DEPTH_COMPONENT</td><td>24</td></tr>
         * <tr><td>GL2.GL_DEPTH_COMPONENT16</td><td>16</td></tr> <tr><td>GL2.GL_DEPTH_COMPONENT24</td><td>24</td></tr>
         * <tr><td>GL2.GL_DEPTH_COMPONENT32</td><td>32</td></tr> <tr><td>GL2.GL_LUMINANCE</td><td>8</td></tr>
         * <tr><td>GL2.GL_LUMINANCE4</td><td>4</td></tr> <tr><td>GL2.GL_LUMINANCE8</td><td>8</td></tr>
         * <tr><td>GL2.GL_LUMINANCE12</td><td>12</td></tr> <tr><td>GL2.GL_LUMINANCE16</td><td>16</td></tr>
         * <tr><td>GL2.GL_LUMINANCE_ALPHA</td><td>16</td></tr> <tr><td>GL2.GL_LUMINANCE4_ALPHA4</td><td>8</td></tr>
         * <tr><td>GL2.GL_LUMINANCE6_ALPHA2</td><td>8</td></tr> <tr><td>GL2.GL_LUMINANCE8_ALPHA8</td><td>16</td></tr>
         * <tr><td>GL2.GL_LUMINANCE12_ALPHA4</td><td>16</td></tr> <tr><td>GL2.GL_LUMINANCE12_ALPHA12</td><td>24</td></tr>
         * <tr><td>GL2.GL_LUMINANCE16_ALPHA16</td><td>32</td></tr> <tr><td>GL2.GL_INTENSITY</td><td>8</td></tr>
         * <tr><td>GL2.GL_INTENSITY4</td><td>4</td></tr> <tr><td>GL2.GL_INTENSITY8</td><td>8</td></tr>
         * <tr><td>GL2.GL_INTENSITY12</td><td>12</td></tr> <tr><td>GL2.GL_INTENSITY16</td><td>16</td></tr>
         * <tr><td>GL2.GL_R3_G3_B2</td><td>8</td></tr> <tr><td>GL2.GL_RGB</td><td>24</td></tr>
         * <tr><td>GL2.GL_RGB4</td><td>12</td></tr> <tr><td>GL2.GL_RGB5</td><td>16 (assume the driver allocates 16 bits per
         * pixel)</td></tr> <tr><td>GL2.GL_RGB8</td><td>24</td></tr> <tr><td>GL2.GL_RGB10</td><td>32 (assume the driver
         * allocates 32 bits per pixel)</td></tr> <tr><td>GL2.GL_RGB12</td><td>36</td></tr>
         * <tr><td>GL2.GL_RGB16</td><td>48</td></tr> <tr><td>GL2.GL_RGBA</td><td>32</td></tr>
         * <tr><td>GL2.GL_RGBA2</td><td>8</td></tr> <tr><td>GL2.GL_RGBA4</td><td>16</td></tr>
         * <tr><td>GL2.GL_RGB5_A1</td><td>16</td></tr> <tr><td>GL2.GL_RGBA8</td><td>32</td></tr>
         * <tr><td>GL2.GL_RGB10_A2</td><td>32</td></tr> <tr><td>GL2.GL_RGBA12</td><td>48</td></tr>
         * <tr><td>GL2.GL_RGBA16</td><td>64</td></tr> <tr><td>GL2.GL_SLUMINANCE</td><td>8</td></tr>
         * <tr><td>GL2.GL_SLUMINANCE8</td><td>8</td></tr> <tr><td>GL2.GL_SLUMINANCE_ALPHA</td><td>16</td></tr>
         * <tr><td>GL2.GL_SLUMINANCE8_ALPHA8</td><td>16<td></tr> <tr><td>GL2.GL_SRGB</td><td>24</td></tr>
         * <tr><td>GL2.GL_SRGB8</td><td>24</td></tr> <tr><td>GL2.GL_SRGB_ALPHA</td><td>32</td></tr>
         * <tr><td>GL2.GL_SRGB8_ALPHA8</td><td>32</td></tr> </code>
         * <p/>
         * The returned estimate assumes that the driver provides does not convert the formats to another supported, such
         * converting as <code>GL2.GL_ALPHA4</code> to <code>GL2.GL_ALPHA8</code>. This returns 0 if the internal format is
         * not one of the recognized types. This does not attempt to estimate a memory size for compressed internal
         * formats.
         *
         * @param internalFormat the OpenGL texture internal format.
         * @param width          the texture width, in pixels.
         * @param height         the texture height, in pixels.
         * @param includeMipmaps true to include the texture's mip map data in the estimated size; false otherwise.
         *
         * @return a pixel format corresponding to the texture internal format, or 0 if the internal format is not
         *         recognized.
         *
         * @throws ArgumentException if either the width or height is less than or equal to zero.
         */
        public static int estimateTextureMemorySize(int internalFormat, int width, int height, bool includeMipmaps)
        {
            if (width < 0)
            {
                String message = Logging.getMessage("Geom.WidthInvalid", width);
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            if (height < 0)
            {
                String message = Logging.getMessage("Geom.HeightInvalid", height);
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            int numPixels = width * height;

            // Add the number of pixels from each level in the mipmap chain to the total number of pixels.
            if (includeMipmaps)
            {
                int maxLevel = Math.Max((int)WWMath.LogBase2(width), (int)WWMath.LogBase2(height));
                for (int level = 1; level <= maxLevel; level++)
                {
                    int w = Math.Max(width >> level, 1);
                    int h = Math.Max(height >> level, 1);
                    numPixels += w * h;
                }
            }

            switch (internalFormat)
            {
            // 4 bits per pixel.
            case GL2.GL_ALPHA4:
            case GL2.GL_LUMINANCE4:
            case GL2.GL_INTENSITY4:
                return(numPixels / 2);

            // 8 bits per pixel.
            case GL2.GL_ALPHA:
            case GL2.GL_ALPHA8:
            case GL2.GL_LUMINANCE:
            case GL2.GL_LUMINANCE8:
            case GL2.GL_LUMINANCE4_ALPHA4:
            case GL2.GL_LUMINANCE6_ALPHA2:
            case GL2.GL_INTENSITY:
            case GL2.GL_INTENSITY8:
            case GL2.GL_R3_G3_B2:
            case GL2.GL_RGBA2:
            case GL2.GL_SLUMINANCE:
            case GL2.GL_SLUMINANCE8:
                return(numPixels);

            // 12 bits per pixel.
            case GL2.GL_ALPHA12:
            case GL2.GL_LUMINANCE12:
            case GL2.GL_INTENSITY12:
            case GL2.GL_RGB4:
                return(12 * numPixels / 8);

            // 16 bits per pixel.
            case GL2.GL_ALPHA16:
            case GL2.GL_DEPTH_COMPONENT16:
            case GL2.GL_LUMINANCE16:
            case GL2.GL_LUMINANCE_ALPHA:
            case GL2.GL_LUMINANCE8_ALPHA8:
            case GL2.GL_LUMINANCE12_ALPHA4:
            case GL2.GL_INTENSITY16:
            case GL2.GL_RGB5: // Assume the driver allocates 16 bits per pixel for GL_RGB5.
            case GL2.GL_RGBA4:
            case GL2.GL_RGB5_A1:
            case GL2.GL_SLUMINANCE_ALPHA:
            case GL2.GL_SLUMINANCE8_ALPHA8:
                return(2 * numPixels);

            // 24 bits per pixel.
            case GL2.GL_DEPTH_COMPONENT:
            case GL2.GL_DEPTH_COMPONENT24:
            case GL2.GL_LUMINANCE12_ALPHA12:
            case GL2.GL_RGB:
            case GL2.GL_RGB8:
            case GL2.GL_SRGB:
            case GL2.GL_SRGB8:
                return(3 * numPixels);

            // 32 bits per pixel.
            case GL2.GL_DEPTH_COMPONENT32:
            case GL2.GL_LUMINANCE16_ALPHA16:
            case GL2.GL_RGB10: // Assume the driver allocates 32 bits per pixel for GL_RGB10.
            case GL2.GL_RGBA:
            case GL2.GL_RGBA8:
            case GL2.GL_RGB10_A2:
            case GL2.GL_SRGB_ALPHA:
            case GL2.GL_SRGB8_ALPHA8:
                return(4 * numPixels);

            // 36 bits per pixel.
            case GL2.GL_RGB12:
                return(36 * numPixels / 8);

            // 48 bits per pixel.
            case GL2.GL_RGB16:
            case GL2.GL_RGBA12:
                return(6 * numPixels);

            // 64 bits per pixel.
            case GL2.GL_RGBA16:
                return(8 * numPixels);

            // Compressed internal formats. Don't try to estimate a size for compressed formats.
            case GL2.GL_COMPRESSED_ALPHA:
            case GL2.GL_COMPRESSED_LUMINANCE:
            case GL2.GL_COMPRESSED_LUMINANCE_ALPHA:
            case GL2.GL_COMPRESSED_INTENSITY:
            case GL2.GL_COMPRESSED_RGB:
            case GL2.GL_COMPRESSED_RGBA:
                return(0);

            default:
                return(0);
            }
        }