Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
TexturedLineRData.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "TexturedLineRData.h"
21 
22 #include "graphics/Terrain.h"
23 #include "maths/MathUtil.h"
24 #include "maths/Quaternion.h"
26 #include "renderer/Renderer.h"
30 
31 /* Note: this implementation uses g_VBMan directly rather than access it through the nicer VertexArray interface,
32  * because it allows you to work with variable amounts of vertices and indices more easily. New code should prefer
33  * to use VertexArray where possible, though. */
34 
36 {
37  if (!m_VB || !m_VBIndices)
38  return; // might have failed to allocate
39 
40  // -- render main line quad strip ----------------------
41 
42  const int streamFlags = shader->GetStreamFlags();
43 
44  shader->BindTexture(str_baseTex, line.m_TextureBase->GetHandle());
45  shader->BindTexture(str_maskTex, line.m_TextureMask->GetHandle());
46  shader->Uniform(str_objectColor, line.m_Color);
47 
48  GLsizei stride = sizeof(CTexturedLineRData::SVertex);
49  CTexturedLineRData::SVertex* vertexBase = reinterpret_cast<CTexturedLineRData::SVertex*>(m_VB->m_Owner->Bind());
50 
51  if (streamFlags & STREAM_POS)
52  shader->VertexPointer(3, GL_FLOAT, stride, &vertexBase->m_Position[0]);
53 
54  if (streamFlags & STREAM_UV0)
55  shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, &vertexBase->m_UVs[0]);
56 
57  if (streamFlags & STREAM_UV1)
58  shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, &vertexBase->m_UVs[0]);
59 
60  u8* indexBase = m_VBIndices->m_Owner->Bind();
61 
62  shader->AssertPointersBound();
63  glDrawElements(GL_TRIANGLES, m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*m_VBIndices->m_Index);
64 
65  g_Renderer.GetStats().m_DrawCalls++;
66  g_Renderer.GetStats().m_OverlayTris += m_VBIndices->m_Count/3;
67 }
68 
70 {
71  if (m_VB)
72  {
74  m_VB = NULL;
75  }
76  if (m_VBIndices)
77  {
79  m_VBIndices = NULL;
80  }
81 
82  if (!line.m_SimContext)
83  {
84  debug_warn(L"[TexturedLineRData] No SimContext set for textured overlay line, cannot render (no terrain data)");
85  return;
86  }
87 
88  const CTerrain& terrain = line.m_SimContext->GetTerrain();
89  CmpPtr<ICmpWaterManager> cmpWaterManager(*line.m_SimContext, SYSTEM_ENTITY);
90 
91  float v = 0.f;
92  std::vector<SVertex> vertices;
93  std::vector<u16> indices;
94 
95  size_t n = line.m_Coords.size() / 2; // number of line points
96  bool closed = line.m_Closed;
97 
98  ENSURE(n >= 2); // minimum needed to avoid errors (also minimum value to make sense, can't draw a line between 1 point)
99 
100  // In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1.
101  // To avoid slightly expensive terrain computations we cycle these around and
102  // recompute p2 at the end of each iteration.
103 
104  CVector3D p0;
105  CVector3D p1(line.m_Coords[0], 0, line.m_Coords[1]);
106  CVector3D p2(line.m_Coords[(1 % n)*2], 0, line.m_Coords[(1 % n)*2+1]);
107 
108  if (closed)
109  // grab the ending point so as to close the loop
110  p0 = CVector3D(line.m_Coords[(n-1)*2], 0, line.m_Coords[(n-1)*2+1]);
111  else
112  // we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that
113  // extends the p2 -> p1 direction, and use that point instead
114  p0 = p1 + (p1 - p2);
115 
116  bool p1floating = false;
117  bool p2floating = false;
118 
119  // Compute terrain heights, clamped to the water height (and remember whether
120  // each point was floating on water, for normal computation later)
121 
122  // TODO: if we ever support more than one water level per map, recompute this per point
123  float w = cmpWaterManager->GetExactWaterLevel(p0.X, p0.Z);
124 
125  p0.Y = terrain.GetExactGroundLevel(p0.X, p0.Z);
126  if (p0.Y < w)
127  p0.Y = w;
128 
129  p1.Y = terrain.GetExactGroundLevel(p1.X, p1.Z);
130  if (p1.Y < w)
131  {
132  p1.Y = w;
133  p1floating = true;
134  }
135 
136  p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
137  if (p2.Y < w)
138  {
139  p2.Y = w;
140  p2floating = true;
141  }
142 
143  for (size_t i = 0; i < n; ++i)
144  {
145  // For vertex i, compute bisector of lines (i-1)..(i) and (i)..(i+1)
146  // perpendicular to terrain normal
147 
148  // Normal is vertical if on water, else computed from terrain
149  CVector3D norm;
150  if (p1floating)
151  norm = CVector3D(0, 1, 0);
152  else
153  norm = terrain.CalcExactNormal(p1.X, p1.Z);
154 
155  CVector3D b = ((p1 - p0).Normalized() + (p2 - p1).Normalized()).Cross(norm);
156 
157  // Adjust bisector length to match the line thickness, along the line's width
158  float l = b.Dot((p2 - p1).Normalized().Cross(norm));
159  if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero
160  b *= line.m_Thickness / l;
161 
162  // Push vertices and indices for each quad in GL_TRIANGLES order. The two triangles of each quad are indexed using
163  // the winding orders (BR, BL, TR) and (TR, BL, TR) (where BR is bottom-right of this iteration's quad, TR top-right etc).
164  SVertex vertex1(p1 + b + norm*OverlayRenderer::OVERLAY_VOFFSET, 0.f, v);
165  SVertex vertex2(p1 - b + norm*OverlayRenderer::OVERLAY_VOFFSET, 1.f, v);
166  vertices.push_back(vertex1);
167  vertices.push_back(vertex2);
168 
169  u16 index1 = vertices.size() - 2; // index of vertex1 in this iteration (TR of this quad)
170  u16 index2 = vertices.size() - 1; // index of the vertex2 in this iteration (TL of this quad)
171 
172  if (i == 0)
173  {
174  // initial two vertices to continue building triangles from (n must be >= 2 for this to work)
175  indices.push_back(index1);
176  indices.push_back(index2);
177  }
178  else
179  {
180  u16 index1Prev = vertices.size() - 4; // index of the vertex1 in the previous iteration (BR of this quad)
181  u16 index2Prev = vertices.size() - 3; // index of the vertex2 in the previous iteration (BL of this quad)
182  ENSURE(index1Prev < vertices.size());
183  ENSURE(index2Prev < vertices.size());
184  // Add two corner points from last iteration and join with one of our own corners to create triangle 1
185  // (don't need to do this if i == 1 because i == 0 are the first two ones, they don't need to be copied)
186  if (i > 1)
187  {
188  indices.push_back(index1Prev);
189  indices.push_back(index2Prev);
190  }
191  indices.push_back(index1); // complete triangle 1
192 
193  // create triangle 2, specifying the adjacent side's vertices in the opposite order from triangle 1
194  indices.push_back(index1);
195  indices.push_back(index2Prev);
196  indices.push_back(index2);
197  }
198 
199  // alternate V coordinate for debugging
200  v = 1 - v;
201 
202  // cycle the p's and compute the new p2
203  p0 = p1;
204  p1 = p2;
205  p1floating = p2floating;
206 
207  // if in closed mode, wrap around the coordinate array for p2 -- otherwise, extend linearly
208  if (!closed && i == n-2)
209  // next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction
210  p2 = p1 + (p1 - p0);
211  else
212  p2 = CVector3D(line.m_Coords[((i+2) % n)*2], 0, line.m_Coords[((i+2) % n)*2+1]);
213 
214  p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
215  if (p2.Y < w)
216  {
217  p2.Y = w;
218  p2floating = true;
219  }
220  else
221  p2floating = false;
222  }
223 
224  if (closed)
225  {
226  // close the path
227  indices.push_back(vertices.size()-2);
228  indices.push_back(vertices.size()-1);
229  indices.push_back(0);
230 
231  indices.push_back(0);
232  indices.push_back(vertices.size()-1);
233  indices.push_back(1);
234  }
235  else
236  {
237  // Create start and end caps. On either end, this is done by taking the centroid between the last and second-to-last pair of
238  // vertices that was generated along the path (i.e. the vertex1's and vertex2's from above), taking a directional vector
239  // between them, and drawing the line cap in the plane given by the two butt-end corner points plus said vector.
240  std::vector<u16> capIndices;
241  std::vector<SVertex> capVertices;
242 
243  // create end cap
245  line,
246  // the order of these vertices is important here, swapping them produces caps at the wrong side
247  vertices[vertices.size()-2].m_Position, // top-right vertex of last quad
248  vertices[vertices.size()-1].m_Position, // top-left vertex of last quad
249  // directional vector between centroids of last vertex pair and second-to-last vertex pair
250  (Centroid(vertices[vertices.size()-2], vertices[vertices.size()-1]) - Centroid(vertices[vertices.size()-4], vertices[vertices.size()-3])).Normalized(),
251  line.m_EndCapType,
252  capVertices,
253  capIndices
254  );
255 
256  for (unsigned i = 0; i < capIndices.size(); i++)
257  capIndices[i] += vertices.size();
258 
259  vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
260  indices.insert(indices.end(), capIndices.begin(), capIndices.end());
261 
262  capIndices.clear();
263  capVertices.clear();
264 
265  // create start cap
267  line,
268  // the order of these vertices is important here, swapping them produces caps at the wrong side
269  vertices[1].m_Position,
270  vertices[0].m_Position,
271  // directional vector between centroids of first vertex pair and second vertex pair
272  (Centroid(vertices[1], vertices[0]) - Centroid(vertices[3], vertices[2])).Normalized(),
273  line.m_StartCapType,
274  capVertices,
275  capIndices
276  );
277 
278  for (unsigned i = 0; i < capIndices.size(); i++)
279  capIndices[i] += vertices.size();
280 
281  vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
282  indices.insert(indices.end(), capIndices.begin(), capIndices.end());
283  }
284 
285  ENSURE(indices.size() % 3 == 0); // GL_TRIANGLES indices, so must be multiple of 3
286 
287  m_VB = g_VBMan.Allocate(sizeof(SVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
288  if (m_VB) // allocation might fail (e.g. due to too many vertices)
289  {
290  m_VB->m_Owner->UpdateChunkVertices(m_VB, &vertices[0]); // copy data into VBO
291 
292  for (size_t k = 0; k < indices.size(); ++k)
293  indices[k] += m_VB->m_Index;
294 
295  m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
296  if (m_VBIndices)
298  }
299 
300 }
301 
302 void CTexturedLineRData::CreateLineCap(const SOverlayTexturedLine& line, const CVector3D& corner1, const CVector3D& corner2,
303  const CVector3D& lineDirectionNormal, SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut,
304  std::vector<u16>& indicesOut)
305 {
306  if (endCapType == SOverlayTexturedLine::LINECAP_FLAT)
307  return; // no action needed, this is the default
308 
309  // When not in closed mode, we've created artificial points for the start- and endpoints that extend the line in the
310  // direction of the first and the last segment, respectively. Thus, we know both the start and endpoints have perpendicular
311  // butt endings, i.e. the end corner vertices on either side of the line extend perpendicularly from the segment direction.
312  // That is to say, when viewed from the top, we will have something like
313  // .
314  // this: and not like this: /|
315  // ----+ / |
316  // | / .
317  // | /
318  // ----+ /
319  //
320 
321  int roundCapPoints = 8; // amount of points to sample along the semicircle for rounded caps (including corner points)
322  float radius = line.m_Thickness;
323 
324  CVector3D centerPoint = (corner1 + corner2) * 0.5f;
325  SVertex centerVertex(centerPoint, 0.5f, 0.5f);
326  u16 indexOffset = verticesOut.size(); // index offset in verticesOut from where we start adding our vertices
327 
328  switch (endCapType)
329  {
331  {
332  roundCapPoints = 3; // creates only one point directly ahead
333  radius *= 1.5f; // make it a bit sharper (note that we don't use the radius for the butt-end corner points so it should be ok)
334  centerVertex.m_UVs[0] = 0.480f; // slight visual correction to make the texture match up better at the corner points
335  }
336  // fall-through
338  {
339  // Draw a rounded line cap in the 3D plane of the line specified by the two corner points and the normal vector of the
340  // line's direction. The terrain normal at the centroid between the two corner points is perpendicular to this plane.
341  // The way this works is by taking a vector from the corner points' centroid to one of the corner points (which is then
342  // of radius length), and rotate it around the terrain normal vector in that centroid. This will rotate the vector in
343  // the line's plane, producing the desired rounded cap.
344 
345  // To please OpenGL's winding order, this angle needs to be negated depending on whether we start rotating from
346  // the (center -> corner1) or (center -> corner2) vector. For the (center -> corner2) vector, we apparently need to use
347  // the negated angle.
348  float stepAngle = -(float)(M_PI/(roundCapPoints-1));
349 
350  // Push the vertices in triangle fan order (easy to generate GL_TRIANGLES indices for afterwards)
351  // Note that we're manually adding the corner vertices instead of having them be generated by the rotating vector.
352  // This is because we want to support an overly large radius to make the sharp line ending look sharper.
353  verticesOut.push_back(centerVertex);
354  verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
355 
356  // Get the base vector that we will incrementally rotate in the cap plane to produce the radial sample points.
357  // Normally corner2 - centerPoint would suffice for this since it is of radius length, but we want to support custom
358  // radii to support tuning the 'sharpness' of sharp end caps (see above)
359  CVector3D rotationBaseVector = (corner2 - centerPoint).Normalized() * radius;
360  // Calculate the normal vector of the plane in which we're going to be drawing the line cap. This is the vector that
361  // is perpendicular to both baseVector and the 'lineDirectionNormal' vector indicating the direction of the line.
362  // Note that we shouldn't use terrain->CalcExactNormal() here because if the line is being rendered on top of water,
363  // then CalcExactNormal will return the normal vector of the terrain that's underwater (which can be quite funky).
364  CVector3D capPlaneNormal = lineDirectionNormal.Cross(rotationBaseVector).Normalized();
365 
366  for (int i = 1; i < roundCapPoints - 1; ++i)
367  {
368  // Rotate the centerPoint -> corner vector by i*stepAngle radians around the cap plane normal at the center point.
369  CQuaternion quatRotation;
370  quatRotation.FromAxisAngle(capPlaneNormal, i * stepAngle);
371  CVector3D worldPos3D = centerPoint + quatRotation.Rotate(rotationBaseVector);
372 
373  // Let v range from 0 to 1 as we move along the semi-circle, keep u fixed at 0 (i.e. curve the left vertical edge
374  // of the texture around the edge of the semicircle)
375  float u = 0.f;
376  float v = clamp((i/(float)(roundCapPoints-1)), 0.f, 1.f); // pos, u, v
377  verticesOut.push_back(SVertex(worldPos3D, u, v));
378  }
379 
380  // connect back to the other butt-end corner point to complete the semicircle
381  verticesOut.push_back(SVertex(corner1, 0.f, 1.f));
382 
383  // now push indices in GL_TRIANGLES order; vertices[indexOffset] is the center vertex, vertices[indexOffset + 1] is the
384  // first corner point, then a bunch of radial samples, and then at the end we have the other corner point again. So:
385  for (int i=1; i < roundCapPoints; ++i)
386  {
387  indicesOut.push_back(indexOffset); // center vertex
388  indicesOut.push_back(indexOffset + i);
389  indicesOut.push_back(indexOffset + i + 1);
390  }
391  }
392  break;
393 
395  {
396  // Extend the (corner1 -> corner2) vector along the direction normal and draw a square line ending consisting of
397  // three triangles (sort of like a triangle fan)
398  // NOTE: The order in which the vertices are pushed out determines the visibility, as they
399  // are rendered only one-sided; the wrong order of vertices will make the cap visible only from the bottom.
400  verticesOut.push_back(centerVertex);
401  verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
402  verticesOut.push_back(SVertex(corner2 + (lineDirectionNormal * (line.m_Thickness)), 0.f, 0.33333f)); // extend butt corner point 2 along the normal vector
403  verticesOut.push_back(SVertex(corner1 + (lineDirectionNormal * (line.m_Thickness)), 0.f, 0.66666f)); // extend butt corner point 1 along the normal vector
404  verticesOut.push_back(SVertex(corner1, 0.f, 1.0f)); // push butt corner point 1
405 
406  for (int i=1; i < 4; ++i)
407  {
408  indicesOut.push_back(indexOffset); // center point
409  indicesOut.push_back(indexOffset + i);
410  indicesOut.push_back(indexOffset + i + 1);
411  }
412  }
413  break;
414 
415  default:
416  break;
417  }
418 
419 }
#define M_PI
Definition: wposix.h:64
#define u8
Definition: types.h:39
size_t m_Index
Start index of this chunk in owner.
Definition: VertexBuffer.h:52
CTexturePtr m_TextureBase
Definition: Overlay.h:84
void CreateLineCap(const SOverlayTexturedLine &line, const CVector3D &corner1, const CVector3D &corner2, const CVector3D &normal, SOverlayTexturedLine::LineCapType endCapType, std::vector< SVertex > &verticesOut, std::vector< u16 > &indicesOut)
Creates a line cap of the specified type endCapType at the end of the segment going in direction norm...
CVertexBufferManager g_VBMan
float Dot(const CVector3D &vector) const
Definition: Vector3D.cpp:48
CVector3D Cross(const CVector3D &vector) const
Definition: Vector3D.cpp:55
CVector3D Rotate(const CVector3D &vec) const
Definition: Quaternion.cpp:300
VertexArray::Attribute m_Position
Definition: DecalRData.h:49
float GetExactGroundLevel(float x, float z) const
Definition: Terrain.cpp:353
CVector3D Centroid(const SVertex &v1, const SVertex &v2)
Small utility function; grabs the centroid of the positions of two vertices.
LineCapType m_EndCapType
Definition: Overlay.h:99
u8 * Bind()
Bind to this buffer; return pointer to address required as parameter to glVertexPointer ( + etc) call...
square end that extends half the line width beyond the line end
Definition: Overlay.h:76
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton &#39;system&#39; components.
Definition: Entity.h:44
#define g_Renderer
Definition: Renderer.h:61
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
CVertexBuffer::VBChunk * Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target)
Try to allocate a vertex buffer of the given size and type.
Textured line overlay, with world-space coordinates, rendered in the world onto the terrain...
Definition: Overlay.h:62
bool m_Closed
Should this line be treated as a closed loop? If set, any end cap settings are ignored.
Definition: Overlay.h:94
float X
Definition: Vector3D.h:31
Semi-circular line ending.
Definition: Overlay.h:74
float Y
Definition: Vector3D.h:31
CColor m_Color
Color to apply to the line texture, where indicated by the mask.
Definition: Overlay.h:88
virtual float GetExactWaterLevel(float x, float z)=0
Get the current water level at the given point.
CVertexBuffer::VBChunk * m_VB
void UpdateChunkVertices(VBChunk *chunk, void *data)
Update vertex data for given chunk. Transfers the provided data to the actual OpenGL vertex buffer...
std::vector< float > m_Coords
(x, z) vertex coordinate pairs; y is computed automatically.
Definition: Overlay.h:90
CTexturePtr m_TextureMask
Definition: Overlay.h:85
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
size_t m_Count
Number of vertices used by chunk.
Definition: VertexBuffer.h:54
void Update(const SOverlayTexturedLine &line)
#define u16
Definition: types.h:40
void Release(CVertexBuffer::VBChunk *chunk)
Returns the given chunk to its owning buffer.
CVertexBuffer * m_Owner
Owning (parent) vertex buffer.
Definition: VertexBuffer.h:50
no line ending; abrupt stop of the line (aka. butt ending)
Definition: Overlay.h:66
void Render(const SOverlayTexturedLine &line, const CShaderProgramPtr &shader)
float Z
Definition: Vector3D.h:31
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
void FromAxisAngle(const CVector3D &axis, float angle)
Definition: Quaternion.cpp:260
const CSimContext * m_SimContext
Simulation context applicable for this overlay line; used to obtain terrain information during automa...
Definition: Overlay.h:105
LineCapType m_StartCapType
Definition: Overlay.h:98
float m_Thickness
Half-width of the line, in world-space units.
Definition: Overlay.h:92
CVector3D Normalized() const
Definition: Vector3D.cpp:86
static const float OVERLAY_VOFFSET
Small vertical offset of overlays from terrain to prevent visual glitches.
shared_ptr< CShaderProgram > CShaderProgramPtr
T clamp(T value, T min, T max)
Definition: MathUtil.h:32
CVertexBuffer::VBChunk * m_VBIndices
CVector3D CalcExactNormal(float x, float z) const
Definition: Terrain.cpp:235
CTerrain & GetTerrain() const
Definition: SimContext.cpp:51