Line data Source code
1 0 : /*
2 :
3 : TextureStore.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "OOCocoa.h"
26 : #import "TextureStore.h"
27 : #if !NEW_PLANETS
28 :
29 : #import "OOMaths.h"
30 :
31 : #ifndef NDEBUG
32 : #import "Universe.h"
33 : #import "MyOpenGLView.h"
34 : #else
35 : #import "OOColor.h"
36 : #endif
37 :
38 : #import "OOCollectionExtractors.h"
39 :
40 0 : #define DEBUG_DUMP ( 0 && OOLITE_DEBUG)
41 :
42 :
43 0 : static NSString * const kOOLogPlanetTextureGen = @"texture.planet.generate";
44 :
45 :
46 : #import "OOTextureGenerator.h" // For FloatRGB
47 :
48 :
49 0 : static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key)
50 : {
51 : OOColor *color = [dictionary objectForKey:key];
52 : if (color == nil)
53 : {
54 : // could not get a color from the dicitionary, return white color instead of hitting the assert below
55 : color = [OOColor colorWithDescription:@"whiteColor"];
56 : OOLog(@"textureStore.FloatRGBFromDictColor.nilColor", @"Expected color for key \"%@\" in dictionary %@, got nil. Setting color to %@", key, dictionary, [color rgbaDescription]);
57 : }
58 : NSCAssert1([color isKindOfClass:[OOColor class]], @"Expected OOColor, got %@", [color class]);
59 :
60 : return (FloatRGB){ [color redComponent], [color greenComponent], [color blueComponent] };
61 : }
62 :
63 :
64 0 : static FloatRGB Blend(float fraction, FloatRGB a, FloatRGB b)
65 : {
66 : return (FloatRGB)
67 : {
68 : OOLerp(a.r, b.r, fraction),
69 : OOLerp(a.g, b.g, fraction),
70 : OOLerp(a.b, b.b, fraction)
71 : };
72 : }
73 :
74 :
75 0 : static FloatRGB PlanetTextureColor(float q, float impress, float bias, FloatRGB seaColor, FloatRGB paleSeaColor, FloatRGB landColor, FloatRGB paleLandColor)
76 : {
77 : const FloatRGB kWhite = { 1.0, 1.0, 1.0 };
78 : float maxq = impress + bias;
79 :
80 : float hi = 0.66667 * maxq;
81 : float oh = 1.0 / hi;
82 : float ih = 1.0 / (1.0 - hi);
83 :
84 : if (q <= 0.0)
85 : {
86 : return seaColor;
87 : }
88 : if (q > 1.0)
89 : {
90 : return (FloatRGB){ 1.0f, 1.0f, 1.0f };
91 : }
92 : if (q < 0.01)
93 : {
94 : return Blend(q * 100.0f, paleSeaColor, landColor);
95 : }
96 : if (q > hi)
97 : {
98 : return Blend((q - hi) * ih, paleLandColor, kWhite); // snow capped peaks
99 : }
100 :
101 : return Blend((hi - q) * oh, paleLandColor, landColor);
102 : }
103 :
104 :
105 : static void fillSquareImageDataWithCloudTexture(unsigned char * imageBuffer, int width, OOColor* cloudcolor, float impress, float bias);
106 : static void fillSquareImageWithPlanetTex(unsigned char * imageBuffer, int width, float impress, float bias, FloatRGB seaColor, FloatRGB paleSeaColor, FloatRGB landColor, FloatRGB paleLandColor);
107 :
108 :
109 : @implementation TextureStore
110 :
111 :
112 0 : #define PROC_TEXTURE_SIZE 512
113 :
114 : + (BOOL) getPlanetTextureNameFor:(NSDictionary *)planetInfo intoData:(unsigned char **)textureData width:(GLuint *)textureWidth height:(GLuint *)textureHeight
115 : {
116 : int texture_h = PROC_TEXTURE_SIZE;
117 : int texture_w = PROC_TEXTURE_SIZE;
118 :
119 : int tex_bytes = texture_w * texture_h * 4;
120 :
121 : NSParameterAssert(textureData != NULL && textureWidth != NULL && textureHeight != NULL);
122 :
123 : unsigned char *imageBuffer = malloc(tex_bytes);
124 : if (imageBuffer == NULL) return NO;
125 :
126 : *textureData = imageBuffer;
127 : *textureWidth = texture_w;
128 : *textureHeight = texture_h;
129 :
130 : float land_fraction = [[planetInfo objectForKey:@"land_fraction"] floatValue];
131 : float sea_bias = land_fraction - 1.0;
132 :
133 : OOLog(kOOLogPlanetTextureGen, @"genning texture for land_fraction %.5f", land_fraction);
134 :
135 : FloatRGB land_color = FloatRGBFromDictColor(planetInfo, @"land_color");
136 : FloatRGB sea_color = FloatRGBFromDictColor(planetInfo, @"sea_color");
137 : FloatRGB polar_land_color = FloatRGBFromDictColor(planetInfo, @"polar_land_color");
138 : FloatRGB polar_sea_color = FloatRGBFromDictColor(planetInfo, @"polar_sea_color");
139 :
140 : // Pale sea colour gives a better transition between land and sea., Backported from the new planets code.
141 : FloatRGB pale_sea_color = Blend(0.45, polar_sea_color, Blend(0.7, sea_color, land_color));
142 :
143 : fillSquareImageWithPlanetTex(imageBuffer, texture_w, 1.0, sea_bias,
144 : sea_color,
145 : pale_sea_color,
146 : land_color,
147 : polar_land_color);
148 :
149 : return YES;
150 : }
151 :
152 :
153 : + (BOOL) getCloudTextureNameFor:(OOColor*)color :(GLfloat)impress :(GLfloat)bias intoData:(unsigned char **)textureData width:(GLuint *)textureWidth height:(GLuint *)textureHeight
154 : {
155 : int texture_h = PROC_TEXTURE_SIZE;
156 : int texture_w = PROC_TEXTURE_SIZE;
157 : int tex_bytes;
158 :
159 : tex_bytes = texture_w * texture_h * 4;
160 :
161 : NSParameterAssert(textureData != NULL && textureWidth != NULL && textureHeight != NULL);
162 :
163 : unsigned char *imageBuffer = malloc(tex_bytes);
164 : if (imageBuffer == NULL) return NO;
165 :
166 : *textureData = imageBuffer;
167 : *textureWidth = texture_w;
168 : *textureHeight = texture_h;
169 :
170 : fillSquareImageDataWithCloudTexture( imageBuffer, texture_w, color, impress, bias);
171 :
172 : return YES;
173 : }
174 :
175 : @end
176 :
177 :
178 0 : static RANROTSeed sNoiseSeed;
179 0 : static float ranNoiseBuffer[128 * 128];
180 :
181 0 : void fillRanNoiseBuffer()
182 : {
183 : sNoiseSeed = RANROTGetFullSeed();
184 :
185 : int i;
186 : for (i = 0; i < 16384; i++)
187 : ranNoiseBuffer[i] = randf();
188 : }
189 :
190 :
191 0 : static void addNoise(float * buffer, int p, int n, float scale)
192 : {
193 : int x, y;
194 :
195 : float r = (float)p / (float)n;
196 : for (y = 0; y < p; y++) for (x = 0; x < p; x++)
197 : {
198 : int ix = floor( (float)x / r);
199 : int jx = (ix + 1) % n;
200 : int iy = floor( (float)y / r);
201 : int jy = (iy + 1) % n;
202 : float qx = x / r - ix;
203 : float qy = y / r - iy;
204 : ix &= 127;
205 : iy &= 127;
206 : jx &= 127;
207 : jy &= 127;
208 : float rix = OOLerp(ranNoiseBuffer[iy * 128 + ix], ranNoiseBuffer[iy * 128 + jx], qx);
209 : float rjx = OOLerp(ranNoiseBuffer[jy * 128 + ix], ranNoiseBuffer[jy * 128 + jx], qx);
210 : float rfinal = scale * OOLerp(rix, rjx, qy);
211 :
212 : buffer[y * p + x] += rfinal;
213 : }
214 : }
215 :
216 :
217 0 : static float q_factor(float* accbuffer, int x, int y, int width, BOOL polar_y_smooth, float polar_y_value, BOOL polar_x_smooth, float polar_x_value, float impress, float bias)
218 : {
219 : while ( x < 0 ) x+= width;
220 : while ( y < 0 ) y+= width;
221 : while ( x >= width ) x-= width;
222 : while ( y >= width ) y-= width;
223 :
224 : float q = accbuffer[ y * width + x]; // 0.0 -> 1.0
225 :
226 : q *= impress; // impress
227 : q += bias; // + bias
228 :
229 : float polar_y = (2.0f * y - width) / (float) width;
230 : float polar_x = (2.0f * x - width) / (float) width;
231 :
232 : polar_x *= polar_x;
233 : polar_y *= polar_y;
234 :
235 : if (polar_x_smooth)
236 : q = q * (1.0 - polar_x) + polar_x * polar_x_value;
237 : if (polar_y_smooth)
238 : q = q * (1.0 - polar_y) + polar_y * polar_y_value;
239 :
240 : if (q > 1.0) q = 1.0;
241 : if (q < 0.0) q = 0.0;
242 :
243 : return q;
244 : }
245 :
246 :
247 0 : static void fillSquareImageDataWithCloudTexture(unsigned char * imageBuffer, int width, OOColor* cloudcolor, float impress, float bias)
248 : {
249 : NSCParameterAssert(width > 0);
250 :
251 : float accbuffer[width * width];
252 : memset(accbuffer, 0, sizeof accbuffer);
253 : int x, y;
254 :
255 : GLfloat rgba[4];
256 : rgba[0] = [cloudcolor redComponent];
257 : rgba[1] = [cloudcolor greenComponent];
258 : rgba[2] = [cloudcolor blueComponent];
259 : rgba[3] = [cloudcolor alphaComponent];
260 :
261 : int octave = 8;
262 : float scale = 0.5;
263 : while (octave < width)
264 : {
265 : addNoise(accbuffer, width, octave, scale);
266 : octave *= 2;
267 : scale *= 0.5;
268 : }
269 :
270 : float pole_value = (impress * accbuffer[0] - bias < 0.0)? 0.0: 1.0;
271 :
272 : for (y = 0; y < width; y++) for (x = 0; x < width; x++)
273 : {
274 : float q = q_factor(accbuffer, x, y, width, YES, pole_value, NO, 0.0, impress, bias);
275 :
276 : imageBuffer[0 + 4 * (y * width + x) ] = 255 * rgba[0];
277 : imageBuffer[1 + 4 * (y * width + x) ] = 255 * rgba[1];
278 : imageBuffer[2 + 4 * (y * width + x) ] = 255 * rgba[2];
279 : imageBuffer[3 + 4 * (y * width + x) ] = 255 * rgba[3] * q;
280 : }
281 : #if DEBUG_DUMP
282 : NSString *name = [NSString stringWithFormat:@"atmosphere-%u-%u-old", sNoiseSeed.high, sNoiseSeed.low];
283 : OOLog(@"planetTex.dump", [NSString stringWithFormat:@"Saving generated texture to file %@.", name]);
284 :
285 : [[UNIVERSE gameView] dumpRGBAToFileNamed:name
286 : bytes:imageBuffer
287 : width:width
288 : height:width
289 : rowBytes:width * 4];
290 : #endif
291 : }
292 :
293 0 : static void fillSquareImageWithPlanetTex(unsigned char * imageBuffer, int width, float impress, float bias,
294 : FloatRGB seaColor,
295 : FloatRGB paleSeaColor,
296 : FloatRGB landColor,
297 : FloatRGB paleLandColor)
298 : {
299 : float accbuffer[width * width];
300 : memset(accbuffer, 0, sizeof accbuffer);
301 :
302 : int octave = 8;
303 : float scale = 0.5;
304 : while (octave < width)
305 : {
306 : addNoise(accbuffer, width, octave, scale);
307 : octave *= 2;
308 : scale *= 0.5;
309 : }
310 :
311 : float pole_value = (impress + bias > 0.5)? 0.5 * (impress + bias) : 0.0;
312 :
313 : int x, y;
314 : for (y = 0; y < width; y++) for (x = 0; x < width; x++)
315 : {
316 : float q = q_factor(accbuffer, x, y, width, YES, pole_value, NO, 0.0, impress, bias);
317 :
318 : float yN = q_factor(accbuffer, x, y - 1, width, YES, pole_value, NO, 0.0, impress, bias);
319 : float yS = q_factor(accbuffer, x, y + 1, width, YES, pole_value, NO, 0.0, impress, bias);
320 : float yW = q_factor(accbuffer, x - 1, y, width, YES, pole_value, NO, 0.0, impress, bias);
321 : float yE = q_factor(accbuffer, x + 1, y, width, YES, pole_value, NO, 0.0, impress, bias);
322 :
323 : Vector norm = make_vector( 24.0 * (yW - yE), 24.0 * (yS - yN), 2.0);
324 :
325 : norm = vector_normal(norm);
326 :
327 : GLfloat shade = pow(norm.z, 3.2);
328 :
329 : FloatRGB color = PlanetTextureColor(q, impress, bias, seaColor, paleSeaColor, landColor, paleLandColor);
330 :
331 : color.r *= shade;
332 : color.g *= shade;
333 : color.b *= shade;
334 :
335 : imageBuffer[0 + 4 * (y * width + x)] = 255 * color.r;
336 : imageBuffer[1 + 4 * (y * width + x)] = 255 * color.g;
337 : imageBuffer[2 + 4 * (y * width + x)] = 255 * color.b;
338 : imageBuffer[3 + 4 * (y * width + x)] = 255;
339 : }
340 : #if DEBUG_DUMP
341 : OOLog(@"planetTex.dump", [NSString stringWithFormat:@"Saving generated texture to file planet-%u-%u-old.", sNoiseSeed.high, sNoiseSeed.low]);
342 :
343 : [[UNIVERSE gameView] dumpRGBAToFileNamed:[NSString stringWithFormat:@"planet-%u-%u-old", sNoiseSeed.high, sNoiseSeed.low]
344 : bytes:imageBuffer
345 : width:width
346 : height:width
347 : rowBytes:width * 4];
348 : #endif
349 : }
350 :
351 : #endif // !NEW_PLANETS
|