OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4FoWDrawStrategy.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"
18 
19 #ifndef USE_CONSOLE
20 
24 #include "graphics/C4DrawGL.h"
25 
27  mode(M_Fan), cur_vertices(0), begin_vertices(0)
28 {
29 }
30 
32 {
33  FinishPrimitive();
34  mode = M_Fan;
35 }
36 
38 {
39  FinishPrimitive();
40  mode = M_Quads;
41 }
42 
43 void C4FoWDrawTriangulator::FinishPrimitive()
44 {
45  // Check the current primitive is complete
46  switch (mode)
47  {
48  case M_Fan:
49  assert(cur_vertices == 0 || cur_vertices >= 3);
50  break;
51  case M_Quads:
52  assert(cur_vertices % 4 == 0);
53  break;
54  default:
55  assert(false);
56  break;
57  }
58 
59  // Reset primitve counter
60  begin_vertices += cur_vertices;
61  cur_vertices = 0;
62 }
63 
65 {
66  switch (mode)
67  {
68  case M_Fan:
69  ++cur_vertices;
70  if (cur_vertices == 3)
71  {
72  indices.push_back(begin_vertices);
73  indices.push_back(begin_vertices + 1);
74  indices.push_back(begin_vertices + 2);
75  }
76  else if (cur_vertices > 3)
77  {
78  indices.push_back(begin_vertices);
79  indices.push_back(begin_vertices + cur_vertices - 2);
80  indices.push_back(begin_vertices + cur_vertices - 1);
81  }
82  break;
83  case M_Quads:
84  ++cur_vertices;
85  if (cur_vertices % 4 == 0)
86  {
87  // upper tri
88  indices.push_back(begin_vertices + cur_vertices - 4);
89  indices.push_back(begin_vertices + cur_vertices - 3);
90  indices.push_back(begin_vertices + cur_vertices - 2);
91 
92  // lower tri
93  indices.push_back(begin_vertices + cur_vertices - 4);
94  indices.push_back(begin_vertices + cur_vertices - 2);
95  indices.push_back(begin_vertices + cur_vertices - 1);
96  }
97  break;
98  default:
99  assert(false);
100  }
101 }
102 
104 {
105  FinishPrimitive();
106  begin_vertices = 0;
107 
108  // Assume this keeps capacity
109  indices.resize(0);
110 }
111 
113  : light(light), region(nullptr), vbo_size(0), ibo_size(0)
114 {
115  bo[0] = bo[1] = 0u;
116 }
117 
119 {
120  if (bo[0])
121  {
122  glDeleteBuffers(2, bo);
123  pGL->FreeVAOID(vaoids[2]);
124  pGL->FreeVAOID(vaoids[1]);
125  pGL->FreeVAOID(vaoids[0]);
126  }
127 }
128 
130 {
131  region = regionPar;
132  if (!bo[0])
133  {
134  // lazy-init buffer objects
135  glGenBuffers(2, bo);
136  vaoids[0] = pGL->GenVAOID();
137  vaoids[1] = pGL->GenVAOID();
138  vaoids[2] = pGL->GenVAOID();
139  }
140 }
141 
143 {
144  // If we have nothing to draw (e.g. directly after initialization), abort early.
145  if (vertices.empty()) return;
146 
147  const GLuint vbo = bo[0];
148  const GLuint ibo = bo[1];
149 
150  // Upload vertices
151  glBindBuffer(GL_ARRAY_BUFFER, vbo);
152  if (vbo_size < vertices.size())
153  {
154  glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_DYNAMIC_DRAW);
155  vbo_size = vertices.size();
156  }
157  else
158  {
159  glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(Vertex), &vertices[0]);
160  }
161  glBindBuffer(GL_ARRAY_BUFFER, 0);
162 
163  // Upload indices
164  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
165  if (ibo_size < triangulator.GetNIndices())
166  {
167  glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangulator.GetNIndices() * sizeof(GLuint), triangulator.GetIndices(), GL_DYNAMIC_DRAW);
168  ibo_size = triangulator.GetNIndices();
169  }
170  else
171  {
172  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, triangulator.GetNIndices() * sizeof(GLuint), triangulator.GetIndices());
173  }
174  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
175 
176  // Region dimensions
177  const float width = region->getSurfaceWidth();
178  const float height = region->getSurfaceHeight() / 2.0;
179 
180  // Set Y offset for vertex
181  float y_offset[] = { 0.0f, 0.0f };
182  call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
183 
184  // Enable scissor test to only draw in upper or lower half of texture
185  glEnable(GL_SCISSOR_TEST);
186  glScissor(0, height, width, height);
187 
188  // Setup state for 1st pass
189  GLuint vao1, vao2, vao3;
190  const bool has_vao1 = pGL->GetVAO(vaoids[0], vao1);
191  glBindVertexArray(vao1);
192  if (!has_vao1)
193  {
194  glBindBuffer(GL_ARRAY_BUFFER, vbo);
195  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
196  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
197  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
198  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
199  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r1)));
200  }
201 
202  // Set up blend equation, see C4FoWDrawLightTextureStrategy::DrawVertex
203  // for details.
204  glBlendFunc(GL_ONE, GL_ONE);
205  glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
206 
207  // Render 1st pass
208  glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
209 
210  // Prepare state for 2nd pass
211  //glBlendFunc(GL_ONE, GL_ONE);
212  glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
213 
214  const bool has_vao2 = pGL->GetVAO(vaoids[1], vao2);
215  glBindVertexArray(vao2);
216  if (!has_vao2)
217  {
218  glBindBuffer(GL_ARRAY_BUFFER, vbo);
219  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
220  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
221  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
222  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
223  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r2)));
224  }
225 
226  // Render 2nd pass
227  glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
228 
229  // Prepare state for 3rd pass (color pass)
230  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
231  glBlendEquation(GL_FUNC_ADD);
232  glScissor(0, 0, width, height);
233  y_offset[1] = height;
234  call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
235 
236  const bool has_vao3 = pGL->GetVAO(vaoids[2], vao3);
237  glBindVertexArray(vao3);
238  if (!has_vao3)
239  {
240  glBindBuffer(GL_ARRAY_BUFFER, vbo);
241  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
242  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
243  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
244  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
245  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r3)));
246  }
247 
248  // Render 3rd pass
249  glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
250 
251  // Reset GL state
252  glBindVertexArray(0);
253  glDisable(GL_SCISSOR_TEST);
254 
255  // Assume the capacity stays the same:
256  vertices.resize(0);
258 }
259 
260 void C4FoWDrawLightTextureStrategy::DrawVertex(float x, float y, bool shadow)
261 {
262 
263  // Here's the master plan for updating the lights texture. We want to
264  //
265  // 1. sum up the normals, weighted by intensity
266  // 2. take over intensity maximum as new intensity
267  //
268  // where intensity is in the A channel and normals are in the GB channels.
269  // Normals are obviously meant to be though of as signed, though,
270  // so the equation we want would be something like
271  //
272  // A_new = max(A_old, A);
273  // G_new = BoundBy(G_old + G - 0.5, 0.0, 1.0);
274  // B_new = BoundBy(B_old + B - 0.5, 0.0, 1.0);
275  //
276  // It seems we can't get that directly though - glBlendFunc only talks
277  // about two operands. Even if we make two passes, we have to take
278  // care that that we don't over- or underflow in the intermediate pass.
279  //
280  // Therefore, we store G/1.5 instead of G, losing a bit of accuracy,
281  // but allowing us to formulate the following approximation without
282  // overflows:
283  //
284  // G_new = BoundBy(BoundBy(G_old + G / 1.5), 0.0, 1.0) - 0.5 / 1.5, 0.0, 1.0)
285  // B_new = BoundBy(BoundBy(B_old + B / 1.5), 0.0, 1.0) - 0.5 / 1.5, 0.0, 1.0)
286 
287  vertices.emplace_back();
288  Vertex& vtx = vertices.back();
289  vtx.x = x; vtx.y = y;
290 
291  // global coords -> region coords
292  // TODO: We could also do this in the shader...
293  vtx.x += -region->getRegion().x;
294  vtx.y += -region->getRegion().y;
295 
296  // First pass color
297  if (shadow)
298  {
299  float dx = x - light->getX();
300  float dy = y - light->getY();
301  float dist = sqrt(dx*dx+dy*dy);
302  float bright = light->getBrightness();
303  float mult = std::min(0.5f / light->getNormalSize(), 0.5f / dist);
304  float normX = Clamp(0.5f + dx * mult, 0.0f, 1.0f) / 1.5f;
305  float normY = Clamp(0.5f + dy * mult, 0.0f, 1.0f) / 1.5f;
306  vtx.r1 = 0.0f; vtx.g1 = normX; vtx.b1 = normY; vtx.a1 = bright;
307  }
308  else
309  {
310  vtx.r1 = 0.0f; vtx.g1 = 0.5f / 1.5f; vtx.b1 = 0.5f / 1.5f; vtx.a1 = 0.0f;
311  }
312 
313  // Second pass color
314  vtx.r2 = 0.0f; vtx.g2 = 0.5f / 1.5f; vtx.b2 = 0.5f / 1.5f; vtx.a2 = 0.0f;
315 
316  // Third pass color
317  float alpha; // 0.0 == fully transparent (takes old color), 1.0 == solid color (takes new color)
318  if (shadow)
319  {
320  alpha = 0.3 + 0.6 * light->getValue() * light->getLightness();
321  }
322  else // draw the edge of the light
323  {
324  alpha = 0.0;
325  }
326 
327  vtx.r3 = light->getR();
328  vtx.g3 = light->getG();
329  vtx.b3 = light->getB();
330  vtx.a3 = alpha;
331 }
332 
334 {
335  DrawVertex(x,y, false);
337 }
338 
340 {
341  DrawVertex(x,y, true);
343 }
344 
346  light(light), screen(screen), vbo_size(0), ibo_size(0)
347 {
348  glGenBuffers(2, bo);
349  vaoid = pGL->GenVAOID();
350 }
351 
353 {
354  glDeleteBuffers(2, bo);
355  pGL->FreeVAOID(vaoid);
356 }
357 
359 {
360 }
361 
363 {
364  // If we have nothing to draw (e.g. directly after initialization), abort early.
365  if (vertices.empty()) return;
366 
367  const GLuint vbo = bo[0];
368  const GLuint ibo = bo[1];
369 
370  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
371 
372  // Upload vertices
373  glBindBuffer(GL_ARRAY_BUFFER, vbo);
374  if (vbo_size < vertices.size())
375  {
376  glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STREAM_DRAW);
377  vbo_size = vertices.size();
378  }
379  else
380  {
381  glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(Vertex), &vertices[0]);
382  }
383  glBindBuffer(GL_ARRAY_BUFFER, 0);
384 
385  // Upload indices
386  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
387  if (ibo_size < triangulator.GetNIndices())
388  {
389  glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangulator.GetNIndices() * sizeof(GLuint), triangulator.GetIndices(), GL_STREAM_DRAW);
390  ibo_size = triangulator.GetNIndices();
391  }
392  else
393  {
394  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, triangulator.GetNIndices() * sizeof(GLuint), triangulator.GetIndices());
395  }
396  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
397 
398  GLuint vao;
399  const bool has_vao = pGL->GetVAO(vaoid, vao);
400  glBindVertexArray(vao);
401  if (!has_vao)
402  {
403  glBindBuffer(GL_ARRAY_BUFFER, vbo);
404  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
405  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
406  glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
407 
408  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
409  glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r)));
410  }
411 
412  // Set Y offset for vertex
413  const float y_offset[] = { 0.0f, 0.0f };
414  call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
415 
416  glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
417 
418  // Reset GL state
419  glBindVertexArray(0);
420  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
421 
422  // Assume the capacity stays the same:
423  vertices.resize(0);
425 }
426 
427 void C4FoWDrawWireframeStrategy::DrawVertex(Vertex& vtx)
428 {
429  // global coords -> screen pos and zoom
430  // TODO: We could do this in the shader...
431  vtx.x += screen->X - screen->TargetX;
432  vtx.y += screen->Y - screen->TargetY;
433  pGL->ApplyZoom(vtx.x, vtx.y);
434 }
435 
437 {
438  vertices.emplace_back();
439  Vertex& vtx = vertices.back();
440  vtx.x = x; vtx.y = y;
441 
442  switch(phase)
443  {
444  case P_None: return;
445  case P_Fade: vtx.r = 0.0f; vtx.g = 0.5f; vtx.b = 0.0f; vtx.a = 1.0f; break;
446  case P_Intermediate: vtx.r = 0.0f; vtx.g = 0.0f; vtx.b = 0.5f; vtx.a = 1.0f; break;
447  default: assert(false); // only fade has dark vertices
448  }
449 
450  DrawVertex(vtx);
452 }
453 
455 {
456  vertices.emplace_back();
457  Vertex& vtx = vertices.back();
458  vtx.x = x; vtx.y = y;
459 
460  switch(phase)
461  {
462  case P_None: return;
463  case P_Fan: vtx.r = 1.0f; vtx.g = 0.0f; vtx.b = 0.0f; vtx.a = 1.0f; break;
464  case P_FanMaxed: vtx.r = 1.0f; vtx.g = 1.0f; vtx.b = 0.0f; vtx.a = 1.0f; break;
465  case P_Fade: vtx.r = 0.0f; vtx.g = 1.0f; vtx.b = 0.0f; vtx.a = 1.0f; break;
466  case P_Intermediate: vtx.r = 0.0f; vtx.g = 0.0f; vtx.b = 1.0f; vtx.a = 1.0f; break;
467  default: assert(false);
468  }
469 
470  DrawVertex(vtx);
472 }
473 
474 #endif
float getBrightness() const
Definition: C4FoWLight.h:67
float Y
Definition: C4Facet.h:120
void ApplyZoom(float &X, float &Y)
Definition: C4Draw.cpp:787
float getR() const
Definition: C4FoWLight.h:68
int32_t getY() const
Definition: C4FoWLight.h:61
float getB() const
Definition: C4FoWLight.h:70
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:195
void FreeVAOID(unsigned int vaoid)
Definition: C4DrawGL.cpp:976
enum C4FoWDrawStrategy::DrawPhase phase
virtual void DrawDarkVertex(float x, float y)
int32_t getX() const
Definition: C4FoWLight.h:60
virtual void DrawDarkVertex(float x, float y)
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:46
unsigned int GetNIndices() const
C4FoWDrawLightTextureStrategy(const C4FoWLight *light)
virtual void Begin(const C4FoWRegion *region)
virtual void DrawDarkVertex(float x, float y)
int32_t y
Definition: C4Rect.h:32
int32_t getSurfaceHeight() const
float getLightness() const
Definition: C4FoWLight.h:72
C4FoWDrawTriangulator triangulator
float getValue() const
Definition: C4FoWLight.h:71
C4FoWDrawWireframeStrategy(const C4FoWLight *light, const C4TargetFacet *screen)
bool GetVAO(unsigned int vaoid, GLuint &vao)
Definition: C4DrawGL.cpp:1023
int32_t getSurfaceWidth() const
virtual void DrawLightVertex(float x, float y)
int32_t x
Definition: C4Rect.h:32
virtual void DrawLightVertex(float x, float y)
float TargetX
Definition: C4Facet.h:167
unsigned int GenVAOID()
Definition: C4DrawGL.cpp:934
const unsigned int * GetIndices() const
int32_t getNormalSize() const
Definition: C4FoWLight.h:66
const C4Rect & getRegion() const
Definition: C4FoWRegion.h:51
float getG() const
Definition: C4FoWLight.h:69
virtual void End(C4ShaderCall &call)
virtual void DrawLightVertex(float x, float y)
CStdGL * pGL
Definition: C4DrawGL.cpp:914
virtual void Begin(const C4FoWRegion *region)
virtual void End(C4ShaderCall &call)
float TargetY
Definition: C4Facet.h:167
virtual void End(C4ShaderCall &call)
float X
Definition: C4Facet.h:120
void SetUniform2fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:230