• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.jme3.texture;
34 
35 import com.jme3.asset.Asset;
36 import com.jme3.asset.AssetKey;
37 import com.jme3.asset.AssetNotFoundException;
38 import com.jme3.asset.TextureKey;
39 import com.jme3.export.*;
40 import com.jme3.util.PlaceholderAssets;
41 import java.io.IOException;
42 import java.util.logging.Level;
43 import java.util.logging.Logger;
44 
45 /**
46  * <code>Texture</code> defines a texture object to be used to display an
47  * image on a piece of geometry. The image to be displayed is defined by the
48  * <code>Image</code> class. All attributes required for texture mapping are
49  * contained within this class. This includes mipmapping if desired,
50  * magnificationFilter options, apply options and correction options. Default
51  * values are as follows: minificationFilter - NearestNeighborNoMipMaps,
52  * magnificationFilter - NearestNeighbor, wrap - EdgeClamp on S,T and R, apply -
53  * Modulate, environment - None.
54  *
55  * @see com.jme3.texture.Image
56  * @author Mark Powell
57  * @author Joshua Slack
58  * @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $
59  */
60 public abstract class Texture implements Asset, Savable, Cloneable {
61 
62     public enum Type {
63 
64         /**
65          * Two dimensional texture (default). A rectangle.
66          */
67         TwoDimensional,
68 
69         /**
70          * An array of two dimensional textures.
71          */
72         TwoDimensionalArray,
73 
74         /**
75          * Three dimensional texture. (A cube)
76          */
77         ThreeDimensional,
78 
79         /**
80          * A set of 6 TwoDimensional textures arranged as faces of a cube facing
81          * inwards.
82          */
83         CubeMap;
84     }
85 
86     public enum MinFilter {
87 
88         /**
89          * Nearest neighbor interpolation is the fastest and crudest filtering
90          * method - it simply uses the color of the texel closest to the pixel
91          * center for the pixel color. While fast, this results in aliasing and
92          * shimmering during minification. (GL equivalent: GL_NEAREST)
93          */
94         NearestNoMipMaps(false),
95 
96         /**
97          * In this method the four nearest texels to the pixel center are
98          * sampled (at texture level 0), and their colors are combined by
99          * weighted averages. Though smoother, without mipmaps it suffers the
100          * same aliasing and shimmering problems as nearest
101          * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR)
102          */
103         BilinearNoMipMaps(false),
104 
105         /**
106          * Same as NearestNeighborNoMipMaps except that instead of using samples
107          * from texture level 0, the closest mipmap level is chosen based on
108          * distance. This reduces the aliasing and shimmering significantly, but
109          * does not help with blockiness. (GL equivalent:
110          * GL_NEAREST_MIPMAP_NEAREST)
111          */
112         NearestNearestMipMap(true),
113 
114         /**
115          * Same as BilinearNoMipMaps except that instead of using samples from
116          * texture level 0, the closest mipmap level is chosen based on
117          * distance. By using mipmapping we avoid the aliasing and shimmering
118          * problems of BilinearNoMipMaps. (GL equivalent:
119          * GL_LINEAR_MIPMAP_NEAREST)
120          */
121         BilinearNearestMipMap(true),
122 
123         /**
124          * Similar to NearestNeighborNoMipMaps except that instead of using
125          * samples from texture level 0, a sample is chosen from each of the
126          * closest (by distance) two mipmap levels. A weighted average of these
127          * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR)
128          */
129         NearestLinearMipMap(true),
130 
131         /**
132          * Trilinear filtering is a remedy to a common artifact seen in
133          * mipmapped bilinearly filtered images: an abrupt and very noticeable
134          * change in quality at boundaries where the renderer switches from one
135          * mipmap level to the next. Trilinear filtering solves this by doing a
136          * texture lookup and bilinear filtering on the two closest mipmap
137          * levels (one higher and one lower quality), and then linearly
138          * interpolating the results. This results in a smooth degradation of
139          * texture quality as distance from the viewer increases, rather than a
140          * series of sudden drops. Of course, closer than Level 0 there is only
141          * one mipmap level available, and the algorithm reverts to bilinear
142          * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR)
143          */
144         Trilinear(true);
145 
146         private boolean usesMipMapLevels;
147 
MinFilter(boolean usesMipMapLevels)148         private MinFilter(boolean usesMipMapLevels) {
149             this.usesMipMapLevels = usesMipMapLevels;
150         }
151 
usesMipMapLevels()152         public boolean usesMipMapLevels() {
153             return usesMipMapLevels;
154         }
155     }
156 
157     public enum MagFilter {
158 
159         /**
160          * Nearest neighbor interpolation is the fastest and crudest filtering
161          * mode - it simply uses the color of the texel closest to the pixel
162          * center for the pixel color. While fast, this results in texture
163          * 'blockiness' during magnification. (GL equivalent: GL_NEAREST)
164          */
165         Nearest,
166 
167         /**
168          * In this mode the four nearest texels to the pixel center are sampled
169          * (at the closest mipmap level), and their colors are combined by
170          * weighted average according to distance. This removes the 'blockiness'
171          * seen during magnification, as there is now a smooth gradient of color
172          * change from one texel to the next, instead of an abrupt jump as the
173          * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR)
174          */
175         Bilinear;
176 
177     }
178 
179     public enum WrapMode {
180         /**
181          * Only the fractional portion of the coordinate is considered.
182          */
183         Repeat,
184         /**
185          * Only the fractional portion of the coordinate is considered, but if
186          * the integer portion is odd, we'll use 1 - the fractional portion.
187          * (Introduced around OpenGL1.4) Falls back on Repeat if not supported.
188          */
189         MirroredRepeat,
190         /**
191          * coordinate will be clamped to [0,1]
192          */
193         Clamp,
194         /**
195          * mirrors and clamps the texture coordinate, where mirroring and
196          * clamping a value f computes:
197          * <code>mirrorClamp(f) = min(1, max(1/(2*N),
198          * abs(f)))</code> where N
199          * is the size of the one-, two-, or three-dimensional texture image in
200          * the direction of wrapping. (Introduced after OpenGL1.4) Falls back on
201          * Clamp if not supported.
202          */
203         MirrorClamp,
204         /**
205          * coordinate will be clamped to the range [-1/(2N), 1 + 1/(2N)] where N
206          * is the size of the texture in the direction of clamping. Falls back
207          * on Clamp if not supported.
208          */
209         BorderClamp,
210         /**
211          * Wrap mode MIRROR_CLAMP_TO_BORDER_EXT mirrors and clamps to border the
212          * texture coordinate, where mirroring and clamping to border a value f
213          * computes:
214          * <code>mirrorClampToBorder(f) = min(1+1/(2*N), max(1/(2*N), abs(f)))</code>
215          * where N is the size of the one-, two-, or three-dimensional texture
216          * image in the direction of wrapping." (Introduced after OpenGL1.4)
217          * Falls back on BorderClamp if not supported.
218          */
219         MirrorBorderClamp,
220         /**
221          * coordinate will be clamped to the range [1/(2N), 1 - 1/(2N)] where N
222          * is the size of the texture in the direction of clamping. Falls back
223          * on Clamp if not supported.
224          */
225         EdgeClamp,
226         /**
227          * mirrors and clamps to edge the texture coordinate, where mirroring
228          * and clamping to edge a value f computes:
229          * <code>mirrorClampToEdge(f) = min(1-1/(2*N), max(1/(2*N), abs(f)))</code>
230          * where N is the size of the one-, two-, or three-dimensional texture
231          * image in the direction of wrapping. (Introduced after OpenGL1.4)
232          * Falls back on EdgeClamp if not supported.
233          */
234         MirrorEdgeClamp;
235     }
236 
237     public enum WrapAxis {
238         /**
239          * S wrapping (u or "horizontal" wrap)
240          */
241         S,
242         /**
243          * T wrapping (v or "vertical" wrap)
244          */
245         T,
246         /**
247          * R wrapping (w or "depth" wrap)
248          */
249         R;
250     }
251 
252     /**
253      * If this texture is a depth texture (the format is Depth*) then
254      * this value may be used to compare the texture depth to the R texture
255      * coordinate.
256      */
257     public enum ShadowCompareMode {
258         /**
259          * Shadow comparison mode is disabled.
260          * Texturing is done normally.
261          */
262         Off,
263 
264         /**
265          * Compares the 3rd texture coordinate R to the value
266          * in this depth texture. If R <= texture value then result is 1.0,
267          * otherwise, result is 0.0. If filtering is set to bilinear or trilinear
268          * the implementation may sample the texture multiple times to provide
269          * smoother results in the range [0, 1].
270          */
271         LessOrEqual,
272 
273         /**
274          * Compares the 3rd texture coordinate R to the value
275          * in this depth texture. If R >= texture value then result is 1.0,
276          * otherwise, result is 0.0. If filtering is set to bilinear or trilinear
277          * the implementation may sample the texture multiple times to provide
278          * smoother results in the range [0, 1].
279          */
280         GreaterOrEqual
281     }
282 
283     /**
284      * The name of the texture (if loaded as a resource).
285      */
286     private String name = null;
287 
288     /**
289      * The image stored in the texture
290      */
291     private Image image = null;
292 
293     /**
294      * The texture key allows to reload a texture from a file
295      * if needed.
296      */
297     private TextureKey key = null;
298 
299     private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps;
300     private MagFilter magnificationFilter = MagFilter.Bilinear;
301     private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off;
302     private int anisotropicFilter;
303 
304     /**
305      * @return
306      */
307     @Override
clone()308     public Texture clone(){
309         try {
310             return (Texture) super.clone();
311         } catch (CloneNotSupportedException ex) {
312             throw new AssertionError();
313         }
314     }
315 
316     /**
317      * Constructor instantiates a new <code>Texture</code> object with default
318      * attributes.
319      */
Texture()320     public Texture() {
321     }
322 
323     /**
324      * @return the MinificationFilterMode of this texture.
325      */
getMinFilter()326     public MinFilter getMinFilter() {
327         return minificationFilter;
328     }
329 
330     /**
331      * @param minificationFilter
332      *            the new MinificationFilterMode for this texture.
333      * @throws IllegalArgumentException
334      *             if minificationFilter is null
335      */
setMinFilter(MinFilter minificationFilter)336     public void setMinFilter(MinFilter minificationFilter) {
337         if (minificationFilter == null) {
338             throw new IllegalArgumentException(
339                     "minificationFilter can not be null.");
340         }
341         this.minificationFilter = minificationFilter;
342     }
343 
344     /**
345      * @return the MagnificationFilterMode of this texture.
346      */
getMagFilter()347     public MagFilter getMagFilter() {
348         return magnificationFilter;
349     }
350 
351     /**
352      * @param magnificationFilter
353      *            the new MagnificationFilter for this texture.
354      * @throws IllegalArgumentException
355      *             if magnificationFilter is null
356      */
setMagFilter(MagFilter magnificationFilter)357     public void setMagFilter(MagFilter magnificationFilter) {
358         if (magnificationFilter == null) {
359             throw new IllegalArgumentException(
360                     "magnificationFilter can not be null.");
361         }
362         this.magnificationFilter = magnificationFilter;
363     }
364 
365     /**
366      * @return The ShadowCompareMode of this texture.
367      * @see ShadowCompareMode
368      */
getShadowCompareMode()369     public ShadowCompareMode getShadowCompareMode(){
370         return shadowCompareMode;
371     }
372 
373     /**
374      * @param compareMode
375      *            the new ShadowCompareMode for this texture.
376      * @throws IllegalArgumentException
377      *             if compareMode is null
378      * @see ShadowCompareMode
379      */
setShadowCompareMode(ShadowCompareMode compareMode)380     public void setShadowCompareMode(ShadowCompareMode compareMode){
381         if (compareMode == null){
382             throw new IllegalArgumentException(
383                     "compareMode can not be null.");
384         }
385         this.shadowCompareMode = compareMode;
386     }
387 
388     /**
389      * <code>setImage</code> sets the image object that defines the texture.
390      *
391      * @param image
392      *            the image that defines the texture.
393      */
setImage(Image image)394     public void setImage(Image image) {
395         this.image = image;
396     }
397 
398     /**
399      * @param key The texture key that was used to load this texture
400      */
setKey(AssetKey key)401     public void setKey(AssetKey key){
402         this.key = (TextureKey) key;
403     }
404 
getKey()405     public AssetKey getKey(){
406         return this.key;
407     }
408 
409     /**
410      * <code>getImage</code> returns the image data that makes up this
411      * texture. If no image data has been set, this will return null.
412      *
413      * @return the image data that makes up the texture.
414      */
getImage()415     public Image getImage() {
416         return image;
417     }
418 
419     /**
420      * <code>setWrap</code> sets the wrap mode of this texture for a
421      * particular axis.
422      *
423      * @param axis
424      *            the texture axis to define a wrapmode on.
425      * @param mode
426      *            the wrap mode for the given axis of the texture.
427      * @throws IllegalArgumentException
428      *             if axis or mode are null or invalid for this type of texture
429      */
setWrap(WrapAxis axis, WrapMode mode)430     public abstract void setWrap(WrapAxis axis, WrapMode mode);
431 
432     /**
433      * <code>setWrap</code> sets the wrap mode of this texture for all axis.
434      *
435      * @param mode
436      *            the wrap mode for the given axis of the texture.
437      * @throws IllegalArgumentException
438      *             if mode is null or invalid for this type of texture
439      */
setWrap(WrapMode mode)440     public abstract void setWrap(WrapMode mode);
441 
442     /**
443      * <code>getWrap</code> returns the wrap mode for a given coordinate axis
444      * on this texture.
445      *
446      * @param axis
447      *            the axis to return for
448      * @return the wrap mode of the texture.
449      * @throws IllegalArgumentException
450      *             if axis is null or invalid for this type of texture
451      */
getWrap(WrapAxis axis)452     public abstract WrapMode getWrap(WrapAxis axis);
453 
getType()454     public abstract Type getType();
455 
getName()456     public String getName() {
457         return name;
458     }
459 
setName(String name)460     public void setName(String name) {
461         this.name = name;
462     }
463 
464     /**
465      * @return the anisotropic filtering level for this texture. Default value
466      * is 1 (no anisotrophy), 2 means x2, 4 is x4, etc.
467      */
getAnisotropicFilter()468     public int getAnisotropicFilter() {
469         return anisotropicFilter;
470     }
471 
472     /**
473      * @param level
474      *            the anisotropic filtering level for this texture.
475      */
setAnisotropicFilter(int level)476     public void setAnisotropicFilter(int level) {
477         if (level < 1)
478             anisotropicFilter = 1;
479         else
480             anisotropicFilter = level;
481     }
482 
483     @Override
toString()484     public String toString(){
485         StringBuilder sb = new StringBuilder();
486         sb.append(getClass().getSimpleName());
487         sb.append("[name=").append(name);
488         if (image != null)
489             sb.append(", image=").append(image.toString());
490 
491         sb.append("]");
492 
493         return sb.toString();
494     }
495 
496     @Override
equals(Object obj)497     public boolean equals(Object obj) {
498         if (obj == null) {
499             return false;
500         }
501         if (getClass() != obj.getClass()) {
502             return false;
503         }
504         final Texture other = (Texture) obj;
505         if (this.image != other.image && (this.image == null || !this.image.equals(other.image))) {
506             return false;
507         }
508         if (this.minificationFilter != other.minificationFilter) {
509             return false;
510         }
511         if (this.magnificationFilter != other.magnificationFilter) {
512             return false;
513         }
514         if (this.shadowCompareMode != other.shadowCompareMode) {
515             return false;
516         }
517         if (this.anisotropicFilter != other.anisotropicFilter) {
518             return false;
519         }
520         return true;
521     }
522 
523     @Override
hashCode()524     public int hashCode() {
525         int hash = 5;
526         hash = 67 * hash + (this.image != null ? this.image.hashCode() : 0);
527         hash = 67 * hash + (this.minificationFilter != null ? this.minificationFilter.hashCode() : 0);
528         hash = 67 * hash + (this.magnificationFilter != null ? this.magnificationFilter.hashCode() : 0);
529         hash = 67 * hash + (this.shadowCompareMode != null ? this.shadowCompareMode.hashCode() : 0);
530         hash = 67 * hash + this.anisotropicFilter;
531         return hash;
532     }
533 
534 
535 
536 //    public abstract Texture createSimpleClone();
537 
538 
539    /** Retrieve a basic clone of this Texture (ie, clone everything but the
540      * image data, which is shared)
541      *
542      * @return Texture
543      */
createSimpleClone(Texture rVal)544     public Texture createSimpleClone(Texture rVal) {
545         rVal.setMinFilter(minificationFilter);
546         rVal.setMagFilter(magnificationFilter);
547         rVal.setShadowCompareMode(shadowCompareMode);
548 //        rVal.setHasBorder(hasBorder);
549         rVal.setAnisotropicFilter(anisotropicFilter);
550         rVal.setImage(image); // NOT CLONED.
551 //        rVal.memReq = memReq;
552         rVal.setKey(key);
553         rVal.setName(name);
554 //        rVal.setBlendColor(blendColor != null ? blendColor.clone() : null);
555 //        if (getTextureKey() != null) {
556 //            rVal.setTextureKey(getTextureKey());
557 //        }
558         return rVal;
559     }
560 
createSimpleClone()561     public abstract Texture createSimpleClone();
562 
write(JmeExporter e)563     public void write(JmeExporter e) throws IOException {
564         OutputCapsule capsule = e.getCapsule(this);
565         capsule.write(name, "name", null);
566 
567         if (key == null){
568             // no texture key is set, try to save image instead then
569             capsule.write(image, "image", null);
570         }else{
571             capsule.write(key, "key", null);
572         }
573 
574         capsule.write(anisotropicFilter, "anisotropicFilter", 1);
575         capsule.write(minificationFilter, "minificationFilter",
576                 MinFilter.BilinearNoMipMaps);
577         capsule.write(magnificationFilter, "magnificationFilter",
578                 MagFilter.Bilinear);
579     }
580 
read(JmeImporter e)581     public void read(JmeImporter e) throws IOException {
582         InputCapsule capsule = e.getCapsule(this);
583         name = capsule.readString("name", null);
584         key = (TextureKey) capsule.readSavable("key", null);
585 
586         // load texture from key, if available
587         if (key != null) {
588             // key is available, so try the texture from there.
589             try {
590                 Texture loadedTex = e.getAssetManager().loadTexture(key);
591                 image = loadedTex.getImage();
592             } catch (AssetNotFoundException ex){
593                 Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot locate texture {0}", key);
594                 image = PlaceholderAssets.getPlaceholderImage();
595             }
596         }else{
597             // no key is set on the texture. Attempt to load an embedded image
598             image = (Image) capsule.readSavable("image", null);
599             if (image == null){
600                 // TODO: what to print out here? the texture has no key or data, there's no useful information ..
601                 // assume texture.name is set even though the key is null
602                 Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot load embedded image {0}", toString() );
603             }
604         }
605 
606         anisotropicFilter = capsule.readInt("anisotropicFilter", 1);
607         minificationFilter = capsule.readEnum("minificationFilter",
608                 MinFilter.class,
609                 MinFilter.BilinearNoMipMaps);
610         magnificationFilter = capsule.readEnum("magnificationFilter",
611                 MagFilter.class, MagFilter.Bilinear);
612     }
613 }