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
experimental
Kae..pl 11 years ago
parent 535a6dd8ce
commit eda3e8d725
  1. 114
      engine/src/blender/com/jme3/asset/BlenderKey.java
  2. 2
      engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  3. 115
      engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.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)

@ -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();

@ -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);
}
}

Loading…
Cancel
Save