Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 textureBinRefCount =
new HashMap();
// 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<= 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 univList = new ArrayList();
ArrayList> 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 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));
}
}
}