1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ 2 3 4 #include "System/TdfParser.h" 5 6 #include <cassert> 7 #include <cstring> 8 #include <deque> 9 10 #include "TerrainBase.h" 11 #include "TerrainNode.h" 12 #include "Textures.h" 13 #include "Map/SM3/Plane.h" 14 #include "Rendering/Textures/Bitmap.h" 15 #include "System/FileSystem/FileSystem.h" 16 #include "System/float3.h" 17 #if defined(TERRAIN_USE_IL) 18 #include <IL/il.h> 19 #endif // defined(TERRAIN_USE_IL) 20 21 namespace terrain { 22 SetTexCoordGen(float * tgv)23 void SetTexCoordGen(float* tgv) 24 { 25 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); 26 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); 27 28 Plane s(tgv[0], 0, 0, tgv[2]); 29 glTexGenfv(GL_S, GL_OBJECT_PLANE, (float*)&s); 30 Plane t(0, 0, tgv[1], tgv[3]); 31 glTexGenfv(GL_T, GL_OBJECT_PLANE, (float*)&t); 32 33 glEnable(GL_TEXTURE_GEN_S); 34 glEnable(GL_TEXTURE_GEN_T); 35 } 36 37 //----------------------------------------------------------------------- 38 // Texturing objects 39 //----------------------------------------------------------------------- 40 BaseTexture()41 BaseTexture::BaseTexture() 42 : id(0) 43 , tilesize(10.0f, 10.0f) 44 { 45 } 46 ~BaseTexture()47 BaseTexture::~BaseTexture() 48 { 49 if (id) { 50 glDeleteTextures(1, &id); 51 id = 0; 52 } 53 } 54 CalcTexGenVector(float * v)55 void BaseTexture::CalcTexGenVector(float* v) 56 { 57 v[0] = 1.0f / (SquareSize * tilesize.x); 58 v[1] = 1.0f / (SquareSize * tilesize.y); 59 v[2] = v[3] = 0.0f; 60 } 61 SetupTexGen()62 void BaseTexture::SetupTexGen() 63 { 64 float tgv[4]; 65 CalcTexGenVector(tgv); 66 67 Plane s(tgv[0], 0, 0, tgv[2]); 68 glTexGenfv(GL_S, GL_OBJECT_PLANE, (float*)&s); 69 Plane t(0, 0, tgv[1], tgv[3]); 70 glTexGenfv(GL_T, GL_OBJECT_PLANE, (float*)&t); 71 } 72 TiledTexture()73 TiledTexture::TiledTexture() 74 {} 75 ~TiledTexture()76 TiledTexture::~TiledTexture() 77 {} 78 Load(const std::string & name,const std::string & section,ILoadCallback * cb,const TdfParser * tdf,bool isBumpmap)79 void TiledTexture::Load(const std::string& name, const std::string& section, ILoadCallback* cb, const TdfParser* tdf, bool isBumpmap) 80 { 81 this->name = name; 82 83 tilesize.x = tilesize.y = atof(tdf->SGetValueDef("10", section + "\\Tilesize").c_str()); 84 85 std::string texture; 86 if (isBumpmap) { 87 if (!tdf->SGetValue(texture, section + "\\Bumpmap")) 88 return; 89 90 this->name += "_BM"; 91 } else { 92 if (!tdf->SGetValue(texture, section + "\\File")) 93 throw content_error("Texture " + section + " has no image file."); 94 } 95 96 if (cb) cb->PrintMsg(" loading %s %s from %s...", isBumpmap ? "bumpmap" : "texture", name.c_str(), texture.c_str()); 97 id = LoadTexture(texture); 98 } 99 CreateFlatBumpmap()100 TiledTexture* TiledTexture::CreateFlatBumpmap() 101 { 102 TiledTexture* tt = new TiledTexture; 103 glGenTextures(1, &tt->id); 104 glBindTexture(GL_TEXTURE_2D, tt->id); 105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 106 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 107 uchar img[3] = { 127, 127, 255 }; 108 glTexImage2D(GL_TEXTURE_2D,0, 3, 1, 1, 0,GL_RGB, GL_UNSIGNED_BYTE, img); 109 tt->name = "_flatbm"; 110 return tt; 111 } 112 Blendmap()113 Blendmap::Blendmap() 114 : generatorInfo(NULL) 115 , image(NULL) 116 , curAreaResult(AlphaImage::AREA_ONE) 117 { 118 } 119 ~Blendmap()120 Blendmap::~Blendmap() 121 { 122 delete generatorInfo; 123 } 124 Load(const std::string & name,const std::string & section,Heightmap * heightmap,ILoadCallback * cb,const TdfParser * tdf)125 void Blendmap::Load(const std::string& name, const std::string& section, Heightmap* heightmap, ILoadCallback* cb, const TdfParser* tdf) 126 { 127 // Create blendmap 128 this->name = name; 129 tilesize.x = heightmap->w; 130 tilesize.y = heightmap->h; 131 132 // Is the blendmap generated or loaded from a file? 133 std::string blendmapImg; 134 tdf->GetDef(blendmapImg, std::string(), section + "\\File"); 135 bool generateBlendmap = blendmapImg.empty(); 136 if (generateBlendmap) { 137 // Load blendfactor function parameters 138 GeneratorInfo* gi = generatorInfo = new GeneratorInfo; 139 gi->coverage = atof(tdf->SGetValueDef("1", section + "\\Coverage").c_str()); 140 gi->noise = atof(tdf->SGetValueDef("0", section + "\\Noise").c_str()); 141 gi->minSlope = atof(tdf->SGetValueDef("0", section + "\\MinSlope").c_str()); 142 gi->maxSlope = atof(tdf->SGetValueDef("1", section + "\\MaxSlope").c_str()); 143 gi->minSlopeFuzzy = atof(tdf->SGetValueDef("0", section + "\\MinSlopeFuzzy").c_str()); 144 gi->maxSlopeFuzzy = atof(tdf->SGetValueDef("0", section + "\\MaxSlopeFuzzy").c_str()); 145 gi->maxHeight = atof(tdf->SGetValueDef("1.01", section + "\\MaxHeight").c_str()); 146 gi->minHeight = atof(tdf->SGetValueDef("-0.01", section + "\\MinHeight").c_str()); 147 gi->maxHeightFuzzy = atof(tdf->SGetValueDef("0", section + "\\MaxHeightFuzzy").c_str()); 148 gi->minHeightFuzzy = atof(tdf->SGetValueDef("0", section + "\\MinHeightFuzzy").c_str()); 149 150 int generatorLOD = atoi(tdf->SGetValueDef("0", section + "\\GeneratorLOD").c_str()); 151 152 float hmScale = atof(tdf->SGetValueDef("1000", "map\\terrain\\HeightScale").c_str()); 153 float hmOffset = atof(tdf->SGetValueDef("0", "map\\terrain\\HeightOffset").c_str()); 154 155 if (cb) cb->PrintMsg(" generating %s...", name.c_str()); 156 Generate(heightmap, generatorLOD, hmScale, hmOffset); 157 } 158 else { // Load blendmap from file 159 CBitmap bitmap; 160 if (!bitmap.LoadGrayscale(blendmapImg)) 161 throw std::runtime_error("Failed to load blendmap image " + blendmapImg); 162 163 if (cb) cb->PrintMsg(" loading blendmap %s from %s...", name.c_str(), blendmapImg.c_str()); 164 165 image = new AlphaImage; 166 image->CopyFromBitmap(bitmap); 167 } 168 } 169 BlendmapFilter(AlphaImage * img)170 static void BlendmapFilter(AlphaImage* img) 171 { 172 float* nd = new float[img->w*img->h]; 173 174 //3.95f*(x-.5f)^3+.5 175 176 for (int y=0;y<img->h;y++) { 177 for (int x=0;x<img->w;x++) { 178 int l=x?x-1:x; 179 int r=x<img->w-1?x+1:x; 180 int t=y?y-1:y; 181 int b=y<img->h-1?y+1:y; 182 183 float result = 0.0f; 184 185 result += img->at(l,t); 186 result += img->at(x,t); 187 result += img->at(r,t); 188 189 result += img->at(l,y); 190 //result += img->at(x,y); 191 result += img->at(r,y); 192 193 result += img->at(l,b); 194 result += img->at(x,b); 195 result += img->at(r,b); 196 197 result *= (1.0f/8.0f); 198 nd[y*img->w+x] = result; 199 } 200 } 201 202 memcpy(img->data, nd, sizeof(float)*img->w*img->h); 203 delete[] nd; 204 } 205 206 Generate(Heightmap * rootHm,int lodLevel,float hmScale,float hmOffset)207 void Blendmap::Generate(Heightmap* rootHm, int lodLevel, float hmScale, float hmOffset) 208 { 209 const Heightmap* heightmap = rootHm->GetLevel(-lodLevel); 210 211 // Allocate the blendmap image 212 AlphaImage* bm = new AlphaImage; 213 bm->Alloc(heightmap->w-1, heightmap->h-1); // texture dimensions have to be power-of-two 214 215 Blendmap::GeneratorInfo* gi = generatorInfo; 216 217 // Calculate blend factors using the function parameters and heightmap input: 218 for (int y=0;y<bm->h;y++) 219 { 220 for (int x=0;x<bm->w;x++) 221 { 222 const float h = (heightmap->atSynced(x, y) - hmOffset) / hmScale; 223 float factor=1.0f; 224 225 if(h < gi->minHeight - gi->minHeightFuzzy) { 226 bm->at(x,y) = 0.0f; 227 continue; 228 } else if (gi->minHeightFuzzy > 0.0f && h < gi->minHeight + gi->minHeightFuzzy) 229 factor = (h - (gi->minHeight - gi->minHeightFuzzy)) / (2.0f * gi->minHeightFuzzy); 230 231 if(h > gi->maxHeight + gi->maxHeightFuzzy) { 232 bm->at (x,y) = 0.0f; 233 continue; 234 } else if (gi->maxHeightFuzzy > 0.0f && h > gi->maxHeight - gi->maxHeightFuzzy) 235 factor *= ((gi->maxHeight + gi->maxHeightFuzzy) - h) / (2.0f * gi->maxHeightFuzzy); 236 237 float norm_y = 0.0f; 238 if (heightmap->normalData) { 239 const uchar* cnorm = heightmap->GetNormal(x, y); 240 norm_y = cnorm[1] / 255.0f * 2.0f - 1.0f; 241 if (norm_y > 1.0f) norm_y = 1.0f; 242 } else { 243 Vector3 tangent, binormal; 244 CalculateTangents(heightmap, x,y,tangent,binormal); 245 Vector3 normal = tangent.cross(binormal); 246 normal.ANormalize(); 247 norm_y = normal.y; 248 } 249 250 // flatness=dotproduct of surface normal with up vector 251 float slope = 1.0f - math::fabs(norm_y); 252 253 if (slope < gi->minSlope - gi->minSlopeFuzzy) { 254 bm->at(x,y) = 0.0f; 255 continue; 256 } else if (gi->minSlopeFuzzy > 0.0f && slope < gi->minSlope + gi->minSlopeFuzzy) 257 factor *= (h - (gi->minSlope - gi->minSlopeFuzzy)) / ( 2.0f * gi->minSlopeFuzzy); 258 259 if (slope > gi->maxSlope + gi->maxSlopeFuzzy) { 260 bm->at(x,y) = 0.0f; 261 continue; 262 } else if (gi->maxSlopeFuzzy > 0.0f && slope > gi->maxSlope - gi->maxSlopeFuzzy) 263 factor *= ((gi->maxSlope + gi->maxSlopeFuzzy) - h) / (2.0f * gi->maxSlopeFuzzy); 264 265 factor *= gi->coverage; 266 factor *= (rand() < gi->noise * RAND_MAX) ? 0.0f : 1.0f; 267 268 bm->at(x,y) = factor; 269 } 270 } 271 272 BlendmapFilter(bm); 273 image = bm; 274 } 275 276 CalcTexGenVector(float * v4)277 void DetailBumpmap::CalcTexGenVector(float* v4) 278 { 279 QuadRenderData* rd = node->renderData; 280 float pw = 1.0f / rd->normalMapTexWidth; 281 282 v4[0] = pw * (rd->normalMapW - 1) / (node->end.x - node->start.x); 283 v4[2] = 0.5f * pw - v4[0] * node->start.x; 284 285 v4[1] = pw * (rd->normalMapW - 1) / (node->end.z - node->start.z); 286 v4[3] = 0.5f * pw - v4[1] * node->start.z; 287 } 288 ~DetailBumpmap()289 DetailBumpmap::~DetailBumpmap() {} 290 291 292 // Util functions 293 GenSphereBumpmap()294 GLuint GenSphereBumpmap() 295 { 296 GLuint id; 297 glGenTextures(1, &id); 298 glBindTexture(GL_TEXTURE_2D, id); 299 300 uchar img[3 * 64 * 64], *p = img; 301 302 for (int y = 0; y < 64; y++) { 303 for (int x = 0; x < 64; x++) 304 { 305 int sx = x - 32; 306 int sy = y - 32; 307 Vector3 n; 308 309 if (sx*sx + sy*sy < 32*32) { 310 const int sz = (int)math::sqrt(static_cast<float>(32 * 32 - sx*sx - sy*sy)); 311 n = Vector3(sx, sy, sz); 312 n.ANormalize(); 313 } 314 315 // compress it into a color 316 *(p++) = (uchar)(255 * (n.x * 0.5f + 0.5f)); 317 *(p++) = (uchar)(255 * (n.y * 0.5f + 0.5f)); 318 *(p++) = (uchar)(255 * (n.z * 0.5f + 0.5f)); 319 } 320 } 321 322 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 324 glTexImage2D(GL_TEXTURE_2D, 0, 3, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, img); 325 326 SaveImage("sphere_bm.jpg", 3, GL_UNSIGNED_BYTE, 64, 64, img); 327 return id; 328 } 329 LoadTexture(const std::string & fn,bool isBumpmap)330 GLuint LoadTexture(const std::string& fn, bool isBumpmap) 331 { 332 CBitmap bmp; 333 334 if (!bmp.Load(fn)) 335 throw content_error("Failed to load texture: " + fn); 336 337 GLuint tex = bmp.CreateTexture(true); 338 return tex; 339 } 340 SaveImage(const char * fn,int components,GLenum type,int w,int h,void * data)341 void SaveImage(const char* fn, int components, GLenum type, int w, int h, void* data) 342 { 343 // We use the fact that DevIL has the same constants for component type as OpenGL 344 /// ( GL_UNSIGNED_BYTE = IL_UNSIGNED_BYTE for example ) 345 #if defined(TERRAIN_USE_IL) 346 // Save image 347 ILuint out; 348 ilGenImages(1, &out); 349 ilBindImage(out); 350 ILenum fmt = IL_RGB; 351 if (components == 4) { 352 fmt = IL_RGBA; 353 } else if (components == 1) { 354 fmt = IL_LUMINANCE; 355 } 356 ilTexImage(w, h, 1, components, fmt, type, data); 357 FileSystem::Remove(fn); 358 ilSaveImage((ILstring) fn); 359 ilDeleteImages(1, &out); 360 #endif // defined(TERRAIN_USE_IL) 361 } 362 363 } 364 365