OpenClonk
C4LandscapeRender.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2011-2016, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include "C4Include.h"
19 
20 #include "c4group/C4Components.h"
21 #include "c4group/C4GroupSet.h"
22 #include "graphics/C4DrawGL.h"
23 #include "landscape/C4Landscape.h"
24 #include "landscape/C4Texture.h"
26 #include "lib/StdColors.h"
27 
28 #ifndef USE_CONSOLE
29 
30 // Automatically reload shaders when changed at runtime?
31 #define AUTO_RELOAD_SHADERS
32 
33 #ifdef _DEBUG
34 
35 // Generate seperator textures into 3D texture so we can make sure that
36 // we are addressing textures using the right coordinates
37 #define DEBUG_SEPERATOR_TEXTURES
38 
39 // Replace all textures by solid colors
40 //#define DEBUG_SOLID_COLOR_TEXTURES
41 
42 #endif
43 
44 // How much to look into each direction for bias
45 const int C4LR_BiasDistanceX = 7;
46 const int C4LR_BiasDistanceY = 7;
47 
48 // Name used for the seperator texture
49 const char *const SEPERATOR_TEXTURE = "--SEP--";
50 
52 {
53  ZeroMem(Surfaces, sizeof(Surfaces));
54  hMaterialTexture = matMapTexture = 0;
55  hVBO = 0;
56  hVAOIDNoLight = 0;
57  hVAOIDLight = 0;
58 }
59 
61 {
62  Clear();
63 }
64 
65 bool C4LandscapeRenderGL::Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pTexs, C4GroupSet *pGraphics)
66 {
67  Clear();
68 
69  // Safe info
70  this->iWidth = iWidth;
71  this->iHeight = iHeight;
72  this->pTexs = pTexs;
73 
74  // Start timer
75  this->TimerStart = std::chrono::steady_clock::now();
76 
77  // Allocate landscape textures
78  if (!InitLandscapeTexture())
79  {
80  LogFatal("[!] Could not initialize landscape texture!");
81  return false;
82  }
83 
84  // Build texture, er, texture
85  if (!InitMaterialTexture(pTexs))
86  {
87  LogFatal("[!] Could not initialize landscape textures for rendering!");
88  return false;
89  }
90 
91  // Load sclaer
92  if (!LoadScaler(pGraphics))
93  {
94  LogFatal("[!] Could not load scaler!");
95  return false;
96  }
97 
98  // Load shader
99  if (!LoadShaders(pGraphics))
100  {
101  LogFatal("[!] Could not initialize landscape shader!");
102  return false;
103  }
104 
105  if (!InitVBO())
106  {
107  LogFatal("[!] Could not initialize landscape VBO!");
108  return false;
109  }
110 
111  return true;
112 }
113 
114 bool C4LandscapeRenderGL::ReInit(int32_t iWidth, int32_t iHeight)
115 {
116  // Safe info
117  this->iWidth = iWidth;
118  this->iHeight = iHeight;
119 
120  // Clear old landscape textures
121  for (auto & Surface : Surfaces)
122  {
123  delete Surface;
124  Surface = nullptr;
125  }
126 
127  // Allocate new landscape textures
128  if (!InitLandscapeTexture())
129  {
130  LogFatal("[!] Could not initialize landscape texture!");
131  return false;
132  }
133  return true;
134 }
135 
137 {
138  ClearShaders();
139 
140  // free textures
141  int i;
142  for (i = 0; i < C4LR_SurfaceCount; i++)
143  {
144  delete Surfaces[i];
145  Surfaces[i] = nullptr;
146  }
147  if (hMaterialTexture) glDeleteTextures(1, &hMaterialTexture);
148  hMaterialTexture = 0;
149  if (matMapTexture) glDeleteTextures(1, &matMapTexture);
150  matMapTexture = 0;
151 
152  if (hVBO != 0)
153  {
154  glDeleteBuffers(1, &hVBO);
155  hVBO = 0;
156  }
157 
158  if (hVAOIDLight != 0)
159  {
160  pGL->FreeVAOID(hVAOIDLight);
161  hVAOIDLight = 0;
162  }
163 
164  if (hVAOIDNoLight != 0)
165  {
166  pGL->FreeVAOID(hVAOIDNoLight);
167  hVAOIDNoLight = 0;
168  }
169 }
170 
171 bool C4LandscapeRenderGL::InitLandscapeTexture()
172 {
173  // Don't round up to the nearest power-of-two here. Modern
174  // hardware should handle NPOT-textures just fine, and they
175  // are already used in other places in Clonk. This avoids
176  // accessing one row or column of pixels below the actually
177  // used surface in the shader, which can lead to strange
178  // one-pixel edges at the bottom or right of the landscape
179  // (see e.g. http://bugs.openclonk.org/view.php?id=771).
180  int iSfcWdt = iWidth;
181  int iSfcHgt = iHeight;
182 
183  // Create our surfaces
184  for(auto & Surface : Surfaces)
185  {
186  Surface = new C4Surface();
187  if(!Surface->Create(iSfcWdt, iSfcHgt))
188  return false;
189  }
190 
191  return true;
192 }
193 
194 bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
195 {
196 
197  // Populate our map with all needed textures
198  MaterialTextureMap.emplace_back("");
199  AddTexturesFromMap(pTexs);
200 
201  // Determine depth to use
202  iMaterialTextureDepth = 2*MaterialTextureMap.size();
203  int32_t iNormalDepth = iMaterialTextureDepth / 2;
204 
205  // Find the largest texture
206  C4Texture *pTex; C4Surface *pRefSfc = nullptr;
207  for(int iTexIx = 0; (pTex = pTexs->GetTexture(pTexs->GetTexture(iTexIx))); iTexIx++)
208  if(C4Surface *pSfc = pTex->Surface32)
209  if (!pRefSfc || pRefSfc->Wdt < pSfc->Wdt || pRefSfc->Hgt < pSfc->Hgt)
210  pRefSfc = pSfc;
211  if(!pRefSfc)
212  return false;
213 
214  // Get size for our textures. We might be limited by hardware
215  int iTexWdt = pRefSfc->Wdt, iTexHgt = pRefSfc->Hgt;
216  GLint iMaxTexSize, iMaxTexLayers;
217  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &iMaxTexSize);
218  glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &iMaxTexLayers);
219  if (iTexWdt > iMaxTexSize || iTexHgt > iMaxTexSize)
220  {
221  iTexWdt = std::min(iTexWdt, iMaxTexSize);
222  iTexHgt = std::min(iTexHgt, iMaxTexSize);
223  LogF(" gl: Material textures too large, GPU only supports %dx%d! Cropping might occur!", iMaxTexSize, iMaxTexSize);
224  }
225  if(iMaterialTextureDepth >= iMaxTexLayers)
226  {
227  LogF(" gl: Too many material textures! GPU only supports 3D texture depth of %d!", iMaxTexSize);
228  return false;
229  }
230  iMaterialWidth = iTexWdt;
231  iMaterialHeight = iTexHgt;
232 
233  // Compose together data of all textures
234  const int iTexSize = iTexWdt * iTexHgt * C4Draw::COLOR_DEPTH_BYTES;
235  const int iSize = iTexSize * iMaterialTextureDepth;
236  BYTE *pData = new BYTE [iSize];
237  for(int i = 0; i < iMaterialTextureDepth; i++)
238  {
239  BYTE *p = pData + i * iTexSize;
240  // Get texture at position
241  StdStrBuf Texture;
242  bool fNormal = i >= iNormalDepth;
243  if(i < int32_t(MaterialTextureMap.size()))
244  Texture.Ref(MaterialTextureMap[i]);
245  else if(fNormal && i < iNormalDepth + int32_t(MaterialTextureMap.size()))
246  Texture.Format("%s_NRM", MaterialTextureMap[i-iNormalDepth].getData());
247  // Try to find the texture
248  C4Texture *pTex; C4Surface *pSurface;
249  if((pTex = pTexs->GetTexture(Texture.getData())) && (pSurface = pTex->Surface32))
250  {
251 #ifdef DEBUG_SOLID_COLOR_TEXTURES
252  // Just write a solid color that depends on the texture index
253  DWORD *texdata = reinterpret_cast<DWORD *>(p);
254  for (int y = 0; y < iTexHgt; ++y)
255  for (int x = 0; x < iTexWdt; ++x)
256  *texdata++ = RGBA((iTex & 48), (iTex & 3) * 16, (i & 12) * 4, 255);
257  continue;
258 #else
259  // Size recheck. It's fine if this texture's size is a divisor
260  // of the maximum texture size, because then we can just tile
261  // the smaller texture.
262  if(pSurface->Wdt != iTexWdt || pSurface->Hgt != iTexHgt)
263  if (iTexWdt % pSurface->Wdt != 0 || iTexHgt % pSurface->Hgt != 0)
264  LogF(" gl: texture %s size mismatch (%dx%d vs %dx%d)!", Texture.getData(), pSurface->Wdt, pSurface->Hgt, iTexWdt, iTexHgt);
265 
266  // Copy bytes
267  DWORD *texdata = reinterpret_cast<DWORD *>(p);
268  pSurface->Lock();
269  for (int y = 0; y < iTexHgt; ++y)
270  for (int x = 0; x < iTexWdt; ++x)
271  *texdata++ = pSurface->GetPixDw(x % pSurface->Wdt, y % pSurface->Hgt, false);
272  pSurface->Unlock();
273  continue;
274 #endif
275  }
276  // Seperator texture?
277  if(SEqual(Texture.getData(), SEPERATOR_TEXTURE))
278  {
279  // Make some ugly stripes
280  DWORD *texdata = reinterpret_cast<DWORD *>(p);
281  for (int y = 0; y < iTexHgt; ++y)
282  for (int x = 0; x < iTexWdt; ++x)
283  *texdata++ = ((x + y) % 32 < 16 ? RGBA(255, 0, 0, 255) : RGBA(0, 255, 255, 255));
284  continue;
285  }
286  // If we didn't "continue" yet, we haven't written the texture yet.
287  // Make color texture transparent, and normal texture flat.
288  if (fNormal)
289  {
290  DWORD *texdata = reinterpret_cast<DWORD *>(p);
291  for (int y = 0; y < iTexHgt; ++y)
292  for (int x = 0; x < iTexWdt; ++x)
293  *texdata++ = RGBA(127, 127, 255, 255);
294  }
295  else
296  memset(p, 0, iTexSize);
297  }
298 
299  // Clear error error(s?)
300  while(glGetError()) {}
301 
302  // Alloc 1D matmap texture
303  glGenTextures(1, &matMapTexture);
304 
305  // Alloc 2D texture array
306  glGenTextures(1, &hMaterialTexture);
307 
308  // Generate textures
309  int iSizeSum = 0;
310 
311  // Select texture
312  glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
313  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
314 
315  // We fully expect to tile these
316  glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
317  glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
318  glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
319  glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
320 
321  // Make it happen!
322  glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA,
323  GL_UNSIGNED_INT_8_8_8_8_REV,
324  pData);
325 
326  glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
327 
328  // Statistics
329  iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * C4Draw::COLOR_DEPTH_BYTES;
330 
331  // Dispose of data
332  delete [] pData;
333 
334  // Check whether we were successful
335  if(int err = glGetError())
336  {
337  LogF(" gl: Could not load textures (error %d)", err);
338  return false;
339  }
340 
341  // Announce the good news
342  LogF(" gl: Texturing uses %d slots at %dx%d (%d MB total)",
343  static_cast<int>(MaterialTextureMap.size()),
344  iMaterialWidth, iMaterialHeight,
345  iSizeSum / 1000000);
346 
347  return true;
348 }
349 
351 {
353  return Rect;
354 }
355 
357 {
358  // clip to landscape size
359  To.Intersect(C4Rect(0,0,iWidth,iHeight));
360  // everything clipped?
361  if (To.Wdt<=0 || To.Hgt<=0) return;
362 
363  // Lock surfaces
364  // We clear the affected region here because ClearBoxDw allocates the
365  // main memory buffer for the box, so that only that box needs to be
366  // sent to the gpu, and not the whole texture, or every pixel
367  // separately. It's an important optimization.
368  for (auto & Surface : Surfaces)
369  {
370  if (!Surface->Lock()) return;
371  Surface->ClearBoxDw(To.x, To.y, To.Wdt, To.Hgt);
372  }
373 
374  // Initialize up & down placement arrays. These arrays are always updated
375  // so that they contain the placement sums of C4LR_BiasDistanceY pixels
376  // above and below the current row.
377  int x, y;
378  int placementSumsWidth = C4LR_BiasDistanceX * 2 + To.Wdt;
379  int *placementSumsUp = new int [placementSumsWidth * 2];
380  int *placementSumsDown = placementSumsUp + placementSumsWidth;
381  for(x = 0; x < placementSumsWidth; x++)
382  {
383  placementSumsUp[x] = 0;
384  placementSumsDown[x] = 0;
385  if (To.x + x - C4LR_BiasDistanceX < 0 || To.x + x - C4LR_BiasDistanceX >= iWidth) continue;
386  for(y = 1; y <= std::min(C4LR_BiasDistanceY, To.y); y++)
387  placementSumsUp[x] += pSource->_GetPlacement(To.x+x-C4LR_BiasDistanceX, To.y-y);
388  for(y = 1; y <= std::min(C4LR_BiasDistanceY, iHeight - 1 - To.y); y++)
389  placementSumsDown[x] += pSource->_GetPlacement(To.x+x-C4LR_BiasDistanceX, To.y+y);
390  }
391 
392  // Get tex refs (shortcut, we will use them quite heavily)
393  C4TexRef *texture[C4LR_SurfaceCount];
394  x = y = 0;
395  for(int i = 0; i < C4LR_SurfaceCount; i++)
396  texture[i] = Surfaces[i]->texture.get();
397 
398  // Go through it from top to bottom
399  for(y = 0; y < To.Hgt; y++)
400  {
401  // Initialize left & right placement sums. These are meant to contain
402  // the placement sum of a (C4LR_BiasDistanceX, 2*C4LR_BiasDistanceY+1)
403  // rectangle left/right of the current pixel. So we initialise it to
404  // be correct at x=0. Note that the placementSum arrays don't contain
405  // information about the current row, therefore we need a special case
406  // for those pixels.
407  int sumLeft = 0, sumRight = 0;
408  for(x = 1; x <= std::min(C4LR_BiasDistanceX, To.x); x++)
409  sumLeft += pSource->_GetPlacement(To.x-x,To.y+y);
410  for(x = 1; x <= std::min(C4LR_BiasDistanceX, iWidth - 1 - To.x ); x++)
411  sumRight += pSource->_GetPlacement(To.x+x,To.y+y);
412  for (int i = 1; i <= C4LR_BiasDistanceX; i++) {
413  sumLeft += placementSumsUp[C4LR_BiasDistanceX - i];
414  sumLeft += placementSumsDown[C4LR_BiasDistanceX - i];
415  sumRight += placementSumsUp[C4LR_BiasDistanceX + i];
416  sumRight += placementSumsDown[C4LR_BiasDistanceX + i];
417  }
418 
419  // Initialise up & down sums. Same principle as above, but slightly
420  // easier as we do not miss pixels if we just use the placement sums.
421  int sumUp = 0, sumDown = 0;
422  for (int i = -C4LR_BiasDistanceX; i <= C4LR_BiasDistanceX; i++) {
423  sumUp += placementSumsUp[C4LR_BiasDistanceX + i];
424  sumDown += placementSumsDown[C4LR_BiasDistanceX + i];
425  }
426 
427  for(x = 0; x < To.Wdt; x++)
428  {
429  int pixel = pSource->_GetPix(To.x+x, To.y+y);
430  int placement = pSource->_GetPlacement(To.x+x, To.y+y);
431 
432  // Calculate bias. The scale here is the size of the rectangle (see above)
433  const int horizontalFactor = C4LR_BiasDistanceX * (2 * C4LR_BiasDistanceY + 1);
434  int horizontalBias = std::max(0, placement * horizontalFactor - sumRight) -
435  std::max(0, placement * horizontalFactor - sumLeft);
436  const int verticalFactor = C4LR_BiasDistanceY * (2 * C4LR_BiasDistanceX + 1);
437  int verticalBias = std::max(0, placement * verticalFactor - sumDown) -
438  std::max(0, placement * verticalFactor - sumUp);
439 
440  // Maximum placement differences that make a difference in the result, after which we are at the limits of
441  // what can be packed into a byte
442  const int maximumPlacementDifference = 40;
443  int horizontalBiasScaled = Clamp(horizontalBias * 127 / maximumPlacementDifference / horizontalFactor + 128, 0, 255);
444  int verticalBiasScaled = Clamp(verticalBias * 127 / maximumPlacementDifference / verticalFactor + 128, 0, 255);
445 
446  // Collect data to save per pixel
447  unsigned char data[C4LR_SurfaceCount * 4];
448  memset(data, 0, sizeof(data));
449 
450  data[C4LR_Material] = pixel;
451  data[C4LR_BiasX] = horizontalBiasScaled;
452  data[C4LR_BiasY] = verticalBiasScaled;
453  data[C4LR_Scaler] = CalculateScalerBitmask(x, y, To, pSource);
454  data[C4LR_Place] = placement;
455 
456  for(int i = 0; i < C4LR_SurfaceCount; i++)
457  texture[i]->SetPix(To.x+x, To.y+y,
458  RGBA(data[i*4+0], data[i*4+1], data[i*4+2], data[i*4+3]));
459 
460  // Update sums (last column would be out-of-bounds, and not
461  // necessary as we will re-initialise it for the next row)
462  if (x < To.Wdt - 1) {
463  sumLeft -= placementSumsUp[x] + placementSumsDown[x];
464  sumLeft += placementSumsUp[x + C4LR_BiasDistanceX] + placementSumsDown[x + C4LR_BiasDistanceX];
465  sumRight -= placementSumsUp[x + C4LR_BiasDistanceX + 1] + placementSumsDown[x + C4LR_BiasDistanceX + 1];
466  sumUp -= placementSumsUp[x];
467  sumDown -= placementSumsDown[x];
468  sumRight += placementSumsUp[x + 2 * C4LR_BiasDistanceX + 1] + placementSumsDown[x + 2 * C4LR_BiasDistanceX + 1];
469  sumUp += placementSumsUp[x + 2 * C4LR_BiasDistanceX + 1];
470  sumDown += placementSumsDown[x + 2 * C4LR_BiasDistanceX + 1];
471  }
472 
473  // Update left & right for next pixel in line
474  if(x + To.x + 1 < iWidth)
475  sumRight -= pSource->_GetPlacement(To.x+x + 1, To.y+y);
476  if(To.x+x + C4LR_BiasDistanceX + 1 < iWidth)
477  sumRight += pSource->_GetPlacement(To.x+x + C4LR_BiasDistanceX + 1, To.y+y);
478  sumLeft += placement;
479  if(To.x+x - C4LR_BiasDistanceX >= 0)
480  sumLeft -= pSource->_GetPlacement(To.x+x - C4LR_BiasDistanceX, To.y+y);
481 
482  // Update up & down arrays (for next line already)
483  if (To.x + x >= C4LR_BiasDistanceX) {
484  if (To.y + y + 1 < iHeight)
485  placementSumsDown[x] -= pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y + 1);
486  if (To.y + y + C4LR_BiasDistanceY + 1 < iHeight)
487  placementSumsDown[x] += pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y + C4LR_BiasDistanceY + 1);
488  if (To.y + y - C4LR_BiasDistanceY >= 0)
489  placementSumsUp[x] -= pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y - C4LR_BiasDistanceY);
490  placementSumsUp[x] += pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y);
491  }
492  }
493 
494  // Finish updating up & down arrays for the next line
495  if (To.x + x >= C4LR_BiasDistanceX)
496  {
497  for (; x < std::min(placementSumsWidth, iWidth - To.x + C4LR_BiasDistanceX); x++) {
498  if (To.y + y + 1 < iHeight)
499  placementSumsDown[x] -= pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y + 1);
500  if (To.y + y + C4LR_BiasDistanceY + 1 < iHeight)
501  placementSumsDown[x] += pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y + C4LR_BiasDistanceY + 1);
502  if (To.y + y - C4LR_BiasDistanceY >= 0)
503  placementSumsUp[x] -= pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y - C4LR_BiasDistanceY);
504  placementSumsUp[x] += pSource->_GetPlacement(To.x + x - C4LR_BiasDistanceX, To.y + y);
505  }
506  }
507  }
508 
509  // done
510  delete[] placementSumsUp;
511  for (auto & Surface : Surfaces)
512  Surface->Unlock();
513 }
514 
522 int C4LandscapeRenderGL::CalculateScalerBitmask(int x, int y, C4Rect To, C4Landscape *pSource)
523 {
524  int pixel = pSource->_GetPix(To.x+x, To.y+y);
525  int placement = pSource->_GetPlacement(To.x+x, To.y+y);
526 
527  int neighbours[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
528  if(To.y+y > 0)
529  {
530  if(To.x+x > 0)
531  neighbours[0] = pSource->_GetPix(To.x+x-1, To.y+y-1);
532  neighbours[1] = pSource->_GetPix(To.x+x, To.y+y-1);
533  if(To.x+x < iWidth-1)
534  neighbours[2] = pSource->_GetPix(To.x+x+1, To.y+y-1);
535  }
536  if(To.x+x > 0)
537  neighbours[3] = pSource->_GetPix(To.x+x-1, To.y+y);
538  if(To.x+x < iWidth-1)
539  neighbours[4] = pSource->_GetPix(To.x+x+1, To.y+y);
540  if(To.y+y < iHeight-1)
541  {
542  if(To.x+x > 0)
543  neighbours[5] = pSource->_GetPix(To.x+x-1, To.y+y+1);
544  neighbours[6] = pSource->_GetPix(To.x+x, To.y+y+1);
545  if(To.x+x < iWidth-1)
546  neighbours[7] = pSource->_GetPix(To.x+x+1, To.y+y+1);
547  }
548 
549  // Look for highest-placement material in our surroundings
550  int maxPixel = pixel, maxPlacement = placement;
551  for(int neighbour : neighbours)
552  {
553  int tempPlacement = MatPlacement(PixCol2Mat(neighbour));
554  if(tempPlacement > maxPlacement || (tempPlacement == maxPlacement && neighbour > maxPixel) )
555  {
556  maxPixel = neighbour;
557  maxPlacement = tempPlacement;
558  }
559  }
560 
561  // Scaler calculation depends on whether this is the highest-placement material around
562  int scaler = 0;
563  if(maxPixel == pixel)
564  {
565  // If yes, we consider all other materials as "other"
566  for(int i = 0; i < 8; i++)
567  if(neighbours[i] == pixel)
568  scaler += (1<<i);
569 
570  } else {
571 
572  // Otherwise, we *only* consider the highest-placement material as "other"
573  for(int i = 0; i < 8; i++)
574  if(neighbours[i] != maxPixel)
575  scaler += (1<<i);
576  }
577  return scaler;
578 }
579 
580 const char *C4LandscapeRenderGL::UniformNames[C4LRU_Count+1];
581 
582 bool C4LandscapeRenderGL::LoadShader(C4GroupSet *pGroups, C4Shader& shader, const char* name, int ssc)
583 {
584  // Setup #defines
585  shader.AddDefine("OPENCLONK");
586  shader.AddDefine("OC_LANDSCAPE");
587  if(ssc & C4SSC_LIGHT) shader.AddDefine("OC_DYNAMIC_LIGHT"); // sample light from light texture
588 
589  // Create vertex shader
590  shader.LoadVertexSlices(pGroups, "LandscapeVertexShader.glsl");
591 
592  // Then load slices for fragment shader
593  shader.LoadFragmentSlices(pGroups, "CommonShader.glsl");
594  shader.LoadFragmentSlices(pGroups, "LandscapeShader.glsl");
595 
596  // Categories for script shaders.
597  shader.SetScriptCategories({"Common", "Landscape"});
598 
599  // Make attribute name map
600  const char* AttributeNames[C4LRA_Count + 1];
601  AttributeNames[C4LRA_Position] = "oc_Position";
602  AttributeNames[C4LRA_LandscapeTexCoord] = "oc_LandscapeTexCoord";
603  AttributeNames[C4LRA_LightTexCoord] = "oc_LightTexCoord"; // unused if no dynamic light
604  AttributeNames[C4LRA_Count] = nullptr;
605 
606  // Initialise!
607  if (!shader.Init(name, UniformNames, AttributeNames)) {
608  shader.ClearSlices();
609  return false;
610  }
611 
612  return true;
613 }
614 
615 bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups)
616 {
617  // First, clear out all existing shaders
618  ClearShaders();
619 
620  // Make uniform name map
621  ZeroMem(UniformNames, sizeof(UniformNames));
622  UniformNames[C4LRU_ProjectionMatrix] = "projectionMatrix";
623  UniformNames[C4LRU_LandscapeTex] = "landscapeTex";
624  UniformNames[C4LRU_ScalerTex] = "scalerTex";
625  UniformNames[C4LRU_MaterialTex] = "materialTex";
626  UniformNames[C4LRU_LightTex] = "lightTex";
627  UniformNames[C4LRU_AmbientTex] = "ambientTex";
628  UniformNames[C4LRU_Gamma] = "gamma";
629  UniformNames[C4LRU_Resolution] = "resolution";
630  UniformNames[C4LRU_Center] = "center";
631  UniformNames[C4LRU_MatMapTex] = "matMapTex";
632  UniformNames[C4LRU_MaterialDepth] = "materialDepth";
633  UniformNames[C4LRU_MaterialSize] = "materialSize";
634  UniformNames[C4LRU_AmbientBrightness] = "ambientBrightness";
635  UniformNames[C4LRU_AmbientTransform] = "ambientTransform";
636  UniformNames[C4LRU_Modulation] = "clrMod";
637  UniformNames[C4LRU_FrameCounter] = "frameCounter";
638  UniformNames[C4LRU_Time] = "time";
639 
640  if(!LoadShader(pGroups, Shader, "landscape", 0))
641  return false;
642  if(!LoadShader(pGroups, ShaderLight, "landscapeLight", C4SSC_LIGHT))
643  return false;
644 
645  return true;
646 }
647 
648 bool C4LandscapeRenderGL::InitVBO()
649 {
650  // Our VBO needs to hold 4 vertices with 6 floats each.
651  assert(hVBO == 0);
652  glGenBuffers(1, &hVBO);
653  glBindBuffer(GL_ARRAY_BUFFER, hVBO);
654  glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(float), nullptr, GL_STREAM_DRAW);
655  glBindBuffer(GL_ARRAY_BUFFER, 0);
656  // Also allocate the VAO IDs
657  assert(hVAOIDLight == 0);
658  assert(hVAOIDNoLight == 0);
659  hVAOIDLight = pGL->GenVAOID();
660  hVAOIDNoLight = pGL->GenVAOID();
661  return true;
662 }
663 
664 void C4LandscapeRenderGL::ClearShaders()
665 {
666  if (Shader.Initialised())
667  {
668  Shader.Clear();
669  Shader.ClearSlices();
670  }
671 
672  if (ShaderLight.Initialised())
673  {
674  ShaderLight.Clear();
675  ShaderLight.ClearSlices();
676  }
677 }
678 
679 bool C4LandscapeRenderGL::LoadScaler(C4GroupSet *pGroups)
680 {
681  // Search for scaler
682  C4Group *pGroup = pGroups->FindEntry(C4CFN_LandscapeScaler);
683  if(!pGroup) return false;
684  // Load scaler from group
685  if(!fctScaler.Load(*pGroup, C4CFN_LandscapeScaler, C4FCT_Full, C4FCT_Full, false, 0))
686  return false;
687  // Check size
688  const int iOrigWdt = 8 * 3, iOrigHgt = 4 * 8 * 3;
689  const int iFactor = fctScaler.Wdt / iOrigWdt;
690  if(fctScaler.Wdt != iFactor * iOrigWdt || fctScaler.Hgt != iFactor * iOrigHgt)
691  {
692  LogF(" gl: Unexpected scaler size - should be multiple of %dx%d!", iOrigWdt, iOrigHgt);
693  return false;
694  }
695  // Walk through all lookups we have in the texture and decide where
696  // to look for the "other" pixel. This might not be unique later on,
697  // so it is a good idea to have a proper priority order here.
698  fctScaler.Surface->Lock();
699  int i;
700  for (i = 0; i < 8 * 4 * 8; i++) {
701  // Decode from ID what pixels are expected to be set in this case
702  enum Px { NW, N, NE, W, E, SW, S, SE, X };
703  int p_x[9] = { -1, 0, 1, -1, 1, -1, 0, 1, 0 };
704  int p_y[9] = { -1, -1, -1, 0, 0, 1, 1, 1, 0 };
705  bool pxAt[X];
706  for(int j = 0; j < X; j++)
707  pxAt[j] = !!(i & (1 << j));
708  // Oc = octant borders. Set up arrays to get righthand border
709  // of an octant, in a way that we can easily rotate further.
710  enum Oc { NWW, NEE, SWW, SEE, NNW, NNE, SSW, SSE };
711  int p2a[8] = { 5, 6, 7, 4, 0, 3, 2, 1 };
712  Oc a2o[8] = { SEE, SSE, SSW, SWW, NWW, NNW, NNE, NEE };
713  // Decide in which octant we want to interpolate towards
714  // which pixel. Pick the nearest unset pixel using a special
715  // priority order.
716  Px opx[8] = { X,X,X,X,X,X,X,X };
717  #define INTERPOLATE(x,da) do { \
718  int y = a2o[(8+p2a[x]+(da)) % 8];\
719  if (!pxAt[x] && opx[y] == X) opx[y] = x; \
720  } while(false)
721  for(int j = 0; j < 4; j++) {
722  // vertical
723  INTERPOLATE(N, j); INTERPOLATE(N, -j-1);
724  INTERPOLATE(S, j); INTERPOLATE(S, -j-1);
725  // horizontal
726  INTERPOLATE(W, j); INTERPOLATE(W, -j-1);
727  INTERPOLATE(E, j); INTERPOLATE(E, -j-1);
728  // diagonals
729  INTERPOLATE(NW, j); INTERPOLATE(NW, -j-1);
730  INTERPOLATE(SW, j); INTERPOLATE(SW, -j-1);
731  INTERPOLATE(NE, j); INTERPOLATE(NE, -j-1);
732  INTERPOLATE(SE, j); INTERPOLATE(SE, -j-1);
733  }
734  // Decide in which octants we will not interpolate normals.
735  // It doesn't make sense when there's another material in that
736  // general direction, as then the bias of that will factor into
737  // the interpolation, giving bright borders on dark shading,
738  // and vice-versa.
739  bool noNormals[8];
740  noNormals[NNW] = noNormals[NWW] = !pxAt[W] || !pxAt[NW] || !pxAt[N];
741  noNormals[NNE] = noNormals[NEE] = !pxAt[E] || !pxAt[NE] || !pxAt[N];
742  noNormals[SSW] = noNormals[SWW] = !pxAt[W] || !pxAt[SW] || !pxAt[S];
743  noNormals[SSE] = noNormals[SEE] = !pxAt[E] || !pxAt[SE] || !pxAt[S];
744  // Set blue and green components to relative coordinates of
745  // "other" pixel, and alpha to mix param for normals
746  const int x0 = (i % 8) * 3 * iFactor;
747  const int y0 = (i / 8) * 3 * iFactor;
748  const int iPxs = 3 * iFactor;
749  int y, x;
750 
751  for(y = 0; y < iPxs; y++)
752  {
753  for(x = 0; x < iPxs; x++)
754  {
755  // Find out in which octagon we are
756  int oct = 0;
757  if(2 * x >= iPxs) oct+=1;
758  if(2 * y >= iPxs) oct+=2;
759  if((x >= y) != (x >= iPxs - y)) oct+=4;
760  // Get pixel, do processing
761  DWORD pix = fctScaler.Surface->GetPixDw(x0+x, y0+y, false);
762  BYTE val = GetGreenValue(pix);
763  if(val >= 250) val = 255;
764  BYTE bx = 64 * (p_x[opx[oct]] + 1);
765  BYTE by = 64 * (p_y[opx[oct]] + 1);
766  BYTE bn = (noNormals[oct] ? 255 : 1);
767  fctScaler.Surface->SetPixDw(x0+x, y0+y, RGBA(val, bx, by, bn));
768  }
769  }
770  }
771  return fctScaler.Surface->Unlock();
772 }
773 
774 int32_t C4LandscapeRenderGL::LookupTextureTransition(const char *szFrom, const char *szTo)
775 {
776  // Is this actually a transition? Otherwise we're looking for a single texture
777  bool fTransit = !SEqual(szFrom, szTo);
778  // Look for a position in the map where the textures appear in sequence
779  uint32_t i;
780  for(i = 1; i < MaterialTextureMap.size(); i++)
781  {
782  if(SEqual(szFrom, MaterialTextureMap[i].getData()))
783  {
784  // Single texture: We're done
785  if(!fTransit) return i;
786  // Check next texture as well
787  if(i + 1 >= MaterialTextureMap.size())
788  return -1;
789  if(SEqual(szTo, MaterialTextureMap[i+1].getData()))
790  return i;
791  }
792  }
793  return -1;
794 }
795 
796 void C4LandscapeRenderGL::AddTextureTransition(const char *szFrom, const char *szTo)
797 {
798  // Empty?
799  if (!szFrom || !szTo) return;
800  // First try the lookup (both directions)
801  if (LookupTextureTransition(szFrom, szTo) >= 0) return;
802  if (LookupTextureTransition(szTo, szFrom) >= 0) return;
803  // Single texture? Add it as single
804  if (SEqual(szTo, szFrom))
805  MaterialTextureMap.emplace_back(szFrom);
806  // Have one of the textures at the end of the list?
807  else if(SEqual(MaterialTextureMap.back().getData(), szFrom))
808  MaterialTextureMap.emplace_back(szTo);
809  else if(SEqual(MaterialTextureMap.back().getData(), szTo))
810  MaterialTextureMap.emplace_back(szFrom);
811  else
812  {
813  // Otherwise add both
814  MaterialTextureMap.emplace_back(szFrom);
815  MaterialTextureMap.emplace_back(szTo);
816  }
817 }
818 
819 void C4LandscapeRenderGL::AddTextureAnim(const char *szTextureAnim)
820 {
821  if(!szTextureAnim) return;
822 #ifdef DEBUG_SEPERATOR_TEXTURES
823  // Save back count of textures at start
824  uint32_t iStartTexCount = MaterialTextureMap.size();
825 #endif
826  // Add all individual transitions
827  const char *pFrom = szTextureAnim;
828  for(;;)
829  {
830  // Get next phase
831  const char *pTo = strchr(pFrom, '-');
832  if(!pTo) pTo = szTextureAnim; else pTo++;
833  // Add transition
834  StdStrBuf From, To;
835  From.CopyUntil(pFrom, '-');
836  To.CopyUntil(pTo, '-');
837  AddTextureTransition(From.getData(), To.getData());
838  // Advance
839  if(pTo == szTextureAnim) break;
840  pFrom = pTo;
841  }
842 #ifdef DEBUG_SEPERATOR_TEXTURES
843  // Add a seperator texture, if we added any new ones
844  if(MaterialTextureMap.size() > iStartTexCount)
845  MaterialTextureMap.push_back(StdCopyStrBuf(SEPERATOR_TEXTURE));
846 #endif
847 }
848 
849 void C4LandscapeRenderGL::AddTexturesFromMap(C4TextureMap *pMap)
850 {
851  // Go through used texture (animations) and add all phases to our map
852 
853  // Note: We can be smarter here, for example add longer animations
854  // first in order to make better reuse of 3D texture slots.
855  // We could even make a full-blown optimization problem out of it.
856  // Future work...
857 
858  const C4TexMapEntry *pEntry;
859  for(int32_t i = 0; (pEntry = pMap->GetEntry(i)); i++)
860  // ToDo: Properly handle jumping back
861  AddTextureAnim(pEntry->GetTextureName());
862 
863 }
864 
865 void C4LandscapeRenderGL::BuildMatMap(uint32_t *pTex)
866 {
867  // TODO: Still merely an inefficient placeholder for things to come...
868 
869  // Build material-texture map (depth parameter where to find appropriate texture)
870  for(int pix = 0; pix < 256; pix++)
871  {
872  // Look up indexed entry
873  const C4TexMapEntry *pEntry = pTexs->GetEntry(PixCol2Tex(BYTE(pix)));
874  if(!pEntry->GetTextureName())
875  {
876  // Undefined textures transparent
877  pTex[2*pix] = 0;
878  pTex[2*pix+1] = RGBA(0,0,0,255);
879  continue;
880  }
881 
882  // Got animation?
883  int iPhases = 1; const char *p = pEntry->GetTextureName();
884  while((p = strchr(p, '-'))) { p++; iPhases++; }
885  // Hard-coded hack. Fix me!
886  C4Material *pMaterial = pEntry->GetMaterial();
887  const int iPhaseLength = pMaterial->AnimationSpeed;
888  float phase = 0;
889  if (iPhases > 1) {
890  phase = C4TimeMilliseconds::Now().AsInt() % (iPhases * iPhaseLength);
891  phase /= iPhaseLength;
892  }
893 
894  // Find our transition
895  const char *pFrom = pEntry->GetTextureName();
896  float gTexCoo = 0;
897  for(int iP = 0;; iP++)
898  {
899  // Get next phase
900  const char *pTo = strchr(pFrom, '-');
901  if(!pTo) pTo = pEntry->GetTextureName(); else pTo++;
902  // Add transition
903  if(iP == int(phase))
904  {
905  StdStrBuf From, To;
906  From.CopyUntil(pFrom, '-');
907  To.CopyUntil(pTo, '-');
908  // Find transition
909  int iTrans;
910  if ((iTrans = LookupTextureTransition(From.getData(), To.getData())) >= 0)
911  gTexCoo = float(iTrans) + fmod(phase, 1.0f);
912  else if ((iTrans = LookupTextureTransition(To.getData(), From.getData())) >= 0)
913  gTexCoo = float(iTrans) + 1.0 - fmod(phase, 1.0f);
914  break;
915  }
916  // Advance
917  pFrom = pTo;
918  }
919 
920  // Assign texture
921  int iTexCoo = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5);
922  pTex[2*pix] = RGBA(
923  Clamp(pMaterial->LightEmit[0], 0, 255),
924  Clamp(pMaterial->LightEmit[1], 0, 255),
925  Clamp(pMaterial->LightEmit[2], 0, 255),
926  iTexCoo);
927  pTex[2*pix+1] = RGBA(
928  Clamp(pMaterial->LightSpot[0], 0, 255),
929  Clamp(pMaterial->LightSpot[1], 0, 255),
930  Clamp(pMaterial->LightSpot[2], 0, 255),
931  Clamp(pMaterial->LightAngle, 0, 255));
932  }
933 }
934 
935 void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Light, uint32_t clrMod)
936 {
937  // Must have GL and be initialized
938  if(!pGL && !Shader.Initialised() && !ShaderLight.Initialised()) return;
939 
940  // prepare rendering to surface
941  C4Surface *sfcTarget = cgo.Surface;
942  if (!pGL->PrepareRendering(sfcTarget)) return;
943 
944  // Choose the right shader depending on whether we have dynamic lighting or not
945  const C4Shader* shader = &Shader;
946  if (Light) shader = &ShaderLight;
947  if (!shader->Initialised()) return;
948 
949  // Activate shader
950  C4ShaderCall ShaderCall(shader);
951  ShaderCall.Start();
952 
953  // Bind data
955  ShaderCall.SetUniform3fv(C4LRU_Gamma, 1, pGL->gammaOut);
956  ShaderCall.SetUniform2f(C4LRU_Resolution, Surfaces[0]->Wdt, Surfaces[0]->Hgt);
957  float centerX = float(cgo.TargetX)+float(cgo.Wdt)/2,
958  centerY = float(cgo.TargetY)+float(cgo.Hgt)/2;
959  ShaderCall.SetUniform2f(C4LRU_Center,
960  centerX / float(Surfaces[0]->Wdt),
961  centerY / float(Surfaces[0]->Hgt));
962  ShaderCall.SetUniform1f(C4LRU_MaterialDepth, float(iMaterialTextureDepth));
963  ShaderCall.SetUniform2f(C4LRU_MaterialSize,
964  float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom,
965  float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom);
966  const float fMod[4] = {
967  ((clrMod >> 16) & 0xff) / 255.0f,
968  ((clrMod >> 8) & 0xff) / 255.0f,
969  ((clrMod ) & 0xff) / 255.0f,
970  ((clrMod >> 24) & 0xff) / 255.0f
971  };
972  ShaderCall.SetUniform4fv(C4LRU_Modulation, 1, fMod);
973 
974  if (Light)
975  {
976  const FLOAT_RECT ViewportRect = Light->getViewportRegion();
977  const C4Rect ClipRect = pDraw->GetClipRect();
978  const C4Rect OutRect = pDraw->GetOutRect();
979  float ambientTransform[6];
980  Light->getFoW()->Ambient.GetFragTransform(ViewportRect, ClipRect, OutRect, ambientTransform);
981  ShaderCall.SetUniformMatrix2x3fv(C4LRU_AmbientTransform, 1, ambientTransform);
983  }
984 
985  // time information
987  const auto now = std::chrono::steady_clock::now();
988  const int epoch = std::chrono::duration_cast<std::chrono::milliseconds>(now - TimerStart).count();
989  ShaderCall.SetUniform1i(C4LRU_Time, epoch);
990 
991  pDraw->scriptUniform.Apply(ShaderCall);
992 
993  // Start binding textures
994  if(shader->HaveUniform(C4LRU_LandscapeTex))
995  {
996  GLint iLandscapeUnits[C4LR_SurfaceCount];
997  for(int i = 0; i < C4LR_SurfaceCount; i++)
998  {
999  iLandscapeUnits[i] = ShaderCall.AllocTexUnit(-1) - GL_TEXTURE0;
1000  glBindTexture(GL_TEXTURE_2D, Surfaces[i]->texture->texName);
1001  if (pGL->Zoom != 1.0)
1002  {
1003  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1004  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1005  }
1006  else
1007  {
1008  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1009  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1010  }
1011  }
1012  ShaderCall.SetUniform1iv(C4LRU_LandscapeTex, C4LR_SurfaceCount, iLandscapeUnits);
1013  }
1014  if(Light && ShaderCall.AllocTexUnit(C4LRU_LightTex))
1015  {
1016  glBindTexture(GL_TEXTURE_2D, Light->getSurfaceName());
1017  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1018  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1019  }
1020  if(Light && ShaderCall.AllocTexUnit(C4LRU_AmbientTex))
1021  {
1022  glBindTexture(GL_TEXTURE_2D, Light->getFoW()->Ambient.Tex);
1023  }
1024  if(ShaderCall.AllocTexUnit(C4LRU_ScalerTex))
1025  {
1026  glBindTexture(GL_TEXTURE_2D, fctScaler.Surface->texture->texName);
1027  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1028  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1029  }
1030  if(ShaderCall.AllocTexUnit(C4LRU_MaterialTex))
1031  {
1032  glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
1033  }
1034  if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex))
1035  {
1036  uint32_t MatMap[2*256];
1037  BuildMatMap(MatMap);
1038  glBindTexture(GL_TEXTURE_1D, matMapTexture);
1039  glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, 2*256, 0, GL_RGBA, GL_UNSIGNED_BYTE, MatMap);
1040  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1041  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1042  }
1043 
1044  // Calculate coordinates into landscape texture
1045  FLOAT_RECT fTexBlt;
1046  float fx = float(cgo.TargetX), fy = float(cgo.TargetY);
1047  fTexBlt.left = fx / Surfaces[0]->Wdt;
1048  fTexBlt.top = fy / Surfaces[0]->Hgt;
1049  fTexBlt.right = (fx + float(cgo.Wdt)) / Surfaces[0]->Wdt;
1050  fTexBlt.bottom= (fy + float(cgo.Hgt)) / Surfaces[0]->Hgt;
1051 
1052  // Calculate coordinates on screen (zoomed!)
1053  FLOAT_RECT tTexBlt;
1054  float tx = float(cgo.X), ty = float(cgo.Y);
1055  pGL->ApplyZoom(tx, ty);
1056  tTexBlt.left = tx;
1057  tTexBlt.top = ty;
1058  tTexBlt.right = tx + float(cgo.Wdt) * pGL->Zoom;
1059  tTexBlt.bottom= ty + float(cgo.Hgt) * pGL->Zoom;
1060 
1061  // Blend it
1062  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1063 
1064  // Prepare vertex data
1065  float vtxData[24];
1066  float* pos = &vtxData[0];
1067  float* tex = &vtxData[8];
1068  float* lightTex = &vtxData[16];
1069 
1070  pos[0] = tTexBlt.left;
1071  pos[1] = tTexBlt.top;
1072  pos[2] = tTexBlt.right;
1073  pos[3] = tTexBlt.top;
1074  pos[4] = tTexBlt.left;
1075  pos[5] = tTexBlt.bottom;
1076  pos[6] = tTexBlt.right;
1077  pos[7] = tTexBlt.bottom;
1078 
1079  tex[0] = fTexBlt.left;
1080  tex[1] = fTexBlt.top;
1081  tex[2] = fTexBlt.right;
1082  tex[3] = fTexBlt.top;
1083  tex[4] = fTexBlt.left;
1084  tex[5] = fTexBlt.bottom;
1085  tex[6] = fTexBlt.right;
1086  tex[7] = fTexBlt.bottom;
1087 
1088  unsigned int nFloats = 16;
1089  if (Light)
1090  {
1091  FLOAT_RECT lTexBlt;
1092  const C4Rect LightRect = Light->getRegion();
1093  int32_t iLightWdt = Light->getSurfaceWidth(),
1094  iLightHgt = Light->getSurfaceHeight();
1095  lTexBlt.left = (fx - LightRect.x) / iLightWdt;
1096  lTexBlt.top = 1.0 - (fy - LightRect.y) / iLightHgt;
1097  lTexBlt.right = (fx + cgo.Wdt - LightRect.x) / iLightWdt;
1098  lTexBlt.bottom = 1.0 - (fy + cgo.Hgt - LightRect.y) / iLightHgt;
1099 
1100  lightTex[0] = lTexBlt.left;
1101  lightTex[1] = lTexBlt.top;
1102  lightTex[2] = lTexBlt.right;
1103  lightTex[3] = lTexBlt.top;
1104  lightTex[4] = lTexBlt.left;
1105  lightTex[5] = lTexBlt.bottom;
1106  lightTex[6] = lTexBlt.right;
1107  lightTex[7] = lTexBlt.bottom;
1108  nFloats = 24;
1109  }
1110 
1111  // Upload vertex data
1112  glBindBuffer(GL_ARRAY_BUFFER, hVBO);
1113  glBufferSubData(GL_ARRAY_BUFFER, 0, nFloats * sizeof(float), vtxData);
1114 
1115  // Bind VAO
1116  unsigned int vaoid = Light ? hVAOIDLight : hVAOIDNoLight;
1117  GLuint vao;
1118  const bool has_vao = pGL->GetVAO(vaoid, vao);
1119  glBindVertexArray(vao);
1120  if (!has_vao)
1121  {
1122  // Setup state
1123  glEnableVertexAttribArray(shader->GetAttribute(C4LRA_Position));
1124  glEnableVertexAttribArray(shader->GetAttribute(C4LRA_LandscapeTexCoord));
1125  if (Light)
1126  glEnableVertexAttribArray(shader->GetAttribute(C4LRA_LightTexCoord));
1127 
1128  glVertexAttribPointer(shader->GetAttribute(C4LRA_Position), 2, GL_FLOAT, GL_FALSE, 0, nullptr);
1129  glVertexAttribPointer(shader->GetAttribute(C4LRA_LandscapeTexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(8 * sizeof(float)));
1130  if (Light)
1131  glVertexAttribPointer(shader->GetAttribute(C4LRA_LightTexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(16 * sizeof(float)));
1132  }
1133 
1134  // Do the blit
1135  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1136 
1137  // Reset state
1138  glBindVertexArray(0);
1139  glBindBuffer(GL_ARRAY_BUFFER, 0);
1140 
1141  ShaderCall.Finish();
1142 }
1143 
1144 #endif // #ifndef USE_CONSOLE
#define X(sdl, oc)
#define C4CFN_LandscapeScaler
Definition: C4Components.h:132
C4Draw * pDraw
Definition: C4Draw.cpp:42
CStdGL * pGL
Definition: C4DrawGL.cpp:907
const int C4FCT_Full
Definition: C4FacetEx.h:26
C4Game Game
Definition: C4Globals.cpp:52
int32_t PixCol2Mat(BYTE pixc)
int32_t PixCol2Tex(BYTE pixc)
Definition: C4Landscape.h:211
const int C4LR_BiasDistanceY
const int C4LR_BiasDistanceX
const char *const SEPERATOR_TEXTURE
#define INTERPOLATE(x, da)
const int C4LR_SurfaceCount
@ C4LR_BiasX
@ C4LR_Scaler
@ C4LR_Place
@ C4LR_Material
@ C4LR_BiasY
@ C4LRU_ScalerTex
@ C4LRU_AmbientTransform
@ C4LRU_Modulation
@ C4LRU_AmbientBrightness
@ C4LRU_LightTex
@ C4LRU_Gamma
@ C4LRU_AmbientTex
@ C4LRU_ProjectionMatrix
@ C4LRU_MaterialDepth
@ C4LRU_FrameCounter
@ C4LRU_LandscapeTex
@ C4LRU_Time
@ C4LRU_Resolution
@ C4LRU_MaterialSize
@ C4LRU_Count
@ C4LRU_MaterialTex
@ C4LRU_Center
@ C4LRU_MatMapTex
@ C4LRA_Position
@ C4LRA_LandscapeTexCoord
@ C4LRA_Count
@ C4LRA_LightTexCoord
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:239
int32_t MatPlacement(int32_t mat)
Definition: C4Material.h:246
float bottom
Definition: C4Rect.h:25
float top
Definition: C4Rect.h:25
float right
Definition: C4Rect.h:25
float left
Definition: C4Rect.h:25
uint8_t BYTE
uint32_t DWORD
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:60
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
uint32_t RGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
Definition: StdColors.h:22
#define GetGreenValue(rgb)
Definition: StdColors.h:28
int iSize
Definition: TstC4NetIO.cpp:32
static constexpr int COLOR_DEPTH_BYTES
Definition: C4Draw.h:90
float gammaOut[3]
Definition: C4Draw.h:98
C4Rect GetClipRect() const
Definition: C4Draw.cpp:724
C4Rect GetOutRect() const
Definition: C4Draw.cpp:733
C4ScriptUniform scriptUniform
Definition: C4Draw.h:100
void ApplyZoom(float &X, float &Y)
Definition: C4Draw.cpp:778
float Zoom
Definition: C4Draw.h:116
C4Surface * Surface
Definition: C4Facet.h:117
float Hgt
Definition: C4Facet.h:118
float Wdt
Definition: C4Facet.h:118
float Y
Definition: C4Facet.h:118
float X
Definition: C4Facet.h:118
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:84
void GetFragTransform(const struct FLOAT_RECT &vpRect, const C4Rect &clipRect, const C4Rect &outRect, float ambientTransform[6]) const
double GetBrightness() const
Definition: C4FoWAmbient.h:56
C4FoWAmbient Ambient
Definition: C4FoW.h:115
GLuint getSurfaceName() const
int32_t getSurfaceHeight() const
const C4Rect & getRegion() const
Definition: C4FoWRegion.h:53
const C4FoW * getFoW() const
Definition: C4FoWRegion.h:52
const FLOAT_RECT & getViewportRegion() const
Definition: C4FoWRegion.h:54
int32_t getSurfaceWidth() const
C4Scenario C4S
Definition: C4Game.h:74
int32_t FrameCounter
Definition: C4Game.h:129
C4Group * FindEntry(const char *szWildcard, int32_t *pPriority=nullptr, int32_t *pID=nullptr)
Definition: C4GroupSet.cpp:175
BYTE _GetPix(int32_t x, int32_t y) const
int32_t _GetPlacement(int32_t x, int32_t y) const
C4Rect GetAffectedRect(C4Rect Rect) override
bool ReInit(int32_t iWidth, int32_t iHeight) override
void Draw(const C4TargetFacet &cgo, const C4FoWRegion *Light, uint32_t clrMod) override
void Update(C4Rect Rect, C4Landscape *pSource) override
bool Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pMap, C4GroupSet *pGraphics) override
C4TextureMap * pTexs
int32_t LightSpot[3]
Definition: C4Material.h:136
int32_t LightEmit[3]
Definition: C4Material.h:135
int32_t LightAngle
Definition: C4Material.h:134
int32_t AnimationSpeed
Definition: C4Material.h:133
Definition: C4Rect.h:28
int32_t y
Definition: C4Rect.h:30
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
void Enlarge(int32_t iByX, int32_t iByY)
Definition: C4Rect.h:51
int32_t x
Definition: C4Rect.h:30
void Intersect(const C4Rect &r2)
Definition: C4Rect.cpp:100
int32_t MaterialZoom
Definition: C4Scenario.h:187
C4SLandscape Landscape
Definition: C4Scenario.h:236
void Apply(C4ShaderCall &call)
Definition: C4Shader.cpp:828
void SetUniform1f(int iUniform, float gX) const
Definition: C4Shader.h:241
void Start()
Definition: C4Shader.cpp:691
void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:351
GLint AllocTexUnit(int iUniform)
Definition: C4Shader.cpp:668
void SetUniform2f(int iUniform, float gX, float gY) const
Definition: C4Shader.h:245
void Finish()
Definition: C4Shader.cpp:705
void SetUniform4fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:301
void SetUniform1iv(int iUniform, int iLength, const int *pVals) const
Definition: C4Shader.h:257
void SetUniform3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:297
void SetUniformMatrix2x3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:307
void SetUniform1i(int iUniform, int iX) const
Definition: C4Shader.h:209
bool LoadVertexSlices(C4GroupSet *pGroupSet, const char *szFile)
Definition: C4Shader.cpp:97
bool LoadFragmentSlices(C4GroupSet *pGroupSet, const char *szFile)
Definition: C4Shader.cpp:92
bool Initialised() const
Definition: C4Shader.h:106
void AddDefine(const char *name)
Definition: C4Shader.cpp:65
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:127
void Clear()
Definition: C4Shader.cpp:335
bool HaveUniform(int iUniform) const
Definition: C4Shader.h:122
void SetScriptCategories(const std::vector< std::string > &categories)
Definition: C4Shader.cpp:102
void ClearSlices()
Definition: C4Shader.cpp:324
bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes)
Definition: C4Shader.cpp:348
bool SetPixDw(int iX, int iY, DWORD dwCol)
Definition: C4Surface.cpp:576
int Wdt
Definition: C4Surface.h:65
DWORD GetPixDw(int iX, int iY, bool fApplyModulation)
Definition: C4Surface.cpp:491
std::unique_ptr< C4TexRef > texture
Definition: C4Surface.h:78
bool Unlock()
Definition: C4Surface.cpp:464
bool Lock()
Definition: C4Surface.cpp:453
int Hgt
Definition: C4Surface.h:65
float TargetY
Definition: C4Facet.h:165
float TargetX
Definition: C4Facet.h:165
Definition: C4Texture.h:49
const char * GetTextureName() const
Definition: C4Texture.h:61
C4Material * GetMaterial() const
Definition: C4Texture.h:63
C4Surface * Surface32
Definition: C4Texture.h:35
const char * GetTexture(int32_t iIndex)
Definition: C4Texture.cpp:494
const C4TexMapEntry * GetEntry(int32_t iIndex) const
Definition: C4Texture.h:85
uint32_t AsInt() const
static C4TimeMilliseconds Now()
const StdProjectionMatrix & GetProjectionMatrix() const
Definition: C4DrawGL.h:254
bool PrepareRendering(C4Surface *sfcToSurface) override
Definition: C4DrawGL.cpp:161
unsigned int GenVAOID()
Definition: C4DrawGL.cpp:927
void FreeVAOID(unsigned int vaoid)
Definition: C4DrawGL.cpp:969
bool GetVAO(unsigned int vaoid, GLuint &vao)
Definition: C4DrawGL.cpp:1015
void CopyUntil(const char *szString, char cUntil)
Definition: StdBuf.h:613
void Ref(const char *pnData)
Definition: StdBuf.h:455
const char * getData() const
Definition: StdBuf.h:442
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174