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