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