diff options
Diffstat (limited to 'engine/src/core/com/jme3/texture')
-rw-r--r-- | engine/src/core/com/jme3/texture/FrameBuffer.java | 460 | ||||
-rw-r--r-- | engine/src/core/com/jme3/texture/Image.java | 790 | ||||
-rw-r--r-- | engine/src/core/com/jme3/texture/Texture.java | 613 | ||||
-rw-r--r-- | engine/src/core/com/jme3/texture/Texture2D.java | 221 | ||||
-rw-r--r-- | engine/src/core/com/jme3/texture/Texture3D.java | 224 | ||||
-rw-r--r-- | engine/src/core/com/jme3/texture/TextureArray.java | 113 | ||||
-rw-r--r-- | engine/src/core/com/jme3/texture/TextureCubeMap.java | 206 |
7 files changed, 2627 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/texture/FrameBuffer.java b/engine/src/core/com/jme3/texture/FrameBuffer.java new file mode 100644 index 0000000..b52927f --- /dev/null +++ b/engine/src/core/com/jme3/texture/FrameBuffer.java @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.texture; + +import com.jme3.renderer.Caps; +import com.jme3.renderer.Renderer; +import com.jme3.texture.Image.Format; +import com.jme3.util.NativeObject; +import java.util.ArrayList; + +/** + * <p> + * <code>FrameBuffer</code>s are rendering surfaces allowing + * off-screen rendering and render-to-texture functionality. + * Instead of the scene rendering to the screen, it is rendered into the + * FrameBuffer, the result can be either a texture or a buffer. + * <p> + * A <code>FrameBuffer</code> supports two methods of rendering, + * using a {@link Texture} or using a buffer. + * When using a texture, the result of the rendering will be rendered + * onto the texture, after which the texture can be placed on an object + * and rendered as if the texture was uploaded from disk. + * When using a buffer, the result is rendered onto + * a buffer located on the GPU, the data of this buffer is not accessible + * to the user. buffers are useful if one + * wishes to retrieve only the color content of the scene, but still desires + * depth testing (which requires a depth buffer). + * Buffers can be copied to other framebuffers + * including the main screen, by using + * {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, com.jme3.texture.FrameBuffer) }. + * The content of a {@link RenderBuffer} can be retrieved by using + * {@link Renderer#readFrameBuffer(com.jme3.texture.FrameBuffer, java.nio.ByteBuffer) }. + * <p> + * <code>FrameBuffer</code>s have several attachment points, there are + * several <em>color</em> attachment points and a single <em>depth</em> + * attachment point. + * The color attachment points support image formats such as + * {@link Format#RGBA8}, allowing rendering the color content of the scene. + * The depth attachment point requires a depth image format. + * + * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) + * + * @author Kirill Vainer + */ +public class FrameBuffer extends NativeObject { + + private int width = 0; + private int height = 0; + private int samples = 1; + private ArrayList<RenderBuffer> colorBufs = new ArrayList<RenderBuffer>(); + private RenderBuffer depthBuf = null; + private int colorBufIndex = 0; + + /** + * <code>RenderBuffer</code> represents either a texture or a + * buffer that will be rendered to. <code>RenderBuffer</code>s + * are attached to an attachment slot on a <code>FrameBuffer</code>. + */ + public class RenderBuffer { + + Texture tex; + Image.Format format; + int id = -1; + int slot = -1; + + /** + * @return The image format of the render buffer. + */ + public Format getFormat() { + return format; + } + + /** + * @return The texture to render to for this <code>RenderBuffer</code> + * or null if content should be rendered into a buffer. + */ + public Texture getTexture(){ + return tex; + } + + /** + * Do not use. + */ + public int getId() { + return id; + } + + /** + * Do not use. + */ + public void setId(int id){ + this.id = id; + } + + /** + * Do not use. + */ + public int getSlot() { + return slot; + } + + public void resetObject(){ + id = -1; + } + + public RenderBuffer createDestructableClone(){ + if (tex != null){ + return null; + }else{ + RenderBuffer destructClone = new RenderBuffer(); + destructClone.id = id; + return destructClone; + } + } + + @Override + public String toString(){ + if (tex != null){ + return "TextureTarget[format=" + format + "]"; + }else{ + return "BufferTarget[format=" + format + "]"; + } + } + } + + /** + * <p> + * Creates a new FrameBuffer with the given width, height, and number + * of samples. If any textures are attached to this FrameBuffer, then + * they must have the same number of samples as given in this constructor. + * <p> + * Note that if the {@link Renderer} does not expose the + * {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown + * if the width and height arguments are not power of two. + * + * @param width The width to use + * @param height The height to use + * @param samples The number of samples to use for a multisampled + * framebuffer, or 1 if the framebuffer should be singlesampled. + * + * @throws IllegalArgumentException If width or height are not positive. + */ + public FrameBuffer(int width, int height, int samples){ + super(FrameBuffer.class); + if (width <= 0 || height <= 0) + throw new IllegalArgumentException("FrameBuffer must have valid size."); + + this.width = width; + this.height = height; + this.samples = samples == 0 ? 1 : samples; + } + + protected FrameBuffer(FrameBuffer src){ + super(FrameBuffer.class, src.id); + /* + for (RenderBuffer renderBuf : src.colorBufs){ + RenderBuffer clone = renderBuf.createDestructableClone(); + if (clone != null) + this.colorBufs.add(clone); + } + + this.depthBuf = src.depthBuf.createDestructableClone(); + */ + } + + /** + * Enables the use of a depth buffer for this <code>FrameBuffer</code>. + * + * @param format The format to use for the depth buffer. + * @throws IllegalArgumentException If <code>format</code> is not a depth format. + */ + public void setDepthBuffer(Image.Format format){ + if (id != -1) + throw new UnsupportedOperationException("FrameBuffer already initialized."); + + if (!format.isDepthFormat()) + throw new IllegalArgumentException("Depth buffer format must be depth."); + + depthBuf = new RenderBuffer(); + depthBuf.slot = -100; // -100 == special slot for DEPTH_BUFFER + depthBuf.format = format; + } + + /** + * Enables the use of a color buffer for this <code>FrameBuffer</code>. + * + * @param format The format to use for the color buffer. + * @throws IllegalArgumentException If <code>format</code> is not a color format. + */ + public void setColorBuffer(Image.Format format){ + if (id != -1) + throw new UnsupportedOperationException("FrameBuffer already initialized."); + + if (format.isDepthFormat()) + throw new IllegalArgumentException("Color buffer format must be color/luminance."); + + RenderBuffer colorBuf = new RenderBuffer(); + colorBuf.slot = 0; + colorBuf.format = format; + + colorBufs.clear(); + colorBufs.add(colorBuf); + } + + private void checkSetTexture(Texture tex, boolean depth){ + Image img = tex.getImage(); + if (img == null) + throw new IllegalArgumentException("Texture not initialized with RTT."); + + if (depth && !img.getFormat().isDepthFormat()) + throw new IllegalArgumentException("Texture image format must be depth."); + else if (!depth && img.getFormat().isDepthFormat()) + throw new IllegalArgumentException("Texture image format must be color/luminance."); + + // check that resolution matches texture resolution + if (width != img.getWidth() || height != img.getHeight()) + throw new IllegalArgumentException("Texture image resolution " + + "must match FB resolution"); + + if (samples != tex.getImage().getMultiSamples()) + throw new IllegalStateException("Texture samples must match framebuffer samples"); + } + + /** + * If enabled, any shaders rendering into this <code>FrameBuffer</code> + * will be able to write several results into the renderbuffers + * by using the <code>gl_FragData</code> array. Every slot in that + * array maps into a color buffer attached to this framebuffer. + * + * @param enabled True to enable MRT (multiple rendering targets). + */ + public void setMultiTarget(boolean enabled){ + if (enabled) colorBufIndex = -1; + else colorBufIndex = 0; + } + + /** + * @return True if MRT (multiple rendering targets) is enabled. + * @see FrameBuffer#setMultiTarget(boolean) + */ + public boolean isMultiTarget(){ + return colorBufIndex == -1; + } + + /** + * If MRT is not enabled ({@link FrameBuffer#setMultiTarget(boolean) } is false) + * then this specifies the color target to which the scene should be rendered. + * <p> + * By default the value is 0. + * + * @param index The color attachment index. + * @throws IllegalArgumentException If index is negative or doesn't map + * to any attachment on this framebuffer. + */ + public void setTargetIndex(int index){ + if (index < 0 || index >= 16) + throw new IllegalArgumentException("Target index must be between 0 and 16"); + + if (colorBufs.size() < index) + throw new IllegalArgumentException("The target at " + index + " is not set!"); + + colorBufIndex = index; + setUpdateNeeded(); + } + + /** + * @return The color target to which the scene should be rendered. + * + * @see FrameBuffer#setTargetIndex(int) + */ + public int getTargetIndex(){ + return colorBufIndex; + } + + /** + * Set the color texture to use for this framebuffer. + * This automatically clears all existing textures added previously + * with {@link FrameBuffer#addColorTexture(com.jme3.texture.Texture2D) } + * and adds this texture as the only target. + * + * @param tex The color texture to set. + */ + public void setColorTexture(Texture2D tex){ + clearColorTargets(); + addColorTexture(tex); + } + + /** + * Clears all color targets that were set or added previously. + */ + public void clearColorTargets(){ + colorBufs.clear(); + } + + /** + * Add a color texture to use for this framebuffer. + * If MRT is enabled, then each subsequently added texture can be + * rendered to through a shader that writes to the array <code>gl_FragData</code>. + * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } + * is rendered to by the shader. + * + * @param tex The texture to add. + */ + public void addColorTexture(Texture2D tex) { + if (id != -1) + throw new UnsupportedOperationException("FrameBuffer already initialized."); + + Image img = tex.getImage(); + checkSetTexture(tex, false); + + RenderBuffer colorBuf = new RenderBuffer(); + colorBuf.slot = colorBufs.size(); + colorBuf.tex = tex; + colorBuf.format = img.getFormat(); + + colorBufs.add(colorBuf); + } + + /** + * Set the depth texture to use for this framebuffer. + * + * @param tex The color texture to set. + */ + public void setDepthTexture(Texture2D tex){ + if (id != -1) + throw new UnsupportedOperationException("FrameBuffer already initialized."); + + Image img = tex.getImage(); + checkSetTexture(tex, true); + + depthBuf = new RenderBuffer(); + depthBuf.slot = -100; // indicates GL_DEPTH_ATTACHMENT + depthBuf.tex = tex; + depthBuf.format = img.getFormat(); + } + + /** + * @return The number of color buffers attached to this texture. + */ + public int getNumColorBuffers(){ + return colorBufs.size(); + } + + /** + * @param index + * @return The color buffer at the given index. + */ + public RenderBuffer getColorBuffer(int index){ + return colorBufs.get(index); + } + + /** + * @return The first color buffer attached to this FrameBuffer, or null + * if no color buffers are attached. + */ + public RenderBuffer getColorBuffer() { + if (colorBufs.isEmpty()) + return null; + + return colorBufs.get(0); + } + + /** + * @return The depth buffer attached to this FrameBuffer, or null + * if no depth buffer is attached + */ + public RenderBuffer getDepthBuffer() { + return depthBuf; + } + + /** + * @return The height in pixels of this framebuffer. + */ + public int getHeight() { + return height; + } + + /** + * @return The width in pixels of this framebuffer. + */ + public int getWidth() { + return width; + } + + /** + * @return The number of samples when using a multisample framebuffer, or + * 1 if this is a singlesampled framebuffer. + */ + public int getSamples() { + return samples; + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + String mrtStr = colorBufIndex >= 0 ? "" + colorBufIndex : "mrt"; + sb.append("FrameBuffer[format=").append(width).append("x").append(height) + .append("x").append(samples).append(", drawBuf=").append(mrtStr).append("]\n"); + if (depthBuf != null) + sb.append("Depth => ").append(depthBuf).append("\n"); + for (RenderBuffer colorBuf : colorBufs){ + sb.append("Color(").append(colorBuf.slot) + .append(") => ").append(colorBuf).append("\n"); + } + return sb.toString(); + } + + @Override + public void resetObject() { + this.id = -1; + + for (int i = 0; i < colorBufs.size(); i++) { + colorBufs.get(i).resetObject(); + } + + if (depthBuf != null) + depthBuf.resetObject(); + + setUpdateNeeded(); + } + + @Override + public void deleteObject(Object rendererObject) { + ((Renderer)rendererObject).deleteFrameBuffer(this); + } + + public NativeObject createDestructableClone(){ + return new FrameBuffer(this); + } +} diff --git a/engine/src/core/com/jme3/texture/Image.java b/engine/src/core/com/jme3/texture/Image.java new file mode 100644 index 0000000..5d23522 --- /dev/null +++ b/engine/src/core/com/jme3/texture/Image.java @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.texture; + +import com.jme3.export.*; +import com.jme3.renderer.Renderer; +import com.jme3.util.NativeObject; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * <code>Image</code> defines a data format for a graphical image. The image + * is defined by a format, a height and width, and the image data. The width and + * height must be greater than 0. The data is contained in a byte buffer, and + * should be packed before creation of the image object. + * + * @author Mark Powell + * @author Joshua Slack + * @version $Id: Image.java 4131 2009-03-19 20:15:28Z blaine.dev $ + */ +public class Image extends NativeObject implements Savable /*, Cloneable*/ { + + public enum Format { + /** + * 8-bit alpha + */ + Alpha8(8), + + /** + * 16-bit alpha + */ + Alpha16(16), + + /** + * 8-bit grayscale/luminance. + */ + Luminance8(8), + + /** + * 16-bit grayscale/luminance. + */ + Luminance16(16), + + /** + * half-precision floating-point grayscale/luminance. + */ + Luminance16F(16,true), + + /** + * single-precision floating-point grayscale/luminance. + */ + Luminance32F(32,true), + + /** + * 8-bit luminance/grayscale and 8-bit alpha. + */ + Luminance8Alpha8(16), + + /** + * 16-bit luminance/grayscale and 16-bit alpha. + */ + Luminance16Alpha16(32), + + /** + * half-precision floating-point grayscale/luminance and alpha. + */ + Luminance16FAlpha16F(32,true), + + Intensity8(8), + Intensity16(16), + + /** + * 8-bit blue, green, and red. + */ + BGR8(24), // BGR and ABGR formats are often used on windows systems + + /** + * 8-bit red, green, and blue. + */ + RGB8(24), + + /** + * 10-bit red, green, and blue. + */ + RGB10(30), + + /** + * 16-bit red, green, and blue. + */ + RGB16(48), + + /** + * 5-bit red, 6-bit green, and 5-bit blue. + */ + RGB565(16), + + /** + * 4-bit alpha, red, green, and blue. Used on Android only. + */ + ARGB4444(16), + + /** + * 5-bit red, green, and blue with 1-bit alpha. + */ + RGB5A1(16), + + /** + * 8-bit red, green, blue, and alpha. + */ + RGBA8(32), + + /** + * 8-bit alpha, blue, green, and red. + */ + ABGR8(32), + + /** + * 16-bit red, green, blue and alpha + */ + RGBA16(64), + + /** + * S3TC compression DXT1. + * Called BC1 in DirectX10. + */ + DXT1(4,false,true, false), + + /** + * S3TC compression DXT1 with 1-bit alpha. + */ + DXT1A(4,false,true, false), + + /** + * S3TC compression DXT3 with 4-bit alpha. + * Called BC2 in DirectX10. + */ + DXT3(8,false,true, false), + + /** + * S3TC compression DXT5 with interpolated 8-bit alpha. + * Called BC3 in DirectX10. + */ + DXT5(8,false,true, false), + + /** + * Luminance-Alpha Texture Compression. + * Called BC5 in DirectX10. + */ + LATC(8, false, true, false), + + /** + * Arbitrary depth format. The precision is chosen by the video + * hardware. + */ + Depth(0,true,false,false), + + /** + * 16-bit depth. + */ + Depth16(16,true,false,false), + + /** + * 24-bit depth. + */ + Depth24(24,true,false,false), + + /** + * 32-bit depth. + */ + Depth32(32,true,false,false), + + /** + * single-precision floating point depth. + */ + Depth32F(32,true,false,true), + + /** + * Texture data is stored as {@link Format#RGB16F} in system memory, + * but will be converted to {@link Format#RGB111110F} when sent + * to the video hardware. + */ + RGB16F_to_RGB111110F(48,true), + + /** + * unsigned floating-point red, green and blue that uses 32 bits. + */ + RGB111110F(32,true), + + /** + * Texture data is stored as {@link Format#RGB16F} in system memory, + * but will be converted to {@link Format#RGB9E5} when sent + * to the video hardware. + */ + RGB16F_to_RGB9E5(48,true), + + /** + * 9-bit red, green and blue with 5-bit exponent. + */ + RGB9E5(32,true), + + /** + * half-precision floating point red, green, and blue. + */ + RGB16F(48,true), + + /** + * half-precision floating point red, green, blue, and alpha. + */ + RGBA16F(64,true), + + /** + * single-precision floating point red, green, and blue. + */ + RGB32F(96,true), + + /** + * single-precision floating point red, green, blue and alpha. + */ + RGBA32F(128,true), + + /** + * Luminance/grayscale texture compression. + * Called BC4 in DirectX10. + */ + LTC(4, false, true, false); + + private int bpp; + private boolean isDepth; + private boolean isCompressed; + private boolean isFloatingPoint; + + private Format(int bpp){ + this.bpp = bpp; + } + + private Format(int bpp, boolean isFP){ + this(bpp); + this.isFloatingPoint = isFP; + } + + private Format(int bpp, boolean isDepth, boolean isCompressed, boolean isFP){ + this(bpp, isFP); + this.isDepth = isDepth; + this.isCompressed = isCompressed; + } + + /** + * @return bits per pixel. + */ + public int getBitsPerPixel(){ + return bpp; + } + + /** + * @return True if this format is a depth format, false otherwise. + */ + public boolean isDepthFormat(){ + return isDepth; + } + + /** + * @return True if this is a compressed image format, false if + * uncompressed. + */ + public boolean isCompressed() { + return isCompressed; + } + + /** + * @return True if this image format is in floating point, + * false if it is an integer format. + */ + public boolean isFloatingPont(){ + return isFloatingPoint; + } + + } + + // image attributes + protected Format format; + protected int width, height, depth; + protected int[] mipMapSizes; + protected ArrayList<ByteBuffer> data; + protected transient Object efficientData; + protected int multiSamples = 1; +// protected int mipOffset = 0; + + @Override + public void resetObject() { + this.id = -1; + setUpdateNeeded(); + } + + @Override + public void deleteObject(Object rendererObject) { + ((Renderer)rendererObject).deleteImage(this); + } + + @Override + public NativeObject createDestructableClone() { + return new Image(id); + } + + /** + * @return A shallow clone of this image. The data is not cloned. + */ + @Override + public Image clone(){ + Image clone = (Image) super.clone(); + clone.mipMapSizes = mipMapSizes != null ? mipMapSizes.clone() : null; + clone.data = data != null ? new ArrayList<ByteBuffer>(data) : null; + clone.setUpdateNeeded(); + return clone; + } + + /** + * Constructor instantiates a new <code>Image</code> object. All values + * are undefined. + */ + public Image() { + super(Image.class); + data = new ArrayList<ByteBuffer>(1); + } + + protected Image(int id){ + super(Image.class, id); + } + + /** + * Constructor instantiates a new <code>Image</code> object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param data + * the image data. + * @param mipMapSizes + * the array of mipmap sizes, or null for no mipmaps. + */ + public Image(Format format, int width, int height, int depth, ArrayList<ByteBuffer> data, + int[] mipMapSizes) { + + this(); + + if (mipMapSizes != null && mipMapSizes.length <= 1) { + mipMapSizes = null; + } + + setFormat(format); + this.width = width; + this.height = height; + this.data = data; + this.depth = depth; + this.mipMapSizes = mipMapSizes; + } + + /** + * Constructor instantiates a new <code>Image</code> object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param data + * the image data. + * @param mipMapSizes + * the array of mipmap sizes, or null for no mipmaps. + */ + public Image(Format format, int width, int height, ByteBuffer data, + int[] mipMapSizes) { + + this(); + + if (mipMapSizes != null && mipMapSizes.length <= 1) { + mipMapSizes = null; + } + + setFormat(format); + this.width = width; + this.height = height; + if (data != null){ + this.data = new ArrayList<ByteBuffer>(1); + this.data.add(data); + } + this.mipMapSizes = mipMapSizes; + } + + /** + * Constructor instantiates a new <code>Image</code> object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param data + * the image data. + */ + public Image(Format format, int width, int height, int depth, ArrayList<ByteBuffer> data) { + this(format, width, height, depth, data, null); + } + + /** + * Constructor instantiates a new <code>Image</code> object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param data + * the image data. + */ + public Image(Format format, int width, int height, ByteBuffer data) { + this(format, width, height, data, null); + } + + /** + * @return The number of samples (for multisampled textures). + * @see Image#setMultiSamples(int) + */ + public int getMultiSamples() { + return multiSamples; + } + + /** + * @param multiSamples Set the number of samples to use for this image, + * setting this to a value higher than 1 turns this image/texture + * into a multisample texture (on OpenGL3.1 and higher). + */ + public void setMultiSamples(int multiSamples) { + if (multiSamples <= 0) + throw new IllegalArgumentException("multiSamples must be > 0"); + + if (getData(0) != null) + throw new IllegalArgumentException("Cannot upload data as multisample texture"); + + if (hasMipmaps()) + throw new IllegalArgumentException("Multisample textures do not support mipmaps"); + + this.multiSamples = multiSamples; + } + + /** + * <code>setData</code> sets the data that makes up the image. This data + * is packed into an array of <code>ByteBuffer</code> objects. + * + * @param data + * the data that contains the image information. + */ + public void setData(ArrayList<ByteBuffer> data) { + this.data = data; + setUpdateNeeded(); + } + + /** + * <code>setData</code> sets the data that makes up the image. This data + * is packed into a single <code>ByteBuffer</code>. + * + * @param data + * the data that contains the image information. + */ + public void setData(ByteBuffer data) { + this.data = new ArrayList<ByteBuffer>(1); + this.data.add(data); + setUpdateNeeded(); + } + + public void addData(ByteBuffer data) { + if (this.data == null) + this.data = new ArrayList<ByteBuffer>(1); + this.data.add(data); + setUpdateNeeded(); + } + + public void setData(int index, ByteBuffer data) { + if (index >= 0) { + while (this.data.size() <= index) { + this.data.add(null); + } + this.data.set(index, data); + setUpdateNeeded(); + } else { + throw new IllegalArgumentException("index must be greater than or equal to 0."); + } + } + + /** + * Set the efficient data representation of this image. + * <p> + * Some system implementations are more efficient at operating + * on data other than ByteBuffers, in that case, this method can be used. + * + * @param efficientData + */ + public void setEfficentData(Object efficientData){ + this.efficientData = efficientData; + setUpdateNeeded(); + } + + /** + * @return The efficient data representation of this image. + * @see Image#setEfficentData(java.lang.Object) + */ + public Object getEfficentData(){ + return efficientData; + } + + /** + * Sets the mipmap sizes stored in this image's data buffer. Mipmaps are + * stored sequentially, and the first mipmap is the main image data. To + * specify no mipmaps, pass null and this will automatically be expanded + * into a single mipmap of the full + * + * @param mipMapSizes + * the mipmap sizes array, or null for a single image map. + */ + public void setMipMapSizes(int[] mipMapSizes) { + if (mipMapSizes != null && mipMapSizes.length <= 1) + mipMapSizes = null; + + this.mipMapSizes = mipMapSizes; + setUpdateNeeded(); + } + + /** + * <code>setHeight</code> sets the height value of the image. It is + * typically a good idea to try to keep this as a multiple of 2. + * + * @param height + * the height of the image. + */ + public void setHeight(int height) { + this.height = height; + setUpdateNeeded(); + } + + /** + * <code>setDepth</code> sets the depth value of the image. It is + * typically a good idea to try to keep this as a multiple of 2. This is + * used for 3d images. + * + * @param depth + * the depth of the image. + */ + public void setDepth(int depth) { + this.depth = depth; + setUpdateNeeded(); + } + + /** + * <code>setWidth</code> sets the width value of the image. It is + * typically a good idea to try to keep this as a multiple of 2. + * + * @param width + * the width of the image. + */ + public void setWidth(int width) { + this.width = width; + setUpdateNeeded(); + } + + /** + * <code>setFormat</code> sets the image format for this image. + * + * @param format + * the image format. + * @throws NullPointerException + * if format is null + * @see Format + */ + public void setFormat(Format format) { + if (format == null) { + throw new NullPointerException("format may not be null."); + } + + this.format = format; + setUpdateNeeded(); + } + + /** + * <code>getFormat</code> returns the image format for this image. + * + * @return the image format. + * @see Format + */ + public Format getFormat() { + return format; + } + + /** + * <code>getWidth</code> returns the width of this image. + * + * @return the width of this image. + */ + public int getWidth() { + return width; + } + + /** + * <code>getHeight</code> returns the height of this image. + * + * @return the height of this image. + */ + public int getHeight() { + return height; + } + + /** + * <code>getDepth</code> returns the depth of this image (for 3d images). + * + * @return the depth of this image. + */ + public int getDepth() { + return depth; + } + + /** + * <code>getData</code> returns the data for this image. If the data is + * undefined, null will be returned. + * + * @return the data for this image. + */ + public List<ByteBuffer> getData() { + return data; + } + + /** + * <code>getData</code> returns the data for this image. If the data is + * undefined, null will be returned. + * + * @return the data for this image. + */ + public ByteBuffer getData(int index) { + if (data.size() > index) + return data.get(index); + else + return null; + } + + /** + * Returns whether the image data contains mipmaps. + * + * @return true if the image data contains mipmaps, false if not. + */ + public boolean hasMipmaps() { + return mipMapSizes != null; + } + + /** + * Returns the mipmap sizes for this image. + * + * @return the mipmap sizes for this image. + */ + public int[] getMipMapSizes() { + return mipMapSizes; + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append("[size=").append(width).append("x").append(height); + + if (depth > 1) + sb.append("x").append(depth); + + sb.append(", format=").append(format.name()); + + if (hasMipmaps()) + sb.append(", mips"); + + if (getId() >= 0) + sb.append(", id=").append(id); + + sb.append("]"); + + return sb.toString(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof Image)) { + return false; + } + Image that = (Image) other; + if (this.getFormat() != that.getFormat()) + return false; + if (this.getWidth() != that.getWidth()) + return false; + if (this.getHeight() != that.getHeight()) + return false; + if (this.getData() != null && !this.getData().equals(that.getData())) + return false; + if (this.getData() == null && that.getData() != null) + return false; + if (this.getMipMapSizes() != null + && !Arrays.equals(this.getMipMapSizes(), that.getMipMapSizes())) + return false; + if (this.getMipMapSizes() == null && that.getMipMapSizes() != null) + return false; + if (this.getMultiSamples() != that.getMultiSamples()) + return false; + + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + (this.format != null ? this.format.hashCode() : 0); + hash = 97 * hash + this.width; + hash = 97 * hash + this.height; + hash = 97 * hash + this.depth; + hash = 97 * hash + Arrays.hashCode(this.mipMapSizes); + hash = 97 * hash + (this.data != null ? this.data.hashCode() : 0); + hash = 97 * hash + this.multiSamples; + return hash; + } + + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(format, "format", Format.RGBA8); + capsule.write(width, "width", 0); + capsule.write(height, "height", 0); + capsule.write(depth, "depth", 0); + capsule.write(mipMapSizes, "mipMapSizes", null); + capsule.write(multiSamples, "multiSamples", 1); + capsule.writeByteBufferArrayList(data, "data", null); + } + + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); + format = capsule.readEnum("format", Format.class, Format.RGBA8); + width = capsule.readInt("width", 0); + height = capsule.readInt("height", 0); + depth = capsule.readInt("depth", 0); + mipMapSizes = capsule.readIntArray("mipMapSizes", null); + multiSamples = capsule.readInt("multiSamples", 1); + data = (ArrayList<ByteBuffer>) capsule.readByteBufferArrayList("data", null); + } + +} diff --git a/engine/src/core/com/jme3/texture/Texture.java b/engine/src/core/com/jme3/texture/Texture.java new file mode 100644 index 0000000..1efedec --- /dev/null +++ b/engine/src/core/com/jme3/texture/Texture.java @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.texture; + +import com.jme3.asset.Asset; +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.asset.TextureKey; +import com.jme3.export.*; +import com.jme3.util.PlaceholderAssets; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * <code>Texture</code> defines a texture object to be used to display an + * image on a piece of geometry. The image to be displayed is defined by the + * <code>Image</code> class. All attributes required for texture mapping are + * contained within this class. This includes mipmapping if desired, + * magnificationFilter options, apply options and correction options. Default + * values are as follows: minificationFilter - NearestNeighborNoMipMaps, + * magnificationFilter - NearestNeighbor, wrap - EdgeClamp on S,T and R, apply - + * Modulate, environment - None. + * + * @see com.jme3.texture.Image + * @author Mark Powell + * @author Joshua Slack + * @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $ + */ +public abstract class Texture implements Asset, Savable, Cloneable { + + public enum Type { + + /** + * Two dimensional texture (default). A rectangle. + */ + TwoDimensional, + + /** + * An array of two dimensional textures. + */ + TwoDimensionalArray, + + /** + * Three dimensional texture. (A cube) + */ + ThreeDimensional, + + /** + * A set of 6 TwoDimensional textures arranged as faces of a cube facing + * inwards. + */ + CubeMap; + } + + public enum MinFilter { + + /** + * Nearest neighbor interpolation is the fastest and crudest filtering + * method - it simply uses the color of the texel closest to the pixel + * center for the pixel color. While fast, this results in aliasing and + * shimmering during minification. (GL equivalent: GL_NEAREST) + */ + NearestNoMipMaps(false), + + /** + * In this method the four nearest texels to the pixel center are + * sampled (at texture level 0), and their colors are combined by + * weighted averages. Though smoother, without mipmaps it suffers the + * same aliasing and shimmering problems as nearest + * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR) + */ + BilinearNoMipMaps(false), + + /** + * Same as NearestNeighborNoMipMaps except that instead of using samples + * from texture level 0, the closest mipmap level is chosen based on + * distance. This reduces the aliasing and shimmering significantly, but + * does not help with blockiness. (GL equivalent: + * GL_NEAREST_MIPMAP_NEAREST) + */ + NearestNearestMipMap(true), + + /** + * Same as BilinearNoMipMaps except that instead of using samples from + * texture level 0, the closest mipmap level is chosen based on + * distance. By using mipmapping we avoid the aliasing and shimmering + * problems of BilinearNoMipMaps. (GL equivalent: + * GL_LINEAR_MIPMAP_NEAREST) + */ + BilinearNearestMipMap(true), + + /** + * Similar to NearestNeighborNoMipMaps except that instead of using + * samples from texture level 0, a sample is chosen from each of the + * closest (by distance) two mipmap levels. A weighted average of these + * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR) + */ + NearestLinearMipMap(true), + + /** + * Trilinear filtering is a remedy to a common artifact seen in + * mipmapped bilinearly filtered images: an abrupt and very noticeable + * change in quality at boundaries where the renderer switches from one + * mipmap level to the next. Trilinear filtering solves this by doing a + * texture lookup and bilinear filtering on the two closest mipmap + * levels (one higher and one lower quality), and then linearly + * interpolating the results. This results in a smooth degradation of + * texture quality as distance from the viewer increases, rather than a + * series of sudden drops. Of course, closer than Level 0 there is only + * one mipmap level available, and the algorithm reverts to bilinear + * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR) + */ + Trilinear(true); + + private boolean usesMipMapLevels; + + private MinFilter(boolean usesMipMapLevels) { + this.usesMipMapLevels = usesMipMapLevels; + } + + public boolean usesMipMapLevels() { + return usesMipMapLevels; + } + } + + public enum MagFilter { + + /** + * Nearest neighbor interpolation is the fastest and crudest filtering + * mode - it simply uses the color of the texel closest to the pixel + * center for the pixel color. While fast, this results in texture + * 'blockiness' during magnification. (GL equivalent: GL_NEAREST) + */ + Nearest, + + /** + * In this mode the four nearest texels to the pixel center are sampled + * (at the closest mipmap level), and their colors are combined by + * weighted average according to distance. This removes the 'blockiness' + * seen during magnification, as there is now a smooth gradient of color + * change from one texel to the next, instead of an abrupt jump as the + * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR) + */ + Bilinear; + + } + + public enum WrapMode { + /** + * Only the fractional portion of the coordinate is considered. + */ + Repeat, + /** + * Only the fractional portion of the coordinate is considered, but if + * the integer portion is odd, we'll use 1 - the fractional portion. + * (Introduced around OpenGL1.4) Falls back on Repeat if not supported. + */ + MirroredRepeat, + /** + * coordinate will be clamped to [0,1] + */ + Clamp, + /** + * mirrors and clamps the texture coordinate, where mirroring and + * clamping a value f computes: + * <code>mirrorClamp(f) = min(1, max(1/(2*N), + * abs(f)))</code> where N + * is the size of the one-, two-, or three-dimensional texture image in + * the direction of wrapping. (Introduced after OpenGL1.4) Falls back on + * Clamp if not supported. + */ + MirrorClamp, + /** + * coordinate will be clamped to the range [-1/(2N), 1 + 1/(2N)] where N + * is the size of the texture in the direction of clamping. Falls back + * on Clamp if not supported. + */ + BorderClamp, + /** + * Wrap mode MIRROR_CLAMP_TO_BORDER_EXT mirrors and clamps to border the + * texture coordinate, where mirroring and clamping to border a value f + * computes: + * <code>mirrorClampToBorder(f) = min(1+1/(2*N), max(1/(2*N), abs(f)))</code> + * where N is the size of the one-, two-, or three-dimensional texture + * image in the direction of wrapping." (Introduced after OpenGL1.4) + * Falls back on BorderClamp if not supported. + */ + MirrorBorderClamp, + /** + * coordinate will be clamped to the range [1/(2N), 1 - 1/(2N)] where N + * is the size of the texture in the direction of clamping. Falls back + * on Clamp if not supported. + */ + EdgeClamp, + /** + * mirrors and clamps to edge the texture coordinate, where mirroring + * and clamping to edge a value f computes: + * <code>mirrorClampToEdge(f) = min(1-1/(2*N), max(1/(2*N), abs(f)))</code> + * where N is the size of the one-, two-, or three-dimensional texture + * image in the direction of wrapping. (Introduced after OpenGL1.4) + * Falls back on EdgeClamp if not supported. + */ + MirrorEdgeClamp; + } + + public enum WrapAxis { + /** + * S wrapping (u or "horizontal" wrap) + */ + S, + /** + * T wrapping (v or "vertical" wrap) + */ + T, + /** + * R wrapping (w or "depth" wrap) + */ + R; + } + + /** + * If this texture is a depth texture (the format is Depth*) then + * this value may be used to compare the texture depth to the R texture + * coordinate. + */ + public enum ShadowCompareMode { + /** + * Shadow comparison mode is disabled. + * Texturing is done normally. + */ + Off, + + /** + * Compares the 3rd texture coordinate R to the value + * in this depth texture. If R <= texture value then result is 1.0, + * otherwise, result is 0.0. If filtering is set to bilinear or trilinear + * the implementation may sample the texture multiple times to provide + * smoother results in the range [0, 1]. + */ + LessOrEqual, + + /** + * Compares the 3rd texture coordinate R to the value + * in this depth texture. If R >= texture value then result is 1.0, + * otherwise, result is 0.0. If filtering is set to bilinear or trilinear + * the implementation may sample the texture multiple times to provide + * smoother results in the range [0, 1]. + */ + GreaterOrEqual + } + + /** + * The name of the texture (if loaded as a resource). + */ + private String name = null; + + /** + * The image stored in the texture + */ + private Image image = null; + + /** + * The texture key allows to reload a texture from a file + * if needed. + */ + private TextureKey key = null; + + private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps; + private MagFilter magnificationFilter = MagFilter.Bilinear; + private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off; + private int anisotropicFilter; + + /** + * @return + */ + @Override + public Texture clone(){ + try { + return (Texture) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + /** + * Constructor instantiates a new <code>Texture</code> object with default + * attributes. + */ + public Texture() { + } + + /** + * @return the MinificationFilterMode of this texture. + */ + public MinFilter getMinFilter() { + return minificationFilter; + } + + /** + * @param minificationFilter + * the new MinificationFilterMode for this texture. + * @throws IllegalArgumentException + * if minificationFilter is null + */ + public void setMinFilter(MinFilter minificationFilter) { + if (minificationFilter == null) { + throw new IllegalArgumentException( + "minificationFilter can not be null."); + } + this.minificationFilter = minificationFilter; + } + + /** + * @return the MagnificationFilterMode of this texture. + */ + public MagFilter getMagFilter() { + return magnificationFilter; + } + + /** + * @param magnificationFilter + * the new MagnificationFilter for this texture. + * @throws IllegalArgumentException + * if magnificationFilter is null + */ + public void setMagFilter(MagFilter magnificationFilter) { + if (magnificationFilter == null) { + throw new IllegalArgumentException( + "magnificationFilter can not be null."); + } + this.magnificationFilter = magnificationFilter; + } + + /** + * @return The ShadowCompareMode of this texture. + * @see ShadowCompareMode + */ + public ShadowCompareMode getShadowCompareMode(){ + return shadowCompareMode; + } + + /** + * @param compareMode + * the new ShadowCompareMode for this texture. + * @throws IllegalArgumentException + * if compareMode is null + * @see ShadowCompareMode + */ + public void setShadowCompareMode(ShadowCompareMode compareMode){ + if (compareMode == null){ + throw new IllegalArgumentException( + "compareMode can not be null."); + } + this.shadowCompareMode = compareMode; + } + + /** + * <code>setImage</code> sets the image object that defines the texture. + * + * @param image + * the image that defines the texture. + */ + public void setImage(Image image) { + this.image = image; + } + + /** + * @param key The texture key that was used to load this texture + */ + public void setKey(AssetKey key){ + this.key = (TextureKey) key; + } + + public AssetKey getKey(){ + return this.key; + } + + /** + * <code>getImage</code> returns the image data that makes up this + * texture. If no image data has been set, this will return null. + * + * @return the image data that makes up the texture. + */ + public Image getImage() { + return image; + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for a + * particular axis. + * + * @param axis + * the texture axis to define a wrapmode on. + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if axis or mode are null or invalid for this type of texture + */ + public abstract void setWrap(WrapAxis axis, WrapMode mode); + + /** + * <code>setWrap</code> sets the wrap mode of this texture for all axis. + * + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if mode is null or invalid for this type of texture + */ + public abstract void setWrap(WrapMode mode); + + /** + * <code>getWrap</code> returns the wrap mode for a given coordinate axis + * on this texture. + * + * @param axis + * the axis to return for + * @return the wrap mode of the texture. + * @throws IllegalArgumentException + * if axis is null or invalid for this type of texture + */ + public abstract WrapMode getWrap(WrapAxis axis); + + public abstract Type getType(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * @return the anisotropic filtering level for this texture. Default value + * is 1 (no anisotrophy), 2 means x2, 4 is x4, etc. + */ + public int getAnisotropicFilter() { + return anisotropicFilter; + } + + /** + * @param level + * the anisotropic filtering level for this texture. + */ + public void setAnisotropicFilter(int level) { + if (level < 1) + anisotropicFilter = 1; + else + anisotropicFilter = level; + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append("[name=").append(name); + if (image != null) + sb.append(", image=").append(image.toString()); + + sb.append("]"); + + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Texture other = (Texture) obj; + if (this.image != other.image && (this.image == null || !this.image.equals(other.image))) { + return false; + } + if (this.minificationFilter != other.minificationFilter) { + return false; + } + if (this.magnificationFilter != other.magnificationFilter) { + return false; + } + if (this.shadowCompareMode != other.shadowCompareMode) { + return false; + } + if (this.anisotropicFilter != other.anisotropicFilter) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 67 * hash + (this.image != null ? this.image.hashCode() : 0); + hash = 67 * hash + (this.minificationFilter != null ? this.minificationFilter.hashCode() : 0); + hash = 67 * hash + (this.magnificationFilter != null ? this.magnificationFilter.hashCode() : 0); + hash = 67 * hash + (this.shadowCompareMode != null ? this.shadowCompareMode.hashCode() : 0); + hash = 67 * hash + this.anisotropicFilter; + return hash; + } + + + +// public abstract Texture createSimpleClone(); + + + /** Retrieve a basic clone of this Texture (ie, clone everything but the + * image data, which is shared) + * + * @return Texture + */ + public Texture createSimpleClone(Texture rVal) { + rVal.setMinFilter(minificationFilter); + rVal.setMagFilter(magnificationFilter); + rVal.setShadowCompareMode(shadowCompareMode); +// rVal.setHasBorder(hasBorder); + rVal.setAnisotropicFilter(anisotropicFilter); + rVal.setImage(image); // NOT CLONED. +// rVal.memReq = memReq; + rVal.setKey(key); + rVal.setName(name); +// rVal.setBlendColor(blendColor != null ? blendColor.clone() : null); +// if (getTextureKey() != null) { +// rVal.setTextureKey(getTextureKey()); +// } + return rVal; + } + + public abstract Texture createSimpleClone(); + + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(name, "name", null); + + if (key == null){ + // no texture key is set, try to save image instead then + capsule.write(image, "image", null); + }else{ + capsule.write(key, "key", null); + } + + capsule.write(anisotropicFilter, "anisotropicFilter", 1); + capsule.write(minificationFilter, "minificationFilter", + MinFilter.BilinearNoMipMaps); + capsule.write(magnificationFilter, "magnificationFilter", + MagFilter.Bilinear); + } + + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); + name = capsule.readString("name", null); + key = (TextureKey) capsule.readSavable("key", null); + + // load texture from key, if available + if (key != null) { + // key is available, so try the texture from there. + try { + Texture loadedTex = e.getAssetManager().loadTexture(key); + image = loadedTex.getImage(); + } catch (AssetNotFoundException ex){ + Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot locate texture {0}", key); + image = PlaceholderAssets.getPlaceholderImage(); + } + }else{ + // no key is set on the texture. Attempt to load an embedded image + image = (Image) capsule.readSavable("image", null); + if (image == null){ + // TODO: what to print out here? the texture has no key or data, there's no useful information .. + // assume texture.name is set even though the key is null + Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot load embedded image {0}", toString() ); + } + } + + anisotropicFilter = capsule.readInt("anisotropicFilter", 1); + minificationFilter = capsule.readEnum("minificationFilter", + MinFilter.class, + MinFilter.BilinearNoMipMaps); + magnificationFilter = capsule.readEnum("magnificationFilter", + MagFilter.class, MagFilter.Bilinear); + } +}
\ No newline at end of file diff --git a/engine/src/core/com/jme3/texture/Texture2D.java b/engine/src/core/com/jme3/texture/Texture2D.java new file mode 100644 index 0000000..9598413 --- /dev/null +++ b/engine/src/core/com/jme3/texture/Texture2D.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.texture; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * @author Joshua Slack + */ +public class Texture2D extends Texture { + + private WrapMode wrapS = WrapMode.EdgeClamp; + private WrapMode wrapT = WrapMode.EdgeClamp; + + /** + * Creates a new two-dimensional texture with default attributes. + */ + public Texture2D(){ + super(); + } + + /** + * Creates a new two-dimensional texture using the given image. + * @param img The image to use. + */ + public Texture2D(Image img){ + super(); + setImage(img); + if (img.getFormat().isDepthFormat()){ + setMagFilter(MagFilter.Nearest); + setMinFilter(MinFilter.NearestNoMipMaps); + } + } + + /** + * Creates a new two-dimensional texture for the purpose of offscreen + * rendering. + * + * @see com.jme3.texture.FrameBuffer + * + * @param width + * @param height + * @param format + */ + public Texture2D(int width, int height, Image.Format format){ + this(new Image(format, width, height, null)); + } + + /** + * Creates a new two-dimensional texture for the purpose of offscreen + * rendering. + * + * @see com.jme3.texture.FrameBuffer + * + * @param width + * @param height + * @param format + * @param numSamples + */ + public Texture2D(int width, int height, int numSamples, Image.Format format){ + this(new Image(format, width, height, null)); + getImage().setMultiSamples(numSamples); + } + + @Override + public Texture createSimpleClone() { + Texture2D clone = new Texture2D(); + createSimpleClone(clone); + return clone; + } + + @Override + public Texture createSimpleClone(Texture rVal) { + rVal.setWrap(WrapAxis.S, wrapS); + rVal.setWrap(WrapAxis.T, wrapT); + return super.createSimpleClone(rVal); + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for a + * particular axis. + * + * @param axis + * the texture axis to define a wrapmode on. + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if axis or mode are null + */ + public void setWrap(WrapAxis axis, WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } else if (axis == null) { + throw new IllegalArgumentException("axis can not be null."); + } + switch (axis) { + case S: + this.wrapS = mode; + break; + case T: + this.wrapT = mode; + break; + default: + throw new IllegalArgumentException("Not applicable for 2D textures"); + } + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for all axis. + * + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if mode is null + */ + public void setWrap(WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } + this.wrapS = mode; + this.wrapT = mode; + } + + /** + * <code>getWrap</code> returns the wrap mode for a given coordinate axis + * on this texture. + * + * @param axis + * the axis to return for + * @return the wrap mode of the texture. + * @throws IllegalArgumentException + * if axis is null + */ + public WrapMode getWrap(WrapAxis axis) { + switch (axis) { + case S: + return wrapS; + case T: + return wrapT; + default: + throw new IllegalArgumentException("invalid WrapAxis: " + axis); + } + } + + @Override + public Type getType() { + return Type.TwoDimensional; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Texture2D)) { + return false; + } + Texture2D that = (Texture2D) other; + if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S)) + return false; + if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T)) + return false; + return super.equals(other); + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 79 * hash + (this.wrapS != null ? this.wrapS.hashCode() : 0); + hash = 79 * hash + (this.wrapT != null ? this.wrapT.hashCode() : 0); + return hash; + } + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp); + capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule capsule = e.getCapsule(this); + wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp); + wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp); + } + +} diff --git a/engine/src/core/com/jme3/texture/Texture3D.java b/engine/src/core/com/jme3/texture/Texture3D.java new file mode 100644 index 0000000..bded644 --- /dev/null +++ b/engine/src/core/com/jme3/texture/Texture3D.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.texture; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * @author Maarten Steur + */ +public class Texture3D extends Texture { + + private WrapMode wrapS = WrapMode.EdgeClamp; + private WrapMode wrapT = WrapMode.EdgeClamp; + private WrapMode wrapR = WrapMode.EdgeClamp; + + /** + * Creates a new two-dimensional texture with default attributes. + */ + public Texture3D() { + super(); + } + + /** + * Creates a new three-dimensional texture using the given image. + * @param img The image to use. + */ + public Texture3D(Image img) { + super(); + setImage(img); + if (img.getFormat().isDepthFormat()) { + setMagFilter(MagFilter.Nearest); + setMinFilter(MinFilter.NearestNoMipMaps); + } + } + + /** + * Creates a new three-dimensional texture for the purpose of offscreen + * rendering. + * + * @see com.jme3.texture.FrameBuffer + * + * @param width + * @param height + * @param depth + * @param format + */ + public Texture3D(int width, int height, int depth, Image.Format format) { + this(new Image(format, width, height, depth, null)); + } + + /** + * Creates a new three-dimensional texture for the purpose of offscreen + * rendering. + * + * @see com.jme3.texture.FrameBuffer + * + * @param width + * @param height + * @param format + * @param numSamples + */ + public Texture3D(int width, int height, int depth, int numSamples, Image.Format format) { + this(new Image(format, width, height, depth, null)); + getImage().setMultiSamples(numSamples); + } + + @Override + public Texture createSimpleClone() { + Texture3D clone = new Texture3D(); + createSimpleClone(clone); + return clone; + } + + @Override + public Texture createSimpleClone(Texture rVal) { + rVal.setWrap(WrapAxis.S, wrapS); + rVal.setWrap(WrapAxis.T, wrapT); + rVal.setWrap(WrapAxis.R, wrapR); + return super.createSimpleClone(rVal); + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for a + * particular axis. + * + * @param axis + * the texture axis to define a wrapmode on. + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if axis or mode are null + */ + public void setWrap(WrapAxis axis, WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } else if (axis == null) { + throw new IllegalArgumentException("axis can not be null."); + } + switch (axis) { + case S: + this.wrapS = mode; + break; + case T: + this.wrapT = mode; + break; + case R: + this.wrapR = mode; + break; + } + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for all axis. + * + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if mode is null + */ + public void setWrap(WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } + this.wrapS = mode; + this.wrapT = mode; + this.wrapR = mode; + } + + /** + * <code>getWrap</code> returns the wrap mode for a given coordinate axis + * on this texture. + * + * @param axis + * the axis to return for + * @return the wrap mode of the texture. + * @throws IllegalArgumentException + * if axis is null + */ + public WrapMode getWrap(WrapAxis axis) { + switch (axis) { + case S: + return wrapS; + case T: + return wrapT; + case R: + return wrapR; + } + throw new IllegalArgumentException("invalid WrapAxis: " + axis); + } + + @Override + public Type getType() { + return Type.ThreeDimensional; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Texture3D)) { + return false; + } + Texture3D that = (Texture3D) other; + if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S)) { + return false; + } + if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T)) { + return false; + } + if (this.getWrap(WrapAxis.R) != that.getWrap(WrapAxis.R)) { + return false; + } + return super.equals(other); + } + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp); + capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp); + capsule.write(wrapR, "wrapR", WrapMode.EdgeClamp); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule capsule = e.getCapsule(this); + wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp); + wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp); + wrapR = capsule.readEnum("wrapR", WrapMode.class, WrapMode.EdgeClamp); + } +}
\ No newline at end of file diff --git a/engine/src/core/com/jme3/texture/TextureArray.java b/engine/src/core/com/jme3/texture/TextureArray.java new file mode 100644 index 0000000..e39d95f --- /dev/null +++ b/engine/src/core/com/jme3/texture/TextureArray.java @@ -0,0 +1,113 @@ +package com.jme3.texture; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements a Texture array + * warning, this feature is only supported on opengl 3.0 version. + * To check if a hardware supports TextureArray check : + * renderManager.getRenderer().getCaps().contains(Caps.TextureArray) + * @author phate666 + */ +public class TextureArray extends Texture { + + private WrapMode wrapS = WrapMode.EdgeClamp; + private WrapMode wrapT = WrapMode.EdgeClamp; + + /** + * Construct a TextureArray + * warning, this feature is only supported on opengl 3.0 version. + * To check if a hardware supports TextureArray check : + * renderManager.getRenderer().getCaps().contains(Caps.TextureArray) + */ + public TextureArray() { + super(); + } + + /** + * Construct a TextureArray from the given list of images + * warning, this feature is only supported on opengl 3.0 version. + * To check if a hardware supports TextureArray check : + * renderManager.getRenderer().getCaps().contains(Caps.TextureArray) + * @param images + */ + public TextureArray(List<Image> images) { + super(); + int width = images.get(0).getWidth(); + int height = images.get(0).getHeight(); + Image arrayImage = new Image(images.get(0).getFormat(), width, height, + null); + + for (Image img : images) { + if (img.getHeight() != height || img.getWidth() != width) { + Logger.getLogger(TextureArray.class.getName()).log( + Level.WARNING, + "all images must have the same width/height"); + continue; + } + arrayImage.addData(img.getData(0)); + } + setImage(arrayImage); + } + + @Override + public Texture createSimpleClone() { + TextureArray clone = new TextureArray(); + createSimpleClone(clone); + return clone; + } + + @Override + public Texture createSimpleClone(Texture rVal) { + rVal.setWrap(WrapAxis.S, wrapS); + rVal.setWrap(WrapAxis.T, wrapT); + return super.createSimpleClone(rVal); + } + + @Override + public Type getType() { + return Type.TwoDimensionalArray; + } + + @Override + public WrapMode getWrap(WrapAxis axis) { + switch (axis) { + case S: + return wrapS; + case T: + return wrapT; + default: + throw new IllegalArgumentException("invalid WrapAxis: " + axis); + } + } + + @Override + public void setWrap(WrapAxis axis, WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } else if (axis == null) { + throw new IllegalArgumentException("axis can not be null."); + } + switch (axis) { + case S: + this.wrapS = mode; + break; + case T: + this.wrapT = mode; + break; + default: + throw new IllegalArgumentException("Not applicable for 2D textures"); + } + } + + @Override + public void setWrap(WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } + this.wrapS = mode; + this.wrapT = mode; + } +}
\ No newline at end of file diff --git a/engine/src/core/com/jme3/texture/TextureCubeMap.java b/engine/src/core/com/jme3/texture/TextureCubeMap.java new file mode 100644 index 0000000..9290d8a --- /dev/null +++ b/engine/src/core/com/jme3/texture/TextureCubeMap.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.texture; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * Describes a cubemap texture. + * The image specified by setImage must contain 6 data units, + * each data contains a 2D image representing a cube's face. + * The slices are specified in this order:<br/> + * <br/> + * 0 => Positive X (+x)<br/> + * 1 => Negative X (-x)<br/> + * 2 => Positive Y (+y)<br/> + * 3 => Negative Y (-y)<br/> + * 4 => Positive Z (+z)<br/> + * 5 => Negative Z (-z)<br/> + * + * @author Joshua Slack + */ +public class TextureCubeMap extends Texture { + + private WrapMode wrapS = WrapMode.EdgeClamp; + private WrapMode wrapT = WrapMode.EdgeClamp; + private WrapMode wrapR = WrapMode.EdgeClamp; + + /** + * Face of the Cubemap as described by its directional offset from the + * origin. + */ +// public enum Face { +// PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ; +// } + + public TextureCubeMap(){ + super(); + } + + public TextureCubeMap(Image img){ + super(); + setImage(img); + } + + public Texture createSimpleClone() { + return createSimpleClone(new TextureCubeMap()); + } + + @Override + public Texture createSimpleClone(Texture rVal) { + rVal.setWrap(WrapAxis.S, wrapS); + rVal.setWrap(WrapAxis.T, wrapT); + rVal.setWrap(WrapAxis.R, wrapR); + return super.createSimpleClone(rVal); + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for a + * particular axis. + * + * @param axis + * the texture axis to define a wrapmode on. + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if axis or mode are null + */ + public void setWrap(WrapAxis axis, WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } else if (axis == null) { + throw new IllegalArgumentException("axis can not be null."); + } + switch (axis) { + case S: + this.wrapS = mode; + break; + case T: + this.wrapT = mode; + break; + case R: + this.wrapR = mode; + break; + } + } + + /** + * <code>setWrap</code> sets the wrap mode of this texture for all axis. + * + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if mode is null + */ + public void setWrap(WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } + this.wrapS = mode; + this.wrapT = mode; + this.wrapR = mode; + } + + /** + * <code>getWrap</code> returns the wrap mode for a given coordinate axis + * on this texture. + * + * @param axis + * the axis to return for + * @return the wrap mode of the texture. + * @throws IllegalArgumentException + * if axis is null + */ + public WrapMode getWrap(WrapAxis axis) { + switch (axis) { + case S: + return wrapS; + case T: + return wrapT; + case R: + return wrapR; + } + throw new IllegalArgumentException("invalid WrapAxis: " + axis); + } + + @Override + public Type getType() { + return Type.CubeMap; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TextureCubeMap)) { + return false; + } + TextureCubeMap that = (TextureCubeMap) other; + if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S)) + return false; + if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T)) + return false; + if (this.getWrap(WrapAxis.R) != that.getWrap(WrapAxis.R)) + return false; + return super.equals(other); + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 53 * hash + (this.wrapS != null ? this.wrapS.hashCode() : 0); + hash = 53 * hash + (this.wrapT != null ? this.wrapT.hashCode() : 0); + hash = 53 * hash + (this.wrapR != null ? this.wrapR.hashCode() : 0); + return hash; + } + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp); + capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp); + capsule.write(wrapR, "wrapR", WrapMode.EdgeClamp); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule capsule = e.getCapsule(this); + wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp); + wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp); + wrapR = capsule.readEnum("wrapR", WrapMode.class, WrapMode.EdgeClamp); + } +} |