فهرست منبع

Feature: sky generated textures can be generated agains a cube or a sphere of a selected size. By default a sphere is now used. This makes the sky look entirely seamless.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10819 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 12 سال پیش
والد
کامیت
eda3e8d725

+ 97 - 17
engine/src/blender/com/jme3/asset/BlenderKey.java

@@ -63,54 +63,58 @@ import com.jme3.texture.Texture;
  */
 public class BlenderKey extends ModelKey {
 
-    protected static final int       DEFAULT_FPS             = 25;
+    protected static final int         DEFAULT_FPS               = 25;
     /**
      * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
      * between the frames.
      */
-    protected int                    fps                     = DEFAULT_FPS;
+    protected int                      fps                       = DEFAULT_FPS;
     /**
      * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
      */
-    protected int                    featuresToLoad          = FeaturesToLoad.ALL;
+    protected int                      featuresToLoad            = FeaturesToLoad.ALL;
     /** This variable determines if assets that are not linked to the objects should be loaded. */
-    protected boolean                loadUnlinkedAssets;
+    protected boolean                  loadUnlinkedAssets;
     /** The root path for all the assets. */
-    protected String                 assetRootPath;
+    protected String                   assetRootPath;
     /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
-    protected boolean                fixUpAxis               = true;
+    protected boolean                  fixUpAxis                 = true;
     /** Generated textures resolution (PPU - Pixels Per Unit). */
-    protected int                    generatedTexturePPU     = 128;
+    protected int                      generatedTexturePPU       = 128;
     /**
      * The name of world settings that the importer will use. If not set or specified name does not occur in the file
      * then the first world settings in the file will be used.
      */
-    protected String                 usedWorld;
+    protected String                   usedWorld;
     /**
      * User's default material that is set fo objects that have no material definition in blender. The default value is
      * null. If the value is null the importer will use its own default material (gray color - like in blender).
      */
-    protected Material               defaultMaterial;
+    protected Material                 defaultMaterial;
     /** Face cull mode. By default it is disabled. */
-    protected FaceCullMode           faceCullMode            = FaceCullMode.Back;
+    protected FaceCullMode             faceCullMode              = FaceCullMode.Back;
     /**
      * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
      * If set to -1 then the current layer will be loaded.
      */
-    protected int                    layersToLoad            = -1;
+    protected int                      layersToLoad              = -1;
     /** A variable that toggles the object custom properties loading. */
-    protected boolean                loadObjectProperties    = true;
+    protected boolean                  loadObjectProperties      = true;
     /** Maximum texture size. Might be dependant on the graphic card. */
-    protected int                    maxTextureSize          = -1;
+    protected int                      maxTextureSize            = -1;
     /** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
-    protected boolean                loadGeneratedTextures;
+    protected boolean                  loadGeneratedTextures;
     /** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
-    protected MipmapGenerationMethod mipmapGenerationMethod  = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
+    protected MipmapGenerationMethod   mipmapGenerationMethod    = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
     /**
      * If the sky has only generated textures applied then they will have the following size (both width and height). If 2d textures are used then the generated
      * textures will get their proper size.
      */
-    protected int                    skyGeneratedTextureSize = 1000;
+    protected int                      skyGeneratedTextureSize   = 1000;
+    /** The radius of a shape that will be used while creating the generated texture for the sky. The higher it is the larger part of the texture will be seen. */
+    protected float                    skyGeneratedTextureRadius = 1;
+    /** The shape against which the generated texture for the sky will be created. */
+    protected SkyGeneratedTextureShape skyGeneratedTextureShape     = SkyGeneratedTextureShape.SPHERE;
 
     /**
      * Constructor used by serialization mechanisms.
@@ -267,7 +271,7 @@ public class BlenderKey extends ModelKey {
      *            bitwise flag of FeaturesToLoad interface values
      */
     public void excludeFromLoading(int featuresNotToLoad) {
-        this.featuresToLoad &= ~featuresNotToLoad;
+        featuresToLoad &= ~featuresNotToLoad;
     }
 
     /**
@@ -379,6 +383,39 @@ public class BlenderKey extends ModelKey {
         this.skyGeneratedTextureSize = skyGeneratedTextureSize;
     }
 
+    /**
+     * @return the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
+     */
+    public float getSkyGeneratedTextureRadius() {
+        return skyGeneratedTextureRadius;
+    }
+
+    /**
+     * @param skyGeneratedTextureRadius
+     *            the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
+     */
+    public void setSkyGeneratedTextureRadius(float skyGeneratedTextureRadius) {
+        this.skyGeneratedTextureRadius = skyGeneratedTextureRadius;
+    }
+
+    /**
+     * @return the shape against which the generated texture for the sky will be created (by default it is a sphere).
+     */
+    public SkyGeneratedTextureShape getSkyGeneratedTextureShape() {
+        return skyGeneratedTextureShape;
+    }
+
+    /**
+     * @param skyGeneratedTextureShape
+     *            the shape against which the generated texture for the sky will be created
+     */
+    public void setSkyGeneratedTextureShape(SkyGeneratedTextureShape skyGeneratedTextureShape) {
+        if(skyGeneratedTextureShape == null) {
+            throw new IllegalArgumentException("The sky generated shape type cannot be null!");
+        }
+        this.skyGeneratedTextureShape = skyGeneratedTextureShape;
+    }
+
     /**
      * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
      * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
@@ -430,6 +467,9 @@ public class BlenderKey extends ModelKey {
         oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
         oc.write(layersToLoad, "layers-to-load", -1);
         oc.write(mipmapGenerationMethod, "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
+        oc.write(skyGeneratedTextureSize, "sky-generated-texture-size", 1000);
+        oc.write(skyGeneratedTextureRadius, "sky-generated-texture-radius", 1f);
+        oc.write(skyGeneratedTextureShape, "sky-generated-texture-shape", SkyGeneratedTextureShape.SPHERE);
     }
 
     @Override
@@ -447,6 +487,9 @@ public class BlenderKey extends ModelKey {
         faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
         layersToLoad = ic.readInt("layers-to=load", -1);
         mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
+        skyGeneratedTextureSize = ic.readInt("sky-generated-texture-size", 1000);
+        skyGeneratedTextureRadius = ic.readFloat("sky-generated-texture-radius", 1f);
+        skyGeneratedTextureShape = ic.readEnum("sky-generated-texture-shape", SkyGeneratedTextureShape.class, SkyGeneratedTextureShape.SPHERE);
     }
 
     @Override
@@ -460,8 +503,15 @@ public class BlenderKey extends ModelKey {
         result = prime * result + (fixUpAxis ? 1231 : 1237);
         result = prime * result + fps;
         result = prime * result + generatedTexturePPU;
+        result = prime * result + (skyGeneratedTextureShape == null ? 0 : skyGeneratedTextureShape.hashCode());
         result = prime * result + layersToLoad;
+        result = prime * result + (loadGeneratedTextures ? 1231 : 1237);
+        result = prime * result + (loadObjectProperties ? 1231 : 1237);
         result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
+        result = prime * result + maxTextureSize;
+        result = prime * result + (mipmapGenerationMethod == null ? 0 : mipmapGenerationMethod.hashCode());
+        result = prime * result + Float.floatToIntBits(skyGeneratedTextureRadius);
+        result = prime * result + skyGeneratedTextureSize;
         result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
         return result;
     }
@@ -507,12 +557,33 @@ public class BlenderKey extends ModelKey {
         if (generatedTexturePPU != other.generatedTexturePPU) {
             return false;
         }
+        if (skyGeneratedTextureShape != other.skyGeneratedTextureShape) {
+            return false;
+        }
         if (layersToLoad != other.layersToLoad) {
             return false;
         }
+        if (loadGeneratedTextures != other.loadGeneratedTextures) {
+            return false;
+        }
+        if (loadObjectProperties != other.loadObjectProperties) {
+            return false;
+        }
         if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
             return false;
         }
+        if (maxTextureSize != other.maxTextureSize) {
+            return false;
+        }
+        if (mipmapGenerationMethod != other.mipmapGenerationMethod) {
+            return false;
+        }
+        if (Float.floatToIntBits(skyGeneratedTextureRadius) != Float.floatToIntBits(other.skyGeneratedTextureRadius)) {
+            return false;
+        }
+        if (skyGeneratedTextureSize != other.skyGeneratedTextureSize) {
+            return false;
+        }
         if (usedWorld == null) {
             if (other.usedWorld != null) {
                 return false;
@@ -548,6 +619,15 @@ public class BlenderKey extends ModelKey {
         int ALL        = 0xFFFFFFFF;
     }
 
+    /**
+     * The shape againts which the sky generated texture will be created.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    public static enum SkyGeneratedTextureShape {
+        CUBE, SPHERE;
+    }
+
     /**
      * This class holds the loading results according to the given loading flag.
      * @author Marcin Roguski (Kaelthas)

+ 1 - 1
engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java

@@ -269,7 +269,7 @@ public class CombinedTexture {
         for (TextureData textureData : textureDatas) {
             TextureCubeMap texture = null;
             if (textureData.texture instanceof GeneratedTexture) {
-                texture = ((GeneratedTexture) textureData.texture).generateSkyTexture(size, horizontalColor, zenithColor);
+                texture = ((GeneratedTexture) textureData.texture).generateSkyTexture(size, horizontalColor, zenithColor, blenderContext);
             } else {
                 // first create a grayscale version of the image
                 Image image = textureData.texture.getImage();

+ 90 - 25
engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java

@@ -23,6 +23,7 @@ import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture;
 import com.jme3.texture.TextureCubeMap;
+import com.jme3.util.TempVars;
 
 /**
  * The generated texture loaded from blender file. The texture is not generated
@@ -32,29 +33,72 @@ import com.jme3.texture.TextureCubeMap;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class GeneratedTexture extends Texture {
-    private static final int       POSITIVE_X       = 0;
-    private static final int       NEGATIVE_X       = 1;
-    private static final int       POSITIVE_Y       = 2;
-    private static final int       NEGATIVE_Y       = 3;
-    private static final int       POSITIVE_Z       = 4;
-    private static final int       NEGATIVE_Z       = 5;
+    private static final int            POSITIVE_X       = 0;
+    private static final int            NEGATIVE_X       = 1;
+    private static final int            POSITIVE_Y       = 2;
+    private static final int            NEGATIVE_Y       = 3;
+    private static final int            POSITIVE_Z       = 4;
+    private static final int            NEGATIVE_Z       = 5;
 
     // flag values
-    public static final int        TEX_COLORBAND    = 1;
-    public static final int        TEX_FLIPBLEND    = 2;
-    public static final int        TEX_NEGALPHA     = 4;
-    public static final int        TEX_CHECKER_ODD  = 8;
-    public static final int        TEX_CHECKER_EVEN = 16;
-    public static final int        TEX_PRV_ALPHA    = 32;
-    public static final int        TEX_PRV_NOR      = 64;
-    public static final int        TEX_REPEAT_XMIR  = 128;
-    public static final int        TEX_REPEAT_YMIR  = 256;
-    public static final int        TEX_FLAG_MASK    = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;
+    public static final int             TEX_COLORBAND    = 1;
+    public static final int             TEX_FLIPBLEND    = 2;
+    public static final int             TEX_NEGALPHA     = 4;
+    public static final int             TEX_CHECKER_ODD  = 8;
+    public static final int             TEX_CHECKER_EVEN = 16;
+    public static final int             TEX_PRV_ALPHA    = 32;
+    public static final int             TEX_PRV_NOR      = 64;
+    public static final int             TEX_REPEAT_XMIR  = 128;
+    public static final int             TEX_REPEAT_YMIR  = 256;
+    public static final int             TEX_FLAG_MASK    = TEX_COLORBAND | TEX_FLIPBLEND | TEX_NEGALPHA | TEX_CHECKER_ODD | TEX_CHECKER_EVEN | TEX_PRV_ALPHA | TEX_PRV_NOR | TEX_REPEAT_XMIR | TEX_REPEAT_YMIR;
 
     /** Material-texture link structure. */
-    private final Structure        mTex;
+    private final Structure             mTex;
     /** Texture generateo for the specified texture type. */
-    private final TextureGenerator textureGenerator;
+    private final TextureGenerator      textureGenerator;
+    /** 
+     * The generated texture cast functions. They are used to cas a given point on a plane to a specified shape in 3D space.
+     * The functions should be ordered as the ordinal of a BlenderKey.CastFunction enums.
+     */
+    private final static CastFunction[] CAST_FUNCTIONS    = new CastFunction[] {
+        /**
+         * The cube casting function (does nothing except scaling if needed because the given points are already on a cube).
+         */
+        new CastFunction() {
+            @Override
+            public void cast(Vector3f pointToCast, float radius) {
+                //computed using the Thales' theorem
+                float length = 2 * pointToCast.subtractLocal(0.5f, 0.5f, 0.5f).length() * radius;
+                pointToCast.normalizeLocal().addLocal(0.5f, 0.5f, 0.5f).multLocal(length);
+            }
+        },
+        
+        /**
+         * The sphere casting function.
+         */
+        new CastFunction() {
+            /**
+             * The method casts a point on a plane to a sphere.
+             * The plane is one of the faces of a cube that has a edge of length 1 and center in (0.5 0.5, 0.5). This cube is a basic 3d area where generated texture
+             * is created.
+             * To cast a point on a cube face to a sphere that is inside the cube we perform several easy vector operations.
+             * 1. create a vector from the cube's center to the point
+             * 2. setting its length to 0.5 (the radius of the sphere)
+             * 3. adding the value of the cube's center to get a point on the sphere
+             * 
+             * The result is stored in the given vector.
+             * 
+             * @param pointToCast
+             *            the point on a plane that will be cast to a sphere
+             * @param radius
+             *            the radius of the sphere
+             */
+            @Override
+            public void cast(Vector3f pointToCast, float radius) {
+                pointToCast.subtractLocal(0.5f, 0.5f, 0.5f).normalizeLocal().multLocal(radius).addLocal(0.5f, 0.5f, 0.5f);
+            }
+        }
+    };
 
     /**
      * Constructor. Reads the required data from the 'tex' structure.
@@ -137,37 +181,48 @@ import com.jme3.texture.TextureCubeMap;
      *            the horizon color
      * @param zenithColor
      *            the zenith color
+     * @param blenderContext
+     *            the blender context
      * @return the sky texture
      */
-    public TextureCubeMap generateSkyTexture(int size, ColorRGBA horizontalColor, ColorRGBA zenithColor) {
+    public TextureCubeMap generateSkyTexture(int size, ColorRGBA horizontalColor, ColorRGBA zenithColor, BlenderContext blenderContext) {
         Image image = ImageUtils.createEmptyImage(Format.RGB8, size, size, 6);
         PixelInputOutput pixelIO = PixelIOFactory.getPixelIO(image.getFormat());
         TexturePixel pixel = new TexturePixel();
 
         float delta = 1 / (float) (size - 1);
         float sideV, sideS = 1, forwardU = 1, forwardV, upS;
+        TempVars tempVars = TempVars.get();
+        CastFunction castFunction = CAST_FUNCTIONS[blenderContext.getBlenderKey().getSkyGeneratedTextureShape().ordinal()];
+        float castRadius = blenderContext.getBlenderKey().getSkyGeneratedTextureRadius();
 
         for (int x = 0; x < size; ++x) {
             sideV = 1;
             forwardV = 1;
             upS = 0;
             for (int y = 0; y < size; ++y) {
-                textureGenerator.getPixel(pixel, 1, sideV, sideS);
+                castFunction.cast(tempVars.vect1.set(1, sideV, sideS), castRadius);
+                textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z);
                 pixelIO.write(image, NEGATIVE_X, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// right
 
-                textureGenerator.getPixel(pixel, 0, sideV, 1 - sideS);
+                castFunction.cast(tempVars.vect1.set(0, sideV, 1 - sideS), castRadius);
+                textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z);
                 pixelIO.write(image, POSITIVE_X, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// left
 
-                textureGenerator.getPixel(pixel, forwardU, forwardV, 0);
+                castFunction.cast(tempVars.vect1.set(forwardU, forwardV, 0), castRadius);
+                textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z);
                 pixelIO.write(image, POSITIVE_Z, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// front
 
-                textureGenerator.getPixel(pixel, 1 - forwardU, forwardV, 1);
+                castFunction.cast(tempVars.vect1.set(1 - forwardU, forwardV, 1), castRadius);
+                textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z);
                 pixelIO.write(image, NEGATIVE_Z, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// back
 
-                textureGenerator.getPixel(pixel, forwardU, 0, upS);
+                castFunction.cast(tempVars.vect1.set(forwardU, 0, upS), castRadius);
+                textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z);
                 pixelIO.write(image, NEGATIVE_Y, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// top
 
-                textureGenerator.getPixel(pixel, forwardU, 1, 1 - upS);
+                castFunction.cast(tempVars.vect1.set(forwardU, 1, 1 - upS), castRadius);
+                textureGenerator.getPixel(pixel, tempVars.vect1.x, tempVars.vect1.y, tempVars.vect1.z);
                 pixelIO.write(image, POSITIVE_Y, ImageUtils.color(pixel, horizontalColor, zenithColor), x, y);// bottom
 
                 sideV = FastMath.clamp(sideV - delta, 0, 1);
@@ -177,6 +232,7 @@ import com.jme3.texture.TextureCubeMap;
             sideS = FastMath.clamp(sideS - delta, 0, 1);
             forwardU = FastMath.clamp(forwardU - delta, 0, 1);
         }
+        tempVars.release();
 
         return new TextureCubeMap(image);
     }
@@ -214,4 +270,13 @@ import com.jme3.texture.TextureCubeMap;
             super.format = imageFormat;
         }
     }
+
+    /**
+     * The casting functions to create a sky generated texture against selected shape of a selected size.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static interface CastFunction {
+        void cast(Vector3f pointToCast, float radius);
+    }
 }