aboutsummaryrefslogtreecommitdiff
path: root/engine/src/blender/com/jme3/scene/plugins/blender/textures
diff options
context:
space:
mode:
authorScott Barta <sbarta@google.com>2012-03-01 12:35:35 -0800
committerScott Barta <sbarta@google.com>2012-03-01 12:40:08 -0800
commit59b2e6871c65f58fdad78cd7229c292f6a177578 (patch)
tree2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/blender/com/jme3/scene/plugins/blender/textures
parentf9b30489e75ac1eabc365064959804e99534f7ab (diff)
downloadjmonkeyengine-59b2e6871c65f58fdad78cd7229c292f6a177578.tar.gz
Adds the jMonkeyEngine library to the build.
Adds the jMonkeyEngine open source 3D game engine to the build. This is built as a static library and is only used by the Finsky client. Change-Id: I06a3f054df7b8a67757267d884854f70c5a16ca0
Diffstat (limited to 'engine/src/blender/com/jme3/scene/plugins/blender/textures')
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java135
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java875
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java416
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java168
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java131
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java135
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java191
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java148
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java137
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java106
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java125
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java171
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java216
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java481
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java294
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java408
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java226
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java195
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java51
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java162
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java96
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java81
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java221
-rw-r--r--engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.datbin0 -> 19598 bytes
24 files changed, 5169 insertions, 0 deletions
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java
new file mode 100644
index 0000000..d124616
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/ImageLoader.java
@@ -0,0 +1,135 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.texture.Image;
+import com.jme3.texture.plugins.AWTLoader;
+import com.jme3.texture.plugins.DDSLoader;
+import com.jme3.texture.plugins.TGALoader;
+import java.io.InputStream;
+import java.util.logging.Logger;
+
+/**
+ * An image loader class. It uses three loaders (AWTLoader, TGALoader and DDSLoader) in an attempt to load the image from the given
+ * input stream.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class ImageLoader extends AWTLoader {
+ private static final Logger LOGGER = Logger.getLogger(ImageLoader.class.getName());
+
+ protected DDSLoader ddsLoader = new DDSLoader(); // DirectX image loader
+
+ /**
+ * This method loads the image from the blender file itself. It tries each loader to load the image.
+ *
+ * @param inputStream
+ * blender input stream
+ * @param startPosition
+ * position in the stream where the image data starts
+ * @param flipY
+ * if the image should be flipped (does not work with DirectX image)
+ * @return loaded image or null if it could not be loaded
+ */
+ public Image loadImage(BlenderInputStream inputStream, int startPosition, boolean flipY) {
+ // loading using AWT loader
+ inputStream.setPosition(startPosition);
+ Image result = this.loadImage(inputStream, ImageType.AWT, flipY);
+ // loading using TGA loader
+ if (result == null) {
+ inputStream.setPosition(startPosition);
+ result = this.loadImage(inputStream, ImageType.TGA, flipY);
+ }
+ // loading using DDS loader
+ if (result == null) {
+ inputStream.setPosition(startPosition);
+ result = this.loadImage(inputStream, ImageType.DDS, flipY);
+ }
+
+ if (result == null) {
+ LOGGER.warning("Image could not be loaded by none of available loaders!");
+ }
+
+ return result;
+ }
+
+ /**
+ * This method loads an image of a specified type from the given input stream.
+ *
+ * @param inputStream
+ * the input stream we read the image from
+ * @param imageType
+ * the type of the image {@link ImageType}
+ * @param flipY
+ * if the image should be flipped (does not work with DirectX image)
+ * @return loaded image or null if it could not be loaded
+ */
+ public Image loadImage(InputStream inputStream, ImageType imageType, boolean flipY) {
+ Image result = null;
+ switch (imageType) {
+ case AWT:
+ try {
+ result = this.load(inputStream, flipY);
+ } catch (Exception e) {
+ LOGGER.info("Unable to load image using AWT loader!");
+ }
+ break;
+ case DDS:
+ try {
+ result = ddsLoader.load(inputStream);
+ } catch (Exception e) {
+ LOGGER.info("Unable to load image using DDS loader!");
+ }
+ break;
+ case TGA:
+ try {
+ result = TGALoader.load(inputStream, flipY);
+ } catch (Exception e) {
+ LOGGER.info("Unable to load image using TGA loader!");
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown image type: " + imageType);
+ }
+ return result;
+ }
+
+ /**
+ * Image types that can be loaded. AWT: png, jpg, jped or bmp TGA: tga DDS: DirectX image files
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ private static enum ImageType {
+ AWT, TGA, DDS;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java
new file mode 100644
index 0000000..a8bd890
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java
@@ -0,0 +1,875 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.TextureGeneratorMusgrave.MusgraveData;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This generator is responsible for creating various noises used to create
+ * generated textures loaded from blender.
+ * It derives from AbstractBlenderHelper but is not stored in blender context.
+ * It is only used by TextureHelper.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/*package*/ class NoiseGenerator extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(NoiseGenerator.class.getName());
+
+ // flag
+ protected static final int TEX_COLORBAND = 1;
+ protected static final int TEX_FLIPBLEND = 2;
+ protected static final int TEX_NEGALPHA = 4;
+ protected static final int TEX_CHECKER_ODD = 8;
+ protected static final int TEX_CHECKER_EVEN = 16;
+ protected static final int TEX_PRV_ALPHA = 32;
+ protected static final int TEX_PRV_NOR = 64;
+ protected static final int TEX_REPEAT_XMIR = 128;
+ protected static final int TEX_REPEAT_YMIR = 256;
+ protected 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;
+
+ // tex->stype
+ protected static final int TEX_PLASTIC = 0;
+ protected static final int TEX_WALLIN = 1;
+ protected static final int TEX_WALLOUT = 2;
+
+ // musgrave stype
+ protected static final int TEX_MFRACTAL = 0;
+ protected static final int TEX_RIDGEDMF = 1;
+ protected static final int TEX_HYBRIDMF = 2;
+ protected static final int TEX_FBM = 3;
+ protected static final int TEX_HTERRAIN = 4;
+
+ // keyblock->type
+ protected static final int KEY_LINEAR = 0;
+ protected static final int KEY_CARDINAL = 1;
+ protected static final int KEY_BSPLINE = 2;
+
+ // CONSTANTS (read from file)
+ protected static float[] hashpntf;
+ protected static short[] hash;
+ protected static float[] hashvectf;
+ protected static short[] p;
+ protected static float[][] g;
+
+ /**
+ * Constructor. Stores the blender version number and loads the constants needed for computations.
+ * @param blenderVersion
+ * the number of blender version
+ */
+ public NoiseGenerator(String blenderVersion) {
+ super(blenderVersion, false);
+ this.loadConstants();
+ }
+
+ /**
+ * This method loads the constants needed for computations. They are exactly like the ones the blender uses. Each
+ * deriving class should override this method and load its own constraints. Be carefult with overriding though, if
+ * an exception will be thrown the class will not be instantiated.
+ */
+ protected void loadConstants() {
+ InputStream is = NoiseGenerator.class.getResourceAsStream("noiseconstants.dat");
+ try {
+ ObjectInputStream ois = new ObjectInputStream(is);
+ hashpntf = (float[]) ois.readObject();
+ hash = (short[]) ois.readObject();
+ hashvectf = (float[]) ois.readObject();
+ p = (short[]) ois.readObject();
+ g = (float[][]) ois.readObject();
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ } catch (ClassNotFoundException e) {
+ assert false : "Constants' classes should be arrays of primitive types, so they are ALWAYS known!";
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, e.getLocalizedMessage());
+ }
+ }
+ }
+ }
+
+ protected static Map<Integer, NoiseFunction> noiseFunctions = new HashMap<Integer, NoiseFunction>();
+ static {
+ noiseFunctions.put(Integer.valueOf(0), new NoiseFunction() {
+ // originalBlenderNoise
+ @Override
+ public float execute(float x, float y, float z) {
+ return NoiseFunctions.originalBlenderNoise(x, y, z);
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return 2.0f * NoiseFunctions.originalBlenderNoise(x, y, z) - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(1), new NoiseFunction() {
+ // orgPerlinNoise
+ @Override
+ public float execute(float x, float y, float z) {
+ return 0.5f + 0.5f * NoiseFunctions.noise3Perlin(x, y, z);
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return NoiseFunctions.noise3Perlin(x, y, z);
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(2), new NoiseFunction() {
+ // newPerlin
+ @Override
+ public float execute(float x, float y, float z) {
+ return 0.5f + 0.5f * NoiseFunctions.newPerlin(x, y, z);
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return this.execute(x, y, z);
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(3), new NoiseFunction() {
+ // voronoi_F1
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[0];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[0] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(4), new NoiseFunction() {
+ // voronoi_F2
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[1];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[1] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(5), new NoiseFunction() {
+ // voronoi_F3
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[2];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[2] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(6), new NoiseFunction() {
+ // voronoi_F4
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[3];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * da[3] - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(7), new NoiseFunction() {
+ // voronoi_F1F2
+ @Override
+ public float execute(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return da[1] - da[0];
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float[] da = new float[4], pa = new float[12];
+ NoiseFunctions.voronoi(x, y, z, da, pa, 1, 0);
+ return 2.0f * (da[1] - da[0]) - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(8), new NoiseFunction() {
+ // voronoi_Cr
+ @Override
+ public float execute(float x, float y, float z) {
+ float t = 10 * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2
+ return t > 1.0f ? 1.0f : t;
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ float t = 10.0f * noiseFunctions.get(Integer.valueOf(7)).execute(x, y, z);// voronoi_F1F2
+ return t > 1.0f ? 1.0f : 2.0f * t - 1.0f;
+ }
+ });
+ noiseFunctions.put(Integer.valueOf(14), new NoiseFunction() {
+ // cellNoise
+ @Override
+ public float execute(float x, float y, float z) {
+ int xi = (int) Math.floor(x);
+ int yi = (int) Math.floor(y);
+ int zi = (int) Math.floor(z);
+ long n = xi + yi * 1301 + zi * 314159;
+ n ^= n << 13;
+ return (n * (n * n * 15731 + 789221) + 1376312589) / 4294967296.0f;
+ }
+
+ @Override
+ public float executeSigned(float x, float y, float z) {
+ return 2.0f * this.execute(x, y, z) - 1.0f;
+ }
+ });
+ }
+ /** Distance metrics for voronoi. e parameter only used in Minkovsky. */
+ protected static Map<Integer, DistanceFunction> distanceFunctions = new HashMap<Integer, NoiseGenerator.DistanceFunction>();
+
+ static {
+ distanceFunctions.put(Integer.valueOf(0), new DistanceFunction() {
+ // real distance
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return (float) Math.sqrt(x * x + y * y + z * z);
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(1), new DistanceFunction() {
+ // distance squared
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return x * x + y * y + z * z;
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(2), new DistanceFunction() {
+ // manhattan/taxicab/cityblock distance
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(3), new DistanceFunction() {
+ // Chebychev
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ x = FastMath.abs(x);
+ y = FastMath.abs(y);
+ z = FastMath.abs(z);
+ float t = x > y ? x : y;
+ return z > t ? z : t;
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(4), new DistanceFunction() {
+ // Minkovsky, preset exponent 0.5 (MinkovskyH)
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ float d = (float) (Math.sqrt(FastMath.abs(x)) + Math.sqrt(FastMath.abs(y)) + Math.sqrt(FastMath.abs(z)));
+ return d * d;
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(5), new DistanceFunction() {
+ // Minkovsky, preset exponent 0.25 (Minkovsky4)
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ x *= x;
+ y *= y;
+ z *= z;
+ return (float) Math.sqrt(Math.sqrt(x * x + y * y + z * z));
+ }
+ });
+ distanceFunctions.put(Integer.valueOf(6), new DistanceFunction() {
+ // Minkovsky, general case
+ @Override
+ public float execute(float x, float y, float z, float e) {
+ return (float) Math.pow(Math.pow(FastMath.abs(x), e) + Math.pow(FastMath.abs(y), e) + Math.pow(FastMath.abs(z), e), 1.0f / e);
+ }
+ });
+ }
+
+ protected static Map<Integer, MusgraveFunction> musgraveFunctions = new HashMap<Integer, NoiseGenerator.MusgraveFunction>();
+ static {
+ musgraveFunctions.put(Integer.valueOf(TEX_MFRACTAL), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float rmd, value = 1.0f, pwr = 1.0f, pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ for (int i = 0; i < (int) musgraveData.octaves; ++i) {
+ value *= pwr * abstractNoiseFunc.executeSigned(x, y, z) + 1.0f;
+ pwr *= pwHL;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+ rmd = (float) (musgraveData.octaves - Math.floor(musgraveData.octaves));
+ if (rmd != 0.0f) {
+ value *= rmd * abstractNoiseFunc.executeSigned(x, y, z) * pwr + 1.0f;
+ }
+ return value;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_RIDGEDMF), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float result, signal, weight;
+ float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ float pwr = pwHL;
+
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ signal = musgraveData.offset - FastMath.abs(abstractNoiseFunc.executeSigned(x, y, z));
+ signal *= signal;
+ result = signal;
+ weight = 1.0f;
+
+ for (int i = 1; i < (int) musgraveData.octaves; ++i) {
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ weight = signal * musgraveData.gain;
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ } else if (weight < 0.0) {
+ weight = 0.0f;
+ }
+ signal = musgraveData.offset - FastMath.abs(abstractNoiseFunc.executeSigned(x, y, z));
+ signal *= signal;
+ signal *= weight;
+ result += signal * pwr;
+ pwr *= pwHL;
+ }
+ return result;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_HYBRIDMF), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float result, signal, weight, rmd;
+ float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ float pwr = pwHL;
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ result = abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset;
+ weight = musgraveData.gain * result;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+
+ for (int i = 1; weight > 0.001f && i < (int) musgraveData.octaves; ++i) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+ signal = (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr;
+ pwr *= pwHL;
+ result += weight * signal;
+ weight *= musgraveData.gain * signal;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+
+ rmd = musgraveData.octaves - (float) Math.floor(musgraveData.octaves);
+ if (rmd != 0.0f) {
+ result += rmd * (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr;
+ }
+ return result;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_FBM), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float rmd, value = 0.0f, pwr = 1.0f, pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ for (int i = 0; i < (int) musgraveData.octaves; ++i) {
+ value += abstractNoiseFunc.executeSigned(x, y, z) * pwr;
+ pwr *= pwHL;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+
+ rmd = (float) (musgraveData.octaves - Math.floor(musgraveData.octaves));
+ if (rmd != 0.f) {
+ value += rmd * abstractNoiseFunc.executeSigned(x, y, z) * pwr;
+ }
+ return value;
+ }
+ });
+ musgraveFunctions.put(Integer.valueOf(TEX_HTERRAIN), new MusgraveFunction() {
+
+ @Override
+ public float execute(MusgraveData musgraveData, float x, float y, float z) {
+ float value, increment, rmd;
+ float pwHL = (float) Math.pow(musgraveData.lacunarity, -musgraveData.h);
+ float pwr = pwHL;
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(musgraveData.noisebasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(0));
+ }
+
+ value = musgraveData.offset + abstractNoiseFunc.executeSigned(x, y, z);
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+
+ for (int i = 1; i < (int) musgraveData.octaves; ++i) {
+ increment = (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ x *= musgraveData.lacunarity;
+ y *= musgraveData.lacunarity;
+ z *= musgraveData.lacunarity;
+ }
+
+ rmd = musgraveData.octaves - (float) Math.floor(musgraveData.octaves);
+ if (rmd != 0.0) {
+ increment = (abstractNoiseFunc.executeSigned(x, y, z) + musgraveData.offset) * pwr * value;
+ value += rmd * increment;
+ }
+ return value;
+ }
+ });
+ }
+
+ public static class NoiseFunctions {
+ public static float noise(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(0);
+ noiseBasis = 0;
+ }
+
+ if (noiseBasis == 0) {
+ ++x;
+ ++y;
+ ++z;
+ }
+
+ if (noiseSize != 0.0) {
+ noiseSize = 1.0f / noiseSize;
+ x *= noiseSize;
+ y *= noiseSize;
+ z *= noiseSize;
+ }
+ float result = abstractNoiseFunc.execute(x, y, z);
+ return isHard ? Math.abs(2.0f * result - 1.0f) : result;
+ }
+
+ public static float turbulence(float x, float y, float z, float noiseSize, int noiseDepth, int noiseBasis, boolean isHard) {
+ NoiseFunction abstractNoiseFunc = noiseFunctions.get(Integer.valueOf(noiseBasis));
+ if (abstractNoiseFunc == null) {
+ abstractNoiseFunc = noiseFunctions.get(0);
+ noiseBasis = 0;
+ }
+
+ if (noiseBasis == 0) {
+ ++x;
+ ++y;
+ ++z;
+ }
+ if (noiseSize != 0.0) {
+ noiseSize = 1.0f / noiseSize;
+ x *= noiseSize;
+ y *= noiseSize;
+ z *= noiseSize;
+ }
+
+ float sum = 0, t, amp = 1, fscale = 1;
+ for (int i = 0; i <= noiseDepth; ++i, amp *= 0.5, fscale *= 2) {
+ t = abstractNoiseFunc.execute(fscale * x, fscale * y, fscale * z);
+ if (isHard) {
+ t = FastMath.abs(2.0f * t - 1.0f);
+ }
+ sum += t * amp;
+ }
+
+ sum *= (float) (1 << noiseDepth) / (float) ((1 << noiseDepth + 1) - 1);
+ return sum;
+ }
+
+ /**
+ * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point coords in pa
+ */
+ public static void voronoi(float x, float y, float z, float[] da, float[] pa, float distanceExponent, int distanceType) {
+ float xd, yd, zd, d, p[] = new float[3];
+
+ DistanceFunction distanceFunc = distanceFunctions.get(Integer.valueOf(distanceType));
+ if (distanceFunc == null) {
+ distanceFunc = distanceFunctions.get(Integer.valueOf(0));
+ }
+
+ int xi = (int) FastMath.floor(x);
+ int yi = (int) FastMath.floor(y);
+ int zi = (int) FastMath.floor(z);
+ da[0] = da[1] = da[2] = da[3] = 1e10f;
+ for (int i = xi - 1; i <= xi + 1; ++i) {
+ for (int j = yi - 1; j <= yi + 1; ++j) {
+ for (int k = zi - 1; k <= zi + 1; ++k) {
+ NoiseMath.hash(i, j, k, p);
+ xd = x - (p[0] + i);
+ yd = y - (p[1] + j);
+ zd = z - (p[2] + k);
+ d = distanceFunc.execute(xd, yd, zd, distanceExponent);
+ if (d < da[0]) {
+ da[3] = da[2];
+ da[2] = da[1];
+ da[1] = da[0];
+ da[0] = d;
+ pa[9] = pa[6];
+ pa[10] = pa[7];
+ pa[11] = pa[8];
+ pa[6] = pa[3];
+ pa[7] = pa[4];
+ pa[8] = pa[5];
+ pa[3] = pa[0];
+ pa[4] = pa[1];
+ pa[5] = pa[2];
+ pa[0] = p[0] + i;
+ pa[1] = p[1] + j;
+ pa[2] = p[2] + k;
+ } else if (d < da[1]) {
+ da[3] = da[2];
+ da[2] = da[1];
+ da[1] = d;
+ pa[9] = pa[6];
+ pa[10] = pa[7];
+ pa[11] = pa[8];
+ pa[6] = pa[3];
+ pa[7] = pa[4];
+ pa[8] = pa[5];
+ pa[3] = p[0] + i;
+ pa[4] = p[1] + j;
+ pa[5] = p[2] + k;
+ } else if (d < da[2]) {
+ da[3] = da[2];
+ da[2] = d;
+ pa[9] = pa[6];
+ pa[10] = pa[7];
+ pa[11] = pa[8];
+ pa[6] = p[0] + i;
+ pa[7] = p[1] + j;
+ pa[8] = p[2] + k;
+ } else if (d < da[3]) {
+ da[3] = d;
+ pa[9] = p[0] + i;
+ pa[10] = p[1] + j;
+ pa[11] = p[2] + k;
+ }
+ }
+ }
+ }
+ }
+
+ // instead of adding another permutation array, just use hash table defined above
+ public static float newPerlin(float x, float y, float z) {
+ int A, AA, AB, B, BA, BB;
+ float floorX = (float) Math.floor(x), floorY = (float) Math.floor(y), floorZ = (float) Math.floor(z);
+ int intX = (int) floorX & 0xFF, intY = (int) floorY & 0xFF, intZ = (int) floorZ & 0xFF;
+ x -= floorX;
+ y -= floorY;
+ z -= floorZ;
+ //computing fading curves
+ floorX = NoiseMath.npfade(x);
+ floorY = NoiseMath.npfade(y);
+ floorZ = NoiseMath.npfade(z);
+ A = hash[intX] + intY;
+ AA = hash[A] + intZ;
+ AB = hash[A + 1] + intZ;
+ B = hash[intX + 1] + intY;
+ BA = hash[B] + intZ;
+ BB = hash[B + 1] + intZ;
+ return NoiseMath.lerp(floorZ, NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA], x, y, z),
+ NoiseMath.grad(hash[BA], x - 1, y, z)),
+ NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB], x, y - 1, z),
+ NoiseMath.grad(hash[BB], x - 1, y - 1, z))),
+ NoiseMath.lerp(floorY, NoiseMath.lerp(floorX, NoiseMath.grad(hash[AA + 1], x, y, z - 1),
+ NoiseMath.grad(hash[BA + 1], x - 1, y, z - 1)),
+ NoiseMath.lerp(floorX, NoiseMath.grad(hash[AB + 1], x, y - 1, z - 1),
+ NoiseMath.grad(hash[BB + 1], x - 1, y - 1, z - 1))));
+ }
+
+ public static float noise3Perlin(float x, float y, float z) {
+ float t = x + 10000.0f;
+ int bx0 = (int) t & 0xFF;
+ int bx1 = bx0 + 1 & 0xFF;
+ float rx0 = t - (int) t;
+ float rx1 = rx0 - 1.0f;
+
+ t = y + 10000.0f;
+ int by0 = (int) t & 0xFF;
+ int by1 = by0 + 1 & 0xFF;
+ float ry0 = t - (int) t;
+ float ry1 = ry0 - 1.0f;
+
+ t = z + 10000.0f;
+ int bz0 = (int) t & 0xFF;
+ int bz1 = bz0 + 1 & 0xFF;
+ float rz0 = t - (int) t;
+ float rz1 = rz0 - 1.0f;
+
+ int i = p[bx0];
+ int j = p[bx1];
+
+ int b00 = p[i + by0];
+ int b10 = p[j + by0];
+ int b01 = p[i + by1];
+ int b11 = p[j + by1];
+
+ float sx = NoiseMath.surve(rx0);
+ float sy = NoiseMath.surve(ry0);
+ float sz = NoiseMath.surve(rz0);
+
+ float[] q = g[b00 + bz0];
+ float u = NoiseMath.at(rx0, ry0, rz0, q);
+ q = g[b10 + bz0];
+ float v = NoiseMath.at(rx1, ry0, rz0, q);
+ float a = NoiseMath.lerp(sx, u, v);
+
+ q = g[b01 + bz0];
+ u = NoiseMath.at(rx0, ry1, rz0, q);
+ q = g[b11 + bz0];
+ v = NoiseMath.at(rx1, ry1, rz0, q);
+ float b = NoiseMath.lerp(sx, u, v);
+
+ float c = NoiseMath.lerp(sy, a, b);
+
+ q = g[b00 + bz1];
+ u = NoiseMath.at(rx0, ry0, rz1, q);
+ q = g[b10 + bz1];
+ v = NoiseMath.at(rx1, ry0, rz1, q);
+ a = NoiseMath.lerp(sx, u, v);
+
+ q = g[b01 + bz1];
+ u = NoiseMath.at(rx0, ry1, rz1, q);
+ q = g[b11 + bz1];
+ v = NoiseMath.at(rx1, ry1, rz1, q);
+ b = NoiseMath.lerp(sx, u, v);
+
+ float d = NoiseMath.lerp(sy, a, b);
+ return 1.5f * NoiseMath.lerp(sz, c, d);
+ }
+
+ public static float originalBlenderNoise(float x, float y, float z) {
+ float n = 0.5f;
+
+ int ix = (int) Math.floor(x);
+ int iy = (int) Math.floor(y);
+ int iz = (int) Math.floor(z);
+
+ float ox = x - ix;
+ float oy = y - iy;
+ float oz = z - iz;
+
+ float jx = ox - 1;
+ float jy = oy - 1;
+ float jz = oz - 1;
+
+ float cn1 = ox * ox;
+ float cn2 = oy * oy;
+ float cn3 = oz * oz;
+ float cn4 = jx * jx;
+ float cn5 = jy * jy;
+ float cn6 = jz * jz;
+
+ cn1 = 1.0f - 3.0f * cn1 + 2.0f * cn1 * ox;
+ cn2 = 1.0f - 3.0f * cn2 + 2.0f * cn2 * oy;
+ cn3 = 1.0f - 3.0f * cn3 + 2.0f * cn3 * oz;
+ cn4 = 1.0f - 3.0f * cn4 - 2.0f * cn4 * jx;
+ cn5 = 1.0f - 3.0f * cn5 - 2.0f * cn5 * jy;
+ cn6 = 1.0f - 3.0f * cn6 - 2.0f * cn6 * jz;
+ float[] cn = new float[] {cn1 * cn2 * cn3, cn1 * cn2 * cn6, cn1 * cn5 * cn3, cn1 * cn5 * cn6,
+ cn4 * cn2 * cn3, cn4 * cn2 * cn6, cn4 * cn5 * cn3, cn4 * cn5 * cn6,};
+
+ int b00 = hash[hash[ix & 0xFF] + (iy & 0xFF)];
+ int b01 = hash[hash[ix & 0xFF] + (iy + 1 & 0xFF)];
+ int b10 = hash[hash[ix + 1 & 0xFF] + (iy & 0xFF)];
+ int b11 = hash[hash[ix + 1 & 0xFF] + (iy + 1 & 0xFF)];
+ int[] b1 = new int[] {b00, b00, b01, b01, b10, b10, b11, b11};
+
+ int[] b2 = new int[] {iz & 0xFF, iz + 1 & 0xFF};
+
+ float[] xFactor = new float[] {ox, ox, ox, ox, jx, jx, jx, jx};
+ float[] yFactor = new float[] {oy, oy, jy, jy, oy, oy, jy, jy};
+ float[] zFactor = new float[] {oz, jz, oz, jz, oz, jz, oz, jz};
+
+ for(int i=0;i<8;++i) {
+ int hIndex = 3 * hash[b1[i] + b2[i%2]];
+ n += cn[i] * (hashvectf[hIndex] * xFactor[i] + hashvectf[hIndex + 1] * yFactor[i] + hashvectf[hIndex + 2] * zFactor[i]);
+ }
+
+ if (n < 0.0f) {
+ n = 0.0f;
+ } else if (n > 1.0f) {
+ n = 1.0f;
+ }
+ return n;
+ }
+ }
+
+ /**
+ * This class is abstract to the noise functions computations. It has two methods. One calculates the Signed (with
+ * 'S' at the end) and the other Unsigned value.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ interface NoiseFunction {
+
+ /**
+ * This method calculates the unsigned value of the noise.
+ * @param x
+ * the x texture coordinate
+ * @param y
+ * the y texture coordinate
+ * @param z
+ * the z texture coordinate
+ * @return value of the noise
+ */
+ float execute(float x, float y, float z);
+
+ /**
+ * This method calculates the signed value of the noise.
+ * @param x
+ * the x texture coordinate
+ * @param y
+ * the y texture coordinate
+ * @param z
+ * the z texture coordinate
+ * @return value of the noise
+ */
+ float executeSigned(float x, float y, float z);
+ }
+
+ public static class NoiseMath {
+ public static float lerp(float t, float a, float b) {
+ return a + t * (b - a);
+ }
+
+ public static float npfade(float t) {
+ return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
+ }
+
+ public static float grad(int hash, float x, float y, float z) {
+ int h = hash & 0x0F;
+ float u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
+ return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
+ }
+
+ public static float surve(float t) {
+ return t * t * (3.0f - 2.0f * t);
+ }
+
+ public static float at(float x, float y, float z, float[] q) {
+ return x * q[0] + y * q[1] + z * q[2];
+ }
+
+ public static void hash(int x, int y, int z, float[] result) {
+ result[0] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF]];
+ result[1] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 1];
+ result[2] = hashpntf[3 * hash[hash[hash[z & 0xFF] + y & 0xFF] + x & 0xFF] + 2];
+ }
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return true;
+ }
+
+ /**
+ * This interface is used for distance calculation classes. Distance metrics for voronoi. e parameter only used in
+ * Minkovsky.
+ */
+ interface DistanceFunction {
+
+ /**
+ * This method calculates the distance for voronoi algorithms.
+ * @param x
+ * the x coordinate
+ * @param y
+ * the y coordinate
+ * @param z
+ * the z coordinate
+ * @param e
+ * this parameter used in Monkovsky (no idea what it really is ;)
+ * @return
+ */
+ float execute(float x, float y, float z, float e);
+ }
+
+ interface MusgraveFunction {
+
+ float execute(MusgraveData musgraveData, float x, float y, float z);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java
new file mode 100644
index 0000000..a727277
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGenerator.java
@@ -0,0 +1,416 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Texture;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is a base class for texture generators.
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */abstract class TextureGenerator {
+ private static final Logger LOGGER = Logger.getLogger(TextureGenerator.class.getName());
+
+ protected NoiseGenerator noiseGenerator;
+
+ public TextureGenerator(NoiseGenerator noiseGenerator) {
+ this.noiseGenerator = noiseGenerator;
+ }
+
+ /**
+ * This method generates the texture.
+ * @param tex
+ * texture's structure
+ * @param width
+ * the width of the result texture
+ * @param height
+ * the height of the result texture
+ * @param depth
+ * the depth of the texture
+ * @param blenderContext
+ * the blender context
+ * @return newly generated texture
+ */
+ protected abstract Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext);
+
+ /**
+ * This method reads the colorband data from the given texture structure.
+ *
+ * @param tex
+ * the texture structure
+ * @param blenderContext
+ * the blender context
+ * @return read colorband or null if not present
+ */
+ private ColorBand readColorband(Structure tex, BlenderContext blenderContext) {
+ ColorBand result = null;
+ int flag = ((Number) tex.getFieldValue("flag")).intValue();
+ if ((flag & NoiseGenerator.TEX_COLORBAND) != 0) {
+ Pointer pColorband = (Pointer) tex.getFieldValue("coba");
+ Structure colorbandStructure;
+ try {
+ colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
+ result = new ColorBand(colorbandStructure);
+ } catch (BlenderFileException e) {
+ LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
+ }
+ }
+ return result;
+ }
+
+ protected float[][] computeColorband(Structure tex, BlenderContext blenderContext) {
+ ColorBand colorBand = this.readColorband(tex, blenderContext);
+ float[][] result = null;
+ if(colorBand!=null) {
+ result = new float[1001][4];//1001 - amount of possible cursor positions; 4 = [r, g, b, a]
+ ColorBandData[] dataArray = colorBand.data;
+
+ if(dataArray.length==1) {//special case; use only one color for all types of colorband interpolation
+ for(int i=0;i<result.length;++i) {
+ result[i][0] = dataArray[0].r;
+ result[i][1] = dataArray[0].g;
+ result[i][2] = dataArray[0].b;
+ result[i][3] = dataArray[0].a;
+ }
+ } else {
+ int currentCursor = 0;
+ ColorBandData currentData = dataArray[0];
+ ColorBandData nextData = dataArray[0];
+ switch(colorBand.ipoType) {
+ case ColorBand.IPO_LINEAR:
+ float rDiff = 0, gDiff = 0, bDiff = 0, aDiff = 0, posDiff;
+ for(int i=0;i<result.length;++i) {
+ posDiff = i - currentData.pos;
+ result[i][0] = currentData.r + rDiff * posDiff;
+ result[i][1] = currentData.g + gDiff * posDiff;
+ result[i][2] = currentData.b + bDiff * posDiff;
+ result[i][3] = currentData.a + aDiff * posDiff;
+ if(nextData.pos==i) {
+ currentData = dataArray[currentCursor++];
+ if(currentCursor < dataArray.length) {
+ nextData = dataArray[currentCursor];
+ //calculate differences
+ int d = nextData.pos - currentData.pos;
+ rDiff = (nextData.r - currentData.r)/d;
+ gDiff = (nextData.g - currentData.g)/d;
+ bDiff = (nextData.b - currentData.b)/d;
+ aDiff = (nextData.a - currentData.a)/d;
+ } else {
+ rDiff = gDiff = bDiff = aDiff = 0;
+ }
+ }
+ }
+ break;
+ case ColorBand.IPO_BSPLINE:
+ case ColorBand.IPO_CARDINAL:
+ Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
+ for(int i=0;i<colorBand.data.length;++i) {
+ cbDataMap.put(Integer.valueOf(i), colorBand.data[i]);
+ }
+
+ if(colorBand.data[0].pos==0) {
+ cbDataMap.put(Integer.valueOf(-1), colorBand.data[0]);
+ } else {
+ ColorBandData cbData = colorBand.data[0].clone();
+ cbData.pos = 0;
+ cbDataMap.put(Integer.valueOf(-1), cbData);
+ cbDataMap.put(Integer.valueOf(-2), cbData);
+ }
+
+ if(colorBand.data[colorBand.data.length - 1].pos==1000) {
+ cbDataMap.put(Integer.valueOf(colorBand.data.length), colorBand.data[colorBand.data.length - 1]);
+ } else {
+ ColorBandData cbData = colorBand.data[colorBand.data.length - 1].clone();
+ cbData.pos = 1000;
+ cbDataMap.put(Integer.valueOf(colorBand.data.length), cbData);
+ cbDataMap.put(Integer.valueOf(colorBand.data.length + 1), cbData);
+ }
+
+ float[] ipoFactors = new float[4];
+ float f;
+
+ ColorBandData data0 = cbDataMap.get(currentCursor - 2);
+ ColorBandData data1 = cbDataMap.get(currentCursor - 1);
+ ColorBandData data2 = cbDataMap.get(currentCursor);
+ ColorBandData data3 = cbDataMap.get(currentCursor + 1);
+
+ for(int i=0;i<result.length;++i) {
+ if (data2.pos != data1.pos) {
+ f = (i - data2.pos) / (float)(data1.pos - data2.pos);
+ } else {
+ f = 0.0f;
+ }
+
+ f = FastMath.clamp(f, 0.0f, 1.0f);
+
+ this.getIpoData(colorBand, f, ipoFactors);
+ result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
+ result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
+ result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
+ result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
+ result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
+ result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
+ result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
+ result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
+
+ if(nextData.pos==i) {
+ ++currentCursor;
+ data0 = cbDataMap.get(currentCursor - 2);
+ data1 = cbDataMap.get(currentCursor - 1);
+ data2 = cbDataMap.get(currentCursor);
+ data3 = cbDataMap.get(currentCursor + 1);
+ }
+ }
+ break;
+ case ColorBand.IPO_EASE:
+ float d, a, b, d2;
+ for(int i=0;i<result.length;++i) {
+ if(nextData.pos != currentData.pos) {
+ d = (i - currentData.pos) / (float)(nextData.pos - currentData.pos);
+ d2 = d * d;
+ a = 3.0f * d2 - 2.0f * d * d2;
+ b = 1.0f - a;
+ } else {
+ d = a = 0.0f;
+ b = 1.0f;
+ }
+
+ result[i][0] = b * currentData.r + a * nextData.r;
+ result[i][1] = b * currentData.g + a * nextData.g;
+ result[i][2] = b * currentData.b + a * nextData.b;
+ result[i][3] = b * currentData.a + a * nextData.a;
+ if(nextData.pos==i) {
+ currentData = dataArray[currentCursor++];
+ if(currentCursor < dataArray.length) {
+ nextData = dataArray[currentCursor];
+ }
+ }
+ }
+ break;
+ case ColorBand.IPO_CONSTANT:
+ for(int i=0;i<result.length;++i) {
+ result[i][0] = currentData.r;
+ result[i][1] = currentData.g;
+ result[i][2] = currentData.b;
+ result[i][3] = currentData.a;
+ if(nextData.pos==i) {
+ currentData = dataArray[currentCursor++];
+ if(currentCursor < dataArray.length) {
+ nextData = dataArray[currentCursor];
+ }
+ }
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown interpolation type: " + colorBand.ipoType);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the data for either B-spline of Cardinal interpolation.
+ * @param colorBand the color band
+ * @param d distance factor for the current intensity
+ * @param ipoFactors table to store the results (size of the table must be at least 4)
+ */
+ private void getIpoData(ColorBand colorBand, float d, float[] ipoFactors) {
+ float d2 = d * d;
+ float d3 = d2 * d;
+ if(colorBand.ipoType==ColorBand.IPO_BSPLINE) {
+ ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
+ ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
+ ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
+ ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
+ } else if(colorBand.ipoType==ColorBand.IPO_CARDINAL) {
+ ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
+ ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
+ ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
+ ipoFactors[3] = 0.16666666f * d3;
+ } else {
+ throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
+ }
+ }
+
+ /**
+ * This method applies brightness and contrast for RGB textures.
+ * @param tex texture structure
+ * @param texres
+ */
+ protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) {
+ texres.red = (texres.red - 0.5f) * bacd.contrast + bacd.brightness;
+ if (texres.red < 0.0f) {
+ texres.red = 0.0f;
+ }
+ texres.green =(texres.green - 0.5f) * bacd.contrast + bacd.brightness;
+ if (texres.green < 0.0f) {
+ texres.green = 0.0f;
+ }
+ texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness;
+ if (texres.blue < 0.0f) {
+ texres.blue = 0.0f;
+ }
+ }
+
+ /**
+ * This method applies brightness and contrast for Luminance textures.
+ * @param texres
+ * @param contrast
+ * @param brightness
+ */
+ protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) {
+ texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
+ if (texres.intensity < 0.0f) {
+ texres.intensity = 0.0f;
+ } else if (texres.intensity > 1.0f) {
+ texres.intensity = 1.0f;
+ }
+ }
+
+ /**
+ * A class constaining the colorband data.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ protected static class ColorBand {
+ //interpolation types
+ public static final int IPO_LINEAR = 0;
+ public static final int IPO_EASE = 1;
+ public static final int IPO_BSPLINE = 2;
+ public static final int IPO_CARDINAL = 3;
+ public static final int IPO_CONSTANT = 4;
+
+ public int cursorsAmount, ipoType;
+ public ColorBandData[] data;
+
+ /**
+ * Constructor. Loads the data from the given structure.
+ *
+ * @param cbdataStructure
+ * the colorband structure
+ */
+ @SuppressWarnings("unchecked")
+ public ColorBand(Structure colorbandStructure) {
+ this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
+ this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
+ this.data = new ColorBandData[this.cursorsAmount];
+ DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
+ for (int i = 0; i < this.cursorsAmount; ++i) {
+ this.data[i] = new ColorBandData(data.get(i));
+ }
+ }
+ }
+
+ /**
+ * Class to store the single colorband cursor data.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+ protected static class ColorBandData implements Cloneable {
+ public final float r, g, b, a;
+ public int pos;
+
+ /**
+ * Copy constructor.
+ */
+ private ColorBandData(ColorBandData data) {
+ this.r = data.r;
+ this.g = data.g;
+ this.b = data.b;
+ this.a = data.a;
+ this.pos = data.pos;
+ }
+
+ /**
+ * Constructor. Loads the data from the given structure.
+ *
+ * @param cbdataStructure
+ * the structure containing the CBData object
+ */
+ public ColorBandData(Structure cbdataStructure) {
+ this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
+ this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
+ this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
+ this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
+ this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
+ }
+
+ @Override
+ public ColorBandData clone() {
+ try {
+ return (ColorBandData) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return new ColorBandData(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "P: " + this.pos + " [" + this.r+", "+this.g+", "+this.b+", "+this.a+"]";
+ }
+ }
+
+ /**
+ * This class contains brightness and contrast data.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ protected static class BrightnessAndContrastData {
+ public final float contrast;
+ public final float brightness;
+ public final float rFactor;
+ public final float gFactor;
+ public final float bFactor;
+
+ /**
+ * Constructor reads the required data from the given structure.
+ * @param tex texture structure
+ */
+ public BrightnessAndContrastData(Structure tex) {
+ contrast = ((Number) tex.getFieldValue("contrast")).floatValue();
+ brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f;
+ rFactor = ((Number) tex.getFieldValue("rfac")).floatValue();
+ gFactor = ((Number) tex.getFieldValue("gfac")).floatValue();
+ bFactor = ((Number) tex.getFieldValue("bfac")).floatValue();
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java
new file mode 100644
index 0000000..18ef409
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorBlend.java
@@ -0,0 +1,168 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'blend' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public final class TextureGeneratorBlend extends TextureGenerator {
+
+ private static final IntensityFunction INTENSITY_FUNCTION[] = new IntensityFunction[7];
+ static {
+ INTENSITY_FUNCTION[0] = new IntensityFunction() {//Linear: stype = 0 (TEX_LIN)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ return (1.0f + x) * 0.5f;
+ }
+ };
+ INTENSITY_FUNCTION[1] = new IntensityFunction() {//Quad: stype = 1 (TEX_QUAD)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = (1.0f + x) * 0.5f;
+ return result * result;
+ }
+ };
+ INTENSITY_FUNCTION[2] = new IntensityFunction() {//Ease: stype = 2 (TEX_EASE)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = (1.0f + x) * 0.5f;
+ if (result <= 0.0f) {
+ return 0.0f;
+ } else if (result >= 1.0f) {
+ return 1.0f;
+ } else {
+ return result * result *(3.0f - 2.0f * result);
+ }
+ }
+ };
+ INTENSITY_FUNCTION[3] = new IntensityFunction() {//Diagonal: stype = 3 (TEX_DIAG)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ return (2.0f + x + y) * 0.25f;
+ }
+ };
+ INTENSITY_FUNCTION[4] = new IntensityFunction() {//Sphere: stype = 4 (TEX_SPHERE)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
+ return result < 0.0f ? 0.0f : result;
+ }
+ };
+ INTENSITY_FUNCTION[5] = new IntensityFunction() {//Halo: stype = 5 (TEX_HALO)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ float result = 1.0f - (float) Math.sqrt(x * x + y * y + z * z);
+ return result <= 0.0f ? 0.0f : result * result;
+ }
+ };
+ INTENSITY_FUNCTION[6] = new IntensityFunction() {//Radial: stype = 6 (TEX_RAD)
+ @Override
+ public float getIntensity(float x, float y, float z) {
+ return (float) Math.atan2(y, x) * FastMath.INV_TWO_PI + 0.5f;
+ }
+ };
+ }
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorBlend(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ int flag = ((Number) tex.getFieldValue("flag")).intValue();
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, x, y;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ boolean flipped = (flag & NoiseGenerator.TEX_FLIPBLEND) != 0;
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ x = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ if (flipped) {
+ y = x;
+ x = hDelta * j;
+ } else {
+ y = hDelta * j;
+ }
+ for (int k = -halfD; k < halfD; ++k) {
+ texres.intensity = INTENSITY_FUNCTION[stype].getIntensity(x, y, dDelta * k);
+
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ private static interface IntensityFunction {
+ float getIntensity(float x, float y, float z);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java
new file mode 100644
index 0000000..9ac24e5
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorClouds.java
@@ -0,0 +1,131 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'clouds' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorClouds extends TextureGenerator {
+ // tex->noisetype
+ protected static final int TEX_NOISESOFT = 0;
+ protected static final int TEX_NOISEPERL = 1;
+
+ // tex->stype
+ protected static final int TEX_DEFAULT = 0;
+ protected static final int TEX_COLOR = 1;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorClouds(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+
+ // reading the data from the texture structure
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ int noiseDepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ int noiseBasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ int noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
+ boolean isHard = noiseType != TEX_NOISESOFT;
+ int sType = ((Number) tex.getFieldValue("stype")).intValue();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = sType == TEX_COLOR || colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = sType == TEX_COLOR || colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = NoiseGenerator.NoiseFunctions.turbulence(texvec[0], texvec[1], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
+ texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else if (sType == TEX_COLOR) {
+ texres.red = texres.intensity;
+ texres.green = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[0], texvec[2], noisesize, noiseDepth, noiseBasis, isHard);
+ texres.blue = NoiseGenerator.NoiseFunctions.turbulence(texvec[1], texvec[2], texvec[0], noisesize, noiseDepth, noiseBasis, isHard);
+
+ texres.green = FastMath.clamp(texres.green, 0.0f, 1.0f);
+ texres.blue = FastMath.clamp(texres.blue, 0.0f, 1.0f);
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (255);//1.0f * 255.0f
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java
new file mode 100644
index 0000000..f66beb6
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorDistnoise.java
@@ -0,0 +1,135 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseFunction;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'distorted noise' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorDistnoise extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorDistnoise(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ float distAmount = ((Number) tex.getFieldValue("dist_amount")).floatValue();
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ int noisebasis2 = ((Number) tex.getFieldValue("noisebasis2")).intValue();
+
+ TexturePixel texres = new TexturePixel();
+ float[] texvec = new float[] { 0, 0, 0 };
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i / noisesize;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j / noisesize;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = this.musgraveVariableLunacrityNoise(texvec[0], texvec[1], texvec[2], distAmount, noisebasis, noisebasis2);
+ texres.intensity = FastMath.clamp(texres.intensity, 0.0f, 1.0f);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ /**
+ * "Variable Lacunarity Noise" A distorted variety of Perlin noise. This method is used to calculate distorted noise
+ * texture.
+ * @param x
+ * @param y
+ * @param z
+ * @param distortion
+ * @param nbas1
+ * @param nbas2
+ * @return
+ */
+ private float musgraveVariableLunacrityNoise(float x, float y, float z, float distortion, int nbas1, int nbas2) {
+ NoiseFunction abstractNoiseFunc1 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(nbas1));
+ if (abstractNoiseFunc1 == null) {
+ abstractNoiseFunc1 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(0));
+ }
+ NoiseFunction abstractNoiseFunc2 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(nbas2));
+ if (abstractNoiseFunc2 == null) {
+ abstractNoiseFunc2 = NoiseGenerator.noiseFunctions.get(Integer.valueOf(0));
+ }
+ // get a random vector and scale the randomization
+ float rx = abstractNoiseFunc1.execute(x + 13.5f, y + 13.5f, z + 13.5f) * distortion;
+ float ry = abstractNoiseFunc1.execute(x, y, z) * distortion;
+ float rz = abstractNoiseFunc1.execute(x - 13.5f, y - 13.5f, z - 13.5f) * distortion;
+ return abstractNoiseFunc2.executeSigned(x + rx, y + ry, z + rz); //distorted-domain noise
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java
new file mode 100644
index 0000000..e8a1637
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMagic.java
@@ -0,0 +1,191 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'magic' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorMagic extends TextureGenerator {
+ private static NoiseDepthFunction[] noiseDepthFunctions = new NoiseDepthFunction[10];
+ static {
+ noiseDepthFunctions[0] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.cos(xyz[0] - xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[1] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = (float) Math.cos(xyz[0] - xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[2] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[2] = (float) Math.sin(-xyz[0] - xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[3] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = -(float) Math.cos(-xyz[0] + xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[4] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.sin(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[5] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.cos(-xyz[0] + xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[6] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = (float) Math.cos(xyz[0] + xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[7] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[2] = (float) Math.sin(xyz[0] + xyz[1] - xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[8] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[0] = -(float) Math.cos(-xyz[0] - xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ noiseDepthFunctions[9] = new NoiseDepthFunction() {
+ @Override
+ public void compute(float[] xyz, float turbulence) {
+ xyz[1] = -(float) Math.sin(xyz[0] - xyz[1] + xyz[2]) * turbulence;
+ }
+ };
+ }
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorMagic(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float xyz[] = new float[3], turb;
+ int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ float turbul = ((Number) tex.getFieldValue("turbul")).floatValue() / 5.0f;
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * 4];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ turb = turbul;
+ texvec[2] = dDelta * k;
+ xyz[0] = (float) Math.sin((texvec[0] + texvec[1] + texvec[2]) * 5.0f);
+ xyz[1] = (float) Math.cos((-texvec[0] + texvec[1] - texvec[2]) * 5.0f);
+ xyz[2] = -(float) Math.cos((-texvec[0] - texvec[1] + texvec[2]) * 5.0f);
+
+ if (colorBand != null) {
+ texres.intensity = FastMath.clamp(0.3333f * (xyz[0] + xyz[1] + xyz[2]), 0.0f, 1.0f);
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+ texres.alpha = colorBand[colorbandIndex][3];
+ } else {
+ if (noisedepth > 0) {
+ xyz[0] *= turb;
+ xyz[1] *= turb;
+ xyz[2] *= turb;
+ for (int m=0;m<noisedepth;++m) {
+ noiseDepthFunctions[m].compute(xyz, turb);
+ }
+ }
+
+ if (turb != 0.0f) {
+ turb *= 2.0f;
+ xyz[0] /= turb;
+ xyz[1] /= turb;
+ xyz[2] /= turb;
+ }
+ texres.red = 0.5f - xyz[0];
+ texres.green = 0.5f - xyz[1];
+ texres.blue = 0.5f - xyz[2];
+ texres.alpha = 1.0f;
+ }
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (texres.alpha * 255.0f);
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+ }
+
+ private static interface NoiseDepthFunction {
+ void compute(float[] xyz, float turbulence);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java
new file mode 100644
index 0000000..06c08a9
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMarble.java
@@ -0,0 +1,148 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'marble' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorMarble extends TextureGeneratorWood {
+ // tex->stype
+ protected static final int TEX_SOFT = 0;
+ protected static final int TEX_SHARP = 1;
+ protected static final int TEX_SHARPER = 2;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorMarble(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+ MarbleData marbleData = new MarbleData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = this.marbleInt(marbleData, texvec[0], texvec[1], texvec[2]);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ public float marbleInt(MarbleData marbleData, float x, float y, float z) {
+ int waveform;
+ if (marbleData.waveform > TEX_TRI || marbleData.waveform < TEX_SIN) {
+ waveform = 0;
+ } else {
+ waveform = marbleData.waveform;
+ }
+
+ float n = 5.0f * (x + y + z);
+ float mi = n + marbleData.turbul * NoiseGenerator.NoiseFunctions.turbulence(x, y, z, marbleData.noisesize, marbleData.noisedepth, marbleData.noisebasis, marbleData.isHard);
+
+ if (marbleData.stype >= TEX_SOFT) {
+ mi = waveformFunctions[waveform].execute(mi);
+ if (marbleData.stype == TEX_SHARP) {
+ mi = (float) Math.sqrt(mi);
+ } else if (marbleData.stype == TEX_SHARPER) {
+ mi = (float) Math.sqrt(Math.sqrt(mi));
+ }
+ }
+ return mi;
+ }
+
+ private static class MarbleData {
+ public final float noisesize;
+ public final int noisebasis;
+ public final int noisedepth;
+ public final int stype;
+ public final float turbul;
+ public final int waveform;
+ public final boolean isHard;
+
+ public MarbleData(Structure tex) {
+ noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ stype = ((Number) tex.getFieldValue("stype")).intValue();
+ turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+ int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
+ waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue();
+ isHard = noisetype != TEX_NOISESOFT;
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java
new file mode 100644
index 0000000..667063f
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorMusgrave.java
@@ -0,0 +1,137 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.NoiseGenerator.MusgraveFunction;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'musgrave' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorMusgrave extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorMusgrave(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ TexturePixel texres = new TexturePixel();
+ float[] texvec = new float[] { 0, 0, 0 };
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ MusgraveData musgraveData = new MusgraveData(tex);
+ MusgraveFunction musgraveFunction;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i / noisesize;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j / noisesize;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k / noisesize;
+ musgraveFunction = NoiseGenerator.musgraveFunctions.get(Integer.valueOf(musgraveData.stype));
+ if(musgraveFunction==null) {
+ throw new IllegalStateException("Unknown type of musgrave texture: " + stype);
+ }
+ texres.intensity = musgraveData.outscale * musgraveFunction.execute(musgraveData, texvec[0], texvec[1], texvec[2]);
+ if(texres.intensity>1) {
+ texres.intensity = 1.0f;
+ } else if(texres.intensity < 0) {
+ texres.intensity = 0.0f;
+ }
+
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ protected static class MusgraveData {
+ public final int stype;
+ public final float outscale;
+ public final float h;
+ public final float lacunarity;
+ public final float octaves;
+ public final int noisebasis;
+ public final float offset;
+ public final float gain;
+
+ public MusgraveData(Structure tex) {
+ stype = ((Number) tex.getFieldValue("stype")).intValue();
+ outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
+ h = ((Number) tex.getFieldValue("mg_H")).floatValue();
+ lacunarity = ((Number) tex.getFieldValue("mg_lacunarity")).floatValue();
+ octaves = ((Number) tex.getFieldValue("mg_octaves")).floatValue();
+ noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ offset = ((Number) tex.getFieldValue("mg_offset")).floatValue();
+ gain = ((Number) tex.getFieldValue("mg_gain")).floatValue();
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java
new file mode 100644
index 0000000..02d4687
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorNoise.java
@@ -0,0 +1,106 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'noise' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorNoise extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorNoise(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ int val, random, loop;
+ int noisedepth = ((Number) tex.getFieldValue("noisedepth")).intValue();
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ for (int j = -halfH; j < halfH; ++j) {
+ for (int k = -halfD; k < halfD; ++k) {
+ random = FastMath.rand.nextInt();
+ val = random & 3;
+
+ loop = noisedepth;
+ while (loop-- != 0) {
+ random >>= 2;
+ val *= random & 3;
+ }
+ texres.intensity = FastMath.clamp(val, 0.0f, 1.0f);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java
new file mode 100644
index 0000000..76a6614
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorStucci.java
@@ -0,0 +1,125 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'stucci' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorStucci extends TextureGenerator {
+ protected static final int TEX_NOISESOFT = 0;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorStucci(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ int noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ int noisetype = ((Number) tex.getFieldValue("noisetype")).intValue();
+ float turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+ boolean isHard = noisetype != TEX_NOISESOFT;
+ int stype = ((Number) tex.getFieldValue("stype")).intValue();
+
+ if(noisesize<=0.001f) {//the texture goes black if this value is lower than 0.001f
+ noisesize = 0.001f;
+ }
+
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD, noiseValue, ofs;;
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ noiseValue = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2], noisesize, 0, noisebasis, isHard);
+ ofs = turbul / 200.0f;
+ if (stype != 0) {
+ ofs *= noiseValue * noiseValue;
+ }
+
+ texres.intensity = NoiseGenerator.NoiseFunctions.noise(texvec[0], texvec[1], texvec[2] + ofs, noisesize, 0, noisebasis, isHard);
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+ texres.alpha = colorBand[colorbandIndex][3];
+ }
+
+ if (stype == NoiseGenerator.TEX_WALLOUT) {
+ texres.intensity = 1.0f - texres.intensity;
+ }
+ if (texres.intensity < 0.0f) {
+ texres.intensity = 0.0f;
+ }
+ //no brightness and contrast needed for stucci (it doesn't affect the texture)
+ if (colorBand != null) {
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (texres.alpha * 255.0f);
+ } else {
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java
new file mode 100644
index 0000000..1dee158
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorVoronoi.java
@@ -0,0 +1,171 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.textures.NoiseGenerator.NoiseMath;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'voronoi' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorVoronoi extends TextureGenerator {
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator
+ * the noise generator
+ */
+ public TextureGeneratorVoronoi(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float voronoiWeight1 = ((Number) tex.getFieldValue("vn_w1")).floatValue();
+ float voronoiWeight2 = ((Number) tex.getFieldValue("vn_w2")).floatValue();
+ float voronoiWeight3 = ((Number) tex.getFieldValue("vn_w3")).floatValue();
+ float voronoiWeight4 = ((Number) tex.getFieldValue("vn_w4")).floatValue();
+ float noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ float outscale = ((Number) tex.getFieldValue("ns_outscale")).floatValue();
+ float mexp = ((Number) tex.getFieldValue("vn_mexp")).floatValue();
+ int distm = ((Number) tex.getFieldValue("vn_distm")).intValue();
+ int voronoiColorType = ((Number) tex.getFieldValue("vn_coltype")).intValue();
+
+ TexturePixel texres = new TexturePixel();
+ float[] texvec = new float[] { 0, 0, 0 };
+ int halfW = width >> 1, halfH = height >> 1, halfD = depth >> 1, index = 0;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = voronoiColorType != 0 || colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = voronoiColorType != 0 || colorBand != null ? 4 : 1;
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ float[] da = new float[4], pa = new float[12];
+ float[] hashPoint = voronoiColorType != 0 ? new float[3] : null;
+ float[] voronoiWeights = new float[] {FastMath.abs(voronoiWeight1), FastMath.abs(voronoiWeight2),
+ FastMath.abs(voronoiWeight3), FastMath.abs(voronoiWeight4)};
+ float weight;
+ float sc = voronoiWeights[0] + voronoiWeights[1] + voronoiWeights[2] + voronoiWeights[3];
+ if (sc != 0.0f) {
+ sc = outscale / sc;
+ }
+
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i / noisesize;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j / noisesize;
+ for (int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ NoiseGenerator.NoiseFunctions.voronoi(texvec[0], texvec[1], texvec[2], da, pa, mexp, distm);
+ texres.intensity = sc * FastMath.abs(voronoiWeight1 * da[0] + voronoiWeight2 * da[1] + voronoiWeight3 * da[2] + voronoiWeight4 * da[3]);
+ if(texres.intensity>1.0f) {
+ texres.intensity = 1.0f;
+ } else if(texres.intensity<0.0f) {
+ texres.intensity = 0.0f;
+ }
+
+ if (colorBand != null) {//colorband ALWAYS goes first and covers the color (if set)
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+ texres.alpha = colorBand[colorbandIndex][3];
+ } else if (voronoiColorType != 0) {
+ texres.red = texres.green = texres.blue = 0.0f;
+ texres.alpha = 1.0f;
+ for(int m=0; m<12; m+=3) {
+ weight = voronoiWeights[m/3];
+ this.cellNoiseV(pa[m], pa[m + 1], pa[m + 2], hashPoint);
+ texres.red += weight * hashPoint[0];
+ texres.green += weight * hashPoint[1];
+ texres.blue += weight * hashPoint[2];
+ }
+ if (voronoiColorType >= 2) {
+ float t1 = (da[1] - da[0]) * 10.0f;
+ if (t1 > 1.0f) {
+ t1 = 1.0f;
+ }
+ if (voronoiColorType == 3) {
+ t1 *= texres.intensity;
+ } else {
+ t1 *= sc;
+ }
+ texres.red *= t1;
+ texres.green *= t1;
+ texres.blue *= t1;
+ } else {
+ texres.red *= sc;
+ texres.green *= sc;
+ texres.blue *= sc;
+ }
+ }
+
+ if (voronoiColorType != 0 || colorBand != null) {
+ this.applyBrightnessAndContrast(bacd, texres);
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (texres.alpha * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ /**
+ * Returns a vector/point/color in ca, using point hasharray directly
+ */
+ private void cellNoiseV(float x, float y, float z, float[] hashPoint) {
+ int xi = (int) Math.floor(x);
+ int yi = (int) Math.floor(y);
+ int zi = (int) Math.floor(z);
+ NoiseMath.hash(xi, yi, zi, hashPoint);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java
new file mode 100644
index 0000000..bf5e050
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureGeneratorWood.java
@@ -0,0 +1,216 @@
+/*
+ *
+ * $Id: noise.c 14611 2008-04-29 08:24:33Z campbellbarton $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * This class generates the 'wood' texture.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureGeneratorWood extends TextureGenerator {
+ // tex->noisebasis2
+ protected static final int TEX_SIN = 0;
+ protected static final int TEX_SAW = 1;
+ protected static final int TEX_TRI = 2;
+
+ // tex->stype
+ protected static final int TEX_BAND = 0;
+ protected static final int TEX_RING = 1;
+ protected static final int TEX_BANDNOISE = 2;
+ protected static final int TEX_RINGNOISE = 3;
+
+ // tex->noisetype
+ protected static final int TEX_NOISESOFT = 0;
+ protected static final int TEX_NOISEPERL = 1;
+
+ /**
+ * Constructor stores the given noise generator.
+ * @param noiseGenerator the noise generator
+ */
+ public TextureGeneratorWood(NoiseGenerator noiseGenerator) {
+ super(noiseGenerator);
+ }
+
+ @Override
+ protected Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext) {
+ float[] texvec = new float[] { 0, 0, 0 };
+ TexturePixel texres = new TexturePixel();
+ int halfW = width >> 1;
+ int halfH = height >> 1;
+ int halfD = depth >> 1;
+ float wDelta = 1.0f / halfW, hDelta = 1.0f / halfH, dDelta = 1.0f / halfD;
+
+ float[][] colorBand = this.computeColorband(tex, blenderContext);
+ Format format = colorBand != null ? Format.RGBA8 : Format.Luminance8;
+ int bytesPerPixel = colorBand != null ? 4 : 1;
+ WoodIntensityData woodIntensityData = new WoodIntensityData(tex);
+ BrightnessAndContrastData bacd = new BrightnessAndContrastData(tex);
+
+ int index = 0;
+ byte[] data = new byte[width * height * depth * bytesPerPixel];
+ for (int i = -halfW; i < halfW; ++i) {
+ texvec[0] = wDelta * i;
+ for (int j = -halfH; j < halfH; ++j) {
+ texvec[1] = hDelta * j;
+ for(int k = -halfD; k < halfD; ++k) {
+ texvec[2] = dDelta * k;
+ texres.intensity = this.woodIntensity(woodIntensityData, texvec[0], texvec[1], texvec[2]);
+
+ if (colorBand != null) {
+ int colorbandIndex = (int) (texres.intensity * 1000.0f);
+ texres.red = colorBand[colorbandIndex][0];
+ texres.green = colorBand[colorbandIndex][1];
+ texres.blue = colorBand[colorbandIndex][2];
+
+ this.applyBrightnessAndContrast(bacd, texres);
+
+ data[index++] = (byte) (texres.red * 255.0f);
+ data[index++] = (byte) (texres.green * 255.0f);
+ data[index++] = (byte) (texres.blue * 255.0f);
+ data[index++] = (byte) (colorBand[colorbandIndex][3] * 255.0f);
+ } else {
+ this.applyBrightnessAndContrast(texres, bacd.contrast, bacd.brightness);
+ data[index++] = (byte) (texres.intensity * 255.0f);
+ }
+ }
+ }
+ }
+
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(BufferUtils.createByteBuffer(data));
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+
+ protected static WaveForm[] waveformFunctions = new WaveForm[3];
+ static {
+ waveformFunctions[0] = new WaveForm() {// sinus (TEX_SIN)
+
+ @Override
+ public float execute(float x) {
+ return 0.5f + 0.5f * (float) Math.sin(x);
+ }
+ };
+ waveformFunctions[1] = new WaveForm() {// saw (TEX_SAW)
+
+ @Override
+ public float execute(float x) {
+ int n = (int) (x * FastMath.INV_TWO_PI);
+ x -= n * FastMath.TWO_PI;
+ if (x < 0.0f) {
+ x += FastMath.TWO_PI;
+ }
+ return x * FastMath.INV_TWO_PI;
+ }
+ };
+ waveformFunctions[2] = new WaveForm() {// triangle (TEX_TRI)
+
+ @Override
+ public float execute(float x) {
+ return 1.0f - 2.0f * FastMath.abs((float) Math.floor(x * FastMath.INV_TWO_PI + 0.5f) - x * FastMath.INV_TWO_PI);
+ }
+ };
+ }
+
+ /**
+ * Computes basic wood intensity value at x,y,z.
+ * @param woodIntData
+ * @param x X coordinate of the texture pixel
+ * @param y Y coordinate of the texture pixel
+ * @param z Z coordinate of the texture pixel
+ * @return wood intensity at position [x, y, z]
+ */
+ public float woodIntensity(WoodIntensityData woodIntData, float x, float y, float z) {
+ float result;
+
+ switch(woodIntData.woodType) {
+ case TEX_BAND:
+ result = woodIntData.waveformFunction.execute((x + y + z) * 10.0f);
+ break;
+ case TEX_RING:
+ result = woodIntData.waveformFunction.execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f);
+ break;
+ case TEX_BANDNOISE:
+ result = woodIntData.turbul * NoiseGenerator.NoiseFunctions.noise(x, y, z, woodIntData.noisesize, 0, woodIntData.noisebasis, woodIntData.isHard);
+ result = woodIntData.waveformFunction.execute((x + y + z) * 10.0f + result);
+ break;
+ case TEX_RINGNOISE:
+ result = woodIntData.turbul * NoiseGenerator.NoiseFunctions.noise(x, y, z, woodIntData.noisesize, 0, woodIntData.noisebasis, woodIntData.isHard);
+ result = woodIntData.waveformFunction.execute((float) Math.sqrt(x * x + y * y + z * z) * 20.0f + result);
+ break;
+ default:
+ result = 0;
+ }
+ return result;
+ }
+
+ /**
+ * A class that collects the data for wood intensity calculations.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ private static class WoodIntensityData {
+ public final WaveForm waveformFunction;
+ public final int noisebasis;
+ public final float noisesize;
+ public final float turbul;
+ public final int noiseType;
+ public final int woodType;
+ public final boolean isHard;
+
+ public WoodIntensityData(Structure tex) {
+ int waveform = ((Number) tex.getFieldValue("noisebasis2")).intValue();//wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2
+ if (waveform > TEX_TRI || waveform < TEX_SIN) {
+ waveform = 0; // check to be sure noisebasis2 is initialized ahead of time
+ }
+ waveformFunction = waveformFunctions[waveform];
+ noisebasis = ((Number) tex.getFieldValue("noisebasis")).intValue();
+ woodType = ((Number) tex.getFieldValue("stype")).intValue();
+ noisesize = ((Number) tex.getFieldValue("noisesize")).floatValue();
+ turbul = ((Number) tex.getFieldValue("turbul")).floatValue();
+ noiseType = ((Number) tex.getFieldValue("noisetype")).intValue();
+ isHard = noiseType != TEX_NOISESOFT;
+ }
+ }
+
+ protected static interface WaveForm {
+
+ float execute(float x);
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
new file mode 100644
index 0000000..0e0861d
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java
@@ -0,0 +1,481 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorConvertOp;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jme3tools.converters.ImageToAwt;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.BlenderKey;
+import com.jme3.asset.BlenderKey.FeaturesToLoad;
+import com.jme3.asset.GeneratedTextureKey;
+import com.jme3.asset.TextureKey;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.materials.MaterialContext;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+
+/**
+ * A class that is used in texture calculations.
+ *
+ * @author Marcin Roguski
+ */
+public class TextureHelper extends AbstractBlenderHelper {
+ private static final Logger LOGGER = Logger.getLogger(TextureHelper.class.getName());
+
+ // texture types
+ public static final int TEX_NONE = 0;
+ public static final int TEX_CLOUDS = 1;
+ public static final int TEX_WOOD = 2;
+ public static final int TEX_MARBLE = 3;
+ public static final int TEX_MAGIC = 4;
+ public static final int TEX_BLEND = 5;
+ public static final int TEX_STUCCI = 6;
+ public static final int TEX_NOISE = 7;
+ public static final int TEX_IMAGE = 8;
+ public static final int TEX_PLUGIN = 9;
+ public static final int TEX_ENVMAP = 10;
+ public static final int TEX_MUSGRAVE = 11;
+ public static final int TEX_VORONOI = 12;
+ public static final int TEX_DISTNOISE = 13;
+ public static final int TEX_POINTDENSITY = 14;//v. 25+
+ public static final int TEX_VOXELDATA = 15;//v. 25+
+
+ // mapto
+ public static final int MAP_COL = 1;
+ public static final int MAP_NORM = 2;
+ public static final int MAP_COLSPEC = 4;
+ public static final int MAP_COLMIR = 8;
+ public static final int MAP_VARS = 0xFFF0;
+ public static final int MAP_REF = 16;
+ public static final int MAP_SPEC = 32;
+ public static final int MAP_EMIT = 64;
+ public static final int MAP_ALPHA = 128;
+ public static final int MAP_HAR = 256;
+ public static final int MAP_RAYMIRR = 512;
+ public static final int MAP_TRANSLU = 1024;
+ public static final int MAP_AMB = 2048;
+ public static final int MAP_DISPLACE = 4096;
+ public static final int MAP_WARP = 8192;
+ public static final int MAP_LAYER = 16384;
+
+ protected NoiseGenerator noiseGenerator;
+ private Map<Integer, TextureGenerator> textureGenerators = new HashMap<Integer, TextureGenerator>();
+
+ /**
+ * This constructor parses the given blender version and stores the result.
+ * It creates noise generator and texture generators.
+ *
+ * @param blenderVersion
+ * the version read from the blend file
+ * @param fixUpAxis
+ * a variable that indicates if the Y asxis is the UP axis or not
+ */
+ public TextureHelper(String blenderVersion, boolean fixUpAxis) {
+ super(blenderVersion, false);
+ noiseGenerator = new NoiseGenerator(blenderVersion);
+ textureGenerators.put(Integer.valueOf(TEX_BLEND), new TextureGeneratorBlend(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_CLOUDS), new TextureGeneratorClouds(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_DISTNOISE), new TextureGeneratorDistnoise(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_MAGIC), new TextureGeneratorMagic(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_MARBLE), new TextureGeneratorMarble(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_MUSGRAVE), new TextureGeneratorMusgrave(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_NOISE), new TextureGeneratorNoise(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_STUCCI), new TextureGeneratorStucci(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_VORONOI), new TextureGeneratorVoronoi(noiseGenerator));
+ textureGenerators.put(Integer.valueOf(TEX_WOOD), new TextureGeneratorWood(noiseGenerator));
+ }
+
+ /**
+ * This class returns a texture read from the file or from packed blender data. The returned texture has the name set to the value of
+ * its blender type.
+ *
+ * @param tex
+ * texture structure filled with data
+ * @param blenderContext
+ * the blender context
+ * @return the texture that can be used by JME engine
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public Texture getTexture(Structure tex, BlenderContext blenderContext) throws BlenderFileException {
+ Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (result != null) {
+ return result;
+ }
+ int type = ((Number) tex.getFieldValue("type")).intValue();
+ int width = blenderContext.getBlenderKey().getGeneratedTextureWidth();
+ int height = blenderContext.getBlenderKey().getGeneratedTextureHeight();
+ int depth = blenderContext.getBlenderKey().getGeneratedTextureDepth();
+
+ switch (type) {
+ case TEX_IMAGE:// (it is first because probably this will be most commonly used)
+ Pointer pImage = (Pointer) tex.getFieldValue("ima");
+ if (pImage.isNotNull()){
+ Structure image = pImage.fetchData(blenderContext.getInputStream()).get(0);
+ result = this.getTextureFromImage(image, blenderContext);
+ }
+ break;
+ case TEX_CLOUDS:
+ case TEX_WOOD:
+ case TEX_MARBLE:
+ case TEX_MAGIC:
+ case TEX_BLEND:
+ case TEX_STUCCI:
+ case TEX_NOISE:
+ case TEX_MUSGRAVE:
+ case TEX_VORONOI:
+ case TEX_DISTNOISE:
+ TextureGenerator textureGenerator = textureGenerators.get(Integer.valueOf(type));
+ result = textureGenerator.generate(tex, width, height, depth, blenderContext);
+ break;
+ case TEX_NONE:// No texture, do nothing
+ break;
+ case TEX_POINTDENSITY:
+ LOGGER.warning("Point density texture loading currently not supported!");
+ break;
+ case TEX_VOXELDATA:
+ LOGGER.warning("Voxel data texture loading currently not supported!");
+ break;
+ case TEX_PLUGIN:
+ case TEX_ENVMAP:// TODO: implement envmap texture
+ LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[]{type, tex.getName()});
+ break;
+ default:
+ throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName());
+ }
+ if (result != null) {
+ result.setName(tex.getName());
+ result.setWrap(WrapMode.Repeat);
+ // NOTE: Enable mipmaps FOR ALL TEXTURES EVER
+ result.setMinFilter(MinFilter.Trilinear);
+ if(type != TEX_IMAGE) {//only generated textures should have this key
+ result.setKey(new GeneratedTextureKey(tex.getName()));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method merges the given textures. The result texture has no alpha
+ * factor (is always opaque).
+ *
+ * @param sources
+ * the textures to be merged
+ * @param materialContext
+ * the context of the material
+ * @return merged textures
+ */
+ public Texture mergeTextures(List<Texture> sources, MaterialContext materialContext) {
+ Texture result = null;
+ if(sources!=null && sources.size()>0) {
+ if(sources.size() == 1) {
+ return sources.get(0);//just return the texture
+ }
+ //checking the sizes of the textures (tehy should perfectly match)
+ int lastTextureWithoutAlphaIndex = 0;
+ int width = sources.get(0).getImage().getWidth();
+ int height = sources.get(0).getImage().getHeight();
+ int depth = sources.get(0).getImage().getDepth();
+
+ for(Texture source : sources) {
+ if(source.getImage().getWidth() != width) {
+ throw new IllegalArgumentException("The texture " + source.getName() + " has invalid width! It should be: " + width + '!');
+ }
+ if(source.getImage().getHeight() != height) {
+ throw new IllegalArgumentException("The texture " + source.getName() + " has invalid height! It should be: " + height + '!');
+ }
+ if(source.getImage().getDepth() != depth) {
+ throw new IllegalArgumentException("The texture " + source.getName() + " has invalid depth! It should be: " + depth + '!');
+ }
+ //support for more formats is not necessary at the moment
+ if(source.getImage().getFormat()!=Format.RGB8 && source.getImage().getFormat()!=Format.BGR8) {
+ ++lastTextureWithoutAlphaIndex;
+ }
+ }
+ if(depth==0) {
+ depth = 1;
+ }
+
+ //remove textures before the one without alpha (they will be covered anyway)
+ if(lastTextureWithoutAlphaIndex > 0 && lastTextureWithoutAlphaIndex<sources.size()-1) {
+ sources = sources.subList(lastTextureWithoutAlphaIndex, sources.size()-1);
+ }
+ int pixelsAmount = width * height * depth;
+
+ ByteBuffer data = BufferUtils.createByteBuffer(pixelsAmount * 3);
+ TexturePixel resultPixel = new TexturePixel();
+ TexturePixel sourcePixel = new TexturePixel();
+ ColorRGBA diffuseColor = materialContext.getDiffuseColor();
+ for (int i = 0; i < pixelsAmount; ++i) {
+ for (int j = 0; j < sources.size(); ++j) {
+ Image image = sources.get(j).getImage();
+ ByteBuffer sourceData = image.getData(0);
+ if(j==0) {
+ resultPixel.fromColor(diffuseColor);
+ sourcePixel.fromImage(image.getFormat(), sourceData, i);
+ resultPixel.merge(sourcePixel);
+ } else {
+ sourcePixel.fromImage(image.getFormat(), sourceData, i);
+ resultPixel.merge(sourcePixel);
+ }
+ }
+ data.put((byte)(255 * resultPixel.red));
+ data.put((byte)(255 * resultPixel.green));
+ data.put((byte)(255 * resultPixel.blue));
+ resultPixel.clear();
+ }
+
+ if(depth==1) {
+ result = new Texture2D(new Image(Format.RGB8, width, height, data));
+ } else {
+ ArrayList<ByteBuffer> arrayData = new ArrayList<ByteBuffer>(1);
+ arrayData.add(data);
+ result = new Texture3D(new Image(Format.RGB8, width, height, depth, arrayData));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method converts the given texture into normal-map texture.
+ * @param source
+ * the source texture
+ * @param strengthFactor
+ * the normal strength factor
+ * @return normal-map texture
+ */
+ public Texture convertToNormalMapTexture(Texture source, float strengthFactor) {
+ Image image = source.getImage();
+ BufferedImage sourceImage = ImageToAwt.convert(image, false, false, 0);
+ BufferedImage heightMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ BufferedImage bumpMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ ColorConvertOp gscale = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
+ gscale.filter(sourceImage, heightMap);
+
+ Vector3f S = new Vector3f();
+ Vector3f T = new Vector3f();
+ Vector3f N = new Vector3f();
+
+ for (int x = 0; x < bumpMap.getWidth(); ++x) {
+ for (int y = 0; y < bumpMap.getHeight(); ++y) {
+ // generating bump pixel
+ S.x = 1;
+ S.y = 0;
+ S.z = strengthFactor * this.getHeight(heightMap, x + 1, y) - strengthFactor * this.getHeight(heightMap, x - 1, y);
+ T.x = 0;
+ T.y = 1;
+ T.z = strengthFactor * this.getHeight(heightMap, x, y + 1) - strengthFactor * this.getHeight(heightMap, x, y - 1);
+
+ float den = (float) Math.sqrt(S.z * S.z + T.z * T.z + 1);
+ N.x = -S.z;
+ N.y = -T.z;
+ N.z = 1;
+ N.divideLocal(den);
+
+ // setting thge pixel in the result image
+ bumpMap.setRGB(x, y, this.vectorToColor(N.x, N.y, N.z));
+ }
+ }
+ ByteBuffer byteBuffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * 3);
+ ImageToAwt.convert(bumpMap, Format.RGB8, byteBuffer);
+ return new Texture2D(new Image(Format.RGB8, image.getWidth(), image.getHeight(), byteBuffer));
+ }
+
+ /**
+ * This method returns the height represented by the specified pixel in the given texture.
+ * The given texture should be a height-map.
+ * @param image
+ * the height-map texture
+ * @param x
+ * pixel's X coordinate
+ * @param y
+ * pixel's Y coordinate
+ * @return height reprezented by the given texture in the specified location
+ */
+ protected int getHeight(BufferedImage image, int x, int y) {
+ if (x < 0) {
+ x = 0;
+ } else if (x >= image.getWidth()) {
+ x = image.getWidth() - 1;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= image.getHeight()) {
+ y = image.getHeight() - 1;
+ }
+ return image.getRGB(x, y) & 0xff;
+ }
+
+ /**
+ * This method transforms given vector's coordinates into ARGB color (A is always = 255).
+ * @param x X factor of the vector
+ * @param y Y factor of the vector
+ * @param z Z factor of the vector
+ * @return color representation of the given vector
+ */
+ protected int vectorToColor(float x, float y, float z) {
+ int r = Math.round(255 * (x + 1f) / 2f);
+ int g = Math.round(255 * (y + 1f) / 2f);
+ int b = Math.round(255 * (z + 1f) / 2f);
+ return (255 << 24) + (r << 16) + (g << 8) + b;
+ }
+
+ /**
+ * This class returns a texture read from the file or from packed blender data.
+ *
+ * @param image
+ * image structure filled with data
+ * @param blenderContext
+ * the blender context
+ * @return the texture that can be used by JME engine
+ * @throws BlenderFileException
+ * this exception is thrown when the blend file structure is somehow invalid or corrupted
+ */
+ public Texture getTextureFromImage(Structure image, BlenderContext blenderContext) throws BlenderFileException {
+ LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", image.getOldMemoryAddress());
+ Texture result = (Texture) blenderContext.getLoadedFeature(image.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
+ if (result == null) {
+ String texturePath = image.getFieldValue("name").toString();
+ Pointer pPackedFile = (Pointer) image.getFieldValue("packedfile");
+ if (pPackedFile.isNull()) {
+ LOGGER.log(Level.INFO, "Reading texture from file: {0}", texturePath);
+ result = this.loadTextureFromFile(texturePath, blenderContext);
+ } else {
+ LOGGER.info("Packed texture. Reading directly from the blend file!");
+ Structure packedFile = pPackedFile.fetchData(blenderContext.getInputStream()).get(0);
+ Pointer pData = (Pointer) packedFile.getFieldValue("data");
+ FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress());
+ blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition());
+ ImageLoader imageLoader = new ImageLoader();
+
+ // Should the texture be flipped? It works for sinbad ..
+ Image im = imageLoader.loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true);
+ if (im != null) {
+ result = new Texture2D(im);
+ }
+ }
+ if (result != null) {
+ result.setName(texturePath);
+ result.setWrap(Texture.WrapMode.Repeat);
+ if(LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] {texturePath, image.getOldMemoryAddress()});
+ }
+ blenderContext.addLoadedFeatures(image.getOldMemoryAddress(), image.getName(), image, result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method loads the textre from outside the blend file.
+ *
+ * @param name
+ * the path to the image
+ * @param blenderContext
+ * the blender context
+ * @return the loaded image or null if the image cannot be found
+ */
+ protected Texture loadTextureFromFile(String name, BlenderContext blenderContext) {
+ if (!name.contains(".")){
+ return null; // no extension means not a valid image
+ }
+
+ AssetManager assetManager = blenderContext.getAssetManager();
+ name = name.replaceAll("\\\\", "\\/");
+ Texture result = null;
+
+ List<String> assetNames = new ArrayList<String>();
+ if (name.startsWith("//")) {
+ String relativePath = name.substring(2);
+ //augument the path with blender key path
+ BlenderKey blenderKey = blenderContext.getBlenderKey();
+ int idx = blenderKey.getName().lastIndexOf('/');
+ String blenderAssetFolder = blenderKey.getName().substring(0, idx != -1 ? idx : 0);
+ assetNames.add(blenderAssetFolder+'/'+relativePath);
+ } else {//use every path from the asset name to the root (absolute path)
+ String[] paths = name.split("\\/");
+ StringBuilder sb = new StringBuilder(paths[paths.length-1]);//the asset name
+ assetNames.add(paths[paths.length-1]);
+
+ for(int i=paths.length-2;i>=0;--i) {
+ sb.insert(0, '/');
+ sb.insert(0, paths[i]);
+ assetNames.add(0, sb.toString());
+ }
+ }
+
+ //now try to locate the asset
+ for(String assetName : assetNames) {
+ try {
+ TextureKey key = new TextureKey(assetName);
+ key.setGenerateMips(true);
+ key.setAsCube(false);
+ result = assetManager.loadTexture(key);
+ break;//if no exception is thrown then accept the located asset and break the loop
+ } catch(AssetNotFoundException e) {
+ LOGGER.fine(e.getLocalizedMessage());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
+ return (blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.TEXTURES) != 0;
+ }
+} \ No newline at end of file
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
new file mode 100644
index 0000000..37c0122
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/TexturePixel.java
@@ -0,0 +1,294 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.texture.Image.Format;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The class that stores the pixel values of a texture.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TexturePixel implements Cloneable {
+ private static final Logger LOGGER = Logger.getLogger(TexturePixel.class.getName());
+
+ /** The pixel data. */
+ public float intensity, red, green, blue, alpha;
+
+ /**
+ * Copies the values from the given pixel.
+ *
+ * @param pixel
+ * the pixel that we read from
+ */
+ public void fromPixel(TexturePixel pixel) {
+ this.intensity = pixel.intensity;
+ this.red = pixel.red;
+ this.green = pixel.green;
+ this.blue = pixel.blue;
+ this.alpha = pixel.alpha;
+ }
+
+ /**
+ * Copies the values from the given color.
+ *
+ * @param colorRGBA
+ * the color that we read from
+ */
+ public void fromColor(ColorRGBA colorRGBA) {
+ this.red = colorRGBA.r;
+ this.green = colorRGBA.g;
+ this.blue = colorRGBA.b;
+ this.alpha = colorRGBA.a;
+ }
+
+ /**
+ * Copies the values from the given values.
+ *
+ * @param a
+ * the alpha value
+ * @param r
+ * the red value
+ * @param g
+ * the green value
+ * @param b
+ * the blue value
+ */
+ public void fromARGB8(float a, float r, float g, float b) {
+ this.alpha = a;
+ this.red = r;
+ this.green = g;
+ this.blue = b;
+ }
+
+ /**
+ * Copies the values from the given integer that stores the ARGB8 data.
+ *
+ * @param argb8
+ * the data stored in an integer
+ */
+ public void fromARGB8(int argb8) {
+ byte pixelValue = (byte) ((argb8 & 0xFF000000) >> 24);
+ this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = (byte) ((argb8 & 0xFF0000) >> 16);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = (byte) ((argb8 & 0xFF00) >> 8);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = (byte) (argb8 & 0xFF);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ }
+
+ /**
+ * Copies the data from the given image.
+ *
+ * @param imageFormat
+ * the image format
+ * @param data
+ * the image data
+ * @param pixelIndex
+ * the index of the required pixel
+ */
+ public void fromImage(Format imageFormat, ByteBuffer data, int pixelIndex) {
+ int firstByteIndex;
+ byte pixelValue;
+ switch (imageFormat) {
+ case ABGR8:
+ firstByteIndex = pixelIndex << 2;
+ pixelValue = data.get(firstByteIndex);
+ this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 3);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case RGBA8:
+ firstByteIndex = pixelIndex << 2;
+ pixelValue = data.get(firstByteIndex);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 3);
+ this.alpha = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case BGR8:
+ firstByteIndex = pixelIndex * 3;
+ pixelValue = data.get(firstByteIndex);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ this.alpha = 1.0f;
+ break;
+ case RGB8:
+ firstByteIndex = pixelIndex * 3;
+ pixelValue = data.get(firstByteIndex);
+ this.red = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 1);
+ this.green = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get(firstByteIndex + 2);
+ this.blue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ this.alpha = 1.0f;
+ break;
+ case Luminance8:
+ pixelValue = data.get(pixelIndex);
+ this.intensity = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ default:
+ LOGGER.log(Level.FINEST, "Unknown type of texture: {0}. Black pixel used!", imageFormat);
+ this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
+ }
+ }
+
+ /**
+ * Stores RGBA values in the given array.
+ *
+ * @param result
+ * the array to store values
+ */
+ public void toRGBA(float[] result) {
+ result[0] = this.red;
+ result[1] = this.green;
+ result[2] = this.blue;
+ result[3] = this.alpha;
+ }
+
+ /**
+ * Stores the data in the given table.
+ *
+ * @param result
+ * the result table
+ */
+ public void toRGBA8(byte[] result) {
+ result[0] = (byte) (this.red * 255.0f);
+ result[1] = (byte) (this.green * 255.0f);
+ result[2] = (byte) (this.blue * 255.0f);
+ result[3] = (byte) (this.alpha * 255.0f);
+ }
+
+ /**
+ * Stores the pixel values in the integer.
+ *
+ * @return the integer that stores the pixel values
+ */
+ public int toARGB8() {
+ int result = 0;
+ int b = (int) (this.alpha * 255.0f);
+ result |= b << 24;
+ b = (int) (this.red * 255.0f);
+ result |= b << 16;
+ b = (int) (this.green * 255.0f);
+ result |= b << 8;
+ b = (int) (this.blue * 255.0f);
+ result |= b;
+ return result;
+ }
+
+ /**
+ * Merges two pixels (adds the values of each color).
+ *
+ * @param pixel
+ * the pixel we merge with
+ */
+ public void merge(TexturePixel pixel) {
+ float oneMinusAlpha = 1 - pixel.alpha;
+ this.red = oneMinusAlpha * this.red + pixel.alpha * pixel.red;
+ this.green = oneMinusAlpha * this.green + pixel.alpha * pixel.green;
+ this.blue = oneMinusAlpha * this.blue + pixel.alpha * pixel.blue;
+ // alpha should be always 1.0f as a result
+ }
+
+ /**
+ * This method negates the colors.
+ */
+ public void negate() {
+ this.red = 1.0f - this.red;
+ this.green = 1.0f - this.green;
+ this.blue = 1.0f - this.blue;
+ this.alpha = 1.0f - this.alpha;
+ }
+
+ /**
+ * This method clears the pixel values.
+ */
+ public void clear() {
+ this.intensity = this.blue = this.red = this.green = this.alpha = 0.0f;
+ }
+
+ /**
+ * This method adds the calues of the given pixel to the current pixel.
+ *
+ * @param pixel
+ * the pixel we add
+ */
+ public void add(TexturePixel pixel) {
+ this.red += pixel.red;
+ this.green += pixel.green;
+ this.blue += pixel.blue;
+ this.alpha += pixel.alpha;
+ this.intensity += pixel.intensity;
+ }
+
+ /**
+ * This method multiplies the values of the given pixel by the given value.
+ *
+ * @param value
+ * multiplication factor
+ */
+ public void mult(float value) {
+ this.red *= value;
+ this.green *= value;
+ this.blue *= value;
+ this.alpha *= value;
+ this.intensity *= value;
+ }
+
+ /**
+ * This method divides the values of the given pixel by the given value.
+ * ATTENTION! Beware of the zero value. This will cause you NaN's in the
+ * pixel values.
+ *
+ * @param value
+ * division factor
+ */
+ public void divide(float value) {
+ this.red /= value;
+ this.green /= value;
+ this.blue /= value;
+ this.alpha /= value;
+ this.intensity /= value;
+ }
+
+ /**
+ * This method clamps the pixel values to the given borders.
+ *
+ * @param min
+ * the minimum value
+ * @param max
+ * the maximum value
+ */
+ public void clamp(float min, float max) {
+ this.red = FastMath.clamp(this.red, min, max);
+ this.green = FastMath.clamp(this.green, min, max);
+ this.blue = FastMath.clamp(this.blue, min, max);
+ this.alpha = FastMath.clamp(this.alpha, min, max);
+ this.intensity = FastMath.clamp(this.intensity, min, max);
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ @Override
+ public String toString() {
+ return "[" + red + ", " + green + ", " + blue + ", " + alpha + " {" + intensity + "}]";
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java
new file mode 100644
index 0000000..56e4293
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVCoordinatesGenerator.java
@@ -0,0 +1,408 @@
+/*
+ * 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.scene.plugins.blender.textures;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.bounding.BoundingVolume;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import java.nio.FloatBuffer;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * This class is used for UV coordinates generation.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class UVCoordinatesGenerator {
+ private static final Logger LOGGER = Logger.getLogger(UVCoordinatesGenerator.class.getName());
+
+ // texture UV coordinates types
+ public static final int TEXCO_ORCO = 1;
+ public static final int TEXCO_REFL = 2;
+ public static final int TEXCO_NORM = 4;
+ public static final int TEXCO_GLOB = 8;
+ public static final int TEXCO_UV = 16;
+ public static final int TEXCO_OBJECT = 32;
+ public static final int TEXCO_LAVECTOR = 64;
+ public static final int TEXCO_VIEW = 128;
+ public static final int TEXCO_STICKY = 256;
+ public static final int TEXCO_OSA = 512;
+ public static final int TEXCO_WINDOW = 1024;
+ public static final int NEED_UV = 2048;
+ public static final int TEXCO_TANGENT = 4096;
+ // still stored in vertex->accum, 1 D
+ public static final int TEXCO_PARTICLE_OR_STRAND = 8192; // strand is used
+ public static final int TEXCO_STRESS = 16384;
+ public static final int TEXCO_SPEED = 32768;
+
+ // 2D texture mapping (projection)
+ public static final int PROJECTION_FLAT = 0;
+ public static final int PROJECTION_CUBE = 1;
+ public static final int PROJECTION_TUBE = 2;
+ public static final int PROJECTION_SPHERE = 3;
+
+ /**
+ * This method generates UV coordinates for the given mesh.
+ * IMPORTANT! This method assumes that all geometries represent one node.
+ * Each containing mesh with separate material.
+ * So all meshes have the same reference to vertex table which stores all their vertices.
+ * @param texco
+ * texture coordinates type
+ * @param projection
+ * the projection type for 2D textures
+ * @param textureDimension
+ * the dimension of the texture (only 2D and 3D)
+ * @param coordinatesSwappingIndexes
+ * an array that tells how UV-coordinates need to be swapped
+ * @param geometries
+ * a list of geometries the UV coordinates will be applied to
+ * @return created UV-coordinates buffer
+ */
+ public static VertexBuffer generateUVCoordinates(int texco, int projection, int textureDimension, int[] coordinatesSwappingIndexes, List<Geometry> geometries) {
+ if (textureDimension != 2 && textureDimension != 3) {
+ throw new IllegalStateException("Unsupported texture dimension: " + textureDimension);
+ }
+
+ VertexBuffer result = new VertexBuffer(VertexBuffer.Type.TexCoord);
+ Mesh mesh = geometries.get(0).getMesh();
+ BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometries);
+ float[] inputData = null;// positions, normals, reflection vectors, etc.
+
+ switch (texco) {
+ case TEXCO_ORCO:
+ inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Position));
+ break;
+ case TEXCO_UV:
+ FloatBuffer uvCoordinatesBuffer = BufferUtils.createFloatBuffer(mesh.getVertexCount() * textureDimension);
+ Vector2f[] data = new Vector2f[] { new Vector2f(0, 1), new Vector2f(0, 0), new Vector2f(1, 0) };
+ for (int i = 0; i < mesh.getVertexCount(); ++i) {
+ Vector2f uv = data[i % 3];
+ uvCoordinatesBuffer.put(uv.x);
+ uvCoordinatesBuffer.put(uv.y);
+ if(textureDimension == 3) {
+ uvCoordinatesBuffer.put(0);
+ }
+ }
+ result.setupData(Usage.Static, textureDimension, Format.Float, uvCoordinatesBuffer);
+ break;
+ case TEXCO_NORM:
+ inputData = BufferUtils.getFloatArray(mesh.getFloatBuffer(VertexBuffer.Type.Normal));
+ break;
+ case TEXCO_REFL:
+ case TEXCO_GLOB:
+ case TEXCO_TANGENT:
+ case TEXCO_STRESS:
+ case TEXCO_LAVECTOR:
+ case TEXCO_OBJECT:
+ case TEXCO_OSA:
+ case TEXCO_PARTICLE_OR_STRAND:
+ case TEXCO_SPEED:
+ case TEXCO_STICKY:
+ case TEXCO_VIEW:
+ case TEXCO_WINDOW:
+ LOGGER.warning("Texture coordinates type not currently supported: " + texco);
+ break;
+ default:
+ throw new IllegalStateException("Unknown texture coordinates value: " + texco);
+ }
+
+ if (inputData != null) {// make calculations
+ if (textureDimension == 2) {
+ switch (projection) {
+ case PROJECTION_FLAT:
+ inputData = UVProjectionGenerator.flatProjection(mesh, bb);
+ break;
+ case PROJECTION_CUBE:
+ inputData = UVProjectionGenerator.cubeProjection(mesh, bb);
+ break;
+ case PROJECTION_TUBE:
+ BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometries);
+ inputData = UVProjectionGenerator.tubeProjection(mesh, bt);
+ break;
+ case PROJECTION_SPHERE:
+ BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometries);
+ inputData = UVProjectionGenerator.sphereProjection(mesh, bs);
+ break;
+ default:
+ throw new IllegalStateException("Unknown projection type: " + projection);
+ }
+ } else {
+ Vector3f min = bb.getMin(null);
+ float[] uvCoordsResults = new float[4];//used for coordinates swapping
+ float[] ext = new float[] { bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2 };
+
+ // now transform the coordinates so that they are in the range of <0; 1>
+ for (int i = 0; i < inputData.length; i += 3) {
+ uvCoordsResults[1] = (inputData[i] - min.x) / ext[0];
+ uvCoordsResults[2] = (inputData[i + 1] - min.y) / ext[1];
+ uvCoordsResults[3] = (inputData[i + 2] - min.z) / ext[2];
+
+
+ inputData[i] = uvCoordsResults[coordinatesSwappingIndexes[0]];
+ inputData[i + 1] = uvCoordsResults[coordinatesSwappingIndexes[1]];
+ inputData[i + 2] = uvCoordsResults[coordinatesSwappingIndexes[2]];
+ }
+ }
+ result.setupData(Usage.Static, textureDimension, Format.Float, BufferUtils.createFloatBuffer(inputData));
+ }
+
+ // each mesh will have the same coordinates
+ for (Geometry geometry : geometries) {
+ mesh = geometry.getMesh();
+ mesh.clearBuffer(VertexBuffer.Type.TexCoord);// in case there are coordinates already set
+ mesh.setBuffer(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * This method returns the bounding box of the given geometries.
+ * @param geometries
+ * the list of geometries
+ * @return bounding box of the given geometries
+ */
+ /* package */static BoundingBox getBoundingBox(List<Geometry> geometries) {
+ BoundingBox result = null;
+ for (Geometry geometry : geometries) {
+ BoundingBox bb = UVCoordinatesGenerator.getBoundingBox(geometry.getMesh());
+ if (result == null) {
+ result = bb;
+ } else {
+ result.merge(bb);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the bounding box of the given mesh.
+ * @param mesh
+ * the mesh
+ * @return bounding box of the given mesh
+ */
+ /* package */static BoundingBox getBoundingBox(Mesh mesh) {
+ mesh.updateBound();
+ BoundingVolume bv = mesh.getBound();
+ if (bv instanceof BoundingBox) {
+ return (BoundingBox) bv;
+ } else if (bv instanceof BoundingSphere) {
+ BoundingSphere bs = (BoundingSphere) bv;
+ float r = bs.getRadius();
+ return new BoundingBox(bs.getCenter(), r, r, r);
+ } else {
+ throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
+ }
+ }
+
+ /**
+ * This method returns the bounding sphere of the given geometries.
+ * @param geometries
+ * the list of geometries
+ * @return bounding sphere of the given geometries
+ */
+ /* package */static BoundingSphere getBoundingSphere(List<Geometry> geometries) {
+ BoundingSphere result = null;
+ for (Geometry geometry : geometries) {
+ BoundingSphere bs = UVCoordinatesGenerator.getBoundingSphere(geometry.getMesh());
+ if (result == null) {
+ result = bs;
+ } else {
+ result.merge(bs);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns the bounding sphere of the given mesh.
+ * @param mesh
+ * the mesh
+ * @return bounding sphere of the given mesh
+ */
+ /* package */static BoundingSphere getBoundingSphere(Mesh mesh) {
+ mesh.updateBound();
+ BoundingVolume bv = mesh.getBound();
+ if (bv instanceof BoundingBox) {
+ BoundingBox bb = (BoundingBox) bv;
+ float r = Math.max(bb.getXExtent(), bb.getYExtent());
+ r = Math.max(r, bb.getZExtent());
+ return new BoundingSphere(r, bb.getCenter());
+ } else if (bv instanceof BoundingSphere) {
+ return (BoundingSphere) bv;
+ } else {
+ throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
+ }
+ }
+
+ /**
+ * This method returns the bounding tube of the given mesh.
+ * @param mesh
+ * the mesh
+ * @return bounding tube of the given mesh
+ */
+ /* package */static BoundingTube getBoundingTube(Mesh mesh) {
+ Vector3f center = new Vector3f();
+ float maxx = -Float.MAX_VALUE, minx = Float.MAX_VALUE;
+ float maxy = -Float.MAX_VALUE, miny = Float.MAX_VALUE;
+ float maxz = -Float.MAX_VALUE, minz = Float.MAX_VALUE;
+
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ int limit = positions.limit();
+ for (int i = 0; i < limit; i += 3) {
+ float x = positions.get(i);
+ float y = positions.get(i + 1);
+ float z = positions.get(i + 2);
+ center.addLocal(x, y, z);
+ maxx = x > maxx ? x : maxx;
+ minx = x < minx ? x : minx;
+ maxy = y > maxy ? y : maxy;
+ miny = y < miny ? y : miny;
+ maxz = z > maxz ? z : maxz;
+ minz = z < minz ? z : minz;
+ }
+ center.divideLocal(limit / 3);
+
+ float radius = Math.max(maxx - minx, maxy - miny) * 0.5f;
+ return new BoundingTube(radius, maxz - minz, center);
+ }
+
+ /**
+ * This method returns the bounding tube of the given geometries.
+ * @param geometries
+ * the list of geometries
+ * @return bounding tube of the given geometries
+ */
+ /* package */static BoundingTube getBoundingTube(List<Geometry> geometries) {
+ BoundingTube result = null;
+ for (Geometry geometry : geometries) {
+ BoundingTube bt = UVCoordinatesGenerator.getBoundingTube(geometry.getMesh());
+ if (result == null) {
+ result = bt;
+ } else {
+ result.merge(bt);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A very simple bounding tube. Id holds only the basic data bout the bounding tube
+ * and does not provide full functionality of a BoundingVolume.
+ * Should be replaced with a bounding tube that extends the BoundingVolume if it is ever created.
+ * @author Marcin Roguski (Kaelthas)
+ */
+ /* package */static class BoundingTube {
+ private float radius;
+ private float height;
+ private Vector3f center;
+
+ /**
+ * Constructor creates the tube with the given params.
+ * @param radius
+ * the radius of the tube
+ * @param height
+ * the height of the tube
+ * @param center
+ * the center of the tube
+ */
+ public BoundingTube(float radius, float height, Vector3f center) {
+ this.radius = radius;
+ this.height = height;
+ this.center = center;
+ }
+
+ /**
+ * This method merges two bounding tubes.
+ * @param boundingTube
+ * bounding tube to be merged woth the current one
+ * @return new instance of bounding tube representing the tubes' merge
+ */
+ public BoundingTube merge(BoundingTube boundingTube) {
+ // get tubes (tube1.radius >= tube2.radius)
+ BoundingTube tube1, tube2;
+ if (this.radius >= boundingTube.radius) {
+ tube1 = this;
+ tube2 = boundingTube;
+ } else {
+ tube1 = boundingTube;
+ tube2 = this;
+ }
+ float r1 = tube1.radius;
+ float r2 = tube2.radius;
+
+ float minZ = Math.min(tube1.center.z - tube1.height * 0.5f, tube2.center.z - tube2.height * 0.5f);
+ float maxZ = Math.max(tube1.center.z + tube1.height * 0.5f, tube2.center.z + tube2.height * 0.5f);
+ float height = maxZ - minZ;
+ Vector3f distance = tube2.center.subtract(tube1.center);
+ Vector3f center = tube1.center.add(distance.mult(0.5f));
+ distance.z = 0;// projecting this vector on XY plane
+ float d = distance.length();
+ // d <= r1 - r2: tube2 is inside tube1 or touches tube1 from the inside
+ // d > r1 - r2: tube2 is outside or touches tube1 or crosses tube1
+ float radius = d <= r1 - r2 ? tube1.radius : (d + r1 + r2) * 0.5f;
+ return new BoundingTube(radius, height, center);
+ }
+
+ /**
+ * This method returns the radius of the tube.
+ * @return the radius of the tube
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * This method returns the height of the tube.
+ * @return the height of the tube
+ */
+ public float getHeight() {
+ return height;
+ }
+
+ /**
+ * This method returns the center of the tube.
+ * @return the center of the tube
+ */
+ public Vector3f getCenter() {
+ return center;
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java
new file mode 100644
index 0000000..4412d60
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/UVProjectionGenerator.java
@@ -0,0 +1,226 @@
+package com.jme3.scene.plugins.blender.textures;
+
+import com.jme3.bounding.BoundingBox;
+import com.jme3.bounding.BoundingSphere;
+import com.jme3.math.FastMath;
+import com.jme3.math.Triangle;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator.BoundingTube;
+import java.nio.FloatBuffer;
+
+/**
+ * This class helps with projection calculations.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */class UVProjectionGenerator {
+ /**
+ * Flat projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bb
+ * the bounding box for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] flatProjection(Mesh mesh, BoundingBox bb) {
+ if (bb == null) {
+ bb = UVCoordinatesGenerator.getBoundingBox(mesh);
+ }
+ Vector3f min = bb.getMin(null);
+ float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f };
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+ for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+ uvCoordinates[j] = (positions.get(i) - min.x) / ext[0];
+ uvCoordinates[j + 1] = (positions.get(i + 1) - min.y) / ext[1];
+ // skip the Z-coordinate
+ }
+ return uvCoordinates;
+ }
+
+ /**
+ * Cube projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bb
+ * the bounding box for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] cubeProjection(Mesh mesh, BoundingBox bb) {
+ Triangle triangle = new Triangle();
+ Vector3f x = new Vector3f(1, 0, 0);
+ Vector3f y = new Vector3f(0, 1, 0);
+ Vector3f z = new Vector3f(0, 0, 1);
+ Vector3f min = bb.getMin(null);
+ float[] ext = new float[] { bb.getXExtent() * 2.0f, bb.getYExtent() * 2.0f, bb.getZExtent() * 2.0f };
+
+ float[] uvCoordinates = new float[mesh.getTriangleCount() * 6];// 6 == 3 * 2
+ float borderAngle = (float) Math.sqrt(2.0f) / 2.0f;
+ for (int i = 0, pointIndex = 0; i < mesh.getTriangleCount(); ++i) {
+ mesh.getTriangle(i, triangle);
+ Vector3f n = triangle.getNormal();
+ float dotNX = Math.abs(n.dot(x));
+ float dorNY = Math.abs(n.dot(y));
+ float dotNZ = Math.abs(n.dot(z));
+ if (dotNX > borderAngle) {
+ if (dotNZ < borderAngle) {// discard X-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
+ } else {// discard Z-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+ }
+ } else {
+ if (dorNY > borderAngle) {// discard Y-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get1().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get2().z - min.z) / ext[2];
+ uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get3().z - min.z) / ext[2];
+ } else {// discard Z-coordinate
+ uvCoordinates[pointIndex++] = (triangle.get1().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get1().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get2().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get2().y - min.y) / ext[1];
+ uvCoordinates[pointIndex++] = (triangle.get3().x - min.x) / ext[0];
+ uvCoordinates[pointIndex++] = (triangle.get3().y - min.y) / ext[1];
+ }
+ }
+ triangle.setNormal(null);// clear the previous normal vector
+ }
+ return uvCoordinates;
+ }
+
+ /**
+ * Tube projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bt
+ * the bounding tube for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] tubeProjection(Mesh mesh, BoundingTube bt) {
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+ Vector3f v = new Vector3f();
+ float cx = bt.getCenter().x, cy = bt.getCenter().y;
+ Vector3f uBase = new Vector3f(0, -1, 0);
+
+ float vBase = bt.getCenter().z - bt.getHeight() * 0.5f;
+ for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+ // calculating U
+ v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
+ v.normalizeLocal();
+ float angle = v.angleBetween(uBase);// result between [0; PI]
+ if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
+ angle = FastMath.TWO_PI - angle;
+ }
+ uvCoordinates[j] = angle / FastMath.TWO_PI;
+
+ // calculating V
+ float z = positions.get(i + 2);
+ uvCoordinates[j + 1] = (z - vBase) / bt.getHeight();
+ }
+
+ //looking for splitted triangles
+ Triangle triangle = new Triangle();
+ for(int i=0;i<mesh.getTriangleCount();++i) {
+ mesh.getTriangle(i, triangle);
+ float sgn1 = Math.signum(triangle.get1().x-cx);
+ float sgn2 = Math.signum(triangle.get2().x-cx);
+ float sgn3 = Math.signum(triangle.get3().x-cx);
+ float xSideFactor = sgn1 + sgn2 + sgn3;
+ float ySideFactor = Math.signum(triangle.get1().y-cy)+
+ Math.signum(triangle.get2().y-cy)+
+ Math.signum(triangle.get3().y-cy);
+ if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
+ //indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
+ if(sgn1==1.0f) {
+ uvCoordinates[i*3*2] += 1.0f;
+ }
+ if(sgn2==1.0f) {
+ uvCoordinates[(i*3+1)*2] += 1.0f;
+ }
+ if(sgn3==1.0f) {
+ uvCoordinates[(i*3+2)*2] += 1.0f;
+ }
+ }
+ }
+ return uvCoordinates;
+ }
+
+ /**
+ * Sphere projection for 2D textures.
+ *
+ * @param mesh
+ * mesh that is to be projected
+ * @param bb
+ * the bounding box for projecting
+ * @return UV coordinates after the projection
+ */
+ public static float[] sphereProjection(Mesh mesh, BoundingSphere bs) {
+ FloatBuffer positions = mesh.getFloatBuffer(VertexBuffer.Type.Position);
+ float[] uvCoordinates = new float[positions.limit() / 3 * 2];
+ Vector3f v = new Vector3f();
+ float cx = bs.getCenter().x, cy = bs.getCenter().y, cz = bs.getCenter().z;
+ Vector3f uBase = new Vector3f(0, -1, 0);
+ Vector3f vBase = new Vector3f(0, 0, -1);
+
+ for (int i = 0, j = 0; i < positions.limit(); i += 3, j += 2) {
+ // calculating U
+ v.set(positions.get(i)-cx, positions.get(i + 1)-cy, 0);
+ v.normalizeLocal();
+ float angle = v.angleBetween(uBase);// result between [0; PI]
+ if (v.x < 0) {// the angle should be greater than PI, we're on the other part of the image then
+ angle = FastMath.TWO_PI - angle;
+ }
+ uvCoordinates[j] = angle / FastMath.TWO_PI;
+
+ // calculating V
+ v.set(positions.get(i)-cx, positions.get(i + 1)-cy, positions.get(i + 2)-cz);
+ v.normalizeLocal();
+ angle = v.angleBetween(vBase);// result between [0; PI]
+ uvCoordinates[j+1] = angle / FastMath.PI;
+ }
+
+ //looking for splitted triangles
+ Triangle triangle = new Triangle();
+ for(int i=0;i<mesh.getTriangleCount();++i) {
+ mesh.getTriangle(i, triangle);
+ float sgn1 = Math.signum(triangle.get1().x-cx);
+ float sgn2 = Math.signum(triangle.get2().x-cx);
+ float sgn3 = Math.signum(triangle.get3().x-cx);
+ float xSideFactor = sgn1 + sgn2 + sgn3;
+ float ySideFactor = Math.signum(triangle.get1().y-cy)+
+ Math.signum(triangle.get2().y-cy)+
+ Math.signum(triangle.get3().y-cy);
+ if((xSideFactor>-3 || xSideFactor<3) && ySideFactor<0) {//the triangle is on the splitting plane
+ //indexOfUcoord = (indexOfTriangle*3 + indexOfTrianglesVertex)*2
+ if(sgn1==1.0f) {
+ uvCoordinates[i*3*2] += 1.0f;
+ }
+ if(sgn2==1.0f) {
+ uvCoordinates[(i*3+1)*2] += 1.0f;
+ }
+ if(sgn3==1.0f) {
+ uvCoordinates[(i*3+2)*2] += 1.0f;
+ }
+ }
+ }
+ return uvCoordinates;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
new file mode 100644
index 0000000..505c94b
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
@@ -0,0 +1,195 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.materials.MaterialHelper;
+
+/**
+ * An abstract class that contains the basic methods used by the classes that
+ * will derive from it.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+/* package */abstract class AbstractTextureBlender implements TextureBlender {
+ /**
+ * This method blends the single pixel depending on the blending type.
+ *
+ * @param result
+ * the result pixel
+ * @param materialColor
+ * the material color
+ * @param pixelColor
+ * the pixel color
+ * @param blendFactor
+ * the blending factor
+ * @param blendtype
+ * the blending type
+ * @param blenderContext
+ * the blender context
+ */
+ protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, float blendFactor, int blendtype, BlenderContext blenderContext) {
+ float oneMinusFactor = 1.0f - blendFactor, col;
+
+ switch (blendtype) {
+ case MTEX_BLEND:
+ result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];
+ result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1];
+ result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2];
+ break;
+ case MTEX_MUL:
+ result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0];
+ result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1];
+ result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2];
+ break;
+ case MTEX_DIV:
+ if (pixelColor[0] != 0.0) {
+ result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f;
+ }
+ if (pixelColor[1] != 0.0) {
+ result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f;
+ }
+ if (pixelColor[2] != 0.0) {
+ result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f;
+ }
+ break;
+ case MTEX_SCREEN:
+ result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
+ result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
+ result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
+ break;
+ case MTEX_OVERLAY:
+ if (materialColor[0] < 0.5f) {
+ result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]);
+ } else {
+ result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]);
+ }
+ if (materialColor[1] < 0.5f) {
+ result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]);
+ } else {
+ result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]);
+ }
+ if (materialColor[2] < 0.5f) {
+ result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]);
+ } else {
+ result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]);
+ }
+ break;
+ case MTEX_SUB:
+ result[0] = materialColor[0] - blendFactor * pixelColor[0];
+ result[1] = materialColor[1] - blendFactor * pixelColor[1];
+ result[2] = materialColor[2] - blendFactor * pixelColor[2];
+ result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
+ result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
+ result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
+ break;
+ case MTEX_ADD:
+ result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f;
+ result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f;
+ result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f;
+ break;
+ case MTEX_DIFF:
+ result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]);
+ result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]);
+ result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]);
+ break;
+ case MTEX_DARK:
+ col = blendFactor * pixelColor[0];
+ result[0] = col < materialColor[0] ? col : materialColor[0];
+ col = blendFactor * pixelColor[1];
+ result[1] = col < materialColor[1] ? col : materialColor[1];
+ col = blendFactor * pixelColor[2];
+ result[2] = col < materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_LIGHT:
+ col = blendFactor * pixelColor[0];
+ result[0] = col > materialColor[0] ? col : materialColor[0];
+ col = blendFactor * pixelColor[1];
+ result[1] = col > materialColor[1] ? col : materialColor[1];
+ col = blendFactor * pixelColor[2];
+ result[2] = col > materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_BLEND_HUE:
+ case MTEX_BLEND_SAT:
+ case MTEX_BLEND_VAL:
+ case MTEX_BLEND_COLOR:
+ System.arraycopy(materialColor, 0, result, 0, 3);
+ this.blendHSV(blendtype, result, blendFactor, pixelColor, blenderContext);
+ break;
+ default:
+ throw new IllegalStateException("Unknown blend type: " + blendtype);
+ }
+ }
+
+ /**
+ * The method that performs the ramp blending.
+ *
+ * @param type
+ * the blend type
+ * @param materialRGB
+ * the rgb value of the material, here the result is stored too
+ * @param fac
+ * color affection factor
+ * @param pixelColor
+ * the texture color
+ * @param blenderContext
+ * the blender context
+ */
+ protected void blendHSV(int type, float[] materialRGB, float fac, float[] pixelColor, BlenderContext blenderContext) {
+ float oneMinusFactor = 1.0f - fac;
+ MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+
+ switch (type) {
+ case MTEX_BLEND_HUE: {// FIXME: not working well for image textures
+ // (works fine for generated textures)
+ float[] colorTransformResult = new float[3];
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
+ if (colorTransformResult[0] != 0.0f) {
+ float colH = colorTransformResult[0];
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
+ materialHelper.hsvToRgb(colH, colorTransformResult[1], colorTransformResult[2], colorTransformResult);
+ materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * colorTransformResult[0];
+ materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * colorTransformResult[1];
+ materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * colorTransformResult[2];
+ }
+ break;
+ }
+ case MTEX_BLEND_SAT: {
+ float[] colorTransformResult = new float[3];
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], colorTransformResult);
+ float h = colorTransformResult[0];
+ float s = colorTransformResult[1];
+ float v = colorTransformResult[2];
+ if (s != 0.0f) {
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colorTransformResult);
+ materialHelper.hsvToRgb(h, (oneMinusFactor * s + fac * colorTransformResult[1]), v, materialRGB);
+ }
+ break;
+ }
+ case MTEX_BLEND_VAL: {
+ float[] rgbToHsv = new float[3];
+ float[] colToHsv = new float[3];
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
+ materialHelper.hsvToRgb(rgbToHsv[0], rgbToHsv[1], (oneMinusFactor * rgbToHsv[2] + fac * colToHsv[2]), materialRGB);
+ break;
+ }
+ case MTEX_BLEND_COLOR: {// FIXME: not working well for image
+ // textures (works fine for generated
+ // textures)
+ float[] rgbToHsv = new float[3];
+ float[] colToHsv = new float[3];
+ materialHelper.rgbToHsv(pixelColor[0], pixelColor[1], pixelColor[2], colToHsv);
+ if (colToHsv[2] != 0) {
+ materialHelper.rgbToHsv(materialRGB[0], materialRGB[1], materialRGB[2], rgbToHsv);
+ materialHelper.hsvToRgb(colToHsv[0], colToHsv[1], rgbToHsv[2], rgbToHsv);
+ materialRGB[0] = oneMinusFactor * materialRGB[0] + fac * rgbToHsv[0];
+ materialRGB[1] = oneMinusFactor * materialRGB[1] + fac * rgbToHsv[1];
+ materialRGB[2] = oneMinusFactor * materialRGB[2] + fac * rgbToHsv[2];
+ }
+ break;
+ }
+ default:
+ throw new IllegalStateException("Unknown ramp type: " + type);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
new file mode 100644
index 0000000..08e25d3
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlender.java
@@ -0,0 +1,51 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Texture;
+
+/**
+ * An interface for texture blending classes (the classes that mix the texture
+ * pixels with the material colors).
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public interface TextureBlender {
+ // types of blending
+ int MTEX_BLEND = 0;
+ int MTEX_MUL = 1;
+ int MTEX_ADD = 2;
+ int MTEX_SUB = 3;
+ int MTEX_DIV = 4;
+ int MTEX_DARK = 5;
+ int MTEX_DIFF = 6;
+ int MTEX_LIGHT = 7;
+ int MTEX_SCREEN = 8;
+ int MTEX_OVERLAY = 9;
+ int MTEX_BLEND_HUE = 10;
+ int MTEX_BLEND_SAT = 11;
+ int MTEX_BLEND_VAL = 12;
+ int MTEX_BLEND_COLOR = 13;
+ int MTEX_NUM_BLENDTYPES = 14;
+
+ /**
+ * This method blends the given texture with material color and the defined
+ * color in 'map to' panel. As a result of this method a new texture is
+ * created. The input texture is NOT.
+ *
+ * @param materialColor
+ * the material diffuse color
+ * @param texture
+ * the texture we use in blending
+ * @param color
+ * the color defined for the texture
+ * @param affectFactor
+ * the factor that the color affects the texture (value form 0.0
+ * to 1.0)
+ * @param blendType
+ * the blending type
+ * @param blenderContext
+ * the blender context
+ * @return new texture that was created after the blending
+ */
+ Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext);
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
new file mode 100644
index 0000000..75fc0c5
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
@@ -0,0 +1,162 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> RGBA8
+ * <li> ABGR8
+ * <li> BGR8
+ * <li> RGB8
+ * Not yet supported (but will be):
+ * <li> ARGB4444:
+ * <li> RGB10:
+ * <li> RGB111110F:
+ * <li> RGB16:
+ * <li> RGB16F:
+ * <li> RGB16F_to_RGB111110F:
+ * <li> RGB16F_to_RGB9E5:
+ * <li> RGB32F:
+ * <li> RGB565:
+ * <li> RGB5A1:
+ * <li> RGB9E5:
+ * <li> RGBA16:
+ * <li> RGBA16F
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderAWT extends AbstractTextureBlender {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderAWT.class.getName());
+
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f };
+ Format format = texture.getImage().getFormat();
+ ByteBuffer data = texture.getImage().getData(0);
+ data.rewind();
+
+ int width = texture.getImage().getWidth();
+ int height = texture.getImage().getHeight();
+ int depth = texture.getImage().getDepth();
+ if (depth == 0) {
+ depth = 1;
+ }
+ ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
+
+ float[] resultPixel = new float[4];
+ int dataIndex = 0;
+ while (data.hasRemaining()) {
+ float tin = this.setupMaterialColor(data, format, neg, pixelColor);
+ this.blendPixel(resultPixel, materialColor, color, tin, blendType, blenderContext);
+ newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
+ newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f));
+ }
+ if (texture.getType() == Texture.Type.TwoDimensional) {
+ return new Texture2D(new Image(Format.RGBA8, width, height, newData));
+ } else {
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(newData);
+ return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+ }
+ }
+
+ /**
+ * This method alters the material color in a way dependent on the type of
+ * the image. For example the color remains untouched if the texture is of
+ * Luminance type. The luminance defines the interaction between the
+ * material color and color defined for texture blending. If the type has 3
+ * or more color channels then the material color is replaced with the
+ * texture's color and later blended with the defined blend color. All alpha
+ * values (if present) are ignored and not used during blending.
+ *
+ * @param data
+ * the image data
+ * @param imageFormat
+ * the format of the image
+ * @param neg
+ * defines it the result color should be nagated
+ * @param materialColor
+ * the material's color (value may be changed)
+ * @return texture intensity for the current pixel
+ */
+ protected float setupMaterialColor(ByteBuffer data, Format imageFormat, boolean neg, float[] materialColor) {
+ float tin = 0.0f;
+ byte pixelValue = data.get();// at least one byte is always taken :)
+ float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ switch (imageFormat) {
+ case RGBA8:
+ materialColor[0] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[3] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case ABGR8:
+ materialColor[3] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case BGR8:
+ materialColor[2] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[0] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ materialColor[3] = 1.0f;
+ break;
+ case RGB8:
+ materialColor[0] = firstPixelValue;
+ pixelValue = data.get();
+ materialColor[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ pixelValue = data.get();
+ materialColor[2] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ materialColor[3] = 1.0f;
+ break;
+ case ARGB4444:
+ case RGB10:
+ case RGB111110F:
+ case RGB16:
+ case RGB16F:
+ case RGB16F_to_RGB111110F:
+ case RGB16F_to_RGB9E5:
+ case RGB32F:
+ case RGB565:
+ case RGB5A1:
+ case RGB9E5:
+ case RGBA16:
+ case RGBA16F:
+ case RGBA32F:// TODO: implement these textures
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
+ break;
+ default:
+ throw new IllegalStateException("Invalid image format type for AWT texture blender: " + imageFormat);
+ }
+ if (neg) {
+ materialColor[0] = 1.0f - materialColor[0];
+ materialColor[1] = 1.0f - materialColor[1];
+ materialColor[2] = 1.0f - materialColor[2];
+ }
+ // Blender formula for texture intensity calculation:
+ // 0.35*texres.tr+0.45*texres.tg+0.2*texres.tb
+ tin = 0.35f * materialColor[0] + 0.45f * materialColor[1] + 0.2f * materialColor[2];
+ return tin;
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
new file mode 100644
index 0000000..a532cfb
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
@@ -0,0 +1,96 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jme3tools.converters.RGB565;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.textures.TexturePixel;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.util.BufferUtils;
+
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> DXT1
+ * <li> DXT3
+ * <li> DXT5
+ * Not yet supported (but will be):
+ * <li> DXT1A:
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderDDS extends AbstractTextureBlender {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderDDS.class.getName());
+
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ Format format = texture.getImage().getFormat();
+ ByteBuffer data = texture.getImage().getData(0);
+ data.rewind();
+
+ int width = texture.getImage().getWidth();
+ int height = texture.getImage().getHeight();
+ int depth = texture.getImage().getDepth();
+ if (depth == 0) {
+ depth = 1;
+ }
+ ByteBuffer newData = BufferUtils.createByteBuffer(data.remaining());
+
+ float[] resultPixel = new float[4];
+ float[] pixelColor = new float[4];
+ TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel() };
+ int dataIndex = 0;
+ while (data.hasRemaining()) {
+ switch (format) {
+ case DXT3:
+ case DXT5:
+ newData.putLong(dataIndex, data.getLong());// just copy the
+ // 8 bytes of
+ // alphas
+ dataIndex += 8;
+ case DXT1:
+ int col0 = RGB565.RGB565_to_ARGB8(data.getShort());
+ int col1 = RGB565.RGB565_to_ARGB8(data.getShort());
+ colors[0].fromARGB8(col0);
+ colors[1].fromARGB8(col1);
+ break;
+ case DXT1A:
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", format);
+ break;
+ default:
+ throw new IllegalStateException("Invalid image format type for DDS texture blender: " + format);
+ }
+
+ // blending colors
+ for (int i = 0; i < colors.length; ++i) {
+ if (neg) {
+ colors[i].negate();
+ }
+ colors[i].toRGBA(pixelColor);
+ this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext);
+ colors[i].fromARGB8(1, resultPixel[0], resultPixel[1], resultPixel[2]);
+ int argb8 = colors[i].toARGB8();
+ short rgb565 = RGB565.ARGB8_to_RGB565(argb8);
+ newData.putShort(dataIndex, rgb565);
+ dataIndex += 2;
+ }
+
+ // just copy the remaining 4 bytes of the current texel
+ newData.putInt(dataIndex, data.getInt());
+ dataIndex += 4;
+ }
+ if (texture.getType() == Texture.Type.TwoDimensional) {
+ return new Texture2D(new Image(format, width, height, newData));
+ } else {
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(newData);
+ return new Texture3D(new Image(format, width, height, depth, dataArray));
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
new file mode 100644
index 0000000..c9ad61a
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
@@ -0,0 +1,81 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Image.Format;
+
+/**
+ * This class creates the texture blending class depending on the texture type.
+ *
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderFactory {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderFactory.class.getName());
+
+ /**
+ * This method creates the blending class.
+ *
+ * @param format
+ * the texture format
+ * @returntexture blending class
+ */
+ public static TextureBlender createTextureBlender(Format format) {
+ switch (format) {
+ case Luminance8:
+ case Luminance8Alpha8:
+ case Luminance16:
+ case Luminance16Alpha16:
+ case Luminance16F:
+ case Luminance16FAlpha16F:
+ case Luminance32F:
+ return new TextureBlenderLuminance();
+ case RGBA8:
+ case ABGR8:
+ case BGR8:
+ case RGB8:
+ case RGB10:
+ case RGB111110F:
+ case RGB16:
+ case RGB16F:
+ case RGB16F_to_RGB111110F:
+ case RGB16F_to_RGB9E5:
+ case RGB32F:
+ case RGB565:
+ case RGB5A1:
+ case RGB9E5:
+ case RGBA16:
+ case RGBA16F:
+ case RGBA32F:
+ return new TextureBlenderAWT();
+ case DXT1:
+ case DXT1A:
+ case DXT3:
+ case DXT5:
+ return new TextureBlenderDDS();
+ case Alpha16:
+ case Alpha8:
+ case ARGB4444:
+ case Depth:
+ case Depth16:
+ case Depth24:
+ case Depth32:
+ case Depth32F:
+ case Intensity16:
+ case Intensity8:
+ case LATC:
+ case LTC:
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}. Returning a blender that does not change the texture.", format);
+ return new TextureBlender() {
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ return texture;
+ }
+ };
+ default:
+ throw new IllegalStateException("Unknown image format type: " + format);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
new file mode 100644
index 0000000..a616685
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
@@ -0,0 +1,221 @@
+package com.jme3.scene.plugins.blender.textures.blending;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture3D;
+import com.jme3.texture.Image.Format;
+import com.jme3.util.BufferUtils;
+
+/**
+ * The class that is responsible for blending the following texture types:
+ * <li> Luminance8
+ * <li> Luminance8Alpha8
+ * Not yet supported (but will be):
+ * <li> Luminance16:
+ * <li> Luminance16Alpha16:
+ * <li> Luminance16F:
+ * <li> Luminance16FAlpha16F:
+ * <li> Luminance32F:
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class TextureBlenderLuminance extends AbstractTextureBlender {
+ private static final Logger LOGGER = Logger.getLogger(TextureBlenderLuminance.class.getName());
+
+ @Override
+ public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
+ Format format = texture.getImage().getFormat();
+ ByteBuffer data = texture.getImage().getData(0);
+ data.rewind();
+
+ int width = texture.getImage().getWidth();
+ int height = texture.getImage().getHeight();
+ int depth = texture.getImage().getDepth();
+ if (depth == 0) {
+ depth = 1;
+ }
+ ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
+
+ float[] resultPixel = new float[4];
+ float[] tinAndAlpha = new float[2];
+ int dataIndex = 0;
+ while (data.hasRemaining()) {
+ this.getTinAndAlpha(data, format, neg, tinAndAlpha);
+ this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], affectFactor, blendType, blenderContext);
+ newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
+ newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
+ newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
+ }
+ if (texture.getType() == Texture.Type.TwoDimensional) {
+ return new Texture2D(new Image(Format.RGBA8, width, height, newData));
+ } else {
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
+ dataArray.add(newData);
+ return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
+ }
+ }
+
+ /**
+ * This method return texture intensity and alpha value.
+ *
+ * @param data
+ * the texture data
+ * @param imageFormat
+ * the image format
+ * @param neg
+ * indicates if the texture is negated
+ * @param result
+ * the table (2 elements) where the result is being stored
+ */
+ protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
+ byte pixelValue = data.get();// at least one byte is always taken
+ float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ switch (imageFormat) {
+ case Luminance8:
+ result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
+ result[1] = 1.0f;
+ break;
+ case Luminance8Alpha8:
+ result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
+ pixelValue = data.get();
+ result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
+ break;
+ case Luminance16:
+ case Luminance16Alpha16:
+ case Luminance16F:
+ case Luminance16FAlpha16F:
+ case Luminance32F:
+ LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
+ break;
+ default:
+ throw new IllegalStateException("Invalid image format type for DDS texture blender: " + imageFormat);
+ }
+ }
+
+ /**
+ * This method blends the texture with an appropriate color.
+ *
+ * @param result
+ * the result color (variable 'in' in blender source code)
+ * @param materialColor
+ * the texture color (variable 'out' in blender source coude)
+ * @param color
+ * the previous color (variable 'tex' in blender source code)
+ * @param textureIntensity
+ * texture intensity (variable 'fact' in blender source code)
+ * @param textureFactor
+ * texture affection factor (variable 'facg' in blender source
+ * code)
+ * @param blendtype
+ * the blend type
+ * @param blenderContext
+ * the blender context
+ */
+ protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
+ float oneMinusFactor, col;
+ textureIntensity *= textureFactor;
+
+ switch (blendtype) {
+ case MTEX_BLEND:
+ oneMinusFactor = 1.0f - textureIntensity;
+ result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
+ result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
+ result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
+ break;
+ case MTEX_MUL:
+ oneMinusFactor = 1.0f - textureFactor;
+ result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
+ result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
+ result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
+ break;
+ case MTEX_DIV:
+ oneMinusFactor = 1.0f - textureIntensity;
+ if (color[0] != 0.0) {
+ result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
+ }
+ if (color[1] != 0.0) {
+ result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
+ }
+ if (color[2] != 0.0) {
+ result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
+ }
+ break;
+ case MTEX_SCREEN:
+ oneMinusFactor = 1.0f - textureFactor;
+ result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
+ result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
+ result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
+ break;
+ case MTEX_OVERLAY:
+ oneMinusFactor = 1.0f - textureFactor;
+ if (materialColor[0] < 0.5f) {
+ result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
+ } else {
+ result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
+ }
+ if (materialColor[1] < 0.5f) {
+ result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
+ } else {
+ result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
+ }
+ if (materialColor[2] < 0.5f) {
+ result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
+ } else {
+ result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
+ }
+ break;
+ case MTEX_SUB:
+ result[0] = materialColor[0] - textureIntensity * color[0];
+ result[1] = materialColor[1] - textureIntensity * color[1];
+ result[2] = materialColor[2] - textureIntensity * color[2];
+ result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
+ result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
+ result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
+ break;
+ case MTEX_ADD:
+ result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
+ result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
+ result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
+ break;
+ case MTEX_DIFF:
+ oneMinusFactor = 1.0f - textureIntensity;
+ result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
+ result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
+ result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
+ break;
+ case MTEX_DARK:
+ col = textureIntensity * color[0];
+ result[0] = col < materialColor[0] ? col : materialColor[0];
+ col = textureIntensity * color[1];
+ result[1] = col < materialColor[1] ? col : materialColor[1];
+ col = textureIntensity * color[2];
+ result[2] = col < materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_LIGHT:
+ col = textureIntensity * color[0];
+ result[0] = col > materialColor[0] ? col : materialColor[0];
+ col = textureIntensity * color[1];
+ result[1] = col > materialColor[1] ? col : materialColor[1];
+ col = textureIntensity * color[2];
+ result[2] = col > materialColor[2] ? col : materialColor[2];
+ break;
+ case MTEX_BLEND_HUE:
+ case MTEX_BLEND_SAT:
+ case MTEX_BLEND_VAL:
+ case MTEX_BLEND_COLOR:
+ System.arraycopy(materialColor, 0, result, 0, 3);
+ this.blendHSV(blendtype, result, textureIntensity, color, blenderContext);
+ break;
+ default:
+ throw new IllegalStateException("Unknown blend type: " + blendtype);
+ }
+ }
+}
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat b/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat
new file mode 100644
index 0000000..81fea0b
--- /dev/null
+++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat
Binary files differ