ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PCV.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "PCV.h"
9 
10 #include "PCVContext.h"
11 
12 // Qt
13 #include <QString>
14 
15 #ifdef USE_VLD
16 // VLD
17 #include <vld.h>
18 #endif
19 
20 // System
21 #include <assert.h>
22 #include <string.h>
23 
24 #include <algorithm>
25 #include <vector>
26 
27 using namespace CVLib;
28 
29 static int gcd(int num1, int num2) {
30  int remainder = (num2 % num1);
31 
32  return (remainder != 0 ? gcd(remainder, num1) : num1);
33 }
34 
36 
47 static bool SampleSphere(unsigned N, std::vector<CCVector3>& dirs) {
48  static const double c_eps = 2.2204e-16;
49  static const double c_twist = 4.0;
50 
51  if (N == 0) {
52  assert(false);
53  return false;
54  }
55 
56  try {
57  dirs.resize(N, CCVector3(0, 0, 1));
58  } catch (const std::bad_alloc&) {
59  // not enough memory
60  return false;
61  }
62 
63  if (N == 1) {
64  return true;
65  }
66 
67  try {
68  double area = (4 * M_PI) / N;
69  double beta = acos(1.0 - 2.0 / N); // return in [0,pi/2] as '1-2/N'
70  // goes from 0 to 1 when N --> inf
71  double gamma = M_PI - 2 * beta; // in [0,pi]
72  double fuzz = c_eps * 2 * N;
73 
74  int Ltemp = static_cast<int>(ceil(gamma / sqrt(area) - fuzz));
75  int L = 2 + std::max(Ltemp, 1);
76 
77  // init mbar
78  std::vector<double> mbar;
79  mbar.resize(L, 0);
80  assert(L >= 3);
81  {
82  mbar[0] = 1.0;
83  double theta = gamma / (L - 2);
84  for (int i = 1; i < L - 1; ++i) {
85  mbar[i] =
86  N *
87  (cos(theta * (i - 1) + beta) - cos(theta * i + beta)) /
88  2;
89  }
90  mbar[L - 1] = 1.0;
91  }
92 
93  // init m
94  std::vector<int> m;
95  m.resize(L, 0);
96  {
97  m[0] = 1;
98 
99  double alpha = 0.0;
100  for (int i = 1; i < L; ++i) {
101  double f = floor(mbar[i] + alpha + fuzz);
102  if ((mbar[i] - f) >= 0.5) f = ceil(mbar[i] + alpha - fuzz);
103  m[i] = static_cast<int>(f);
104 
105  alpha += mbar[i] - m[i];
106  }
107  }
108 
109  // now we can build the rays
110  {
111  std::vector<double> offset;
112  offset.resize(L - 1, 0);
113 
114  double z = 1.0 - static_cast<double>(2 + m[1]) / N;
115 
116  unsigned int rayIndex = 1; // the first one is already (0,0,1)
117  for (int i = 1; i < L - 1; ++i) {
118  if (m[i - 1] != 0 && m[i] != 0) {
119  offset[i] = offset[i - 1] +
120  static_cast<double>(gcd(m[i], m[i - 1])) /
121  (2 * m[i] * m[i - 1]) +
122  std::min<double>(c_twist,
123  floor(m[i - 1] / c_twist)) /
124  m[i - 1];
125  } else {
126  offset[i] = 0.0;
127  }
128 
129  double temp = static_cast<double>(m[i]) / N;
130 
131  double h = cos((acos(z + temp) + acos(z - temp)) / 2);
132  double r = sqrt(1.0 - h * h);
133 
134  for (int j = 0; j < m[i]; ++j) {
135  double theta = 2.0 * M_PI *
136  (offset[i] + static_cast<double>(j) / m[i]);
137 
138  dirs[rayIndex++] = CCVector3(
139  static_cast<PointCoordinateType>(r * cos(theta)),
140  static_cast<PointCoordinateType>(r * sin(theta)),
141  static_cast<PointCoordinateType>(h));
142  }
143 
144  z -= static_cast<double>(m[i] + m[i + 1]) / N;
145  }
146 
147  assert(rayIndex + 1 == N);
148  }
149  } catch (const std::bad_alloc&) {
150  // not enough memory
151  return false;
152  }
153 
154  dirs[N - 1] = CCVector3(0, 0, -1);
155 
156  return true;
157 }
158 
159 bool PCV::GenerateRays(unsigned numberOfRays,
160  std::vector<CCVector3>& rays,
161  bool mode360 /*=true*/) {
162  // generates light directions
163  unsigned rayCount = numberOfRays * (mode360 ? 1 : 2);
164  if (!SampleSphere(rayCount, rays)) {
165  return false;
166  }
167 
168  // we keep only the light directions that meets input parameters (non
169  // predictible if not in 360?mode!)
170  if (!mode360) {
171  unsigned lastIndex = 0;
172  for (size_t i = 0; i < rays.size(); ++i) {
173  if (rays[i].z < 0) {
174  if (lastIndex != i) {
175  rays[lastIndex] = rays[i];
176  }
177  ++lastIndex;
178  }
179  }
180  rayCount = lastIndex;
181  rays.resize(rayCount);
182  }
183 
184  return true;
185 }
186 
187 int PCV::Launch(unsigned numberOfRays,
188  GenericCloud* vertices,
189  GenericMesh* mesh /*=0*/,
190  bool meshIsClosed /*=false*/,
191  bool mode360 /*=true*/,
192  unsigned width /*=1024*/,
193  unsigned height /*=1024*/,
194  CVLib::GenericProgressCallback* progressCb /*=0*/,
195  QString entityName /*=QString()*/) {
196  // generates light directions
197  std::vector<CCVector3> rays;
198  if (!GenerateRays(numberOfRays, rays, mode360)) {
199  return -2;
200  }
201 
202  if (!Launch(rays, vertices, mesh, meshIsClosed, width, height, progressCb,
203  entityName)) {
204  return -1;
205  }
206 
207  return static_cast<int>(rays.size());
208 }
209 
210 bool PCV::Launch(std::vector<CCVector3>& rays,
211  CVLib::GenericCloud* vertices,
212  CVLib::GenericMesh* mesh /*=0*/,
213  bool meshIsClosed /*=false*/,
214  unsigned width /*=1024*/,
215  unsigned height /*=1024*/,
216  CVLib::GenericProgressCallback* progressCb /*=0*/,
217  QString entityName /*=QString()*/) {
218  if (rays.empty()) return false;
219 
220  if (!vertices || !vertices->enableScalarField()) return false;
221 
222  // vertices/points
223  unsigned numberOfPoints = vertices->size();
224  // rays
225  unsigned numberOfRays = static_cast<unsigned>(rays.size());
226 
227  // for each vertex we keep count of the number of light directions for which
228  // it is "illuminated"
229  std::vector<int> visibilityCount;
230  try {
231  visibilityCount.resize(numberOfPoints, 0);
232  } catch (const std::bad_alloc&) {
233  // not enough memory?
234  return false;
235  }
236 
237  /*** Main illumination loop ***/
238 
239  CVLib::NormalizedProgress nProgress(progressCb, numberOfRays);
240  if (progressCb) {
241  if (progressCb->textCanBeEdited()) {
242  progressCb->setMethodTitle("ShadeVis");
243  QString infoStr;
244  if (!entityName.isEmpty()) infoStr = entityName + "\n";
245  infoStr.append(QString("Rays: %1").arg(numberOfRays));
246  if (mesh)
247  infoStr.append(QString("\nFaces: %1").arg(mesh->size()));
248  else
249  infoStr.append(QString("\nVertices: %1").arg(numberOfPoints));
250  progressCb->setInfo(qPrintable(infoStr));
251  }
252  progressCb->update(0);
253  progressCb->start();
254  }
255 
256  bool success = true;
257 
258  // must be done after progress dialog display!
259  PCVContext win;
260  if (win.init(width, height, vertices, mesh, meshIsClosed)) {
261  for (unsigned i = 0; i < numberOfRays; ++i) {
262  // set current 'light' direction
263  win.setViewDirection(rays[i]);
264 
265  // flag viewed vertices
266  win.GLAccumPixel(visibilityCount);
267 
268  if (progressCb && !nProgress.oneStep()) {
269  success = false;
270  break;
271  }
272  }
273 
274  if (success) {
275  // we convert per-vertex accumulators to an 'intensity' scalar field
276  for (unsigned j = 0; j < numberOfPoints; ++j) {
277  ScalarType visValue =
278  static_cast<ScalarType>(visibilityCount[j]) /
279  numberOfRays;
280  vertices->setPointScalarValue(j, visValue);
281  }
282  }
283  } else {
284  success = false;
285  }
286 
287  return success;
288 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int width
int height
int offset
static int gcd(int num1, int num2)
Definition: PCV.cpp:29
static bool SampleSphere(unsigned N, std::vector< CCVector3 > &dirs)
Sample points on the unit sphere.
Definition: PCV.cpp:47
PCV (Portion de Ciel Visible / Ambiant Illumination) OpenGL context.
Definition: PCVContext.h:22
bool init(unsigned W, unsigned H, cloudViewer::GenericCloud *cloud, cloudViewer::GenericMesh *mesh=0, bool closedMesh=true)
Initialization.
Definition: PCVContext.cpp:69
void setViewDirection(const CCVector3 &V)
Set the viewing directions.
Definition: PCVContext.cpp:166
int GLAccumPixel(std::vector< int > &visibilityCount)
Definition: PCVContext.cpp:242
static bool GenerateRays(unsigned numberOfRays, std::vector< CCVector3 > &rays, bool mode360=true)
Generates a given number of rays.
Definition: PCV.cpp:159
static int Launch(unsigned numberOfRays, cloudViewer::GenericCloud *vertices, cloudViewer::GenericMesh *mesh=nullptr, bool meshIsClosed=false, bool mode360=true, unsigned width=1024, unsigned height=1024, cloudViewer::GenericProgressCallback *progressCb=nullptr, QString entityName=QString())
bool oneStep()
Increments total progress value of a single unit.
int max(int a, int b)
Definition: cutil_math.h:48
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
cloudViewer::NormalizedProgress * nProgress