aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/TextureRetained.java
diff options
context:
space:
mode:
authorHarvey Harrison <[email protected]>2015-04-19 21:02:06 -0700
committerHarvey Harrison <[email protected]>2015-04-19 21:02:06 -0700
commit7a2e20caac9db6f789a7b3fab344b9758af45335 (patch)
treeb5236ff2570178de356eab569225108948eb4d30 /src/javax/media/j3d/TextureRetained.java
parentf76ce302c4bb2a9f03bbee571ec5d05c29633023 (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.java2515
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));
+ }
+ }
+
+}