diff options
author | Scott Barta <sbarta@google.com> | 2012-03-01 12:35:35 -0800 |
---|---|---|
committer | Scott Barta <sbarta@google.com> | 2012-03-01 12:40:08 -0800 |
commit | 59b2e6871c65f58fdad78cd7229c292f6a177578 (patch) | |
tree | 2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/blender/com/jme3/scene/plugins/blender/textures | |
parent | f9b30489e75ac1eabc365064959804e99534f7ab (diff) | |
download | jmonkeyengine-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')
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 Binary files differnew file mode 100644 index 0000000..81fea0b --- /dev/null +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/noiseconstants.dat |