1package com.jme3.scene.plugins.blender.textures.blending;
2
3import java.nio.ByteBuffer;
4import java.util.ArrayList;
5import java.util.logging.Level;
6import java.util.logging.Logger;
7
8import com.jme3.math.FastMath;
9import com.jme3.scene.plugins.blender.BlenderContext;
10import com.jme3.texture.Image;
11import com.jme3.texture.Texture;
12import com.jme3.texture.Texture2D;
13import com.jme3.texture.Texture3D;
14import com.jme3.texture.Image.Format;
15import com.jme3.util.BufferUtils;
16
17/**
18 * The class that is responsible for blending the following texture types:
19 * <li> Luminance8
20 * <li> Luminance8Alpha8
21 * Not yet supported (but will be):
22 * <li> Luminance16:
23 * <li> Luminance16Alpha16:
24 * <li> Luminance16F:
25 * <li> Luminance16FAlpha16F:
26 * <li> Luminance32F:
27 * @author Marcin Roguski (Kaelthas)
28 */
29public class TextureBlenderLuminance extends AbstractTextureBlender {
30	private static final Logger	LOGGER	= Logger.getLogger(TextureBlenderLuminance.class.getName());
31
32	@Override
33	public Texture blend(float[] materialColor, Texture texture, float[] color, float affectFactor, int blendType, boolean neg, BlenderContext blenderContext) {
34		Format format = texture.getImage().getFormat();
35		ByteBuffer data = texture.getImage().getData(0);
36		data.rewind();
37
38		int width = texture.getImage().getWidth();
39		int height = texture.getImage().getHeight();
40		int depth = texture.getImage().getDepth();
41		if (depth == 0) {
42			depth = 1;
43		}
44		ByteBuffer newData = BufferUtils.createByteBuffer(width * height * depth * 4);
45
46		float[] resultPixel = new float[4];
47		float[] tinAndAlpha = new float[2];
48		int dataIndex = 0;
49		while (data.hasRemaining()) {
50			this.getTinAndAlpha(data, format, neg, tinAndAlpha);
51			this.blendPixel(resultPixel, materialColor, color, tinAndAlpha[0], affectFactor, blendType, blenderContext);
52			newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f));
53			newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f));
54			newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f));
55			newData.put(dataIndex++, (byte) (tinAndAlpha[1] * 255.0f));
56		}
57		if (texture.getType() == Texture.Type.TwoDimensional) {
58			return new Texture2D(new Image(Format.RGBA8, width, height, newData));
59		} else {
60			ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(1);
61			dataArray.add(newData);
62			return new Texture3D(new Image(Format.RGBA8, width, height, depth, dataArray));
63		}
64	}
65
66	/**
67	 * This method return texture intensity and alpha value.
68	 *
69	 * @param data
70	 *            the texture data
71	 * @param imageFormat
72	 *            the image format
73	 * @param neg
74	 *            indicates if the texture is negated
75	 * @param result
76	 *            the table (2 elements) where the result is being stored
77	 */
78	protected void getTinAndAlpha(ByteBuffer data, Format imageFormat, boolean neg, float[] result) {
79		byte pixelValue = data.get();// at least one byte is always taken
80		float firstPixelValue = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
81		switch (imageFormat) {
82			case Luminance8:
83				result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
84				result[1] = 1.0f;
85				break;
86			case Luminance8Alpha8:
87				result[0] = neg ? 1.0f - firstPixelValue : firstPixelValue;
88				pixelValue = data.get();
89				result[1] = pixelValue >= 0 ? pixelValue / 255.0f : 1.0f - (~pixelValue) / 255.0f;
90				break;
91			case Luminance16:
92			case Luminance16Alpha16:
93			case Luminance16F:
94			case Luminance16FAlpha16F:
95			case Luminance32F:
96				LOGGER.log(Level.WARNING, "Image type not yet supported for blending: {0}", imageFormat);
97				break;
98			default:
99				throw new IllegalStateException("Invalid image format type for DDS texture blender: " + imageFormat);
100		}
101	}
102
103	/**
104	 * This method blends the texture with an appropriate color.
105	 *
106	 * @param result
107	 *            the result color (variable 'in' in blender source code)
108	 * @param materialColor
109	 *            the texture color (variable 'out' in blender source coude)
110	 * @param color
111	 *            the previous color (variable 'tex' in blender source code)
112	 * @param textureIntensity
113	 *            texture intensity (variable 'fact' in blender source code)
114	 * @param textureFactor
115	 *            texture affection factor (variable 'facg' in blender source
116	 *            code)
117	 * @param blendtype
118	 *            the blend type
119	 * @param blenderContext
120	 *            the blender context
121	 */
122	protected void blendPixel(float[] result, float[] materialColor, float[] color, float textureIntensity, float textureFactor, int blendtype, BlenderContext blenderContext) {
123		float oneMinusFactor, col;
124		textureIntensity *= textureFactor;
125
126		switch (blendtype) {
127			case MTEX_BLEND:
128				oneMinusFactor = 1.0f - textureIntensity;
129				result[0] = textureIntensity * color[0] + oneMinusFactor * materialColor[0];
130				result[1] = textureIntensity * color[1] + oneMinusFactor * materialColor[1];
131				result[2] = textureIntensity * color[2] + oneMinusFactor * materialColor[2];
132				break;
133			case MTEX_MUL:
134				oneMinusFactor = 1.0f - textureFactor;
135				result[0] = (oneMinusFactor + textureIntensity * materialColor[0]) * color[0];
136				result[1] = (oneMinusFactor + textureIntensity * materialColor[1]) * color[1];
137				result[2] = (oneMinusFactor + textureIntensity * materialColor[2]) * color[2];
138				break;
139			case MTEX_DIV:
140				oneMinusFactor = 1.0f - textureIntensity;
141				if (color[0] != 0.0) {
142					result[0] = (oneMinusFactor * materialColor[0] + textureIntensity * materialColor[0] / color[0]) * 0.5f;
143				}
144				if (color[1] != 0.0) {
145					result[1] = (oneMinusFactor * materialColor[1] + textureIntensity * materialColor[1] / color[1]) * 0.5f;
146				}
147				if (color[2] != 0.0) {
148					result[2] = (oneMinusFactor * materialColor[2] + textureIntensity * materialColor[2] / color[2]) * 0.5f;
149				}
150				break;
151			case MTEX_SCREEN:
152				oneMinusFactor = 1.0f - textureFactor;
153				result[0] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
154				result[1] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
155				result[2] = 1.0f - (oneMinusFactor + textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
156				break;
157			case MTEX_OVERLAY:
158				oneMinusFactor = 1.0f - textureFactor;
159				if (materialColor[0] < 0.5f) {
160					result[0] = color[0] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[0]);
161				} else {
162					result[0] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[0])) * (1.0f - color[0]);
163				}
164				if (materialColor[1] < 0.5f) {
165					result[1] = color[1] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[1]);
166				} else {
167					result[1] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[1])) * (1.0f - color[1]);
168				}
169				if (materialColor[2] < 0.5f) {
170					result[2] = color[2] * (oneMinusFactor + 2.0f * textureIntensity * materialColor[2]);
171				} else {
172					result[2] = 1.0f - (oneMinusFactor + 2.0f * textureIntensity * (1.0f - materialColor[2])) * (1.0f - color[2]);
173				}
174				break;
175			case MTEX_SUB:
176				result[0] = materialColor[0] - textureIntensity * color[0];
177				result[1] = materialColor[1] - textureIntensity * color[1];
178				result[2] = materialColor[2] - textureIntensity * color[2];
179				result[0] = FastMath.clamp(result[0], 0.0f, 1.0f);
180				result[1] = FastMath.clamp(result[1], 0.0f, 1.0f);
181				result[2] = FastMath.clamp(result[2], 0.0f, 1.0f);
182				break;
183			case MTEX_ADD:
184				result[0] = (textureIntensity * color[0] + materialColor[0]) * 0.5f;
185				result[1] = (textureIntensity * color[1] + materialColor[1]) * 0.5f;
186				result[2] = (textureIntensity * color[2] + materialColor[2]) * 0.5f;
187				break;
188			case MTEX_DIFF:
189				oneMinusFactor = 1.0f - textureIntensity;
190				result[0] = oneMinusFactor * materialColor[0] + textureIntensity * Math.abs(materialColor[0] - color[0]);
191				result[1] = oneMinusFactor * materialColor[1] + textureIntensity * Math.abs(materialColor[1] - color[1]);
192				result[2] = oneMinusFactor * materialColor[2] + textureIntensity * Math.abs(materialColor[2] - color[2]);
193				break;
194			case MTEX_DARK:
195				col = textureIntensity * color[0];
196				result[0] = col < materialColor[0] ? col : materialColor[0];
197				col = textureIntensity * color[1];
198				result[1] = col < materialColor[1] ? col : materialColor[1];
199				col = textureIntensity * color[2];
200				result[2] = col < materialColor[2] ? col : materialColor[2];
201				break;
202			case MTEX_LIGHT:
203				col = textureIntensity * color[0];
204				result[0] = col > materialColor[0] ? col : materialColor[0];
205				col = textureIntensity * color[1];
206				result[1] = col > materialColor[1] ? col : materialColor[1];
207				col = textureIntensity * color[2];
208				result[2] = col > materialColor[2] ? col : materialColor[2];
209				break;
210			case MTEX_BLEND_HUE:
211			case MTEX_BLEND_SAT:
212			case MTEX_BLEND_VAL:
213			case MTEX_BLEND_COLOR:
214				System.arraycopy(materialColor, 0, result, 0, 3);
215				this.blendHSV(blendtype, result, textureIntensity, color, blenderContext);
216				break;
217			default:
218				throw new IllegalStateException("Unknown blend type: " + blendtype);
219		}
220	}
221}
222