diff options
author | Harvey Harrison <[email protected]> | 2015-04-19 21:02:06 -0700 |
---|---|---|
committer | Harvey Harrison <[email protected]> | 2015-04-19 21:02:06 -0700 |
commit | 7a2e20caac9db6f789a7b3fab344b9758af45335 (patch) | |
tree | b5236ff2570178de356eab569225108948eb4d30 /src/javax/media/j3d/TextureRetained.java | |
parent | f76ce302c4bb2a9f03bbee571ec5d05c29633023 (diff) |
j3dcore: flatten the directory structure a bit
Signed-off-by: Harvey Harrison <[email protected]>
Diffstat (limited to 'src/javax/media/j3d/TextureRetained.java')
-rw-r--r-- | src/javax/media/j3d/TextureRetained.java | 2515 |
1 files changed, 2515 insertions, 0 deletions
diff --git a/src/javax/media/j3d/TextureRetained.java b/src/javax/media/j3d/TextureRetained.java new file mode 100644 index 0000000..fdb16ee --- /dev/null +++ b/src/javax/media/j3d/TextureRetained.java @@ -0,0 +1,2515 @@ +/* + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.awt.image.DataBufferByte; +import java.awt.image.RenderedImage; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.vecmath.Color4f; +import javax.vecmath.Point2f; +import javax.vecmath.Point3f; +import javax.vecmath.Tuple3f; + +/** + * The Texture object is a component object of an Appearance object + * that defines the texture properties used when texture mapping is + * enabled. Texture object is an abstract class and all texture + * objects must be created as either a Texture2D object or a + * Texture3D object. + */ +abstract class TextureRetained extends NodeComponentRetained { + // A list of pre-defined bits to indicate which component + // in this Texture object changed. + static final int ENABLE_CHANGED = 0x001; + static final int COLOR_CHANGED = 0x002; + static final int IMAGE_CHANGED = 0x004; + static final int STATE_CHANGED = 0x008; + static final int UPDATE_IMAGE = 0x010; + static final int IMAGES_CHANGED = 0x020; + static final int BASE_LEVEL_CHANGED = 0x040; + static final int MAX_LEVEL_CHANGED = 0x080; + static final int MIN_LOD_CHANGED = 0x100; + static final int MAX_LOD_CHANGED = 0x200; + static final int LOD_OFFSET_CHANGED = 0x400; + + // constants for min and mag filter + static final int MIN_FILTER = 0; + static final int MAG_FILTER = 1; + + // Boundary width + int boundaryWidth = 0; + + // Boundary modes (wrap, clamp, clamp_to_edge, clamp_to_boundary) + int boundaryModeS = Texture.WRAP; + int boundaryModeT = Texture.WRAP; + + // Filter modes + int minFilter = Texture.BASE_LEVEL_POINT; + int magFilter = Texture.BASE_LEVEL_POINT; + + // Integer flag that contains bitset to indicate + // which field changed. + int isDirty = 0xffff; + + // Texture boundary color + Color4f boundaryColor = new Color4f(0.0f, 0.0f, 0.0f, 0.0f); + + // Texture Object Id used by native code. + int objectId = -1; + + int mipmapMode = Texture.BASE_LEVEL; // Type of mip-mapping + int format = Texture.RGB; // Texture format + int width = 1; // Width in pixels (2**n) + int height = 1; // Height in pixels (2**m) + + // true if width or height is non power of two + private boolean widthOrHeightIsNPOT = false; + // Array of images (one for each mipmap level) + ImageComponentRetained images[][]; + // maximum number of levels needed for the mipmapMode of this texture + int maxLevels = 0; + // maximum number of mipmap levels that can be defined for this texture + private int maxMipMapLevels = 0; + + int numFaces = 1; // For CubeMap, it is 6 + int baseLevel = 0; + int maximumLevel = 0; + float minimumLod = -1000.0f; + float maximumLod = 1000.0f; + Point3f lodOffset = null; + + private boolean useAsRaster = false; // true if used by Raster or Background. + + // Texture mapping enable switch + // This enable is derived from the user specified enable + // and whether the buf image in the imagecomp is null + boolean enable = true; + + // User specified enable + boolean userSpecifiedEnable = true; + + + // true if alpha channel need update during rendering + boolean isAlphaNeedUpdate = false; + + // sharpen texture info + int numSharpenTextureFuncPts = 0; + float sharpenTextureFuncPts[] = null; // array of pairs of floats + // first value for LOD + // second value for the fcn value + + // filter4 info + float filter4FuncPts[] = null; + + // anisotropic filter info + int anisotropicFilterMode = Texture.ANISOTROPIC_NONE; + float anisotropicFilterDegree = 1.0f; + + + // Each bit corresponds to a unique renderer if shared context + // or a unique canvas otherwise. + // This mask specifies which renderer/canvas has loaded the + // texture images. 0 means no renderer/canvas has loaded the texture. + // 1 at the particular bit means that renderer/canvas has loaded the + // texture. 0 means otherwise. + int resourceCreationMask = 0x0; + + // Each bit corresponds to a unique renderer if shared context + // or a unique canvas otherwise + // This mask specifies if texture images are up-to-date. + // 0 at a particular bit means texture images are not up-to-date. + // 1 means otherwise. If it specifies 0, then it needs to go + // through the imageUpdateInfo to update the images accordingly. + // + int resourceUpdatedMask = 0x0; + + // Each bit corresponds to a unique renderer if shared context + // or a unique canvas otherwise + // This mask specifies if texture lod info is up-to-date. + // 0 at a particular bit means texture lod info is not up-to-date. + // 1 means otherwise. + // + int resourceLodUpdatedMask = 0x0; + + // Each bit corresponds to a unique renderer if shared context + // or a unique canvas otherwise + // This mask specifies if texture is in the resource reload list + // 0 at a particular bit means texture is not in reload list + // 1 means otherwise. + // + int resourceInReloadList = 0x0; + + // image update info + ArrayList imageUpdateInfo[][]; + + + int imageUpdatePruneMask[]; + + // Issue 357 - we need a separate reference counter per RenderBin, since + // each RenderBin keeps an independent list of Texture objects to be freed. + // Since this is accessed infrequently, we will use a HashMap for the + // textureBin reference counter + private HashMap<RenderBin,Integer> textureBinRefCount = + new HashMap<RenderBin,Integer>(); + + // need to synchronize access from multiple rendering threads + Object resourceLock = new Object(); + + private static boolean isPowerOfTwo(int val) { + return ((val & (val - 1)) == 0); + } + + void initialize(int format, int width, int widLevels, + int height, int heiLevels, int mipmapMode, + int boundaryWidth) { + + this.mipmapMode = mipmapMode; + this.format = format; + this.width = width; + this.height = height; + this.boundaryWidth = boundaryWidth; + + if(!isPowerOfTwo(width) || !isPowerOfTwo(height)) { + this.widthOrHeightIsNPOT = true; + } + + // determine the maximum number of mipmap levels that can be + // defined from the specified dimension + + if (widLevels > heiLevels) { + maxMipMapLevels = widLevels + 1; + } else { + maxMipMapLevels = heiLevels + 1; + } + + + // determine the maximum number of mipmap levels that will be + // needed with the current mipmapMode + + if (mipmapMode != Texture.BASE_LEVEL) { + baseLevel = 0; + maximumLevel = maxMipMapLevels - 1; + maxLevels = maxMipMapLevels; + } else { + baseLevel = 0; + maximumLevel = 0; + maxLevels = 1; + } + + images = new ImageComponentRetained[numFaces][maxLevels]; + + for (int j = 0; j < numFaces; j++) { + for (int i = 0; i < maxLevels; i++) { + images[j][i] = null; + } + } + } + + final int getFormat() { + return this.format; + } + + final int getWidth() { + return this.width; + } + + final int getHeight() { + return this.height; + } + + final int numMipMapLevels() { + return (maximumLevel - baseLevel + 1); + } + + /** + * Sets the boundary mode for the S coordinate in this texture object. + * @param boundaryModeS the boundary mode for the S coordinate, + * one of: CLAMP or WRAP. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final void initBoundaryModeS(int boundaryModeS) { + this.boundaryModeS = boundaryModeS; + } + + /** + * Retrieves the boundary mode for the S coordinate. + * @return the current boundary mode for the S coordinate. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final int getBoundaryModeS() { + return boundaryModeS; + } + + /** + * Sets the boundary mode for the T coordinate in this texture object. + * @param boundaryModeT the boundary mode for the T coordinate, + * one of: CLAMP or WRAP. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final void initBoundaryModeT(int boundaryModeT) { + this.boundaryModeT = boundaryModeT; + } + + /** + * Retrieves the boundary mode for the T coordinate. + * @return the current boundary mode for the T coordinate. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final int getBoundaryModeT() { + return boundaryModeT; + } + + /** + * Retrieves the boundary width. + * @return the boundary width of this texture. + */ + final int getBoundaryWidth() { + return boundaryWidth; + } + + /** + * Sets the minification filter function. This + * function is used when the pixel being rendered maps to an area + * greater than one texel. + * @param minFilter the minification filter, one of: + * FASTEST, NICEST, BASE_LEVEL_POINT, BASE_LEVEL_LINEAR, + * MULTI_LEVEL_POINT, MULTI_LEVEL_LINEAR. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final void initMinFilter(int minFilter) { + this.minFilter = minFilter; + } + + /** + * Retrieves the minification filter. + * @return the current minification filter function. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final int getMinFilter() { + return minFilter; + } + + /** + * Sets the magnification filter function. This + * function is used when the pixel being rendered maps to an area + * less than or equal to one texel. + * @param magFilter the magnification filter, one of: + * FASTEST, NICEST, BASE_LEVEL_POINT, or BASE_LEVEL_LINEAR. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final void initMagFilter(int magFilter) { + this.magFilter = magFilter; + } + + /** + * Retrieves the magnification filter. + * @return the current magnification filter function. + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final int getMagFilter() { + return magFilter; + } + + /** + * Sets a specified mipmap level. + * @param level mipmap level to set: 0 is the base level + * @param image pixel array object containing the texture image + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + * @exception IllegalArgumentException if an ImageComponent3D + * is used in a Texture2D or ImageComponent2D in Texture3D + * power of 2 OR invalid format/mipmapMode is specified. + */ + void initImage(int level, ImageComponent image) { + + // Issue 172 : call checkImageSize even for non-live setImage calls + checkImageSize(level, image); + + if (this.images == null) { + throw new IllegalArgumentException(J3dI18N.getString("TextureRetained0")); + } + + if (this.source instanceof Texture2D) { + if (image instanceof ImageComponent3D) + throw new IllegalArgumentException(J3dI18N.getString("Texture8")) +; + } else { + + if (image instanceof ImageComponent2D) + throw new IllegalArgumentException(J3dI18N.getString("Texture14") +); + } + + + if (this.source.isLive()) { + + if (this.images[0][level] != null) { + this.images[0][level].clearLive(refCount); + } + + + if (image != null) { + ((ImageComponentRetained)image.retained).setLive(inBackgroundGroup, refCount); + } + } + + if (image != null) { + this.images[0][level] = (ImageComponentRetained)image.retained; + + } else { + this.images[0][level] = null; + } + } + + final void checkImageSize(int level, ImageComponent image) { + if (image != null) { + int imgWidth = ((ImageComponentRetained)image.retained).width; + int imgHeight = ((ImageComponentRetained)image.retained).height; + + int wdh = width; + int hgt = height; + for (int i = 0; i < level; i++) { + wdh >>= 1; + hgt >>= 1; + } + + if (wdh < 1) wdh = 1; + if (hgt < 1) hgt = 1; + + if ((wdh != (imgWidth - 2*boundaryWidth)) || + (hgt != (imgHeight - 2*boundaryWidth))) { + throw new IllegalArgumentException( + J3dI18N.getString("TextureRetained1")); + } + } + } + + final void checkSizes(ImageComponentRetained images[]) { + // Issue 172 : this method is now redundant + + // Assertion check that the image at each level is the correct size + // This shouldn't be needed since we already should have checked the + // size at each level, and verified that all levels are set. + if (images != null) { + int hgt = height; + int wdh = width; + for (int level = 0; level < images.length; level++) { + int imgWidth = images[level].width; + int imgHeight = images[level].height; + + assert (wdh == (imgWidth - 2*boundaryWidth)) && + (hgt == (imgHeight - 2*boundaryWidth)); + + wdh /= 2; + hgt /= 2; + if (wdh < 1) wdh = 1; + if (hgt < 1) hgt = 1; + } + } + } + + final void setImage(int level, ImageComponent image) { + initImage(level, image); + + Object arg[] = new Object[3]; + arg[0] = new Integer(level); + arg[1] = image; + arg[2] = new Integer(0); + sendMessage(IMAGE_CHANGED, arg); + + // If the user has set enable to true, then if the image is null + // turn off texture enable + + if (userSpecifiedEnable) { + enable = userSpecifiedEnable; + if (image != null && level >= baseLevel && level <= maximumLevel) { + ImageComponentRetained img= (ImageComponentRetained)image.retained; + if (img.isByReference()) { + if (img.getRefImage(0) == null) { + enable = false; + } + } + else { + if (img.getImageData(isUseAsRaster()).get() == null) { + enable = false; + } + } + if (!enable) + sendMessage(ENABLE_CHANGED, Boolean.FALSE); + } + } + } + + void initImages(ImageComponent[] images) { + + if (images.length != maxLevels) + throw new IllegalArgumentException(J3dI18N.getString("Texture20")); + + for (int i = 0; i < images.length; i++) { + initImage(i, images[i]); + } + } + + final void setImages(ImageComponent[] images) { + + int i; + + initImages(images); + + ImageComponent [] imgs = new ImageComponent[images.length]; + for (i = 0; i < images.length; i++) { + imgs[i] = images[i]; + } + + Object arg[] = new Object[2]; + arg[0] = imgs; + arg[1] = new Integer(0); + + sendMessage(IMAGES_CHANGED, arg); + + // If the user has set enable to true, then if the image is null + // turn off texture enable + + if (userSpecifiedEnable) { + enable = userSpecifiedEnable; + for (i = baseLevel; i <= maximumLevel && enable; i++) { + if (images[i] != null) { + ImageComponentRetained img= + (ImageComponentRetained)images[i].retained; + if (img.isByReference()) { + if (img.getRefImage(0) == null) { + enable = false; + } + } + else { + if (img.getImageData(isUseAsRaster()).get() == null) { + enable = false; + } + } + } + } + if (!enable) { + sendMessage(ENABLE_CHANGED, Boolean.FALSE); + } + } + } + + + + + /** + * Gets a specified mipmap level. + * @param level mipmap level to get: 0 is the base level + * @return the pixel array object containing the texture image + * @exception RestrictedAccessException if the method is called + * when this object is part of live or compiled scene graph. + */ + final ImageComponent getImage(int level) { + return (((images != null) && (images[0][level] != null)) ? + (ImageComponent)images[0][level].source : null); + } + + final ImageComponent[] getImages() { + if (images == null) + return null; + + ImageComponent [] rImages = new ImageComponent[images[0].length]; + for (int i = 0; i < images[0].length; i++) { + if (images[0][i] != null) + rImages[i] = (ImageComponent)images[0][i].source; + else + rImages[i] = null; + } + return rImages; + } + + /** + * Sets mipmap mode for texture mapping for this texture object. + * @param mipMapMode the new mipmap mode for this object. One of: + * BASE_LEVEL or MULTI_LEVEL_MIPMAP. + * @exception RestrictedAccessException if the method is called + */ + final void initMipMapMode(int mipmapMode) { + + if (this.mipmapMode == mipmapMode) + return; + + + int prevMaxLevels = maxLevels; // previous maxLevels + + this.mipmapMode = mipmapMode; + + if (mipmapMode != Texture.BASE_LEVEL) { + maxLevels = maxMipMapLevels; + } else { + baseLevel = 0; + maximumLevel = 0; + maxLevels = 1; + } + + + ImageComponentRetained[][] newImages = + new ImageComponentRetained[numFaces][maxLevels]; + + if (prevMaxLevels < maxLevels) { + for (int f = 0; f < numFaces; f++) { + for (int i = 0; i < prevMaxLevels; i++) { + newImages[f][i] = images[f][i]; + } + + for (int j = prevMaxLevels; j < maxLevels; j++) { + newImages[f][j] = null; + } + } + } else { + for (int f = 0; f < numFaces; f++) { + for (int i = 0; i < maxLevels; i++) + newImages[f][i] = images[f][i]; + } + } + images = newImages; + } + + /** + * Retrieves current mipmap mode. + * @return current mipmap mode of this texture object. + * @exception RestrictedAccessException if the method is called + */ + final int getMipMapMode() { + return this.mipmapMode; + } + + /** + * Enables or disables texture mapping for this + * appearance component object. + * @param state true or false to enable or disable texture mapping + */ + final void initEnable(boolean state) { + userSpecifiedEnable = state; + } + + /** + * Enables or disables texture mapping for this + * appearance component object and sends a + * message notifying the interested structures of the change. + * @param state true or false to enable or disable texture mapping + */ + final void setEnable(boolean state) { + + initEnable(state); + + if (state == enable) { + // if enable flag is same as user specified one + // this is only possible when enable is false + // because one of the images is null and user specifies false + return; + } + + enable = state; + + for (int j = 0; j < numFaces && enable; j++) { + for (int i = baseLevel; i <= maximumLevel && enable; i++) { + if (images[j][i].isByReference()) { + if (images[j][i].getRefImage(0) == null) { + enable = false; + } + } else { + if (images[j][i].getImageData(isUseAsRaster()).get() == null) { + enable = false; + } + } + } + } + sendMessage(ENABLE_CHANGED, (enable ? Boolean.TRUE: Boolean.FALSE)); + } + + /** + * Retrieves the state of the texture enable flag. + * @return true if texture mapping is enabled, + * false if texture mapping is disabled + */ + final boolean getEnable() { + return userSpecifiedEnable; + } + + + final void initBaseLevel(int level) { + if ((level < 0) || (level > maximumLevel)) { + throw new IllegalArgumentException( + J3dI18N.getString("Texture36")); + } + baseLevel = level; + } + + + final void setBaseLevel(int level) { + + if (level == baseLevel) + return; + + initBaseLevel(level); + sendMessage(BASE_LEVEL_CHANGED, new Integer(level)); + } + + final int getBaseLevel() { + return baseLevel; + } + + + final void initMaximumLevel(int level) { + if ((level < baseLevel) || (level >= maxMipMapLevels)) { + throw new IllegalArgumentException( + J3dI18N.getString("Texture37")); + } + if((mipmapMode == Texture.BASE_LEVEL) && (level != 0)) { + throw new IllegalArgumentException( + J3dI18N.getString("Texture48")); + } + + maximumLevel = level; + } + + final void setMaximumLevel(int level) { + + if (level == maximumLevel) + return; + + initMaximumLevel(level); + sendMessage(MAX_LEVEL_CHANGED, new Integer(level)); + } + + final int getMaximumLevel() { + return maximumLevel; + } + + final void initMinimumLOD(float lod) { + if (lod > maximumLod) { + throw new IllegalArgumentException( + J3dI18N.getString("Texture42")); + } + minimumLod = lod; + } + + final void setMinimumLOD(float lod) { + initMinimumLOD(lod); + sendMessage(MIN_LOD_CHANGED, new Float(lod)); + } + + final float getMinimumLOD() { + return minimumLod; + } + + + final void initMaximumLOD(float lod) { + if (lod < minimumLod) { + throw new IllegalArgumentException( + J3dI18N.getString("Texture42")); + } + maximumLod = lod; + } + + final void setMaximumLOD(float lod) { + initMaximumLOD(lod); + sendMessage(MAX_LOD_CHANGED, new Float(lod)); + } + + final float getMaximumLOD() { + return maximumLod; + } + + + final void initLodOffset(float s, float t, float r) { + if (lodOffset == null) { + lodOffset = new Point3f(s, t, r); + } else { + lodOffset.set(s, t, r); + } + } + + final void setLodOffset(float s, float t, float r) { + initLodOffset(s, t, r); + sendMessage(LOD_OFFSET_CHANGED, new Point3f(s, t, r)); + } + + final void getLodOffset(Tuple3f offset) { + if (lodOffset == null) { + offset.set(0.0f, 0.0f, 0.0f); + } else { + offset.set(lodOffset); + } + } + + + /** + * Sets the texture boundary color for this texture object. The + * texture boundary color is used when boundaryModeS or boundaryModeT + * is set to CLAMP. + * @param boundaryColor the new texture boundary color. + */ + final void initBoundaryColor(Color4f boundaryColor) { + this.boundaryColor.set(boundaryColor); + } + + /** + * Sets the texture boundary color for this texture object. The + * texture boundary color is used when boundaryModeS or boundaryModeT + * is set to CLAMP. + * @param r the red component of the color. + * @param g the green component of the color. + * @param b the blue component of the color. + * @param a the alpha component of the color. + */ + final void initBoundaryColor(float r, float g, float b, float a) { + this.boundaryColor.set(r, g, b, a); + } + + /** + * Retrieves the texture boundary color for this texture object. + * @param boundaryColor the vector that will receive the + * current texture boundary color. + */ + final void getBoundaryColor(Color4f boundaryColor) { + boundaryColor.set(this.boundaryColor); + } + + + /** + * Set Anisotropic Filter + */ + final void initAnisotropicFilterMode(int mode) { + anisotropicFilterMode = mode; + } + + final int getAnisotropicFilterMode() { + return anisotropicFilterMode; + } + + final void initAnisotropicFilterDegree(float degree) { + anisotropicFilterDegree = degree; + } + + final float getAnisotropicFilterDegree() { + return anisotropicFilterDegree; + } + + /** + * Set Sharpen Texture function + */ + final void initSharpenTextureFunc(float[] lod, float[] pts) { + if (lod == null) { // pts will be null too. + sharpenTextureFuncPts = null; + numSharpenTextureFuncPts = 0; + } else { + numSharpenTextureFuncPts = lod.length; + if ((sharpenTextureFuncPts == null) || + (sharpenTextureFuncPts.length != lod.length * 2)) { + sharpenTextureFuncPts = new float[lod.length * 2]; + } + for (int i = 0, j = 0; i < lod.length; i++) { + sharpenTextureFuncPts[j++] = lod[i]; + sharpenTextureFuncPts[j++] = pts[i]; + } + } + } + + final void initSharpenTextureFunc(Point2f[] pts) { + if (pts == null) { + sharpenTextureFuncPts = null; + numSharpenTextureFuncPts = 0; + } else { + numSharpenTextureFuncPts = pts.length; + if ((sharpenTextureFuncPts == null) || + (sharpenTextureFuncPts.length != pts.length * 2)) { + sharpenTextureFuncPts = new float[pts.length * 2]; + } + for (int i = 0, j = 0; i < pts.length; i++) { + sharpenTextureFuncPts[j++] = pts[i].x; + sharpenTextureFuncPts[j++] = pts[i].y; + } + } + } + + final void initSharpenTextureFunc(float[] pts) { + if (pts == null) { + sharpenTextureFuncPts = null; + numSharpenTextureFuncPts = 0; + } else { + numSharpenTextureFuncPts = pts.length / 2; + if ((sharpenTextureFuncPts == null) || + (sharpenTextureFuncPts.length != pts.length)) { + sharpenTextureFuncPts = new float[pts.length]; + } + for (int i = 0; i < pts.length; i++) { + sharpenTextureFuncPts[i] = pts[i]; + } + } + } + + /** + * Get number of points in the sharpen texture LOD function + */ + final int getSharpenTextureFuncPointsCount() { + return numSharpenTextureFuncPts; + } + + + /** + * Copies the array of sharpen texture LOD function points into the + * specified arrays + */ + final void getSharpenTextureFunc(float[] lod, float[] pts) { + if (sharpenTextureFuncPts != null) { + for (int i = 0, j = 0; i < numSharpenTextureFuncPts; i++) { + lod[i] = sharpenTextureFuncPts[j++]; + pts[i] = sharpenTextureFuncPts[j++]; + } + } + } + + final void getSharpenTextureFunc(Point2f[] pts) { + if (sharpenTextureFuncPts != null) { + for (int i = 0, j = 0; i < numSharpenTextureFuncPts; i++) { + pts[i].x = sharpenTextureFuncPts[j++]; + pts[i].y = sharpenTextureFuncPts[j++]; } } + } + + + final void initFilter4Func(float[] weights) { + if (weights == null) { + filter4FuncPts = null; + } else { + if ((filter4FuncPts == null) || + (filter4FuncPts.length != weights.length)) { + filter4FuncPts = new float[weights.length]; + } + for (int i = 0; i < weights.length; i++) { + filter4FuncPts[i] = weights[i]; + } + } + } + + + final int getFilter4FuncPointsCount() { + if (filter4FuncPts == null) { + return 0; + } else { + return filter4FuncPts.length; + } + } + + final void getFilter4Func(float[] weights) { + if (filter4FuncPts != null) { + for (int i = 0; i < filter4FuncPts.length; i++) { + weights[i] = filter4FuncPts[i]; + } + } + } + + + /** + * internal method only -- returns internal function points + */ + final float[] getSharpenTextureFunc() { + return sharpenTextureFuncPts; + } + + final float[] getFilter4Func(){ + return filter4FuncPts; + } + + + + + @Override + void setLive(boolean backgroundGroup, int refCount) { + + // This line should be assigned before calling doSetLive, so that + // the mirror object's enable is assigned correctly! + enable = userSpecifiedEnable; + + super.doSetLive(backgroundGroup, refCount); + + // XXXX: for now, do setLive for all the defined images. + // But in theory, we only need to setLive those within the + // baseLevel and maximumLevel range. But then we'll need + // setLive and clearLive image when the range changes. + + if (images != null) { + + for (int j = 0; j < numFaces; j++) { + for (int i = 0; i < maxLevels; i++){ + if (images[j][i] == null) { + throw new IllegalArgumentException( + J3dI18N.getString("TextureRetained3") + i); + } + images[j][i].setLive(backgroundGroup, refCount); + } + } + } + + // Issue 172 : assertion check the sizes of the images after we have + // checked for all mipmap levels being set + if (images != null) { + for (int j = 0; j < numFaces; j++) { + checkSizes(images[j]); + } + } + + // Send a message to Rendering Attr stucture to update the resourceMask + J3dMessage createMessage = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES; + createMessage.type = J3dMessage.TEXTURE_CHANGED; + createMessage.args[0] = this; + createMessage.args[1]= new Integer(UPDATE_IMAGE); + createMessage.args[2] = null; + createMessage.args[3] = new Integer(changedFrequent); + VirtualUniverse.mc.processMessage(createMessage); + + // If the user has set enable to true, then if the image is null + // turn off texture enable + if (userSpecifiedEnable) { + if (images != null) { + for (int j = 0; j < numFaces && enable; j++) { + for (int i = baseLevel; i <= maximumLevel && enable; i++){ + if (images[j][i].isByReference()) { + if (images[j][i].getRefImage(0) == null) { + enable = false; + } + } else { + if (images[j][i].getImageData(isUseAsRaster()).get() == null) { + enable = false; + } + } + } + } + } else { + enable = false; + } + if (!enable) + sendMessage(ENABLE_CHANGED, Boolean.FALSE); + } + + super.markAsLive(); + } + + @Override + void clearLive(int refCount) { + super.clearLive(refCount); + + if (images != null) { + for (int j = 0; j < numFaces; j++) { + for (int i = 0; i < maxLevels; i++) { + images[j][i].clearLive(refCount); + images[j][i].removeUser(mirror); + } + } + } + } + + /* + * The following methods update the native context. + * The implementation for Texture2D happens here. + * Texture3D and TextureCubeMap implement their own versions. + */ + + void bindTexture(Context ctx, int objectId, boolean enable) { + Pipeline.getPipeline().bindTexture2D(ctx, objectId, enable); + } + + void updateTextureBoundary(Context ctx, + int boundaryModeS, int boundaryModeT, + float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) { + + Pipeline.getPipeline().updateTexture2DBoundary(ctx, + boundaryModeS, boundaryModeT, + boundaryRed, boundaryGreen, + boundaryBlue, boundaryAlpha); + } + + void updateTextureFilterModes(Context ctx, + int minFilter, int magFilter) { + + Pipeline.getPipeline().updateTexture2DFilterModes(ctx, + minFilter, magFilter); + } + + void updateTextureSharpenFunc(Context ctx, + int numSharpenTextureFuncPts, + float[] sharpenTextureFuncPts) { + + Pipeline.getPipeline().updateTexture2DSharpenFunc(ctx, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + } + + void updateTextureFilter4Func(Context ctx, + int numFilter4FuncPts, + float[] filter4FuncPts) { + + Pipeline.getPipeline().updateTexture2DFilter4Func(ctx, + numFilter4FuncPts, filter4FuncPts); + } + + void updateTextureAnisotropicFilter(Context ctx, float degree) { + Pipeline.getPipeline().updateTexture2DAnisotropicFilter(ctx, degree); + } + + void updateTextureLodRange(Context ctx, + int baseLevel, int maximumLevel, + float minimumLod, float maximumLod) { + + Pipeline.getPipeline().updateTexture2DLodRange(ctx, baseLevel, maximumLevel, + minimumLod, maximumLod); + } + + void updateTextureLodOffset(Context ctx, + float lodOffsetX, float lodOffsetY, + float lodOffsetZ) { + + Pipeline.getPipeline().updateTexture2DLodOffset(ctx, + lodOffsetX, lodOffsetY, lodOffsetZ); + } + + // free a Texture2D id + void freeTextureId(int id) { + synchronized (resourceLock) { + if (objectId == id) + objectId = -1; + } + } + + private boolean isEnabled(Canvas3D cv) { + if(widthOrHeightIsNPOT && !isUseAsRaster() && + ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_NON_POWER_OF_TWO ) == 0)) { + return false; + } + return enable; + } + + + // bind a named texture to a texturing target + void bindTexture(Canvas3D cv) { + + synchronized(resourceLock) { + if (objectId == -1) + objectId = Canvas3D.generateTexID(cv.ctx); + cv.addTextureResource(objectId, this); + } + bindTexture(cv.ctx, objectId, isEnabled(cv)); + } + + + /** + * load level 0 explicitly with null pointer to enable + * mipmapping when level 0 is not the base level + */ + void updateTextureDimensions(Canvas3D cv) { + if(images[0][0] != null) { + updateTextureImage(cv, 0, maxLevels, 0, + format, images[0][0].getImageFormatTypeIntValue(false), + width, height, boundaryWidth, + images[0][0].getImageDataTypeIntValue(), null); + } + } + + + void updateTextureLOD(Canvas3D cv) { + + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_RANGE) != 0 ) { + + int max = 0; + if( mipmapMode == Texture.BASE_LEVEL ) { + max = maxMipMapLevels; + } + else { + max = maximumLevel; + } + + updateTextureLodRange(cv.ctx, baseLevel, max, + minimumLod, maximumLod); + } + + if ((lodOffset != null) && + ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_OFFSET) != 0)) { + updateTextureLodOffset(cv.ctx, + lodOffset.x, lodOffset.y, lodOffset.z); + } + } + + + void updateTextureBoundary(Canvas3D cv) { + updateTextureBoundary(cv.ctx, boundaryModeS, boundaryModeT, + boundaryColor.x, boundaryColor.y, + boundaryColor.z, boundaryColor.w); + } + + + void updateTextureFields(Canvas3D cv) { + + int magnificationFilter = magFilter; + int minificationFilter = minFilter; + + // update sharpen texture function if applicable + + if ((magnificationFilter >= Texture.LINEAR_SHARPEN) && + (magnificationFilter <= Texture.LINEAR_SHARPEN_ALPHA)) { + + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_SHARPEN) != 0 ) { + + // send down sharpen texture LOD function + // + updateTextureSharpenFunc(cv.ctx, + numSharpenTextureFuncPts, sharpenTextureFuncPts); + + } else { + + // sharpen texture is not supported by the underlying + // library, fallback to BASE_LEVEL_LINEAR + + magnificationFilter = Texture.BASE_LEVEL_LINEAR; + } + } else if ((magnificationFilter >= Texture2D.LINEAR_DETAIL) && + (magnificationFilter <= Texture2D.LINEAR_DETAIL_ALPHA)) { + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_DETAIL) == 0) { + + // detail texture is not supported by the underlying + // library, fallback to BASE_LEVEL_LINEAR + + magnificationFilter = Texture.BASE_LEVEL_LINEAR; + } + } + + if (minificationFilter == Texture.FILTER4 || magnificationFilter == Texture.FILTER4) { + + boolean noFilter4 = false; + + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_FILTER4) != 0) { + + if (filter4FuncPts == null) { + + // filter4 function is not defined, + // fallback to BASE_LEVEL_LINEAR + + noFilter4 = true; + } else { + updateTextureFilter4Func(cv.ctx, filter4FuncPts.length, + filter4FuncPts); + } + } else { + + // filter4 is not supported by the underlying + // library, fallback to BASE_LEVEL_LINEAR + + noFilter4 = true; + } + + if (noFilter4) { + if (minificationFilter == Texture.FILTER4) { + minificationFilter = Texture.BASE_LEVEL_LINEAR; + } + if (magnificationFilter == Texture.FILTER4) { + magnificationFilter = Texture.BASE_LEVEL_LINEAR; + } + } + } + + // Fallback to BASE mode if hardware mipmap generation is not supported. + if ((mipmapMode == Texture.BASE_LEVEL) && ((cv.textureExtendedFeatures & + Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION) == 0)) { + + if (minificationFilter == Texture.NICEST || + minificationFilter == Texture.MULTI_LEVEL_LINEAR) { + minificationFilter = Texture.BASE_LEVEL_LINEAR; + } else if (minificationFilter == Texture.MULTI_LEVEL_POINT) { + minificationFilter = Texture.BASE_LEVEL_POINT; + } + } + + // update texture filtering modes + updateTextureFilterModes(cv.ctx, minificationFilter, + magnificationFilter); + + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_ANISOTROPIC_FILTER) + != 0) { + if (anisotropicFilterMode == Texture.ANISOTROPIC_NONE) { + updateTextureAnisotropicFilter(cv.ctx, 1.0f); + } else { + updateTextureAnisotropicFilter(cv.ctx, anisotropicFilterDegree); + } + } + + // update texture boundary modes, boundary color + + updateTextureBoundary(cv); + + } + + + // Wrapper around the native call for 2D textures; overridden for + // TextureCureMap + void updateTextureImage(Canvas3D cv, + int face, int numLevels, int level, + int textureFormat, int imageFormat, + int width, int height, + int boundaryWidth, + int imageDataType, Object data) { + + Pipeline.getPipeline().updateTexture2DImage(cv.ctx, + numLevels, level, + textureFormat, imageFormat, + width, height, boundaryWidth, + imageDataType, data, useAutoMipMapGeneration(cv)); + } + + // Wrapper around the native call for 2D textures; overridden for + // TextureCureMap + void updateTextureSubImage(Canvas3D cv, + int face, int level, + int xoffset, int yoffset, + int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, + int tilew, int width, int height, + int imageDataType, Object data) { + + Pipeline.getPipeline().updateTexture2DSubImage(cv.ctx, + level, xoffset, yoffset, + textureFormat, imageFormat, + imgXOffset, imgYOffset, + tilew, width, height, + imageDataType, data, useAutoMipMapGeneration(cv)); + } + + + /** + * reloadTextureImage is used to load a particular level of image + * This method needs to take care of RenderedImage as well as + * BufferedImage + */ + void reloadTextureImage(Canvas3D cv, int face, int level, + ImageComponentRetained image, int numLevels) { + + boolean useAsRaster = isUseAsRaster(); + ImageComponentRetained.ImageData imageData = image.getImageData(useAsRaster); + + assert imageData != null; + + updateTextureImage(cv, + face, numLevels, level, + format, image.getImageFormatTypeIntValue(useAsRaster), + imageData.getWidth(), imageData.getHeight(), + boundaryWidth, image.getImageDataTypeIntValue(), + imageData.get()); + + + // TODO : Dead code - need to clean up for 1.6 + // Now take care of the RenderedImage (byRef and yUp) case. Note, if image + // is a RenderedImage ( byRef and yUp), then imageData will be null + + if (imageData == null) { + // System.err.println("==========. subImage"); + // Download all the tiles for this texture + int xoffset = 0, yoffset = 0; + int tmpw = image.width; + int tmph = image.height; + int endXTile = image.tilew; + int endYTile = image.tileh; + int curw = endXTile; + int curh = endYTile; + + if (tmpw < curw) { + curw = tmpw; + } + + if (tmph < curh) { + curh = tmph; + } + + int startw = curw; + int imageXOffset = image.tilew - curw; + int imageYOffset = image.tileh - curh; + for (int m = 0; m < image.numYTiles; m++) { + xoffset = 0; + tmpw = width; + curw = startw; + imageXOffset = image.tilew - curw; + for (int n = 0; n < image.numXTiles; n++) { + java.awt.image.Raster ras; + ras = ((RenderedImage)image.getRefImage(0)).getTile(n,m); + byte[] data = ((DataBufferByte)ras.getDataBuffer()).getData(); + updateTextureSubImage(cv, face, + level, xoffset, yoffset, format, + image.getImageFormatTypeIntValue(false), + imageXOffset, imageYOffset, + image.tilew, + curw, curh, + ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY, + (Object) data); + xoffset += curw; + imageXOffset = 0; + tmpw -= curw; + if (tmpw < image.tilew) + curw = tmpw; + else + curw = image.tilew; + } + yoffset += curh; + imageYOffset = 0; + tmph -= curh; + if (tmph < image.tileh) + curh = tmph; + else + curh = image.tileh; + } + } + } + + + /** + * update a subregion of the texture image + * This method needs to take care of RenderedImage as well as + * BufferedImage + */ + void reloadTextureSubImage(Canvas3D cv, int face, int level, + ImageComponentUpdateInfo info, + ImageComponentRetained image) { + + int x = info.x, + y = info.y, + width = info.width, + height = info.height; + + //The x and y here specifies the subregion of the imageData of + //the associated RenderedImage. + + //System.err.println("\nupdateTextureSubImage: x= " + x + " y= " + y + + // " width= " + width + " height= " + height + + // " format= " + format); + + ImageComponentRetained.ImageData imageData = image.getImageData(isUseAsRaster()); + if(imageData != null) { + int xoffset = x; + int yoffset = y; + + // TODO Check this logic : If !yUp adjust yoffset + if (!image.yUp) { + yoffset = image.height - yoffset - height; + } + + updateTextureSubImage(cv, face, level, + xoffset, yoffset, + format, image.getImageFormatTypeIntValue(false), + xoffset, yoffset, + image.width, width, height, + image.getImageDataTypeIntValue(), + imageData.get()); + + } else { + + assert false; + + // TODO : Dead code - need to clean up for 1.6 + // System.err.println("RenderedImage subImage update"); + // determine the first tile of the image + + float mt; + int minTileX, minTileY; + + int rx = x; + int ry = y; + + mt = (float)(rx) / (float)image.tilew; + if (mt < 0) { + minTileX = (int)(mt - 1); + } else { + minTileX = (int)mt; + } + + mt = (float)(ry) / (float)image.tileh; + if (mt < 0) { + minTileY = (int)(mt - 1); + } else { + minTileY = (int)mt; + } + + // determine the pixel offset of the upper-left corner of the + // first tile + int startXTile = minTileX * image.tilew; + int startYTile = minTileY * image.tilew; + + + // image dimension in the first tile + + int curw = (startXTile + image.tilew - rx); + int curh = (startYTile + image.tileh - ry); + + // check if the to-be-copied region is less than the tile image + // if so, update the to-be-copied dimension of this tile + + if (curw > width) { + curw = width; + } + + if (curh > height) { + curh = height; + } + + // save the to-be-copied width of the left most tile + + int startw = curw; + + + // temporary variable for dimension of the to-be-copied region + + int tmpw = width; + int tmph = height; + + + // offset of the first pixel of the tile to be copied; offset is + // relative to the upper left corner of the title + + int imgX = rx - startXTile; + int imgY = ry - startYTile; + + + // determine the number of tiles in each direction that the + // image spans + + int numXTiles = (width + imgX) / image.tilew; + int numYTiles = (height + imgY) / image.tileh; + + if (((float)(width + imgX ) % (float)image.tilew) > 0) { + numXTiles += 1; + } + + if (((float)(height + imgY ) % (float)image.tileh) > 0) { + numYTiles += 1; + } + + java.awt.image.Raster ras; + + int textureX = x; // x offset in the texture + int textureY = y; // y offset in the texture + + for (int yTile = minTileY; yTile < minTileY + numYTiles; + yTile++) { + + tmpw = width; + curw = startw; + imgX = rx - startXTile; + + for (int xTile = minTileX; xTile < minTileX + numXTiles; + xTile++) { + ras = ((RenderedImage)image.getRefImage(0)).getTile(xTile, yTile); + byte[] data = ((DataBufferByte)ras.getDataBuffer()).getData(); + + updateTextureSubImage(cv, face, level, + textureX, textureY, + format, image.getImageFormatTypeIntValue(false), + imgX, imgY, + image.tilew, curw, curh, + ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY, + (Object)data); + + // move to the next tile in x direction + + textureX += curw; + imgX = 0; + + // determine the width of copy region of the next tile + + tmpw -= curw; + if (tmpw < image.tilew) { + curw = tmpw; + } else { + curw = image.tilew; + } + } + + // move to the next set of tiles in y direction + textureY += curh; + imgY = 0; + + // determine the height of copy region for the next set + // of tiles + tmph -= curh; + if (tmph < image.tileh) { + curh = tmph; + } else { + curh = image.tileh; + } + } + } + } + + + // reload texture mipmap + + void reloadTexture(Canvas3D cv) { + + + int blevel, mlevel; + + //System.err.println("reloadTexture: baseLevel= " + baseLevel + + // " maximumLevel= " + maximumLevel); + + if ((cv.textureExtendedFeatures & Canvas3D.TEXTURE_LOD_RANGE) == 0 ) { + blevel = 0; + mlevel = maxLevels - 1; + } else { + blevel = baseLevel; + mlevel = maximumLevel; + } + + if (blevel != 0) { + // level 0 is not the base level, hence, need + // to load level 0 explicitly with a null pointer in order + // for mipmapping to be active. + + updateTextureDimensions(cv); + } + + for (int j = 0; j < numFaces; j++) { + for (int i = blevel; i <= mlevel; i++) { + + // it is possible to have null pointer if only a subset + // of mipmap levels is defined but the canvas does not + // support lod_range extension + + ImageComponentRetained image = images[j][i]; + if (image != null) { + // Issue 366: call evaluateExtensions, since it may not + // have been called yet in all cases + image.evaluateExtensions(cv); + reloadTextureImage(cv, j, i, image, maxLevels); + } + } + } + } + + + // update texture mipmap based on the imageUpdateInfo + + void updateTexture(Canvas3D cv, int resourceBit) { + + //System.err.println("updateTexture\n"); + + ImageComponentUpdateInfo info; + + for (int k = 0; k < numFaces; k++) { + for (int i = baseLevel; i <= maximumLevel; i++) { + if (imageUpdateInfo[k][i] != null) { + for (int j = 0; j < imageUpdateInfo[k][i].size(); j++) { + + info = (ImageComponentUpdateInfo) + imageUpdateInfo[k][i].get(j); + + + synchronized(resourceLock) { + + // if this info is updated, move on to the next one + + if ((info.updateMask & resourceBit) == 0) + continue; + + + // check if all canvases have processed this update + info.updateMask &= ~resourceBit; + + // all the current resources have updated this + // info, so this info can be removed from the + // update list + if ((info.updateMask & resourceCreationMask) + == 0) { + info.updateMask = 0; // mark this update as + // done + + // mark the prune flag so as to prune the + // update list next time when the update + // list is to be modified. + // Don't want to clean up the list at + // rendering time because (1) MT issue, + // other renderer could be processing + // the update list now; + // (2) takes up rendering time. + if (imageUpdatePruneMask == null) { + imageUpdatePruneMask = new int[numFaces]; + } + imageUpdatePruneMask[k] = 1 << i; + } + } + + if (info.entireImage == true) { + reloadTextureImage(cv, k, i, + images[k][i], maxLevels); + } else { + reloadTextureSubImage(cv, k, i, info, images[k][i]); + } + + } + } + } + } + } + + + /** + * reloadTextureSharedContext is called to reload texture + * on a shared context. It is invoked from the Renderer + * before traversing the RenderBin. The idea is to reload + * all necessary textures up front for all shared contexts + * in order to minimize the context switching overhead. + */ + void reloadTextureSharedContext(Canvas3D cv) { + + // if texture is not enabled, don't bother downloading the + // the texture state + + if (!isEnabled(cv)) { + return; + } + + bindTexture(cv); + + // reload all levels of texture image + + // update texture parameters such as boundary modes, filtering + + updateTextureFields(cv); + + + // update texture Lod parameters + + updateTextureLOD(cv); + + + // update all texture images + + reloadTexture(cv); + + synchronized(resourceLock) { + resourceCreationMask |= cv.screen.renderer.rendererBit; + resourceUpdatedMask |= cv.screen.renderer.rendererBit; + resourceLodUpdatedMask |= cv.screen.renderer.rendererBit; + resourceInReloadList &= ~cv.screen.renderer.rendererBit; + } + } + + + /** + * updateNative is called while traversing the RenderBin to + * update the texture state + */ + void updateNative(Canvas3D cv) { + boolean reloadTexture = false; // true - reload all levels of texture + boolean updateTexture = false; // true - update a portion of texture + boolean updateTextureLod = false; // true - update texture Lod info + + //System.err.println("Texture/updateNative: " + this + "object= " + objectId + " enable= " + enable); + + bindTexture(cv); + + // if texture is not enabled, don't bother downloading the + // the texture state + + if (!isEnabled(cv)) { + return; + } + + if (cv.useSharedCtx && cv.screen.renderer.sharedCtx != null) { + + if ((resourceCreationMask & cv.screen.renderer.rendererBit) == 0) { + reloadTexture = true; + } else { + if (((resourceUpdatedMask & + cv.screen.renderer.rendererBit) == 0) && + (imageUpdateInfo != null)) { + updateTexture = true; + } + + if ((resourceLodUpdatedMask & + cv.screen.renderer.rendererBit) == 0) { + updateTextureLod = true; + } + } + if (reloadTexture || updateTexture || updateTextureLod) { + cv.makeCtxCurrent(cv.screen.renderer.sharedCtx); + bindTexture(cv); + } + } else { + if ((resourceCreationMask & cv.canvasBit) == 0) { + reloadTexture = true; + } else { + if (((resourceUpdatedMask & cv.canvasBit) == 0) && + (imageUpdateInfo != null)) { + updateTexture = true; + } + + if ((resourceLodUpdatedMask & cv.canvasBit) == 0) { + updateTextureLod = true; + } + } + } + +//System.err.println("......... reloadTexture= " + reloadTexture + +// " updateTexture= " + updateTexture + +// " updateTextureLod= " + updateTextureLod); + +//System.err.println("......... resourceCreationMask= " + resourceCreationMask + +// " resourceUpdatedMask= " + resourceUpdatedMask); + + if (reloadTexture) { + + // reload all levels of texture image + + // update texture parameters such as boundary modes, filtering + + updateTextureFields(cv); + + + // update texture Lod parameters + + updateTextureLOD(cv); + + + // update all texture images + + reloadTexture(cv); + + + if (cv.useSharedCtx) { + cv.makeCtxCurrent(cv.ctx); + synchronized(resourceLock) { + resourceCreationMask |= cv.screen.renderer.rendererBit; + resourceUpdatedMask |= cv.screen.renderer.rendererBit; + resourceLodUpdatedMask |= cv.screen.renderer.rendererBit; + } + } + else { + synchronized(resourceLock) { + resourceCreationMask |= cv.canvasBit; + resourceUpdatedMask |= cv.canvasBit; + resourceLodUpdatedMask |= cv.canvasBit; + } + } + } else if (updateTextureLod || updateTexture) { + + if (updateTextureLod) { + updateTextureLOD(cv); + } + + if (updateTexture) { + + // update texture image + + int resourceBit = 0; + + if (cv.useSharedCtx) { + resourceBit = cv.screen.renderer.rendererBit; + } else { + resourceBit = cv.canvasBit; + } + + // update texture based on the imageComponent update info + + updateTexture(cv, resourceBit); + } + + // set the appropriate bit in the resource update masks showing + // that the resource is up-to-date + + if (cv.useSharedCtx) { + cv.makeCtxCurrent(cv.ctx); + synchronized(resourceLock) { + resourceUpdatedMask |= cv.screen.renderer.rendererBit; + resourceLodUpdatedMask |= cv.screen.renderer.rendererBit; + } + } else { + synchronized(resourceLock) { + resourceUpdatedMask |= cv.canvasBit; + resourceLodUpdatedMask |= cv.canvasBit; + } + } + } + } + + @Override + synchronized void createMirrorObject() { + if (mirror == null) { + if (this instanceof Texture3DRetained) { + Texture3DRetained t3d = (Texture3DRetained)this; + Texture3D tex = new Texture3D(t3d.mipmapMode, + t3d.format, + t3d.width, + t3d.height, + t3d.depth, + t3d.boundaryWidth); + mirror = (Texture3DRetained)tex.retained;; + + } else if (this instanceof TextureCubeMapRetained) { + TextureCubeMap tex = new TextureCubeMap(mipmapMode, + format, width, + boundaryWidth); + mirror = (TextureCubeMapRetained)tex.retained; + + } else { + Texture2D tex = new Texture2D(mipmapMode, + format, + width, + height, + boundaryWidth); + mirror = (Texture2DRetained)tex.retained; + } + + ((TextureRetained)mirror).objectId = -1; + } + initMirrorObject(); + } + + + /** + * Initializes a mirror object, point the mirror object to the retained + * object if the object is not editable + */ + @Override + synchronized void initMirrorObject() { + mirror.source = source; + if (this instanceof Texture3DRetained) { + Texture3DRetained t3d = (Texture3DRetained)this; + + ((Texture3DRetained)mirror).boundaryModeR = t3d.boundaryModeR; + ((Texture3DRetained)mirror).depth = t3d.depth; + } + TextureRetained mirrorTexture = (TextureRetained)mirror; + + mirrorTexture.boundaryModeS = boundaryModeS; + mirrorTexture.boundaryModeT = boundaryModeT; + mirrorTexture.minFilter = minFilter; + mirrorTexture.magFilter = magFilter; + mirrorTexture.boundaryColor.set(boundaryColor); + mirrorTexture.enable = enable; + mirrorTexture.userSpecifiedEnable = enable; + mirrorTexture.enable = enable; + mirrorTexture.numFaces = numFaces; + mirrorTexture.resourceCreationMask = 0x0; + mirrorTexture.resourceUpdatedMask = 0x0; + mirrorTexture.resourceLodUpdatedMask = 0x0; + mirrorTexture.resourceInReloadList = 0x0; + + // LOD information + mirrorTexture.baseLevel = baseLevel; + mirrorTexture.maximumLevel = maximumLevel; + mirrorTexture.minimumLod = minimumLod; + mirrorTexture.maximumLod = maximumLod; + mirrorTexture.lodOffset = lodOffset; + + // sharpen texture LOD function + + mirrorTexture.numSharpenTextureFuncPts = numSharpenTextureFuncPts; + if (sharpenTextureFuncPts == null) { + mirrorTexture.sharpenTextureFuncPts = null; + } else { + if ((mirrorTexture.sharpenTextureFuncPts == null) || + (mirrorTexture.sharpenTextureFuncPts.length != + sharpenTextureFuncPts.length)) { + mirrorTexture.sharpenTextureFuncPts = + new float[sharpenTextureFuncPts.length]; + } + for (int i = 0; i < sharpenTextureFuncPts.length; i++) { + mirrorTexture.sharpenTextureFuncPts[i] = + sharpenTextureFuncPts[i]; + } + } + + // filter4 function + if (filter4FuncPts == null) { + mirrorTexture.filter4FuncPts = null; + } else { + if ((mirrorTexture.filter4FuncPts == null) || + (mirrorTexture.filter4FuncPts.length != + filter4FuncPts.length)) { + mirrorTexture.filter4FuncPts = + new float[filter4FuncPts.length]; + } + for (int i = 0; i < filter4FuncPts.length; i++) { + mirrorTexture.filter4FuncPts[i] = + filter4FuncPts[i]; + } + } + + // Anisotropic Filter + mirrorTexture.anisotropicFilterMode = anisotropicFilterMode; + mirrorTexture.anisotropicFilterDegree = anisotropicFilterDegree; + + mirrorTexture.maxLevels = maxLevels; + if (images != null) { + + for (int j = 0; j < numFaces; j++) { + for (int i = 0; i < maxLevels; i++) { + mirrorTexture.images[j][i] = images[j][i]; + + // add texture to the userList of the images + if (images[j][i] != null) { + images[j][i].addUser(mirrorTexture); + } + } + } + } + } + + boolean useAutoMipMapGeneration(Canvas3D cv) { + if (mipmapMode == Texture.BASE_LEVEL && + (minFilter == Texture.NICEST || + minFilter == Texture.MULTI_LEVEL_POINT || + minFilter == Texture.MULTI_LEVEL_LINEAR) && + ((cv.textureExtendedFeatures & + Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION) != 0)) { + return true; + } + + return false; + } + + /** + * Go through the image update info list + * and remove those that are already done + * by all the resources + */ + void pruneImageUpdateInfo() { + ImageComponentUpdateInfo info; + + //System.err.println("Texture.pruneImageUpdateInfo"); + + for (int k = 0; k < numFaces; k++) { + for (int i = baseLevel; i <= maximumLevel; i++) { + if ((imageUpdatePruneMask[k] & (1<<i)) != 0) { + if (imageUpdateInfo[k][i] != null) { + for (int j = 0; j < imageUpdateInfo[k][i].size(); j++) { + info = (ImageComponentUpdateInfo) + imageUpdateInfo[k][i].get(j); + if (info.updateMask == 0) { + // this update info is done, remove it + // from the update list + imageUpdateInfo[k][i].remove(j); + } + } + } + imageUpdatePruneMask[k] &= ~(1<<i); + } + } + } + } + + /** + * addImageUpdateInfo(int level) is to update a particular level. + * In this case, it supercedes all the subImage update for this level, + * and all those update info can be removed from the update list. + * + * Note: this method is called from mirror only + */ + void addImageUpdateInfo(int level, int face, ImageComponentUpdateInfo arg) { + + ImageComponentUpdateInfo info; + + if (imageUpdateInfo == null) { + imageUpdateInfo = new ArrayList[numFaces][maxLevels]; + } + + if (imageUpdateInfo[face][level] == null) { + imageUpdateInfo[face][level] = new ArrayList(); + } + + info = new ImageComponentUpdateInfo(); + + + if (arg == null) { + // no subimage info, so the entire image is to be updated + info.entireImage = true; + // Fix issue 117 using ogl subimage always +// } else if ((arg.width >= width/2) && (arg.height >= height/2)) { +// +// // if the subimage dimension is close to the complete dimension, +// // use the full update (it's more efficient) +// info.entireImage = true; + } else { + info.entireImage = false; + } + + if (info.entireImage) { + // the entire image update supercedes all the subimage update; + // hence, remove all the existing updates from the list + imageUpdateInfo[face][level].clear(); + + // reset the update prune mask for this level + if (imageUpdatePruneMask != null) { + imageUpdatePruneMask[face] &= ~(1 << level); + } + + } else { + // subimage update, needs to save the subimage info + info.x = arg.x; + info.y = arg.y; + info.z = arg.z; + info.width = arg.width; + info.height = arg.height; + } + + // save the mask which shows the canvases that have created resources + // for this image, aka, these are the resources that need to be + // updated. + info.updateMask = resourceCreationMask; + + // add the image update to the list + imageUpdateInfo[face][level].add(info); + + // check if the update list stills need to be pruned + if (imageUpdatePruneMask != null) { + pruneImageUpdateInfo(); + } + } + + void validate() { + enable = true; + for (int j = 0; j < numFaces && enable; j++) { + for (int i = baseLevel; i <= maximumLevel && enable; i++) { + if (images[j][i] == null) { + enable = false; + } + } + } + } + + /** + * Update the "component" field of the mirror object with the + * given "value" + */ + @Override + synchronized void updateMirrorObject(int component, Object value) { + + TextureRetained mirrorTexture = (TextureRetained)mirror; + + if ((component & ENABLE_CHANGED) != 0) { + mirrorTexture.enable = ((Boolean)value).booleanValue(); + + } else if ((component & IMAGE_CHANGED) != 0) { + + Object [] arg = (Object []) value; + int level = ((Integer)arg[0]).intValue(); + ImageComponent image = (ImageComponent)arg[1]; + int face = ((Integer)arg[2]).intValue(); + + // first remove texture from the userList of the current + // referencing image and + + if (mirrorTexture.images[face][level] != null) { + mirrorTexture.images[face][level].removeUser(mirror); + } + + // assign the new image and add texture to the userList + if (image == null) { + mirrorTexture.images[face][level] = null; + + } else { + mirrorTexture.images[face][level] = + (ImageComponentRetained)image.retained; + mirrorTexture.images[face][level].addUser(mirror); + + } + + // NOTE: the old image has to be removed from the + // renderBins' NodeComponentList and new image has to be + // added to the lists. This will be taken care of + // in the RenderBin itself in response to the + // IMAGE_CHANGED message + + + // mark that texture images need to be updated + mirrorTexture.resourceUpdatedMask = 0; + + // add update info to the update list + mirrorTexture.addImageUpdateInfo(level, face, null); + + } else if ((component & IMAGES_CHANGED) != 0) { + + Object [] arg = (Object []) value; + ImageComponent [] images = (ImageComponent[])arg[0]; + int face = ((Integer)arg[1]).intValue(); + + for (int i = 0; i < images.length; i++) { + + // first remove texture from the userList of the current + // referencing image + if (mirrorTexture.images[face][i] != null) { + mirrorTexture.images[face][i].removeUser(mirror); + } + + // assign the new image and add texture to the userList + if (images[i] == null) { + mirrorTexture.images[face][i] = null; + } else { + mirrorTexture.images[face][i] = + (ImageComponentRetained)images[i].retained; + mirrorTexture.images[face][i].addUser(mirror); + } + } + mirrorTexture.updateResourceCreationMask(); + + // NOTE: the old images have to be removed from the + // renderBins' NodeComponentList and new image have to be + // added to the lists. This will be taken care of + // in the RenderBin itself in response to the + // IMAGES_CHANGED message + + } else if ((component & BASE_LEVEL_CHANGED) != 0) { + int level = ((Integer)value).intValue(); + + if (level < mirrorTexture.baseLevel) { + + // add texture to the userList of those new levels of + // enabling images + + for (int j = 0; j < numFaces; j++) { + for (int i = level; i < mirrorTexture.baseLevel; i++) { + + if (mirrorTexture.images[j][i] == null) { + mirrorTexture.enable = false; + } else { + mirrorTexture.addImageUpdateInfo(i, j, null); + } + } + } + + mirrorTexture.baseLevel = level; + + // mark that texture images need to be updated + mirrorTexture.resourceUpdatedMask = 0; + + + } else { + + mirrorTexture.baseLevel = level; + + if (userSpecifiedEnable && (mirrorTexture.enable == false)) { + + // if texture is to be enabled but is currently + // disabled, it's probably disabled because + // some of the images are missing. Now that + // the baseLevel is modified, validate the + // texture images again + + mirrorTexture.validate(); + } + } + + // mark that texture lod info needs to be updated + mirrorTexture.resourceLodUpdatedMask = 0; + + } else if ((component & MAX_LEVEL_CHANGED) != 0) { + int level = ((Integer)value).intValue(); + + if (level > mirrorTexture.maximumLevel) { + + // add texture to the userList of those new levels of + // enabling images + + for (int j = 0; j < numFaces; j++) { + for (int i = mirrorTexture.maximumLevel; i < level; i++) { + + if (mirrorTexture.images[j][i] == null) { + mirrorTexture.enable = false; + } else { + mirrorTexture.addImageUpdateInfo(i, j, null); + } + } + } + + mirrorTexture.maximumLevel = level; + + // mark that texture images need to be updated + mirrorTexture.resourceUpdatedMask = 0; + + } else { + + mirrorTexture.maximumLevel = level; + + if (userSpecifiedEnable && (mirrorTexture.enable == false)) { + + // if texture is to be enabled but is currently + // disabled, it's probably disabled because + // some of the images are missing. Now that + // the baseLevel is modified, validate the + // texture images again + + mirrorTexture.validate(); + } + } + + // mark that texture lod info needs to be updated + mirrorTexture.resourceLodUpdatedMask = 0; + + } else if ((component & MIN_LOD_CHANGED) != 0) { + mirrorTexture.minimumLod = ((Float)value).floatValue(); + + // mark that texture lod info needs to be updated + mirrorTexture.resourceLodUpdatedMask = 0; + + } else if ((component & MAX_LOD_CHANGED) != 0) { + mirrorTexture.maximumLod = ((Float)value).floatValue(); + + // mark that texture lod info needs to be updated + mirrorTexture.resourceLodUpdatedMask = 0; + + } else if ((component & LOD_OFFSET_CHANGED) != 0) { + if ((mirrorTexture.lodOffset) == null) { + mirrorTexture.lodOffset = + new Point3f((Point3f)value); + } else { + mirrorTexture.lodOffset.set((Point3f)value); + } + + // mark that texture lod info needs to be updated + mirrorTexture.resourceLodUpdatedMask = 0; + + } else if ((component & UPDATE_IMAGE) != 0) { + mirrorTexture.updateResourceCreationMask(); + } + + } + + + // notifies the Texture mirror object that the image data in a referenced + // ImageComponent object is changed. Need to update the texture image + // accordingly. + // Note: this is called from mirror object only + + void notifyImageComponentImageChanged(ImageComponentRetained image, + ImageComponentUpdateInfo value) { + + //System.err.println("Texture.notifyImageComponentImageChanged"); + + + // if this texture is to be reloaded, don't bother to keep + // the update info + + if (resourceCreationMask == 0) { + + if (imageUpdateInfo != null) { + + //remove all the existing updates from the list + + for (int face = 0; face < numFaces; face++) { + for (int level = 0; level < maxLevels; level++) { + if (imageUpdateInfo[face][level] != null) { + imageUpdateInfo[face][level].clear(); + } + } + + // reset the update prune mask for this level + if (imageUpdatePruneMask != null) { + imageUpdatePruneMask[face] = 0; + } + } + } + + return; + } + + + // first find which texture image is being affected + + boolean done; + + for (int j = 0; j < numFaces; j++) { + + done = false; + for (int i = baseLevel; i <= maximumLevel && !done; i++) { + if (images[j][i] == image) { + + // reset the resourceUpdatedMask to tell the + // rendering method to update the resource + resourceUpdatedMask = 0; + + // add update info to the update list + addImageUpdateInfo(i, j, value); + + // set done to true for this face because no two levels + // can reference the same ImageComponent object + done = true; + } + } + } + } + + + // reset the resourceCreationMask + // Note: called from the mirror object only + + void updateResourceCreationMask() { + resourceCreationMask = 0x0; + } + + void incTextureBinRefCount(TextureBin tb) { + + ImageComponentRetained image; + + setTextureBinRefCount(tb, getTextureBinRefCount(tb) + 1); + + // check to see if there is any modifiable images, + // if yes, add those images to nodeComponentList in RenderBin + // so that RenderBin can acquire a lock before rendering + // to prevent updating of image data while rendering + + for (int j = 0; j < numFaces; j++) { + for (int i = 0; i < maxLevels; i++) { + image = images[j][i]; + + // it is possible that image.source == null because + // the mipmap could have been created by the library, and + // hence don't have source and therefore they are + // guaranteed not modifiable + + if (image != null && + (image.isByReference() || + (image.source != null && + image.source.getCapability( + ImageComponent.ALLOW_IMAGE_WRITE)))) { + tb.renderBin.addNodeComponent(image); + } + } + } + } + + void decTextureBinRefCount(TextureBin tb) { + + ImageComponentRetained image; + + setTextureBinRefCount(tb, getTextureBinRefCount(tb) - 1); + + // remove any modifiable images from RenderBin nodeComponentList + + for (int j = 0; j < numFaces; j++) { + for (int i = 0; i < maxLevels; i++) { + image = images[j][i]; + if (image != null && + (image.isByReference() || + (image.source != null && + image.source.getCapability( + ImageComponent.ALLOW_IMAGE_WRITE)))) { + tb.renderBin.removeNodeComponent(image); + } + } + } + } + + + final void sendMessage(int attrMask, Object attr) { + + ArrayList<VirtualUniverse> univList = new ArrayList<VirtualUniverse>(); + ArrayList<ArrayList<GeometryAtom>> gaList = Shape3DRetained.getGeomAtomsList(mirror.users, univList); + + // Send to rendering attribute structure, regardless of + // whether there are users or not (alternate appearance case ..) + J3dMessage createMessage = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDERING_ATTRIBUTES; + createMessage.type = J3dMessage.TEXTURE_CHANGED; + createMessage.universe = null; + createMessage.args[0] = this; + createMessage.args[1] = new Integer(attrMask); + createMessage.args[2] = attr; + createMessage.args[3] = new Integer(changedFrequent); + VirtualUniverse.mc.processMessage(createMessage); + + // System.err.println("univList.size is " + univList.size()); + for(int i=0; i<univList.size(); i++) { + createMessage = new J3dMessage(); + createMessage.threads = J3dThread.UPDATE_RENDER; + createMessage.type = J3dMessage.TEXTURE_CHANGED; + + createMessage.universe = univList.get(i); + createMessage.args[0] = this; + createMessage.args[1]= new Integer(attrMask); + createMessage.args[2] = attr; + + ArrayList<GeometryAtom> gL = gaList.get(i); + GeometryAtom[] gaArr = new GeometryAtom[gL.size()]; + gL.toArray(gaArr); + createMessage.args[3] = gaArr; + + VirtualUniverse.mc.processMessage(createMessage); + } + + } + + @Override + void handleFrequencyChange(int bit) { + switch (bit) { + case Texture.ALLOW_ENABLE_WRITE: + case Texture.ALLOW_IMAGE_WRITE: + case Texture.ALLOW_LOD_RANGE_WRITE: { + setFrequencyChangeMask(bit, bit); + } + default: + break; + } + } + + void setUseAsRaster(boolean useAsRaster) { + this.useAsRaster = useAsRaster; + } + + boolean isUseAsRaster() { + return this.useAsRaster; + } + + // Issue 357 - {get/set}TextureBinRefCount now uses a separate reference + // counter per RenderBin. The absence of the RenderBin key in the hash map + // is used to indicate a value of 0. This makes initialization easier, and + // prevents a small amount of garbage accumulating for inactive RenderBins. + + int getTextureBinRefCount(TextureBin tb) { + Integer i = textureBinRefCount.get(tb.renderBin); + return i == null ? 0 : i.intValue(); + } + + private void setTextureBinRefCount(TextureBin tb, int refCount) { + if (refCount == 0) { + textureBinRefCount.remove(tb.renderBin); + } else { + textureBinRefCount.put(tb.renderBin, new Integer(refCount)); + } + } + +} |