Stellarium 0.12.4
StelGeometryBuilder.hpp
1 /*
2  * Stellarium
3  * Copyright (C) 2012 Ferdinand Majerech
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  */
19 
20 #ifndef _STELGEOMETRYBUILDER_HPP_
21 #define _STELGEOMETRYBUILDER_HPP_
22 
23 
24 #include "GenericVertexTypes.hpp"
25 #include "StelIndexBuffer.hpp"
26 #include "StelLight.hpp"
27 #include "StelProjector.hpp"
28 #include "StelVertexBuffer.hpp"
29 #include "VecMath.hpp"
30 
31 
51 {
59  SphereParams(const float radius)
60  : radius_(radius)
61  , oneMinusOblateness_(1.0f)
62  , slices_(20)
63  , stacks_(20)
64  , orientInside_(false)
65  , flipTexture_(false)
66  {
67  }
68 
74  SphereParams& oneMinusOblateness(const float rhs)
75  {
76  oneMinusOblateness_ = rhs;
77  return *this;
78  }
79 
88  SphereParams& resolution(const int slices, const int stacks)
89  {
90  slices_ = slices;
91  stacks_ = stacks;
92  return *this;
93  }
94 
97  {
98  orientInside_ = true;
99  return *this;
100  }
101 
104  {
105  flipTexture_ = true;
106  return *this;
107  }
108 
110  float radius_;
114  int slices_;
116  int stacks_;
121 };
122 
123 
137 {
138 friend class StelGeometryBuilder;
139 private:
141  enum SphereType
142  {
144  SphereType_Fisheye,
146  SphereType_Unlit,
148  SphereType_Lit
149  };
150 
151 public:
154  {
155  if(NULL != unlitVertices)
156  {
157  Q_ASSERT_X(NULL == litVertices, Q_FUNC_INFO,
158  "Both lit and unlit vertex buffers are used");
159  delete unlitVertices;
160  unlitVertices = NULL;
161  }
162  if(NULL != litVertices)
163  {
164  Q_ASSERT_X(NULL == unlitVertices, Q_FUNC_INFO,
165  "Both lit and unlit vertex buffers are used");
166  delete litVertices;
167  litVertices = NULL;
168  }
169  for(int row = 0; row < rowIndices.size(); ++row)
170  {
171  delete rowIndices[row];
172  }
173  rowIndices.clear();
174  }
175 
180  void draw(class StelRenderer* renderer, StelProjectorP projector);
181 
185  void setRadius(const float radius)
186  {
187  Q_ASSERT_X(radius > 0.0f, Q_FUNC_INFO, "Sphere radius must be greater than zero");
188  this->radius = radius;
189  updated = true;
190  }
191 
197  void setOneMinusOblateness(const float oneMinusOblateness)
198  {
199  Q_ASSERT_X(oneMinusOblateness >= 0.0f && oneMinusOblateness <= 1.0f, Q_FUNC_INFO,
200  "Sphere oneMinusOblateness parameter must be at least zero and at most one");
201  this->oneMinusOblateness = oneMinusOblateness;
202  updated = true;
203  }
204 
213  void setResolution(const int slices, const int stacks)
214  {
215  // The maximums here must match MAX_STACKS and MAX_SLICES in the cpp file.
216  Q_ASSERT_X(stacks >= 3 && stacks < 4096, Q_FUNC_INFO,
217  "There must be at least 3 stacks in a sphere, and at most 4096.");
218  Q_ASSERT_X(slices >= 3 && slices < 4096, Q_FUNC_INFO,
219  "There must be at least 3 slices in a sphere, and at most 4096.");
220  this->stacks = stacks;
221  this->slices = slices;
222  updated = true;
223  }
224 
226  void setOrientInside(const bool orientInside)
227  {
228  this->orientInside = orientInside;
229  updated = true;
230  }
231 
233  void setFlipTexture(const bool flipTexture)
234  {
235  this->flipTexture = flipTexture;
236  updated = true;
237  }
238 
240  void setLight(const StelLight& light)
241  {
242  Q_ASSERT_X(type == SphereType_Lit, Q_FUNC_INFO,
243  "Trying to set light for an unlit sphere");
244  if(this->light == light){return;}
245  this->light = light;
246  updated = true;
247  }
248 
249 private:
251  const SphereType type;
252 
256  bool updated;
257 
259  float radius;
261  float oneMinusOblateness;
263  int stacks;
265  int slices;
267  bool orientInside;
269  bool flipTexture;
270 
272  const float fisheyeTextureFov;
273 
275  StelLight light;
276 
278  StelVertexBuffer<VertexP3T2>* unlitVertices;
280  StelVertexBuffer<VertexP3T2C4>* litVertices;
281 
284  QVector<StelIndexBuffer*> rowIndices;
285 
295  StelGeometrySphere(const SphereType type, const float textureFov = -100.0f,
296  const StelLight& light = StelLight())
297  : type(type)
298  , updated(true)
299  , radius(0.0f)
300  , oneMinusOblateness(0.0f)
301  , stacks(0)
302  , slices(0)
303  , orientInside(false)
304  , flipTexture(false)
305  , fisheyeTextureFov(textureFov)
306  , light(light)
307  , unlitVertices(NULL)
308  , litVertices(NULL)
309  {
310  if(type == SphereType_Fisheye)
311  {
312  Q_ASSERT_X(textureFov > 0.0f, Q_FUNC_INFO,
313  "Fisheye sphere texture FOV must be greater than zero");
314  }
315  }
316 
323  void regenerate(class StelRenderer* renderer, StelProjectorP projector);
324 };
325 
326 
346 {
354  RingParams(const float innerRadius, const float outerRadius)
355  : innerRadius_(innerRadius)
356  , outerRadius_(outerRadius)
357  , loops_(20)
358  , slices_(20)
359  , flipFaces_(false)
360  {
361  }
362 
371  RingParams& resolution(const int slices, const int loops)
372  {
373  slices_ = slices;
374  loops_ = loops;
375  return *this;
376  }
377 
380  {
381  flipFaces_ = true;
382  return *this;
383  }
384 
386  float innerRadius_;
390  int loops_;
392  int slices_;
395 };
396 
397 
411 {
412 friend class StelGeometryBuilder;
413 private:
415  enum RingType
416  {
417  RingType_Textured,
418  RingType_Plain2D
419  };
420 
421 public:
424  {
425  if(NULL != texturedVertices)
426  {
427  Q_ASSERT_X(NULL == plain2DVertices, Q_FUNC_INFO,
428  "Both textured and 2D vertex buffers are used");
429  delete texturedVertices;
430  texturedVertices = NULL;
431  }
432  if(NULL != plain2DVertices)
433  {
434  Q_ASSERT_X(NULL == texturedVertices, Q_FUNC_INFO,
435  "Both textured and 2D vertex buffers are used");
436  delete plain2DVertices;
437  plain2DVertices = NULL;
438  }
439  for(int loop = 0; loop < loopIndices.size(); ++loop)
440  {
441  delete loopIndices[loop];
442  }
443  loopIndices.clear();
444  }
445 
451  void draw(class StelRenderer* renderer, StelProjectorP projector = StelProjectorP());
452 
457  void setInnerOuterRadius(const float inner, const float outer)
458  {
459  Q_ASSERT_X(inner > 0.0f, Q_FUNC_INFO, "Inner ring radius must be greater than zero");
460 
461  // No need to regenerate
462  if(inner == this->innerRadius && outer == this->outerRadius) {return;}
463 
464  this->innerRadius = inner;
465  this->outerRadius = outer;
466  updated = true;
467  }
468 
477  void setResolution(const int slices, const int loops)
478  {
479  Q_ASSERT_X(loops >= 1, Q_FUNC_INFO, "There must be at least 1 loop in a ring");
480  // Must correspond with MAX_SLICES in the .cpp file.
481  Q_ASSERT_X(slices >= 3 && slices <= 4096, Q_FUNC_INFO,
482  "There must be at least 3 and at most 4096 slices in a ring.");
483 
484  // No need to regenerate
485  if(slices == this->slices && loops == this->loops) {return;}
486 
487  this->loops = loops;
488  this->slices = slices;
489  updated = true;
490  }
491 
493  void setFlipFaces(const bool flipFaces)
494  {
495  // No need to regenerate
496  if(flipFaces == this->flipFaces) {return;}
497  this->flipFaces = flipFaces;
498  updated = true;
499  }
500 
502  void setOffset(const Vec3f offset)
503  {
504  // No need to regenerate
505  if(offset == this->offset) {return;}
506  this->offset = offset;
507  updated = true;
508  }
509 
510 private:
512  const RingType type;
513 
517  bool updated;
518 
520  float innerRadius;
522  float outerRadius;
524  int loops;
526  int slices;
528  bool flipFaces;
530  Vec3f offset;
531 
533  StelVertexBuffer<VertexP3T2>* texturedVertices;
535  StelVertexBuffer<VertexP2>* plain2DVertices;
536 
539  QVector<StelIndexBuffer*> loopIndices;
540 
548  StelGeometryRing(const RingType type)
549  : type(type)
550  , updated(true)
551  , innerRadius(0.0f)
552  , outerRadius(0.0f)
553  , loops(0)
554  , slices(0)
555  , flipFaces(0)
556  , offset(0.0f, 0.0f, 0.0f)
557  , texturedVertices(NULL)
558  , plain2DVertices(NULL)
559  {
560  }
561 
567  void regenerate(class StelRenderer* renderer);
568 };
569 
570 
573 {
574 public:
575 
585  const float x, const float y, const float radius,
586  const int segments = 128)
587  {
588  Q_ASSERT_X(radius > 0.0f, Q_FUNC_INFO, "Circle must have a radius greater than zero");
589  Q_ASSERT_X(segments > 3, Q_FUNC_INFO, "Circle must have at least 3 segments");
590  Q_ASSERT_X(vertexBuffer->length() == 0, Q_FUNC_INFO,
591  "Need an empty vertex buffer to build a circle");
592  Q_ASSERT_X(vertexBuffer->primitiveType() == PrimitiveType_LineStrip, Q_FUNC_INFO,
593  "Need a line loop vertex buffer to build a circle");
594 
595  const Vec2f center(x,y);
596  const float phi = 2.0f * M_PI / segments;
597  const float cp = std::cos(phi);
598  const float sp = std::sin(phi);
599  float dx = radius;
600  float dy = 0;
601 
602  vertexBuffer->unlock();
603 
604  for (int i = 0; i < segments; i++)
605  {
606  vertexBuffer->addVertex(VertexP2(x + dx, y + dy));
607  const float dxNew = dx * cp - dy * sp;
608  dy = dx * sp + dy * cp;
609  dx = dxNew;
610  }
611  vertexBuffer->addVertex(VertexP2(x + radius, y));
612  vertexBuffer->lock();
613  }
614 
624  void buildCylinder(StelVertexBuffer<VertexP3T2>* const vertexBuffer,
625  const float radius, const float height, const int slices,
626  const bool orientInside = false)
627  {
628  // No need for indices - w/o top/bottom a cylinder is just a single tristrip
629  // with no duplicate vertices except start/end
630 
631  Q_ASSERT_X(vertexBuffer->length() == 0, Q_FUNC_INFO,
632  "Need an empty vertex buffer to start building a cylinder");
633  Q_ASSERT_X(vertexBuffer->primitiveType() == PrimitiveType_TriangleStrip,
634  Q_FUNC_INFO, "Need a triangle strip vertex buffer to build a cylinder");
635  Q_ASSERT_X(slices >= 3, Q_FUNC_INFO,
636  "can't build a cylinder with less than 3 slices");
637 
638  vertexBuffer->unlock();
639  float texCoord = orientInside ? 1.0f : 0.0f;
640  float angle = 0.0f;
641 
642  // Needed to generate vertices with opposite winding if orientInside == true.
643  const float sign = orientInside ? -1.0f : 1.0f;
644  const float deltaTexCoord = 1.0f / slices;
645  const float deltaAngle = 2.0f * M_PI / slices;
646 
647  for (int i = 0; i <= slices; ++i)
648  {
649  const float x = std::sin(angle) * radius;
650  const float y = std::cos(angle) * radius;
651 
652  vertexBuffer->addVertex(VertexP3T2(Vec3f(x, y, 0.0f), Vec2f(texCoord, 0.0f)));
653  vertexBuffer->addVertex(VertexP3T2(Vec3f(x, y, height), Vec2f(texCoord, 1.0f)));
654 
655  // If oriented outside, we go from 0 at 0deg to 1 at 360deg
656  // If oriented inside, we go from 1 at 360 deg to 0 at 0deg
657  texCoord += sign * deltaTexCoord;
658  angle += sign * deltaAngle;
659  }
660  vertexBuffer->lock();
661  }
662 
677  void buildFanDisk(StelVertexBuffer<VertexP3T2>* const vertexBuffer,
678  StelIndexBuffer* const indexBuffer, const float radius,
679  const int innerFanSlices, const int level);
680 
689  StelGeometrySphere* buildSphereFisheye(const SphereParams& params, const float textureFov)
690  {
691  StelGeometrySphere* result =
692  new StelGeometrySphere(StelGeometrySphere::SphereType_Fisheye, textureFov);
693  result->setRadius(params.radius_);
695  result->setResolution(params.slices_, params.stacks_);
696  result->setOrientInside(params.orientInside_);
697  result->setFlipTexture(params.flipTexture_);
698  return result;
699  }
700 
713  {
714  StelGeometrySphere* result =
715  new StelGeometrySphere(StelGeometrySphere::SphereType_Unlit);
716  result->setRadius(params.radius_);
718  result->setResolution(params.slices_, params.stacks_);
719  result->setOrientInside(params.orientInside_);
720  result->setFlipTexture(params.flipTexture_);
721  return result;
722  }
723 
737  {
738  StelGeometrySphere* result =
739  new StelGeometrySphere(StelGeometrySphere::SphereType_Lit, -100.0f, light);
740  result->setRadius(params.radius_);
742  result->setResolution(params.slices_, params.stacks_);
743  result->setOrientInside(params.orientInside_);
744  result->setFlipTexture(params.flipTexture_);
745  return result;
746  }
747 
765  (const RingParams& params, const Vec3f offset = Vec3f(0.0f, 0.0f, 0.0f))
766  {
767  StelGeometryRing* result = new StelGeometryRing(StelGeometryRing::RingType_Textured);
768  result->setInnerOuterRadius(params.innerRadius_, params.outerRadius_);
769  result->setResolution(params.slices_, params.loops_);
770  result->setFlipFaces(params.flipFaces_);
771  result->setOffset(offset);
772  return result;
773  }
774 
792  (const RingParams& params, const Vec2f offset = Vec2f(0.0f, 0.0f))
793  {
794  StelGeometryRing* result = new StelGeometryRing(StelGeometryRing::RingType_Plain2D);
795  result->setInnerOuterRadius(params.innerRadius_, params.outerRadius_);
796  result->setResolution(params.slices_, params.loops_);
797  result->setFlipFaces(params.flipFaces_);
798  result->setOffset(Vec3f(offset[0], offset[1], 0.0f));
799  return result;
800  }
801 };
802 #endif // _STELGEOMETRYBUILDER_HPP_