/** {@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)); }
/** * 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())); }
/** {@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); }
/** {@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())); }
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); }
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)); }
/** * 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)); }
/** * 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)); }
/** * 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)); }
protected static int getInitialCapacity(int capacity, float loadFactor) { return(WWMath.powerOfTwoCeiling((int)Math.Ceiling(capacity / loadFactor))); }
/** {@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)); }
/** * 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)); }
/** {@inheritDoc} */ public Intersection[] intersect(Line line) { return(WWMath.polytopeIntersect(line, this.planes)); }
/** * 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); } }