1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <vector>
22 #include <math.h>
23 #include <GLEXT/GLImageItterator.h>
24 #include <GLEXT/GLImageModifier.h>
25 #include <image/Image.h>
26 #include <image/ImageFactory.h>
27 #include <engine/ScorchedContext.h>
28 #include <landscape/Landscape.h>
29 #include <landscapemap/LandscapeMaps.h>
30 #include <client/ScorchedClient.h>
31 #include <lang/LangResource.h>
32 #include <common/OptionsTransient.h>
33 #include <common/Defines.h>
34 
findIntersection(HeightMap & hMap,Vector start,Vector end,float & dist,float stopDist)35 bool ImageModifier::findIntersection(HeightMap &hMap,
36 										Vector start,
37 										Vector end,
38 										float &dist,
39 										float stopDist)
40 {
41 	bool result = false;
42 	dist = 0.0f;
43 	Vector point = start;
44 	Vector direction = end - start;
45 	float &pt0 = point[0];
46 	float &pt1 = point[1];
47 	float &pt2 = point[2];
48 	int width = hMap.getMapWidth();
49 	int height = hMap.getMapHeight();
50 
51 	// Calculate how many pixels to jump for each itteration
52 	if (fabsf(direction[0]) > fabsf(direction[1])) direction /= fabsf(direction[0]);
53 	else direction /= fabsf(direction[1]);
54 
55 	while (pt0 >= 0.0f && pt1 >= 0.0f &&
56 		pt0 <= width && pt1 <= height)
57 	{
58 		float height = hMap.getHeight(int(point[0]), int(point[1])).asFloat() - 0.1f;
59 		float rayHeight = height - pt2;
60 		if (rayHeight > 0.0f)
61 		{
62 			if (rayHeight > dist) dist = rayHeight;
63 			result = true;
64 			if (dist > stopDist) return result;
65 		}
66 
67 		point += direction;
68 	}
69 
70 	return result;
71 }
72 
tileBitmap(Image & src,Image & dest)73 void ImageModifier::tileBitmap(Image &src, Image &dest)
74 {
75 	DIALOG_ASSERT(dest.getComponents() == 3);
76 
77 	GLubyte *destBytes = dest.getBits();
78 	for (int j=0; j<dest.getHeight(); j++)
79 	{
80 		for (int i=0; i<dest.getWidth();i++, destBytes+=3)
81 		{
82 			int srcX = i % src.getWidth();
83 			int srcY = j % src.getHeight();
84 
85 			GLubyte *srcBytes = src.getBits() + (src.getComponents() * srcX) +
86 				(src.getComponents() * src.getWidth() * srcY);
87 
88 			destBytes[0] = srcBytes[0];
89 			if (src.getComponents() == 1)
90 			{
91 				destBytes[1] = srcBytes[0];
92 				destBytes[2] = srcBytes[0];
93 			}
94 			else
95 			{
96 				destBytes[1] = srcBytes[1];
97 				destBytes[2] = srcBytes[2];
98 			}
99 		}
100 	}
101 }
102 
addLightMapToBitmap(Image & destBitmap,HeightMap & hMap,Vector & sunPos,Vector & ambience,Vector & diffuse,ProgressCounter * counter)103 void ImageModifier::addLightMapToBitmap(Image &destBitmap,
104 	HeightMap &hMap,
105 	Vector &sunPos,
106 	Vector &ambience,
107 	Vector &diffuse,
108 	ProgressCounter *counter)
109 {
110 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
111 
112 	const float softShadow = 3.0f;
113 	const int lightMapWidth = 256; // Resolution of the light map
114 
115 	if (counter) counter->setNewOp(LANG_RESOURCE("LIGHT_MAP", "Light Map"));
116 
117 	// Itterate the dest bitmap pixels
118 	GLfloat *bitmap = new GLfloat[lightMapWidth * lightMapWidth * 3];
119 	GLfloat *bitmapBits = bitmap;
120 	int y;
121 	for (y=0; y<lightMapWidth; y++)
122 	{
123 		if (counter) counter->setNewPercentage(100.0f * float(y) / float(lightMapWidth));
124 
125 		for (int x=0; x<lightMapWidth; x++)
126 		{
127 			float dx = float(x)/float(lightMapWidth)*float(hMap.getMapWidth());
128 			float dy = float(y)/float(lightMapWidth)*float(hMap.getMapHeight());
129 			float dz = hMap.getInterpHeight(
130 				fixed::fromFloat(dx), fixed::fromFloat(dy)).asFloat();
131 
132 			Vector testPosition(dx, dy, dz);
133 			FixedVector fixedTestNormal;
134 			hMap.getInterpNormal(
135 				fixed::fromFloat(dx), fixed::fromFloat(dy), fixedTestNormal);
136 			Vector testNormal = fixedTestNormal.asVector();
137 			Vector sunDirection = (sunPos - testPosition).Normalize();
138 
139 			// Calculate light based on whether obejcts in path
140 			float diffuseLightMult =
141 				(((testNormal.dotP(sunDirection)) / 2.0f) + 0.5f);
142 			float dist = 0.0f;
143 			if (findIntersection(hMap,
144 				testPosition, sunPos, dist, softShadow))
145 			{
146 				// An object is in the path
147 				if (dist < softShadow)
148 				{
149 					// The object is only just in the path
150 					// Create soft shadow
151 					diffuseLightMult *= 1.0f - (dist / softShadow);
152 				}
153 				else
154 				{
155 					// Totaly in path, dark shadow
156 					diffuseLightMult = 0.0f;
157 				}
158 			}
159 
160 			Vector diffuseLight = diffuse * diffuseLightMult;
161 			Vector ambientLight = ambience;
162 			Vector lightColor = diffuseLight + ambientLight;
163 			lightColor[0] = MIN(1.0f, lightColor[0]);
164 			lightColor[1] = MIN(1.0f, lightColor[1]);
165 			lightColor[2] = MIN(1.0f, lightColor[2]);
166 
167 			bitmapBits[0] = lightColor[0];
168 			bitmapBits[1] = lightColor[1];
169 			bitmapBits[2] = lightColor[2];
170 			bitmapBits +=3;
171 		}
172 	}
173 
174 	GLfloat *copyDest = new GLfloat[destBitmap.getWidth() * destBitmap.getHeight() * 3];
175 	gluScaleImage(
176 		GL_RGB,
177 		lightMapWidth, lightMapWidth,
178 		GL_FLOAT, bitmap,
179 		destBitmap.getWidth(), destBitmap.getHeight(),
180 		GL_FLOAT, copyDest);
181 
182 	GLfloat *srcBits = copyDest;
183 	GLubyte *destBits = destBitmap.getBits();
184 	for (y=0; y<destBitmap.getHeight(); y++)
185 	{
186 		for (int x=0; x<destBitmap.getWidth(); x++)
187 		{
188 			destBits[0] = GLubyte(MIN(float(destBits[0]) * (srcBits[0] * 1.2f), 255.0f));
189 			destBits[1] = GLubyte(MIN(float(destBits[1]) * (srcBits[1] * 1.2f), 255.0f));
190 			destBits[2] = GLubyte(MIN(float(destBits[2]) * (srcBits[2] * 1.2f), 255.0f));
191 
192 			srcBits += 3;
193 			destBits += 3;
194 		}
195 	}
196 
197 	delete [] copyDest;
198 	delete [] bitmap;
199 }
200 
blendPixels(GLubyte * destBits,GLubyte * sourceBits,int sourceComponents,float blendAmount)201 static void blendPixels(GLubyte *destBits, GLubyte *sourceBits, int sourceComponents, float blendAmount)
202 {
203 	if (sourceComponents == 1)
204 	{
205 		destBits[0] += (GLubyte) ((float) sourceBits[0] * blendAmount);
206 		destBits[1] += (GLubyte) ((float) sourceBits[0] * blendAmount);
207 		destBits[2] += (GLubyte) ((float) sourceBits[0] * blendAmount);
208 	}
209 	else
210 	{
211 		destBits[0] += (GLubyte) ((float) sourceBits[0] * blendAmount);
212 		destBits[1] += (GLubyte) ((float) sourceBits[1] * blendAmount);
213 		destBits[2] += (GLubyte) ((float) sourceBits[2] * blendAmount);
214 	}
215 }
216 
addHeightToBitmap(HeightMap & hMap,Image & destBitmap,Image & slopeBitmap,Image & shoreBitmap,Image ** origHeightBitmaps,int numberSources,int destBitmapScaleSize,ProgressCounter * counter)217 void ImageModifier::addHeightToBitmap(HeightMap &hMap,
218 										 Image &destBitmap,
219 										 Image &slopeBitmap,
220 										 Image &shoreBitmap,
221 										 Image **origHeightBitmaps,
222 										 int numberSources,
223 										 int destBitmapScaleSize,
224 										 ProgressCounter *counter)
225 {
226 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
227 
228 	const float maxHeight = 30.0f; // Last texture ends at height 30
229 	const float blendHeightFactor = 0.4f; // Ends blend when 40% into height band
230 	const float blendNormalSlopeStart = 0.8f; // Starts blending slope at .80
231 	const float blendNormalSlopeLength = 0.3f; // Blends when 30% more slope
232 	const float blendNormalShoreStart = 0.8f; // Starts the sand
233 	const float blendNormalShoreLength = 0.1f; // Amount of sand
234 	const float noiseMax = 0.4f;
235 
236 	float hMapMaxHeight = 0;
237 	for (int ma=0; ma<hMap.getMapWidth(); ma++)
238 	{
239 		for (int mb=0;mb<hMap.getMapHeight(); mb++)
240 		{
241 			float height = hMap.getHeight(ma, mb).asFloat();
242 			if (height > hMapMaxHeight) hMapMaxHeight = height;
243 		}
244 	}
245 
246 	// Create new bitmaps with the bitmap scaled to the correct size
247 	Image **heightBitmaps = new Image*[numberSources];
248 	ImageItterator ** bitmapItors = new ImageItterator*[numberSources+2];
249 	float bitmapScale = float(destBitmap.getWidth()) / float(destBitmapScaleSize);
250 
251 	// Create a bitmap iterator for each bitmap
252 	// Create a bitmap correctly scaled to the scene
253 	int i;
254 	for (i=0; i<numberSources; i++)
255 	{
256 		if (bitmapScale != 1.0f)
257 		{
258 			// Create the newly scaled bitmaps
259 			heightBitmaps[i] = new Image(origHeightBitmaps[i]->createResize(
260 				int(bitmapScale * origHeightBitmaps[i]->getWidth()),
261 				int(bitmapScale * origHeightBitmaps[i]->getHeight())));
262 		}
263 		else
264 		{
265 			heightBitmaps[i] = origHeightBitmaps[i];
266 		}
267 
268 		// Create iterator
269 		bitmapItors[i] = new ImageItterator(
270 			*heightBitmaps[i],
271 			destBitmap.getWidth(),
272 			destBitmap.getHeight(),
273 			ImageItterator::wrap);
274 	}
275 	// Add shore and slopt itterators
276 	bitmapItors[numberSources] =
277 		new ImageItterator(
278 			slopeBitmap,
279 			destBitmap.getWidth(),
280 			destBitmap.getHeight(),
281 			ImageItterator::wrap);
282 	bitmapItors[numberSources + 1] =
283 		new ImageItterator(
284 			shoreBitmap,
285 			destBitmap.getWidth(),
286 			destBitmap.getHeight(),
287 			ImageItterator::wrap);
288 
289 	GLfloat hdx = (GLfloat) hMap.getMapWidth() / (GLfloat) destBitmap.getWidth();
290 	GLfloat hdy = (GLfloat) hMap.getMapHeight() / (GLfloat) destBitmap.getHeight();
291 
292 	GLubyte *destBits = destBitmap.getBits();
293 
294 	GLfloat hy = 0.0f;
295 	for (int by=0; by<destBitmap.getHeight(); by++, hy+=hdy)
296 	{
297 		if (counter) counter->setNewPercentage((100.0f * float (by)) / float(destBitmap.getHeight()));
298 
299 		GLfloat hx = 0.0f;
300 		for (int bx=0; bx<destBitmap.getWidth(); bx++, destBits+=3, hx+=hdx)
301 		{
302 			static FixedVector fixedNormal;
303 			hMap.getInterpNormal(fixed::fromFloat(hx), fixed::fromFloat(hy), fixedNormal);
304 			Vector &normal = fixedNormal.asVector();
305 			float height = hMap.getInterpHeight(fixed::fromFloat(hx), fixed::fromFloat(hy)).asFloat();
306 			float offSetHeight = hMap.getInterpHeight(
307 				fixed::fromFloat((float)hMap.getMapWidth() - hx),
308 				fixed::fromFloat((float)hMap.getMapHeight() - hy)).asFloat();
309 			height *= (1.0f - (noiseMax/2.0f)) + ((offSetHeight*noiseMax)/hMapMaxHeight);
310 
311 			// Find the index of the current texture by deviding the height into strips
312 			float heightPer = (height / maxHeight) * (float) numberSources;
313 			int heightIndex = (int) heightPer;
314 			if (heightIndex >= numberSources)
315 			{
316 				heightIndex = numberSources - 1;
317 			}
318 
319 			// Check if we are in a blending transition phase
320 			float blendFirstAmount = 1.0f;
321 			float blendSecondAmount = 0.0f;
322 			if (heightIndex < numberSources - 1)
323 			{
324 				float remainderIndex = heightPer - heightIndex;
325 				if (remainderIndex > blendHeightFactor)
326 				{
327 					// We need to do some blending, figure how much
328 					remainderIndex -= blendHeightFactor;
329 					blendSecondAmount = remainderIndex / (1.0f - blendHeightFactor);
330 					blendFirstAmount = 1.0f - blendSecondAmount;
331 				}
332 			}
333 
334 			// Check to see if we need to blend in the side texture
335 			float blendSideAmount = 0.0f;
336 			float blendShoreAmount = 0.0f;
337 			if (normal[2] < blendNormalSlopeStart)
338 			{
339 				if (normal[2] < blendNormalSlopeStart - blendNormalSlopeLength)
340 				{
341 					// Only use the side texture
342 					blendSideAmount = 1.0f;
343 					blendFirstAmount = 0.0f;
344 					blendSecondAmount = 0.0f;
345 				}
346 				else
347 				{
348 					// Blend in the side texture
349 					float remainderIndex = normal[2] - (blendNormalSlopeStart - blendNormalSlopeLength);
350 					remainderIndex /= blendNormalSlopeLength;
351 
352 					blendSideAmount = (1.0f - remainderIndex);
353 					blendFirstAmount *= remainderIndex;
354 					blendSecondAmount *= remainderIndex;
355 				}
356 			}
357 			else if (normal[2] > blendNormalShoreStart &&
358 				height > 3.5f && height < 5.5f)
359 			{
360 				if (normal[2] > blendNormalShoreStart + blendNormalShoreLength)
361 				{
362 					// Only use the side texture
363 					blendShoreAmount = 1.0f;
364 					blendFirstAmount = 0.0f;
365 					blendSecondAmount = 0.0f;
366 				}
367 				else
368 				{
369 					// Blend in the side texture
370 					float remainderIndex = normal[2] - blendNormalSlopeStart;
371 					remainderIndex /= blendNormalSlopeLength;
372 
373 					blendShoreAmount = (1.0f - remainderIndex);
374 					blendFirstAmount *= remainderIndex;
375 					blendSecondAmount *= remainderIndex;
376 				}
377 			}
378 
379 			destBits[0] = destBits[1] = destBits[2] = 0;
380 
381 			// Add first height component
382 			GLubyte *sourceBits1 = bitmapItors[heightIndex]->getPos();
383 			blendPixels(destBits, sourceBits1, bitmapItors[heightIndex]->getComponents(), blendFirstAmount);
384 
385 			if (blendSecondAmount > 0.0f)
386 			{
387 				// Add second height component (if blending)
388 				GLubyte *sourceBits2 = bitmapItors[heightIndex + 1]->getPos();
389 				blendPixels(destBits, sourceBits2, bitmapItors[heightIndex + 1]->getComponents(), blendSecondAmount);
390 			}
391 
392 			if (blendSideAmount > 0.0f)
393 			{
394 				// Add side component (if blending normals)
395 				GLubyte *sourceBits3 = bitmapItors[numberSources]->getPos();
396 				blendPixels(destBits, sourceBits3, bitmapItors[numberSources]->getComponents(), blendSideAmount);
397 			}
398 
399 			if (blendShoreAmount > 0.0f)
400 			{
401 				// Add side component (if blending normals)
402 				GLubyte *sourceBits4 = bitmapItors[numberSources + 1]->getPos();
403 				blendPixels(destBits, sourceBits4, bitmapItors[numberSources + 1]->getComponents(), blendShoreAmount);
404 			}
405 
406 			for (i=0; i<numberSources+2; i++) bitmapItors[i]->incX();
407 		}
408 
409 		for (i=0; i<numberSources+2; i++) bitmapItors[i]->incY();
410 	}
411 
412 	// Cleanup iterator and extra bitmaps
413 	for (i=0; i<numberSources+2; i++)
414 	{
415 		delete bitmapItors[i];
416 	}
417 	delete [] bitmapItors;
418 	for (i=0; i<numberSources; i++)
419 	{
420 		if (bitmapScale != 1.0f)
421 		{
422 			delete heightBitmaps[i];
423 		}
424 	}
425 	delete [] heightBitmaps;
426 }
427 
redBitmap(Image & destBitmap)428 void ImageModifier::redBitmap(Image &destBitmap)
429 {
430 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
431 
432 	unsigned char *destBits = destBitmap.getBits();
433 	for (int y=0; y<destBitmap.getHeight(); y++)
434 	{
435 		for (int x=0; x<destBitmap.getWidth(); x++, destBits += 4)
436 		{
437 			destBits[0] = 255;
438 			destBits[1] = 0;
439 			destBits[2] = 0;
440 			destBits[3] = 0;
441 		}
442 	}
443 }
444 
addTexturesToBitmap(Image & destBitmap,Image & slopeBitmap,Image & shoreBitmap,Image ** heightBitmaps,int numberSources)445 void ImageModifier::addTexturesToBitmap(
446 		Image &destBitmap,
447 		Image &slopeBitmap,
448 		Image &shoreBitmap,
449 		Image **heightBitmaps,
450 		int numberSources)
451 {
452 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
453 
454 	std::vector<Image *> sources;
455 	for (int i=0; i<numberSources; i++)
456 	{
457 		sources.push_back(heightBitmaps[i]);
458 	}
459 	sources.push_back(&slopeBitmap);
460 	sources.push_back(&shoreBitmap);
461 
462 	int currentCount = (int) sources.size();
463 	for (int i=currentCount; i<9; i++)
464 	{
465 		sources.push_back(&shoreBitmap);
466 	}
467 
468 	unsigned char *destBits = destBitmap.getBits();
469 	for (int y=0; y<destBitmap.getHeight(); y++)
470 	{
471 		for (int x=0; x<destBitmap.getWidth(); x++, destBits += 3)
472 		{
473 			int texx = x / (destBitmap.getWidth() / 3);
474 			int texy = y / (destBitmap.getHeight() / 3);
475 			texx = MIN(2, texx);
476 			texy = MIN(2, texy);
477 
478 			Image *src = sources[texx + texy * 3];
479 			int srcx = x % src->getWidth();
480 			int srcy = y % src->getHeight();
481 
482 			unsigned char *srcBits = &src->getBits()[srcx * 3 + srcy * src->getWidth() * 3];
483 			destBits[0] = srcBits[0];
484 			destBits[1] = srcBits[1];
485 			destBits[2] = srcBits[2];
486 		}
487 	}
488 }
489 
removeWaterFromBitmap(HeightMap & hMap,Image & srcBitmap,Image & destBitmap,Image & alphaBitmap,float waterHeight)490 void ImageModifier::removeWaterFromBitmap(HeightMap &hMap,
491 							Image &srcBitmap,
492 							Image &destBitmap,
493 							Image &alphaBitmap,
494 							float waterHeight)
495 {
496 	DIALOG_ASSERT(destBitmap.getComponents() == 4);
497 	DIALOG_ASSERT(srcBitmap.getWidth() == destBitmap.getWidth() &&
498 		srcBitmap.getWidth() == alphaBitmap.getWidth());
499 	DIALOG_ASSERT(srcBitmap.getHeight() == destBitmap.getHeight() &&
500 		srcBitmap.getHeight() == alphaBitmap.getHeight());
501 
502 	GLubyte *destBits = destBitmap.getBits();
503 	GLubyte *srcBits = srcBitmap.getBits();
504 	GLubyte *alphaBits = alphaBitmap.getBits();
505 
506 	GLfloat hdx = (GLfloat) hMap.getMapWidth() / (GLfloat) destBitmap.getWidth();
507 	GLfloat hdy = (GLfloat) hMap.getMapHeight() / (GLfloat) destBitmap.getHeight();
508 
509 	GLfloat hy = 0.0f;
510 	for (int y=0; y<srcBitmap.getHeight(); y++, hy+=hdy)
511 	{
512 		GLfloat hx = 0.0f;
513 		for (int x=0; x<srcBitmap.getWidth(); x++, hx+=hdx,
514 			destBits+=destBitmap.getComponents(), srcBits+=srcBitmap.getComponents(), alphaBits+=alphaBitmap.getComponents())
515 		{
516 			GLubyte alpha = 255 - alphaBits[0];
517 			if (alpha > 0)
518 			{
519 				float height = hMap.getInterpHeight(
520 					fixed::fromFloat(hx), fixed::fromFloat(hy)).asFloat();
521 				if (height > waterHeight - 0.3)
522 				{
523 					alpha = 128;
524 					if (height > waterHeight)
525 					{
526 						alpha = 255;
527 					}
528 				}
529 				else alpha = 0;
530 			}
531 
532 			destBits[0] = srcBits[0];
533 			if (srcBitmap.getComponents() == 1)
534 			{
535 				destBits[1] = srcBits[0];
536 				destBits[2] = srcBits[0];
537 			}
538 			else
539 			{
540 				destBits[1] = srcBits[1];
541 				destBits[2] = srcBits[2];
542 			}
543 			destBits[3] = alpha;
544 		}
545 	}
546 }
547 
addWaterToBitmap(HeightMap & hMap,Image & destBitmap,Image & waterBitmap,float waterHeight)548 void ImageModifier::addWaterToBitmap(HeightMap &hMap,
549 										Image &destBitmap,
550 										Image &waterBitmap,
551 										float waterHeight)
552 {
553 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
554 
555 	const float waterPercentage = 0.75f;
556 	const float oneMinusPercentage = 1.0f - waterPercentage;
557 
558 	ImageItterator bitmapItor(waterBitmap,
559 								destBitmap.getWidth(),
560 								destBitmap.getHeight(),
561 								ImageItterator::wrap);
562 
563 	GLfloat hdx = (GLfloat) hMap.getMapWidth() / (GLfloat) destBitmap.getWidth();
564 	GLfloat hdy = (GLfloat) hMap.getMapHeight() / (GLfloat) destBitmap.getHeight();
565 
566 	GLubyte *destBits = destBitmap.getBits();
567 
568 	GLfloat hy = 0.0f;
569 	for (int by=0; by<destBitmap.getHeight(); by++, hy+=hdy, bitmapItor.incY())
570 	{
571 		GLfloat hx = 0.0f;
572 		for (int bx=0; bx<destBitmap.getWidth(); bx++, destBits+=destBitmap.getComponents(), hx+=hdx, bitmapItor.incX())
573 		{
574 			float height = hMap.getInterpHeight(
575 				fixed::fromFloat(hx), fixed::fromFloat(hy)).asFloat();
576 
577 			if (height <= waterHeight)
578 			{
579 				if (height <= waterHeight - 0.3)
580 				{
581 					GLubyte *sourceBits = bitmapItor.getPos();
582 
583 					destBits[0] = GLubyte(
584 						(waterPercentage * float(sourceBits[0])) +
585 						(oneMinusPercentage * float(destBits[0])));
586 					destBits[1] = GLubyte(
587 						(waterPercentage * float(sourceBits[1])) +
588 						(oneMinusPercentage * float(destBits[1])));
589 					destBits[2] = GLubyte(
590 						(waterPercentage * float(sourceBits[2])) +
591 						(oneMinusPercentage * float(destBits[2])));
592 				}
593 				else
594 				{
595 					destBits[0] = 200;
596 					destBits[1] = 200;
597 					destBits[2] = 200;
598 				}
599 			}
600 		}
601 	}
602 }
603 
makeArenaBitmap()604 Image ImageModifier::makeArenaBitmap()
605 {
606 	Image handle(128, 128, 4, 0);
607 
608 	int arenaX = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaX();
609 	int arenaY = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaY();
610 	int arenaWidth = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaWidth();
611 	int arenaHeight = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaHeight();
612 	int landscapeWidth = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getLandscapeWidth();
613 	int landscapeHeight = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getLandscapeHeight();
614 
615 	int lx = arenaX * handle.getWidth() / landscapeWidth;
616 	int ly = arenaY * handle.getHeight() / landscapeHeight;
617 	int lw = lx + (arenaWidth * handle.getWidth() / landscapeWidth);
618 	int lh = ly + (arenaHeight * handle.getHeight() / landscapeHeight);
619 
620 	unsigned char *bits = handle.getBits();
621 	for (int y=0; y<handle.getHeight(); y++)
622 	{
623 		for (int x=0; x<handle.getWidth(); x++, bits+=4)
624 		{
625 			if (x >= lx && x < lw &&
626 				y >= ly && y < lh)
627 			{
628 				bits[3] = 0;
629 			}
630 			else
631 			{
632 				bits[3] = 255;
633 			}
634 		}
635 	}
636 
637 	return handle;
638 }
639 
makeArenaSurroundBitmap()640 Image ImageModifier::makeArenaSurroundBitmap()
641 {
642 	Image handle(128, 128, 4, 0);
643 
644 	unsigned char *bits = handle.getBits();
645 	for (int y=0; y<handle.getHeight(); y++)
646 	{
647 		for (int x=0; x<handle.getWidth(); x++, bits+=4)
648 		{
649 			bits[3] = 255;
650 		}
651 	}
652 
653 	return handle;
654 }
655 
addBorderToBitmap(Image & destBitmap,int borderSize,float colors[3])656 void ImageModifier::addBorderToBitmap(Image &destBitmap,
657 	int borderSize,
658 	float colors[3])
659 {
660 	int arenaX = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaX();
661 	int arenaY = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaY();
662 	int arenaWidth = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaWidth();
663 	int arenaHeight = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getArenaHeight();
664 	int landscapeWidth = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getLandscapeWidth();
665 	int landscapeHeight = ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getLandscapeHeight();
666 
667 	int borderX = int(float(arenaX) / float(landscapeWidth) * float(destBitmap.getWidth()));
668 	int borderY = int(float(arenaY) / float(landscapeHeight) * float(destBitmap.getHeight()));
669 	int borderWidth = int(float(arenaWidth) / float(landscapeWidth) * float(destBitmap.getWidth()));
670 	int borderHeight = int(float(arenaHeight) / float(landscapeHeight) * float(destBitmap.getHeight()));
671 	if (borderWidth + borderX >= destBitmap.getWidth()) borderWidth = destBitmap.getWidth() - borderX - 1;
672 	if (borderHeight + borderY >= destBitmap.getHeight()) borderHeight = destBitmap.getHeight() - borderY - 1;
673 
674 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
675 
676 	for (int x=borderX; x<=borderX+borderWidth; x++)
677 	{
678 		for (int i=0; i<borderSize; i++)
679 		{
680 			int pos = (x * 3) + ((borderY + i) * destBitmap.getWidth() * 3);
681 			GLubyte *destBits = destBitmap.getBitsOffset(pos);
682 			destBits[0] = GLubyte(colors[0] * 255.0f);
683 			destBits[1] = GLubyte(colors[1] * 255.0f);
684 			destBits[2] = GLubyte(colors[2] * 255.0f);
685 
686 			pos = (x * 3) + ((borderY + borderHeight - i) * destBitmap.getWidth() * 3);
687 			destBits = destBitmap.getBitsOffset(pos);
688 			destBits[0] = GLubyte(colors[0] * 255.0f);
689 			destBits[1] = GLubyte(colors[1] * 255.0f);
690 			destBits[2] = GLubyte(colors[2] * 255.0f);
691 		}
692 	}
693 
694 	for (int y=borderY; y<=borderY+borderHeight; y++)
695 	{
696 		for (int i=0; i<borderSize; i++)
697 		{
698 			int pos = ((borderX + i) * 3) + (y * destBitmap.getWidth() * 3);
699 			GLubyte *destBits = destBitmap.getBitsOffset(pos);
700 			destBits[0] = GLubyte(colors[0] * 255.0f);
701 			destBits[1] = GLubyte(colors[1] * 255.0f);
702 			destBits[2] = GLubyte(colors[2] * 255.0f);
703 
704 			pos = ((borderX + borderWidth - i) * 3) + (y * destBitmap.getWidth() * 3);
705 			destBits = destBitmap.getBitsOffset(pos);
706 			destBits[0] = GLubyte(colors[0] * 255.0f);
707 			destBits[1] = GLubyte(colors[1] * 255.0f);
708 			destBits[2] = GLubyte(colors[2] * 255.0f);
709 		}
710 	}
711 }
712 
makeBitmapTransparent(Image & output,Image & input,Image & mask)713 void ImageModifier::makeBitmapTransparent(Image &output,
714 		Image &input,
715 		Image &mask)
716 {
717 	DIALOG_ASSERT(output.getComponents() == 4);
718 	DIALOG_ASSERT(input.getComponents() == 3 || input.getComponents() == 1);
719 	DIALOG_ASSERT(mask.getComponents() == 4);
720 	DIALOG_ASSERT(output.getWidth() == input.getWidth());
721 	DIALOG_ASSERT(output.getWidth() == mask.getWidth());
722 	DIALOG_ASSERT(output.getHeight() == input.getHeight());
723 	DIALOG_ASSERT(output.getHeight() == mask.getHeight());
724 
725 	GLubyte *outputBits = output.getBits();
726 	GLubyte *maskBits = mask.getBits();
727 	GLubyte *inputBits = input.getBits();
728 
729 	for (int i=0; i<output.getWidth() * output.getHeight(); i++)
730 	{
731 		if (input.getComponents() == 1)
732 		{
733 			outputBits[0] = inputBits[0];
734 			outputBits[1] = inputBits[0];
735 			outputBits[2] = inputBits[0];
736 			outputBits[3] = maskBits[3];
737 		}
738 		else
739 		{
740 			outputBits[0] = inputBits[0];
741 			outputBits[1] = inputBits[1];
742 			outputBits[2] = inputBits[2];
743 			outputBits[3] = maskBits[3];
744 		}
745 
746 		inputBits += input.getComponents();
747 		outputBits += 4;
748 		maskBits += 4;
749 	}
750 }
751 
addCircleToLandscape(ScorchedContext & context,float sx,float sy,float sw,float opacity)752 void ImageModifier::addCircleToLandscape(
753 	ScorchedContext &context,
754 	float sx, float sy, float sw, float opacity)
755 {
756 	float shadowMultWidth = (float) Landscape::instance()->getMainMap().getWidth() /
757 		context.getLandscapeMaps().getGroundMaps().getLandscapeWidth();
758 	float shadowMultHeight = (float) Landscape::instance()->getMainMap().getHeight() /
759 		context.getLandscapeMaps().getGroundMaps().getLandscapeHeight();
760 
761 	addCircle(Landscape::instance()->getMainMap(),
762 		sx * shadowMultWidth, sy * shadowMultHeight,
763 		sw * shadowMultWidth, opacity);
764 }
765 
addCircle(Image & destBitmap,float sx,float sy,float sw,float opacity)766 void ImageModifier::addCircle(Image &destBitmap,
767 								 float sx, float sy, float sw, float opacity)
768 {
769 	float halfW = sw / 2.0f;
770 
771 	float minX = sx - halfW;
772 	float minY = sy - halfW;
773 	float maxX = sx + halfW;
774 	float maxY = sy + halfW;
775 	/*minX /= 2.0f;
776 	minY /= 2.0f;
777 	maxX /= 2.0f;
778 	maxY /= 2.0f;*/
779 
780 	minX = MAX(minX, 0.0f);
781 	minY = MAX(minY, 0.0f);
782 	maxX = MIN(maxX, destBitmap.getWidth() - 1.0f);
783 	maxY = MIN(maxY, destBitmap.getHeight() - 1.0f);
784 
785 	int xStart = int(minX);
786 	int yStart = int(minY);
787 	int xWidth = int(maxX - minX);
788 	int yWidth = int(maxY - minY);
789 	int yInc = (destBitmap.getWidth() - xWidth) * 3;
790 
791 	if (xWidth <= 0 || yWidth <= 0) return;
792 	double degMult = (1 / double(yWidth)) * 3.14;
793 
794 	GLubyte *start = &destBitmap.getBits()[(yStart * destBitmap.getWidth() * 3) + xStart * 3];
795 	for (int y=0; y<yWidth; y++, start += yInc)
796 	{
797 		double deg = double(y) * degMult;
798 		int realXSize = int(sin(deg) * double(xWidth));
799 		int halfSize = (xWidth - realXSize) / 2;
800 
801 		start+=halfSize * 3;
802 		int x;
803 		for (x=0; x<realXSize; x++, start+=3)
804 		{
805 			start[0] = start[0] / 2;
806 			start[1] = start[1] / 2;
807 			start[2] = start[2] / 2;
808 		}
809 		start+=(xWidth - (halfSize + x)) * 3;
810 	}
811 }
812 
813 
addBitmapToLandscape(ScorchedContext & context,Image & srcBitmap,float sx,float sy,float scalex,float scaley,bool commit)814 void ImageModifier::addBitmapToLandscape(
815 	ScorchedContext &context,
816 	Image &srcBitmap,
817 	float sx, float sy, float scalex, float scaley,
818 	bool commit)
819 {
820 	float shadowMultWidth = (float) Landscape::instance()->getMainMap().getWidth() /
821 		context.getLandscapeMaps().getGroundMaps().getLandscapeWidth();
822 	float shadowMultHeight = (float) Landscape::instance()->getMainMap().getHeight() /
823 		context.getLandscapeMaps().getGroundMaps().getLandscapeHeight();
824 
825 	addBitmap(
826 		Landscape::instance()->getMainMap(),
827 		srcBitmap,
828 		sx * shadowMultWidth,
829 		sy * shadowMultHeight,
830 		shadowMultWidth * scalex,
831 		shadowMultHeight * scaley,
832 		commit);
833 }
834 
addBitmap(Image & destBitmap,Image & srcBitmap,float sx,float sy,float scalex,float scaley,bool commit)835 void ImageModifier::addBitmap(Image &destBitmap,
836 	Image &srcBitmap,
837 	float sx, float sy, float scalex, float scaley,
838 	bool commit)
839 {
840 	DIALOG_ASSERT(destBitmap.getComponents() == 3);
841 
842 	int srcScaleWidth = int(float(srcBitmap.getWidth()) * scalex);
843 	int srcScaleHeight = int(float(srcBitmap.getHeight()) * scaley);
844 
845 	float minX = sx - srcScaleWidth / 2;
846 	float minY = sy - srcScaleHeight / 2;
847 	float maxX = sx + srcScaleWidth / 2;
848 	float maxY = sy + srcScaleHeight / 2;
849 
850 	minX = MAX(minX, 0.0f);
851 	minY = MAX(minY, 0.0f);
852 	maxX = MIN(maxX, destBitmap.getWidth() - 1.0f);
853 	maxY = MIN(maxY, destBitmap.getHeight() - 1.0f);
854 
855 	int xStart = int(minX);
856 	int yStart = int(minY);
857 	int xWidth = int(maxX - minX);
858 	int yWidth = int(maxY - minY);
859 
860 	if (xWidth <= 0 || yWidth <= 0) return;
861 
862 	int yDestInc = (destBitmap.getWidth() * 3);
863 
864 	GLubyte *dest = &destBitmap.getBits()[
865 		(yStart * destBitmap.getWidth() * 3) + xStart * 3];
866 	for (int y=0; y<yWidth; y++, dest += yDestInc)
867 	{
868 		GLubyte *tmpDest = dest;
869 		for (int x=0; x<xWidth; x++)
870 		{
871 			int srcX = int(float(x) / scalex);
872 			srcX = MIN(srcX, srcBitmap.getWidth());
873 			int srcY = int(float(y) / scaley);
874 			srcY = MIN(srcY, srcBitmap.getHeight());
875 
876 			GLubyte *tmpSrc = srcBitmap.getBits() +
877 				srcX * srcBitmap.getComponents() +
878 				srcY * srcBitmap.getComponents() * srcBitmap.getWidth();
879 
880 			float alpha = 1.0f;
881 			float invAlpha = 0.0f;
882 			if (srcBitmap.getComponents() == 4)
883 			{
884 				alpha = float(tmpSrc[3]) / 255.0f;
885 				invAlpha = 1.0f - alpha;
886 			}
887 
888 			tmpDest[0] = GLubyte(float(tmpSrc[0]) * alpha + float(tmpDest[0]) * invAlpha);
889 			if (srcBitmap.getComponents() == 1)
890 			{
891 				tmpDest[1] = GLubyte(float(tmpSrc[0]) * alpha + float(tmpDest[1]) * invAlpha);
892 				tmpDest[2] = GLubyte(float(tmpSrc[0]) * alpha + float(tmpDest[2]) * invAlpha);
893 			}
894 			else
895 			{
896 				tmpDest[1] = GLubyte(float(tmpSrc[1]) * alpha + float(tmpDest[1]) * invAlpha);
897 				tmpDest[2] = GLubyte(float(tmpSrc[2]) * alpha + float(tmpDest[2]) * invAlpha);
898 			}
899 
900 			tmpDest += 3;
901 		}
902 	}
903 
904 	if (commit)
905 	{
906 		int landscapeWidth = Landscape::instance()->getMainMap().getWidth();
907 		int width = 3 * landscapeWidth;
908 		width   = (width + 3) & ~3;
909 
910 		GLubyte *bytes =
911 			Landscape::instance()->getMainMap().getBits() + ((width * yStart) + xStart * 3);
912 
913 		GLState currentState(GLState::TEXTURE_ON);
914 		Landscape::instance()->getMainTexture().draw(true);
915 
916 		glPixelStorei(GL_UNPACK_ROW_LENGTH, landscapeWidth);
917 		glTexSubImage2D(GL_TEXTURE_2D, 0,
918 						xStart, yStart,
919 						xWidth, yWidth,
920 						GL_RGB, GL_UNSIGNED_BYTE,
921 						bytes);
922 		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
923 	}
924 }
925 
scalePlanBitmap(Image & destBitmap,Image & srcIncBitmap,int landscapeX,int landscapeY)926 void ImageModifier::scalePlanBitmap(Image &destBitmap,
927 	Image &srcIncBitmap,
928 	int landscapeX, int landscapeY)
929 {
930 	int maxSize = MAX(landscapeX, landscapeY);
931 	float xScale = landscapeX / float(maxSize);
932 	float yScale = landscapeY / float(maxSize);
933 
934 	int newX = int(float(destBitmap.getWidth()) / xScale);
935 	int newY = int(float(destBitmap.getHeight()) / yScale);
936 	int offsetX = (newX - destBitmap.getWidth()) / 2;
937 	int offsetY = (newY - destBitmap.getHeight()) / 2;
938 
939 	Image srcBitmap =
940 		srcIncBitmap.createResize(newX, newY);
941 
942 	GLubyte *dest = destBitmap.getBits();
943 	for (int y=0; y<destBitmap.getHeight(); y++)
944 	{
945 		for (int x=0; x<destBitmap.getWidth(); x++, dest+=destBitmap.getComponents())
946 		{
947 			int srcX = MIN(x + offsetX, srcBitmap.getWidth() - 1);
948 			int srcY = MIN(y + offsetY, srcBitmap.getHeight() - 1);
949 			GLubyte *src = srcBitmap.getBits() +
950 				srcX * srcBitmap.getComponents() +
951 				srcY * srcBitmap.getComponents() * srcBitmap.getWidth();
952 
953 			dest[0] = src[0];
954 			dest[1] = src[1];
955 			dest[2] = src[2];
956 		}
957 	}
958 }
959