OpenClonk
C4FoWRegion.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2014-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 #include "graphics/C4DrawGL.h"
20 
22  : pFoW(pFoW)
23 #ifndef USE_CONSOLE
24  , pPlayer(pPlayer)
25  , hFrameBufDraw(0), hFrameBufRead(0), hVBO(0), vaoid(0)
26 #endif
27  , Region(0,0,0,0), OldRegion(0,0,0,0)
28  , pSurface(new C4Surface), pBackSurface(new C4Surface)
29 {
30  ViewportRegion.left = ViewportRegion.right = ViewportRegion.top = ViewportRegion.bottom = 0.0f;
31 }
32 
34 {
35 #ifndef USE_CONSOLE
36  if (hFrameBufDraw) {
37  glDeleteFramebuffers(1, &hFrameBufDraw);
38  glDeleteFramebuffers(1, &hFrameBufRead);
39  }
40 
41  if (hVBO) {
42  glDeleteBuffers(1, &hVBO);
43  }
44 
45  if (vaoid) {
46  pGL->FreeVAOID(vaoid);
47  }
48 #endif
49 }
50 
51 #ifndef USE_CONSOLE
52 bool C4FoWRegion::BindFramebuf(GLuint prev_fb)
53 {
54  // Flip texture
55  pSurface.swap(pBackSurface);
56 
57  // Can simply reuse old texture?
58  if (pSurface->Wdt < Region.Wdt || (pSurface->Hgt / 2) < Region.Hgt)
59  {
60  // Determine texture size. Round up to next power of two in order to
61  // prevent rounding errors, as well as preventing lots of
62  // re-allocations when region size changes quickly (think zoom).
63  int iWdt = 1, iHgt = 1;
64  while (iWdt < Region.Wdt) iWdt *= 2;
65  while (iHgt < Region.Hgt) iHgt *= 2;
66 
67  // Double the texture size. The second half of the texture
68  // will contain the light color information, while the
69  // first half contains the brightness and direction information
70  iHgt *= 2;
71 
72  // Create the new surfaces
73  std::unique_ptr<C4Surface> pNewSurface(new C4Surface);
74  std::unique_ptr<C4Surface> pNewBackSurface(new C4Surface);
75  if (!pNewSurface->Create(iWdt, iHgt))
76  return false;
77  if (!pNewBackSurface->Create(iWdt, iHgt))
78  return false;
79 
80  // Copy over old content. This avoids flicker in already
81  // explored regions that might get temporarily dark and
82  // re-faded-in with the surface swap. New area in the surface
83  // is initialized with darkness (black).
84  pSurface->Lock();
85  pBackSurface->Lock();
86  pNewSurface->Lock();
87  pNewBackSurface->Lock();
88 
89  // Take into account that the texture
90  // is split for normals/intensity and colors, and also that
91  // OpenGL textures are upside down.
92  for (int y = 0; y < iHgt / 2; ++y)
93  {
94  for (int x = 0; x < iWdt; ++x)
95  {
96  if (y < pSurface->Hgt / 2 && x < pSurface->Wdt)
97  {
98  // Normals and intensity
99  pNewSurface->SetPixDw(x, pNewSurface->Hgt/2 - y - 1, pSurface->GetPixDw(x, pSurface->Hgt/2 - y - 1, false));
100  pNewBackSurface->SetPixDw(x, pNewBackSurface->Hgt/2 - y - 1, pBackSurface->GetPixDw(x, pBackSurface->Hgt/2 - y - 1, false));
101 
102  // Color
103  pNewSurface->SetPixDw(x, pNewSurface->Hgt/2 - y + iHgt / 2 - 1, pSurface->GetPixDw(x, pSurface->Hgt/2 - y + pSurface->Hgt / 2 - 1, false));
104  pNewBackSurface->SetPixDw(x, pNewBackSurface->Hgt/2 - y + iHgt / 2 - 1, pBackSurface->GetPixDw(x, pBackSurface->Hgt/2 - y + pBackSurface->Hgt / 2 - 1, false));
105  }
106  else
107  {
108  // Normals and intensity
109  pNewSurface->SetPixDw(x, pNewSurface->Hgt/2 - y - 1, 0x000000ff);
110  pNewBackSurface->SetPixDw(x, pNewBackSurface->Hgt/2 - y - 1, 0x000000ff);
111 
112  // Color
113  pNewSurface->SetPixDw(x, pNewSurface->Hgt/2 - y + iHgt / 2 - 1, 0x000000ff);
114  pNewBackSurface->SetPixDw(x, pNewBackSurface->Hgt/2 - y + iHgt / 2 - 1, 0x000000ff);
115  }
116  }
117  }
118 
119  pSurface = std::move(pNewSurface);
120  pBackSurface = std::move(pNewBackSurface);
121 
122  pSurface->Unlock();
123  pBackSurface->Unlock();
124  }
125 
126  // Cannot bind empty surface
127  if (!pSurface->iTexSize) return false;
128 
129  // Generate frame buffer object
130  if (!hFrameBufDraw)
131  {
132  glGenFramebuffers(1, &hFrameBufDraw);
133  glGenFramebuffers(1, &hFrameBufRead);
134  }
135 
136  // Bind current texture to frame buffer
137  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hFrameBufDraw);
138  glBindFramebuffer(GL_READ_FRAMEBUFFER, hFrameBufRead);
139  glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
140  GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
141  pSurface->texture->texName, 0);
142  if (pBackSurface->texture)
143  glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
144  GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
145  pBackSurface->texture->texName, 0);
146 
147  // Check status, unbind if something was amiss
148  GLenum status1 = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER),
149  status2 = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
150  if (status1 != GL_FRAMEBUFFER_COMPLETE ||
151  (pBackSurface && status2 != GL_FRAMEBUFFER_COMPLETE))
152  {
153  glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
154  return false;
155  }
156  // Worked!
157  return true;
158 }
159 #endif
160 
162 {
163  return pSurface->Hgt;
164 }
165 
167 {
168  return pSurface->Wdt;
169 }
170 
171 #ifndef USE_CONSOLE
173 {
174  assert(pSurface->texture);
175  if (!pSurface->texture)
176  return 0;
177  return pSurface->texture->texName;
178 }
179 #endif
180 
182 {
183  // Set the new region
184  Region = r;
185  ViewportRegion = vp;
186 }
187 
188 bool C4FoWRegion::Render(const C4TargetFacet *pOnScreen)
189 {
190 #ifndef USE_CONSOLE
191  GLint prev_fb;
192  glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fb);
193 
194  // Update FoW at interesting location
195  pFoW->Update(Region, pPlayer);
196 
197  // On screen? No need to set up frame buffer - simply shortcut
198  if (pOnScreen)
199  {
200  pFoW->Render(this, pOnScreen, pPlayer, pGL->GetProjectionMatrix());
201  return true;
202  }
203 
204  // Set up shader. If this one doesn't work, we're really in trouble.
205  C4Shader *pShader = pFoW->GetFramebufShader();
206  assert(pShader);
207  if (!pShader) return false;
208 
209  // Create & bind the frame buffer
211  if(!BindFramebuf(prev_fb))
212  {
214  return false;
215  }
216  assert(pSurface && hFrameBufDraw);
217  if (!pSurface || !hFrameBufDraw)
218  return false;
219 
220  // Set up a clean context
221  glViewport(0, 0, pSurface->Wdt, pSurface->Hgt);
222  const StdProjectionMatrix projectionMatrix = StdProjectionMatrix::Orthographic(0.0f, pSurface->Wdt, pSurface->Hgt, 0.0f);
223 
224  // Clear texture contents
225  assert(pSurface->Hgt % 2 == 0);
226  glScissor(0, pSurface->Hgt / 2, pSurface->Wdt, pSurface->Hgt / 2);
227  glClearColor(0.0f, 0.5f / 1.5f, 0.5f / 1.5f, 0.0f);
228  glEnable(GL_SCISSOR_TEST);
229  glClear(GL_COLOR_BUFFER_BIT);
230 
231  // clear lower half of texture
232  glScissor(0, 0, pSurface->Wdt, pSurface->Hgt / 2);
233  glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
234  glClear(GL_COLOR_BUFFER_BIT);
235  glDisable(GL_SCISSOR_TEST);
236 
237  // Render FoW to frame buffer object
238  glBlendFunc(GL_ONE, GL_ONE);
239  pFoW->Render(this, nullptr, pPlayer, projectionMatrix);
240 
241  // Copy over the old state
242  if (OldRegion.Wdt > 0)
243  {
244 
245  // How much the borders have moved
246  int dx0 = Region.x - OldRegion.x,
247  dy0 = Region.y - OldRegion.y,
248  dx1 = Region.x + Region.Wdt - OldRegion.x - OldRegion.Wdt,
249  dy1 = Region.y + Region.Hgt - OldRegion.y - OldRegion.Hgt;
250 
251  // Source and target rect coordinates (landscape coordinate system)
252  int sx0 = std::max(0, dx0), sy0 = std::max(0, dy0),
253  sx1 = OldRegion.Wdt - std::max(0, -dx1), sy1 = OldRegion.Hgt - std::max(0, -dy1),
254  tx0 = std::max(0, -dx0), ty0 = std::max(0, -dy0),
255  tx1 = Region.Wdt - std::max(0, dx1), ty1 = Region.Hgt - std::max(0, dy1);
256 
257  // Quad coordinates
258  float vtxData[16];
259  float* squad = &vtxData[0];
260  float* tquad = &vtxData[8];
261 
262  squad[0] = float(sx0);
263  squad[1] = float(sy0);
264  squad[2] = float(sx0);
265  squad[3] = float(sy1);
266  squad[4] = float(sx1);
267  squad[5] = float(sy0);
268  squad[6] = float(sx1);
269  squad[7] = float(sy1);
270 
271  tquad[0] = float(tx0);
272  tquad[1] = float(ty0);
273  tquad[2] = float(tx0);
274  tquad[3] = float(ty1);
275  tquad[4] = float(tx1);
276  tquad[5] = float(ty0);
277  tquad[6] = float(tx1);
278  tquad[7] = float(ty1);
279 
280  // Transform into texture coordinates
281  for (int i = 0; i < 4; i++)
282  {
283  squad[i*2] = squad[i*2] / pBackSurface->Wdt;
284  squad[i*2+1] = 1.0 - squad[i*2+1] / pBackSurface->Hgt;
285  }
286 
287  // Load coordinates into vertex buffer
288  if (hVBO == 0)
289  {
290  glGenBuffers(1, &hVBO);
291  glBindBuffer(GL_ARRAY_BUFFER, hVBO);
292  glBufferData(GL_ARRAY_BUFFER, sizeof(vtxData), vtxData, GL_STREAM_DRAW);
293 
294  assert(vaoid == 0);
295  vaoid = pGL->GenVAOID();
296  }
297  else
298  {
299  glBindBuffer(GL_ARRAY_BUFFER, hVBO);
300  glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vtxData), vtxData);
301  }
302 
303  // Copy using shader
304  C4ShaderCall Call(pShader);
305  Call.Start();
306  if (Call.AllocTexUnit(C4FoWFSU_Texture))
307  glBindTexture(GL_TEXTURE_2D, pBackSurface->texture->texName);
308  Call.SetUniformMatrix4x4(C4FoWFSU_ProjectionMatrix, projectionMatrix);
309  glBlendFunc(GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_COLOR);
310  float normalBlend = 1.0f / 4.0f, // Normals change quickly
311  brightBlend = 1.0f / 16.0f; // Intensity more slowly
312  glBlendColor(0.0f,normalBlend,normalBlend,brightBlend);
313 
314  GLuint vao;
315  const bool has_vao = pGL->GetVAO(vaoid, vao);
316  glBindVertexArray(vao);
317  if (!has_vao)
318  {
319  glEnableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_Position));
320  glEnableVertexAttribArray(pShader->GetAttribute(C4FoWFSA_TexCoord));
321  glVertexAttribPointer(pShader->GetAttribute(C4FoWFSA_Position), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(8 * sizeof(float)));
322  glVertexAttribPointer(pShader->GetAttribute(C4FoWFSA_TexCoord), 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast<const uint8_t*>(0));
323  }
324 
325  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
326 
327  glBindVertexArray(0);
328  glBindBuffer(GL_ARRAY_BUFFER, 0);
329 
330  Call.Finish();
331  }
332 
333  // Done!
334  glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
336 
337  OldRegion = Region;
338 #endif
339  return true;
340 }
341 
342 void C4FoWRegion::GetFragTransform(const C4Rect& clipRect, const C4Rect& outRect, float lightTransform[6]) const
343 {
344  const C4Rect& lightRect = getRegion();
345  const FLOAT_RECT& vpRect = ViewportRegion;
346 
347  C4FragTransform trans;
348  // Clip offset
349  assert(outRect.Hgt >= clipRect.y + clipRect.Hgt);
350  trans.Translate(-clipRect.x, -(outRect.Hgt - clipRect.y - clipRect.Hgt));
351  // Clip normalization (0,0 -> 1,1)
352  trans.Scale(1.0f / clipRect.Wdt, 1.0f / clipRect.Hgt);
353  // Viewport/Landscape normalization
354  trans.Scale(vpRect.right - vpRect.left, vpRect.bottom - vpRect.top);
355  // Offset between viewport and light texture
356  trans.Translate(vpRect.left - lightRect.x, vpRect.top - lightRect.y);
357  // Light surface normalization
358  trans.Scale(1.0f / pSurface->Wdt, 1.0f / pSurface->Hgt);
359  // Light surface Y offset
360  trans.Translate(0.0f, 1.0f - (float)(lightRect.Hgt) / (float)pSurface->Hgt);
361 
362  // Extract matrix
363  trans.Get2x3(lightTransform);
364 }
C4Draw * pDraw
Definition: C4Draw.cpp:42
CStdGL * pGL
Definition: C4DrawGL.cpp:907
@ C4FoWFSU_Texture
Definition: C4FoW.h:71
@ C4FoWFSU_ProjectionMatrix
Definition: C4FoW.h:70
@ C4FoWFSA_Position
Definition: C4FoW.h:77
@ C4FoWFSA_TexCoord
Definition: C4FoW.h:78
float bottom
Definition: C4Rect.h:25
float top
Definition: C4Rect.h:25
float right
Definition: C4Rect.h:25
float left
Definition: C4Rect.h:25
bool RestorePrimaryClipper()
Definition: C4Draw.cpp:210
bool StorePrimaryClipper()
Definition: C4Draw.cpp:203
Definition: C4FoW.h:102
C4Shader * GetFramebufShader()
Definition: C4FoW.cpp:47
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, C4Player *pPlr, const StdProjectionMatrix &projectionMatrix)
Definition: C4FoW.cpp:238
void Update(C4Rect r, C4Player *player)
Definition: C4FoW.cpp:229
GLuint getSurfaceName() const
int32_t getSurfaceHeight() const
const C4Rect & getRegion() const
Definition: C4FoWRegion.h:53
C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer)
Definition: C4FoWRegion.cpp:21
void Update(C4Rect r, const FLOAT_RECT &vp)
int32_t getSurfaceWidth() const
void GetFragTransform(const C4Rect &clipRect, const C4Rect &outRect, float lightTransform[6]) const
bool Render(const C4TargetFacet *pOnScreen=nullptr)
void Scale(float sx, float sy)
Definition: C4FoW.h:45
void Get2x3(float transform[6])
Definition: C4FoW.h:54
void Translate(float dx, float dy)
Definition: C4FoW.h:38
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
int32_t x
Definition: C4Rect.h:30
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:127
const StdProjectionMatrix & GetProjectionMatrix() const
Definition: C4DrawGL.h:254
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
static StdProjectionMatrix Orthographic(float left, float right, float bottom, float top)