• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.scene.plugins.blender.textures;
33 
34 import com.jme3.math.FastMath;
35 import com.jme3.scene.plugins.blender.BlenderContext;
36 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
37 import com.jme3.scene.plugins.blender.file.DynamicArray;
38 import com.jme3.scene.plugins.blender.file.Pointer;
39 import com.jme3.scene.plugins.blender.file.Structure;
40 import com.jme3.texture.Texture;
41 import java.util.Map;
42 import java.util.TreeMap;
43 import java.util.logging.Level;
44 import java.util.logging.Logger;
45 
46 /**
47  * This class is a base class for texture generators.
48  * @author Marcin Roguski (Kaelthas)
49  */
50 /* package */abstract class TextureGenerator {
51 	private static final Logger	LOGGER	= Logger.getLogger(TextureGenerator.class.getName());
52 
53 	protected NoiseGenerator	noiseGenerator;
54 
TextureGenerator(NoiseGenerator noiseGenerator)55 	public TextureGenerator(NoiseGenerator noiseGenerator) {
56 		this.noiseGenerator = noiseGenerator;
57 	}
58 
59 	/**
60 	 * This method generates the texture.
61 	 * @param tex
62 	 *        texture's structure
63 	 * @param width
64 	 *        the width of the result texture
65 	 * @param height
66 	 *        the height of the result texture
67 	 * @param depth
68 	 *        the depth of the texture
69 	 * @param blenderContext
70 	 *        the blender context
71 	 * @return newly generated texture
72 	 */
generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext)73 	protected abstract Texture generate(Structure tex, int width, int height, int depth, BlenderContext blenderContext);
74 
75 	/**
76 	 * This method reads the colorband data from the given texture structure.
77 	 *
78 	 * @param tex
79 	 *        the texture structure
80 	 * @param blenderContext
81 	 *        the blender context
82 	 * @return read colorband or null if not present
83 	 */
readColorband(Structure tex, BlenderContext blenderContext)84 	private ColorBand readColorband(Structure tex, BlenderContext blenderContext) {
85 		ColorBand result = null;
86 		int flag = ((Number) tex.getFieldValue("flag")).intValue();
87 		if ((flag & NoiseGenerator.TEX_COLORBAND) != 0) {
88 			Pointer pColorband = (Pointer) tex.getFieldValue("coba");
89 			Structure colorbandStructure;
90 			try {
91 				colorbandStructure = pColorband.fetchData(blenderContext.getInputStream()).get(0);
92 				result = new ColorBand(colorbandStructure);
93 			} catch (BlenderFileException e) {
94 				LOGGER.log(Level.WARNING, "Cannot fetch the colorband structure. The reason: {0}", e.getLocalizedMessage());
95 			}
96 		}
97 		return result;
98 	}
99 
computeColorband(Structure tex, BlenderContext blenderContext)100 	protected float[][] computeColorband(Structure tex, BlenderContext blenderContext) {
101 		ColorBand colorBand = this.readColorband(tex, blenderContext);
102 		float[][] result = null;
103 		if(colorBand!=null) {
104 			result = new float[1001][4];//1001 - amount of possible cursor positions; 4 = [r, g, b, a]
105 			ColorBandData[] dataArray = colorBand.data;
106 
107 			if(dataArray.length==1) {//special case; use only one color for all types of colorband interpolation
108 				for(int i=0;i<result.length;++i) {
109 					result[i][0] = dataArray[0].r;
110 					result[i][1] = dataArray[0].g;
111 					result[i][2] = dataArray[0].b;
112 					result[i][3] = dataArray[0].a;
113 				}
114 			} else {
115 				int currentCursor = 0;
116 				ColorBandData currentData = dataArray[0];
117 				ColorBandData nextData = dataArray[0];
118 				switch(colorBand.ipoType) {
119 					case ColorBand.IPO_LINEAR:
120 						float rDiff = 0, gDiff = 0, bDiff = 0, aDiff = 0, posDiff;
121 						for(int i=0;i<result.length;++i) {
122 							posDiff = i - currentData.pos;
123 							result[i][0] = currentData.r + rDiff * posDiff;
124 							result[i][1] = currentData.g + gDiff * posDiff;
125 							result[i][2] = currentData.b + bDiff * posDiff;
126 							result[i][3] = currentData.a + aDiff * posDiff;
127 							if(nextData.pos==i) {
128 								currentData = dataArray[currentCursor++];
129 								if(currentCursor < dataArray.length) {
130 									nextData = dataArray[currentCursor];
131 									//calculate differences
132 									int d = nextData.pos - currentData.pos;
133 									rDiff = (nextData.r - currentData.r)/d;
134 									gDiff = (nextData.g - currentData.g)/d;
135 									bDiff = (nextData.b - currentData.b)/d;
136 									aDiff = (nextData.a - currentData.a)/d;
137 								} else {
138 									rDiff = gDiff = bDiff = aDiff = 0;
139 								}
140 							}
141 						}
142 						break;
143 					case ColorBand.IPO_BSPLINE:
144 					case ColorBand.IPO_CARDINAL:
145 						Map<Integer, ColorBandData> cbDataMap = new TreeMap<Integer, ColorBandData>();
146 						for(int i=0;i<colorBand.data.length;++i) {
147 							cbDataMap.put(Integer.valueOf(i), colorBand.data[i]);
148 						}
149 
150 						if(colorBand.data[0].pos==0) {
151 							cbDataMap.put(Integer.valueOf(-1), colorBand.data[0]);
152 						} else {
153 							ColorBandData cbData = colorBand.data[0].clone();
154 							cbData.pos = 0;
155 							cbDataMap.put(Integer.valueOf(-1), cbData);
156 							cbDataMap.put(Integer.valueOf(-2), cbData);
157 						}
158 
159 						if(colorBand.data[colorBand.data.length - 1].pos==1000) {
160 							cbDataMap.put(Integer.valueOf(colorBand.data.length), colorBand.data[colorBand.data.length - 1]);
161 						} else {
162 							ColorBandData cbData = colorBand.data[colorBand.data.length - 1].clone();
163 							cbData.pos = 1000;
164 							cbDataMap.put(Integer.valueOf(colorBand.data.length), cbData);
165 							cbDataMap.put(Integer.valueOf(colorBand.data.length + 1), cbData);
166 						}
167 
168 						float[] ipoFactors = new float[4];
169 						float f;
170 
171 						ColorBandData data0 = cbDataMap.get(currentCursor - 2);
172 						ColorBandData data1 = cbDataMap.get(currentCursor - 1);
173 						ColorBandData data2 = cbDataMap.get(currentCursor);
174 						ColorBandData data3 = cbDataMap.get(currentCursor + 1);
175 
176 						for(int i=0;i<result.length;++i) {
177 							if (data2.pos != data1.pos) {
178 		                        f = (i - data2.pos) / (float)(data1.pos - data2.pos);
179 		                    } else {
180 		                        f = 0.0f;
181 		                    }
182 
183 							f = FastMath.clamp(f, 0.0f, 1.0f);
184 
185 							this.getIpoData(colorBand, f, ipoFactors);
186 							result[i][0] = ipoFactors[3] * data0.r + ipoFactors[2] * data1.r + ipoFactors[1] * data2.r + ipoFactors[0] * data3.r;
187 							result[i][1] = ipoFactors[3] * data0.g + ipoFactors[2] * data1.g + ipoFactors[1] * data2.g + ipoFactors[0] * data3.g;
188 							result[i][2] = ipoFactors[3] * data0.b + ipoFactors[2] * data1.b + ipoFactors[1] * data2.b + ipoFactors[0] * data3.b;
189 							result[i][3] = ipoFactors[3] * data0.a + ipoFactors[2] * data1.a + ipoFactors[1] * data2.a + ipoFactors[0] * data3.a;
190 							result[i][0] = FastMath.clamp(result[i][0], 0.0f, 1.0f);
191 							result[i][1] = FastMath.clamp(result[i][1], 0.0f, 1.0f);
192 							result[i][2] = FastMath.clamp(result[i][2], 0.0f, 1.0f);
193 							result[i][3] = FastMath.clamp(result[i][3], 0.0f, 1.0f);
194 
195 							if(nextData.pos==i) {
196 								++currentCursor;
197 								data0 = cbDataMap.get(currentCursor - 2);
198 								data1 = cbDataMap.get(currentCursor - 1);
199 								data2 = cbDataMap.get(currentCursor);
200 								data3 = cbDataMap.get(currentCursor + 1);
201 							}
202 						}
203 						break;
204 					case ColorBand.IPO_EASE:
205 						float d, a, b, d2;
206 						for(int i=0;i<result.length;++i) {
207 							if(nextData.pos != currentData.pos) {
208 								d = (i - currentData.pos) / (float)(nextData.pos - currentData.pos);
209 								d2 = d * d;
210 								a = 3.0f * d2 - 2.0f * d * d2;
211 								b = 1.0f - a;
212 							} else {
213 								d = a = 0.0f;
214 								b = 1.0f;
215 							}
216 
217 							result[i][0] = b * currentData.r + a * nextData.r;
218 							result[i][1] = b * currentData.g + a * nextData.g;
219 							result[i][2] = b * currentData.b + a * nextData.b;
220 							result[i][3] = b * currentData.a + a * nextData.a;
221 							if(nextData.pos==i) {
222 								currentData = dataArray[currentCursor++];
223 								if(currentCursor < dataArray.length) {
224 									nextData = dataArray[currentCursor];
225 								}
226 							}
227 						}
228 						break;
229 					case ColorBand.IPO_CONSTANT:
230 						for(int i=0;i<result.length;++i) {
231 							result[i][0] = currentData.r;
232 							result[i][1] = currentData.g;
233 							result[i][2] = currentData.b;
234 							result[i][3] = currentData.a;
235 							if(nextData.pos==i) {
236 								currentData = dataArray[currentCursor++];
237 								if(currentCursor < dataArray.length) {
238 									nextData = dataArray[currentCursor];
239 								}
240 							}
241 						}
242 						break;
243 					default:
244 						throw new IllegalStateException("Unknown interpolation type: " + colorBand.ipoType);
245 				}
246 			}
247 		}
248 		return result;
249 	}
250 
251 	/**
252 	 * This method returns the data for either B-spline of Cardinal interpolation.
253 	 * @param colorBand the color band
254 	 * @param d distance factor for the current intensity
255 	 * @param ipoFactors table to store the results (size of the table must be at least 4)
256 	 */
getIpoData(ColorBand colorBand, float d, float[] ipoFactors)257 	private void getIpoData(ColorBand colorBand, float d, float[] ipoFactors) {
258 		float d2 = d * d;
259 		float d3 = d2 * d;
260 		if(colorBand.ipoType==ColorBand.IPO_BSPLINE) {
261 			ipoFactors[0] = -0.71f * d3 + 1.42f * d2 - 0.71f * d;
262 			ipoFactors[1] = 1.29f * d3 - 2.29f * d2 + 1.0f;
263 			ipoFactors[2] = -1.29f * d3 + 1.58f * d2 + 0.71f * d;
264 			ipoFactors[3] = 0.71f * d3 - 0.71f * d2;
265 		} else if(colorBand.ipoType==ColorBand.IPO_CARDINAL) {
266 			ipoFactors[0] = -0.16666666f * d3 + 0.5f * d2 - 0.5f * d + 0.16666666f;
267 			ipoFactors[1] = 0.5f * d3 - d2 + 0.6666666f;
268 			ipoFactors[2] = -0.5f * d3 + 0.5f * d2 + 0.5f * d + 0.16666666f;
269 			ipoFactors[3] = 0.16666666f * d3;
270 		} else {
271 			throw new IllegalStateException("Cannot get interpolation data for other colorband types than B-spline and Cardinal!");
272 		}
273 	}
274 
275 	/**
276 	 * This method applies brightness and contrast for RGB textures.
277 	 * @param tex texture structure
278 	 * @param texres
279 	 */
applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres)280 	protected void applyBrightnessAndContrast(BrightnessAndContrastData bacd, TexturePixel texres) {
281         texres.red = (texres.red - 0.5f) * bacd.contrast + bacd.brightness;
282         if (texres.red < 0.0f) {
283             texres.red = 0.0f;
284         }
285         texres.green =(texres.green - 0.5f) * bacd.contrast + bacd.brightness;
286         if (texres.green < 0.0f) {
287             texres.green = 0.0f;
288         }
289         texres.blue = (texres.blue - 0.5f) * bacd.contrast + bacd.brightness;
290         if (texres.blue < 0.0f) {
291             texres.blue = 0.0f;
292         }
293     }
294 
295 	/**
296 	 * This method applies brightness and contrast for Luminance textures.
297 	 * @param texres
298 	 * @param contrast
299 	 * @param brightness
300 	 */
applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness)301 	protected void applyBrightnessAndContrast(TexturePixel texres, float contrast, float brightness) {
302         texres.intensity = (texres.intensity - 0.5f) * contrast + brightness;
303         if (texres.intensity < 0.0f) {
304             texres.intensity = 0.0f;
305         } else if (texres.intensity > 1.0f) {
306             texres.intensity = 1.0f;
307         }
308     }
309 
310 	/**
311 	 * A class constaining the colorband data.
312 	 *
313 	 * @author Marcin Roguski (Kaelthas)
314 	 */
315 	protected static class ColorBand {
316 		//interpolation types
317 		public static final int IPO_LINEAR 		= 0;
318 		public static final int IPO_EASE 		= 1;
319 		public static final int IPO_BSPLINE 	= 2;
320 		public static final int IPO_CARDINAL 	= 3;
321 		public static final int IPO_CONSTANT 	= 4;
322 
323 		public int		cursorsAmount, ipoType;
324 		public ColorBandData[]	data;
325 
326 		/**
327 		 * Constructor. Loads the data from the given structure.
328 		 *
329 		 * @param cbdataStructure
330 		 *        the colorband structure
331 		 */
332 		@SuppressWarnings("unchecked")
ColorBand(Structure colorbandStructure)333 		public ColorBand(Structure colorbandStructure) {
334 			this.cursorsAmount = ((Number) colorbandStructure.getFieldValue("tot")).intValue();
335 			this.ipoType = ((Number) colorbandStructure.getFieldValue("ipotype")).intValue();
336 			this.data = new ColorBandData[this.cursorsAmount];
337 			DynamicArray<Structure> data = (DynamicArray<Structure>) colorbandStructure.getFieldValue("data");
338 			for (int i = 0; i < this.cursorsAmount; ++i) {
339 				this.data[i] = new ColorBandData(data.get(i));
340 			}
341 		}
342 	}
343 
344 	/**
345 	 * Class to store the single colorband cursor data.
346 	 *
347 	 * @author Marcin Roguski (Kaelthas)
348 	 */
349 	protected static class ColorBandData implements Cloneable {
350 		public final float	r, g, b, a;
351 		public int 	pos;
352 
353 		/**
354 		 * Copy constructor.
355 		 */
ColorBandData(ColorBandData data)356 		private ColorBandData(ColorBandData data) {
357 			this.r = data.r;
358 			this.g = data.g;
359 			this.b = data.b;
360 			this.a = data.a;
361 			this.pos = data.pos;
362 		}
363 
364 		/**
365 		 * Constructor. Loads the data from the given structure.
366 		 *
367 		 * @param cbdataStructure
368 		 *        the structure containing the CBData object
369 		 */
ColorBandData(Structure cbdataStructure)370 		public ColorBandData(Structure cbdataStructure) {
371 			this.r = ((Number) cbdataStructure.getFieldValue("r")).floatValue();
372 			this.g = ((Number) cbdataStructure.getFieldValue("g")).floatValue();
373 			this.b = ((Number) cbdataStructure.getFieldValue("b")).floatValue();
374 			this.a = ((Number) cbdataStructure.getFieldValue("a")).floatValue();
375 			this.pos = (int) (((Number) cbdataStructure.getFieldValue("pos")).floatValue() * 1000.0f);
376 		}
377 
378 		@Override
clone()379 		public ColorBandData clone() {
380 			try {
381 				return (ColorBandData) super.clone();
382 			} catch (CloneNotSupportedException e) {
383 				return new ColorBandData(this);
384 			}
385 		}
386 
387 		@Override
toString()388 		public String toString() {
389 			return "P: " + this.pos + " [" + this.r+", "+this.g+", "+this.b+", "+this.a+"]";
390 		}
391 	}
392 
393 	/**
394 	 * This class contains brightness and contrast data.
395 	 * @author Marcin Roguski (Kaelthas)
396 	 */
397 	protected static class BrightnessAndContrastData {
398 		public final float contrast;
399         public final float brightness;
400         public final float rFactor;
401         public final float gFactor;
402         public final float bFactor;
403 
404         /**
405          * Constructor reads the required data from the given structure.
406          * @param tex texture structure
407          */
BrightnessAndContrastData(Structure tex)408 		public BrightnessAndContrastData(Structure tex) {
409 			contrast = ((Number) tex.getFieldValue("contrast")).floatValue();
410 	        brightness = ((Number) tex.getFieldValue("bright")).floatValue() - 0.5f;
411 	        rFactor = ((Number) tex.getFieldValue("rfac")).floatValue();
412 	        gFactor = ((Number) tex.getFieldValue("gfac")).floatValue();
413 	        bFactor = ((Number) tex.getFieldValue("bfac")).floatValue();
414 		}
415 	}
416 }
417