/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.scenegraph; import org.jagatoo.datatypes.Enableable; import org.openmali.types.twodee.Sized2iRO; import org.openmali.vecmath2.Colorf; import org.jagatoo.loaders.textures.AbstractTexture; import org.jagatoo.loaders.textures.AbstractTextureImage; import org.jagatoo.opengl.enums.TextureBoundaryMode; import org.jagatoo.opengl.enums.TextureFilter; import org.jagatoo.opengl.enums.TextureFormat; import org.jagatoo.opengl.enums.TextureType; import org.xith3d.loaders.texture.TextureLoader; import org.xith3d.render.CanvasPeer; import org.xith3d.render.SceneGraphOpenGLReferences; /** * A Texture represents an image to be applied to a Shape3D's Appearance. * The renderer positiones the Texture on the Shape's Geometry according * to its texture coordinates.<br> * <br> * One Texture instance can be reused for for an arbitrary number of Shapes. * * @author David Yazel * @author Marvin Froehlich (aka Qudus) * @author Amos Wenger (aka BlueSky) */ public abstract class Texture extends NodeComponent implements AbstractTexture, Enableable { public static enum MipmapMode { /** * Mipmap mode - Indicates that this texture only has a base-level image. */ BASE_LEVEL( false ), /** * Mipmap mode - Indicates that this texture object has multiple images, one * for each mipmap level. Images for all levels must be set. */ MULTI_LEVEL_MIPMAP( true ); private boolean boolVal; public boolean booleanValue() { return ( boolVal ); } private MipmapMode( boolean boolVal ) { this.boolVal = boolVal; } public static MipmapMode valueOf( boolean boolVal ) { return ( boolVal ? MULTI_LEVEL_MIPMAP : BASE_LEVEL ); } } /* * State ID 0 reserved for null texture, State ID -1 reserved for * non-initialized state (of any shader) */ private static long TEX_STATE_ID_SEQ = 1; private final TextureType type; /** * The desired mipmap mode. */ private TextureFormat format = null; /** * The image data. Must be set with the setImage method. */ private TextureImage[] images = null; /** * The desired boundary mode S */ private TextureBoundaryMode boundaryModeS = TextureBoundaryMode.WRAP; /** * The desired boundary mode T */ private TextureBoundaryMode boundaryModeT = TextureBoundaryMode.WRAP; /** * The desired boundary color. */ private final Colorf boundaryColor = new Colorf( 0f, 0f, 0f, 0f ); /** * The width of the image boundary. */ private int boundaryWidth = 0; /** * texture mapping is enabled or not for this texture. */ private boolean mappingEnabled = true; private static TextureFilter defaultFilter = null; //TextureFilter.NICER; private TextureFilter filter = defaultFilter; private long texStateId; private boolean dirty = true; private String cacheKey = null; private String resourceName = null; private boolean markedAsLocalDataToBeFreed = false; private final SceneGraphOpenGLReferences openGLReferences = new SceneGraphOpenGLReferences( 1 ); public final TextureType getType() { return ( type ); } /** * @return this Texture's format */ public final TextureFormat getFormat() { return ( format ); } /** * Enables disable texture mapping for this texture. */ public void setEnabled( boolean enabled ) { mappingEnabled = enabled; setChanged( true ); } /** * Is texture mapping enabled for this texture. */ public final boolean isEnabled() { return ( mappingEnabled ); } final void setDirty( boolean dirty ) { this.dirty = dirty; } public final boolean isDirty() { return ( dirty ); } /** * This method is called by {@link #setImage(int, TextureImage)}. * It checks the added image's type and throws an exception, * if the type is not accepted. * * @param image */ protected abstract void checkImageType( TextureImage image ); /** * Sets the image data for a specified mipmap level. */ public void setImage( int level, TextureImage image ) { if ( image != null ) { checkImageType( image ); } if ( images == null ) { if ( image == null ) return; images = new TextureImage[ level + 1 ]; images[ level ] = image; } else { if ( images.length - 1 >= level ) { if ( image == null ) { if ( images.length == 1 ) { images = null; } else { TextureImage[] temp = new TextureImage[ images.length - 1 ]; System.arraycopy( images, 0, temp, 0, level ); System.arraycopy( images, level + 1, temp, level, images.length - level - 1 ); images = temp; } } else { images[ level ] = image; } } else { if ( image == null ) return; TextureImage[] temp = new TextureImage[ level + 1 ]; for ( int loop = 0; loop < images.length; loop++ ) temp[ loop ] = images[ loop ]; temp[ level ] = image; images = temp; } } dirty = true; } /** * {@inheritDoc} */ public final void setImage( int level, AbstractTextureImage image ) { setImage( level, (TextureImage)image ); } public final void addImage( AbstractTextureImage image ) { setImage( getImagesCount(), image ); } /** * Gets the image for the specified mipmap level. */ public final TextureImage getImage( int level ) { return ( images[ level ] ); } /** * Get the number of elements in the images array. This doesn't mean that * each one of the elements is non-null. */ public final int getImagesCount() { if ( images == null ) return ( 0 ); return ( images.length ); } /** * Gets the mipmap mode for texture mapping for this texture. */ public final MipmapMode getMipMapMode() { if ( ( images == null ) || ( images.length <= 1 ) ) return ( MipmapMode.BASE_LEVEL ); return ( MipmapMode.MULTI_LEVEL_MIPMAP ); } private boolean sizeChanged = false; public void setSizeChanged() { sizeChanged = true; } public final boolean hasSizeChanged() { return ( sizeChanged ); } final void resetSizeChanged() { sizeChanged = false; } public final Sized2iRO getSize() { if ( getImagesCount() == 0 ) return ( null ); return ( getImage( 0 ).getSize() ); } public final int getWidth() { if ( getImagesCount() == 0 ) return ( -1 ); return ( getImage( 0 ).getWidth() ); } public final int getHeight() { if ( getImagesCount() == 0 ) return ( -1 ); return ( getImage( 0 ).getHeight() ); } public final Sized2iRO getOriginalSize() { if ( getImagesCount() == 0 ) return ( null ); return ( getImage( 0 ).getOriginalSize() ); } public final int getOriginalWidth() { if ( getImagesCount() == 0 ) return ( -1 ); return ( getImage( 0 ).getOriginalWidth() ); } public final int getOriginalHeight() { if ( getImagesCount() == 0 ) return ( -1 ); return ( getImage( 0 ).getOriginalHeight() ); } /** * Sets the boundary mode S. * * @param mode */ public final void setBoundaryModeS( TextureBoundaryMode mode ) { boundaryModeS = mode; setChanged( true ); } /** * Gets the boundary mode S. */ public final TextureBoundaryMode getBoundaryModeS() { return ( boundaryModeS ); } /** * Sets the boundary mode T. * * @param mode */ public final void setBoundaryModeT( TextureBoundaryMode mode ) { boundaryModeT = mode; setChanged( true ); } /** * Gets the boundary mode T. */ public final TextureBoundaryMode getBoundaryModeT() { return ( boundaryModeT ); } /** * Sets the boundary modes S and T. * * @param modeS * @param modeT */ public final void setBoundaryModes( TextureBoundaryMode modeS, TextureBoundaryMode modeT ) { setBoundaryModeS( modeS ); setBoundaryModeT( modeT ); } /** * Sets the boundary color. */ public void setBoundaryColor( Colorf color ) { boundaryColor.set( color ); setChanged( true ); } public final void setBoundaryColor( float r, float g, float b, float a ) { boundaryColor.set( r, g, b, a ); setChanged( true ); } /** * Gets the boundary color. */ public final Colorf getBoundaryColor() { return ( boundaryColor.getReadOnly() ); } public final Colorf getBoundaryColor( Colorf c ) { c.set( boundaryColor ); return ( c ); } public void setBoundaryWidth( int boundaryWidth ) { this.boundaryWidth = boundaryWidth; setChanged( true ); } public final int getBoundaryWidth() { return ( boundaryWidth ); } /** * Sets the default filter for textures. * * @param defaultFilter */ public static void setDefaultFilter( TextureFilter defaultFilter ) { Texture.defaultFilter = defaultFilter; } /** * @return the default filter for textures. */ public static TextureFilter getDefaultFilter() { return ( defaultFilter ); } /** * Sets the texture filter. */ public final void setFilter( TextureFilter filter ) { this.filter = filter; setChanged( true ); } /** * @return the texture filter. */ public final TextureFilter getFilter() { return ( filter ); } /** * @return the state ID used to sort the item */ public final long getStateId() { return ( texStateId ); } /** * {@inheritDoc} */ @Override protected void duplicateNodeComponent( NodeComponent original, boolean forceDuplicate ) { super.duplicateNodeComponent( original, forceDuplicate ); Texture orgTex = (Texture)original; this.format = orgTex.format; this.mappingEnabled = orgTex.mappingEnabled; if ( orgTex.images == null ) { this.images = null; } else { this.images = new TextureImage[ orgTex.images.length ]; for ( int i = 0; i < orgTex.images.length; i++ ) { this.images[ i ] = orgTex.images[ i ]; } } this.boundaryModeS = orgTex.boundaryModeS; this.boundaryModeT = orgTex.boundaryModeT; this.boundaryColor.set( orgTex.boundaryColor ); this.boundaryWidth = orgTex.boundaryWidth; this.filter = orgTex.filter; } /** * This marks this Texture to free its local (RAM) texture-data * after it has been sent to OpenGL.<br> * In most cases this will be usefull, since we don't need the * texture-data locally, since it will reside in the VRAM. */ public void enableAutoFreeLocalData() { this.markedAsLocalDataToBeFreed = true; } public final boolean isMarkedAsLocalDataToBeFreed() { return ( markedAsLocalDataToBeFreed ); } /* public final boolean isLocalDataFreed() { return ( localDataFreed ); } */ public final SceneGraphOpenGLReferences getOpenGLReferences() { return ( openGLReferences ); } /** * {@inheritDoc} */ @Override protected void finalize() { openGLReferences.prepareObjectForDestroy(); } /** * {@inheritDoc} */ @Override public void freeOpenGLResources( CanvasPeer canvasPeer ) { if ( openGLReferences.referenceExists( canvasPeer ) ) openGLReferences.prepareObjectForDestroy( canvasPeer ); } /** * {@inheritDoc} */ public void setCacheKey( String cacheKey ) { this.cacheKey = cacheKey; } /** * {@inheritDoc} */ public final String getCacheKey() { return ( cacheKey ); } /** * Removes the Texture from the Cache, so that it can be deleted in OpenGL, * if it is not used anymore anywhere else. */ public final void removeFromCache() { TextureLoader.getInstance().getCache().remove( this ); } public void setResourceName( String resName ) { this.resourceName = resName; } /** * @return the name, this Texture has been loaded by. */ public final String getResourceName() { return ( resourceName ); } /** * {@inheritDoc} */ @Override public String toString() { return ( this.getClass().getSimpleName() + "{ " + "name = " + ( getName() != null ? "\"" : "" ) + getName() + ( getName() != null ? "\"" : "" ) + //", type = " + type + ", size = " + getWidth() + "x" + getHeight() + ( !getSize().equals( getOriginalSize() ) ? ( ", orgSize = " + getOriginalWidth() + "x" + getOriginalHeight() ) : "" ) + ", format = " + format + ", mipmapMode = " + getMipMapMode() + " (" + getImagesCount() + " image" + ( getImagesCount() != 1 ? "s" : "" ) + ")" + ", boundaryMode = " + boundaryModeS + "/" + boundaryModeT + ", boundaryWidth = " + boundaryWidth + ", boundaryColor = ( " + boundaryColor.getRed() + ", " + boundaryColor.getGreen() + ", " + boundaryColor.getBlue() + ( boundaryColor.hasAlpha() ? ( ", " + boundaryColor.getAlpha() ) : "" ) + " )" + ", boundaryWidth = " + boundaryWidth + ", filter = " + filter + ", enabled = " + mappingEnabled + ", resourceName = " + ( resourceName != null ? "\"" : "" ) + resourceName + ( resourceName != null ? "\"" : "" ) + ", cacheKey = " + ( cacheKey != null ? "\"" : "" ) + cacheKey + ( cacheKey != null ? "\"" : "" ) + " }" ); } /** * Constructs a new Texture object. * * @param type * @param format * @param boundaryWidth */ public Texture( TextureType type, TextureFormat format, int boundaryWidth ) { super( true ); texStateId = TEX_STATE_ID_SEQ++; this.type = type; this.format = format; this.boundaryWidth = boundaryWidth; } /** * Constructs a new Texture object. * * @param type * @param format */ public Texture( TextureType type, TextureFormat format ) { this( type, format, 0 ); } }