Panda3D
fisheyeMaker.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file fisheyeMaker.cxx
10  * @author drose
11  * @date 2005-10-03
12  */
13 
14 #include "fisheyeMaker.h"
15 #include "geomNode.h"
16 #include "geom.h"
17 #include "geomTristrips.h"
18 #include "geomVertexWriter.h"
19 #include "geomVertexFormat.h"
20 #include "geomVertexArrayFormat.h"
21 #include "internalName.h"
22 #include "luse.h"
23 #include "cmath.h"
24 #include "mathNumbers.h"
25 #include "graphicsStateGuardian.h"
26 #include "displayRegion.h"
27 
28 /**
29  * Resets all the parameters to their initial defaults.
30  */
32 reset() {
33  set_fov(360.0);
34  _num_vertices = 1000;
35  _square_inscribed = false;
36  _square_radius = 1.0f;
37  set_reflection(false);
38 }
39 
40 /**
41  * Specifies the field of view of the fisheye projection. A sphere map will
42  * have a 360-degree field of view (and this is the default).
43  */
45 set_fov(PN_stdfloat fov) {
46  _fov = fov;
47  _half_fov_rad = deg_2_rad(_fov * 0.5f);
48 }
49 
50 
51 /**
52  * Generates a GeomNode that renders the specified geometry.
53  */
54 PT(PandaNode) FisheyeMaker::
55 generate() {
56  // Get some system-imposed limits.
57  int max_vertices_per_array = 100;
58  int max_vertices_per_primitive = 10000;
59  bool prefers_triangle_strips = true;
60 
61  /*
62  GraphicsStateGuardian *global_gsg = GraphicsStateGuardian::get_global_gsg();
63  if (global_gsg != (GraphicsStateGuardian *)NULL) {
64  max_vertices_per_array = global_gsg->get_max_vertices_per_array();
65  max_vertices_per_primitive = global_gsg->get_max_vertices_per_primitive();
66  prefers_triangle_strips = global_gsg->prefers_triangle_strips();
67  }
68  */
69 
70  // We will generate a rose of radius 1, with vertices approximately evenly
71  // distributed throughout.
72 
73  // Since we will have _num_vertices filling the circle, and the area of a
74  // circle of radius 1 is A = pi*r^2 = pi, it follows that the number of
75  // vertices per square unit is (_num_vertices pi), and thus the number of
76  // vertices per linear unit is the square root of that.
77  PN_stdfloat vertices_per_unit = csqrt(_num_vertices / MathNumbers::pi_f);
78  PN_stdfloat two_pi = 2.0f * MathNumbers::pi_f;
79 
80  // The rose will be made up of concentric rings, originating from the
81  // center, to a radius of 1.0.
82  int num_rings = (int)floor(vertices_per_unit + 0.5f);
83 
84  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
86  (InternalName::get_vertex(), 3,
87  Geom::NT_stdfloat, Geom::C_point,
88  InternalName::get_texcoord(), 3,
89  Geom::NT_stdfloat, Geom::C_texcoord));
90 
91  PT(GeomVertexData) vdata =
92  new GeomVertexData(get_name(), format, Geom::UH_static);
93  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
94  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
95 
96  PT(Geom) geom = new Geom(vdata);
97  PT(GeomPrimitive) tristrips = new GeomTristrips(Geom::UH_static);
98  tristrips->set_shade_model(Geom::SM_uniform);
99 
100  PT(GeomNode) geom_node = new GeomNode(get_name());
101 
102  int last_ring_size = 3;
103  int last_ring_vertex = 0;
104  PN_stdfloat last_r = 1.0f / (PN_stdfloat)num_rings;
105 
106  // Make the first triangle. We actually make a one-triangle strip, but that
107  // seems more sensible than making a single isolated triangle.
108  for (int vi = 0; vi < last_ring_size; ++vi) {
109  add_vertex(vertex, texcoord, last_r,
110  two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size);
111  tristrips->add_vertex(vi);
112  }
113  // Actually, we need to add one more degenerate triangle to make it an even-
114  // length tristrip.
115  tristrips->add_vertex(2);
116  tristrips->close_primitive();
117 
118  // Now make all of the rings.
119  for (int ri = 1; ri < num_rings; ++ri) {
120  PN_stdfloat r = (PN_stdfloat)(ri + 1) / (PN_stdfloat)num_rings;
121 
122  // The circumference of a ring of radius r is 2*pi*r.
123  PN_stdfloat c = two_pi * r;
124  int ring_size = (int)floor(c * vertices_per_unit + 0.5f);
125 
126  // Each ring must either have exactly the same number of vertices as the
127  // previous ring, or exactly double.
128  if (ring_size < last_ring_size * 2) {
129  // This one will be the same.
130  ring_size = last_ring_size;
131  } else {
132  // This one will be double.
133  ring_size = last_ring_size * 2;
134  }
135 
136  if (vdata->get_num_rows() + ring_size > max_vertices_per_array) {
137  // Too many vertices; we need to start a new VertexData.
138  if (tristrips->get_num_vertices() != 0) {
139  geom->add_primitive(tristrips);
140  }
141  if (geom->get_num_primitives() != 0) {
142  if (prefers_triangle_strips) {
143  geom_node->add_geom(geom);
144  } else {
145  geom_node->add_geom(geom->decompose());
146  }
147  }
148 
149  vdata = new GeomVertexData(get_name(), format, Geom::UH_static);
150  vertex = GeomVertexWriter(vdata, InternalName::get_vertex());
151  texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord());
152  geom = new Geom(vdata);
153  tristrips = new GeomTristrips(Geom::UH_static);
154  tristrips->set_shade_model(Geom::SM_uniform);
155 
156  // Now we need to re-make the previous ring in this VertexData.
157  last_ring_vertex = 0;
158  for (int vi = 0; vi < last_ring_size; ++vi) {
159  add_vertex(vertex, texcoord, last_r,
160  two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size);
161  }
162  }
163 
164  // Now make this ring.
165  int ring_vertex = vdata->get_num_rows();
166  for (int vi = 0; vi < ring_size; ++vi) {
167  add_vertex(vertex, texcoord, r,
168  two_pi * (PN_stdfloat)vi / (PN_stdfloat)ring_size);
169  }
170 
171  // Now draw the triangle strip to connect the rings.
172  if (ring_size == last_ring_size) {
173  // Exactly the same size ring. This one is easy.
174  if ((ring_size + 1) * 2 > max_vertices_per_primitive) {
175  // Actually, we need to subdivide the ring to fit within the GSG's
176  // advertised limits.
177  int piece_size = max_vertices_per_primitive / 2 - 1;
178  int vi = 0;
179  while (vi < ring_size) {
180  int piece_end = std::min(ring_size + 1, piece_size + 1 + vi);
181  for (int pi = vi; pi < piece_end; ++pi) {
182  tristrips->add_vertex(last_ring_vertex + pi % last_ring_size);
183  tristrips->add_vertex(ring_vertex + pi % ring_size);
184  }
185  tristrips->close_primitive();
186  vi += piece_size;
187  }
188 
189  } else {
190  // We can fit the entire ring.
191  if (tristrips->get_num_vertices() > 0 &&
192  tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) {
193  geom->add_primitive(tristrips);
194  tristrips = new GeomTristrips(Geom::UH_static);
195  tristrips->set_shade_model(Geom::SM_uniform);
196  }
197  for (int vi = 0; vi < ring_size; ++vi) {
198  tristrips->add_vertex(last_ring_vertex + vi);
199  tristrips->add_vertex(ring_vertex + vi);
200  }
201  tristrips->add_vertex(last_ring_vertex);
202  tristrips->add_vertex(ring_vertex);
203  tristrips->close_primitive();
204  }
205 
206  } else {
207  // Exactly double size ring. This is harder; we can't make a single
208  // tristrip that goes all the way around the ring. Instead, we'll make
209  // an alternating series of four-triangle strips and two-triangle strips
210  // around the ring.
211  int vi = 0;
212  while (vi < last_ring_size) {
213  if (tristrips->get_num_vertices() + 10 > max_vertices_per_primitive) {
214  geom->add_primitive(tristrips);
215  tristrips = new GeomTristrips(Geom::UH_static);
216  tristrips->set_shade_model(Geom::SM_uniform);
217  }
218  tristrips->add_vertex(ring_vertex + (vi * 2 + 1) % ring_size);
219  tristrips->add_vertex(ring_vertex + (vi * 2 + 2) % ring_size);
220  tristrips->add_vertex(last_ring_vertex + (vi + 1) % last_ring_size);
221  tristrips->add_vertex(ring_vertex + (vi * 2 + 3) % ring_size);
222  tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size);
223  tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size);
224  tristrips->close_primitive();
225 
226  tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size);
227  tristrips->add_vertex(ring_vertex + (vi * 2 + 5) % ring_size);
228  tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size);
229  tristrips->add_vertex(last_ring_vertex + (vi + 3) % last_ring_size);
230  tristrips->close_primitive();
231 
232  vi += 2;
233  }
234  }
235 
236  last_ring_size = ring_size;
237  last_ring_vertex = ring_vertex;
238  last_r = r;
239  }
240 
241  if (_square_inscribed) {
242  // Make one more "ring", which extends out to the edges of a squre.
243  int ring_size = last_ring_size;
244 
245  if (vdata->get_num_rows() + ring_size > max_vertices_per_array) {
246  // Too many vertices; we need to start a new VertexData.
247  if (tristrips->get_num_vertices() != 0) {
248  geom->add_primitive(tristrips);
249  }
250  if (geom->get_num_primitives() != 0) {
251  if (prefers_triangle_strips) {
252  geom_node->add_geom(geom);
253  } else {
254  geom_node->add_geom(geom->decompose());
255  }
256  }
257 
258  vdata = new GeomVertexData(get_name(), format, Geom::UH_static);
259  vertex = GeomVertexWriter(vdata, InternalName::get_vertex());
260  texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord());
261  geom = new Geom(vdata);
262  tristrips = new GeomTristrips(Geom::UH_static);
263  tristrips->set_shade_model(Geom::SM_uniform);
264 
265  // Now we need to re-make the previous ring in this VertexData.
266  last_ring_vertex = 0;
267  for (int vi = 0; vi < last_ring_size; ++vi) {
268  add_vertex(vertex, texcoord, last_r,
269  two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size);
270  }
271  }
272 
273  // Now make this ring.
274  int ring_vertex = vdata->get_num_rows();
275  for (int vi = 0; vi < ring_size; ++vi) {
276  add_square_vertex(vertex, texcoord,
277  two_pi * (PN_stdfloat)vi / (PN_stdfloat)ring_size);
278  }
279 
280  // Now draw the triangle strip to connect the rings.
281  if ((ring_size + 1) * 2 > max_vertices_per_primitive) {
282  // Actually, we need to subdivide the ring to fit within the GSG's
283  // advertised limits.
284  int piece_size = max_vertices_per_primitive / 2 - 1;
285  int vi = 0;
286  while (vi < ring_size) {
287  int piece_end = std::min(ring_size + 1, piece_size + 1 + vi);
288  for (int pi = vi; pi < piece_end; ++pi) {
289  tristrips->add_vertex(last_ring_vertex + pi % last_ring_size);
290  tristrips->add_vertex(ring_vertex + pi % ring_size);
291  }
292  tristrips->close_primitive();
293  vi += piece_size;
294  }
295 
296  } else {
297  // We can fit the entire ring.
298  if (tristrips->get_num_vertices() > 0 &&
299  tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) {
300  geom->add_primitive(tristrips);
301  tristrips = new GeomTristrips(Geom::UH_static);
302  tristrips->set_shade_model(Geom::SM_uniform);
303  }
304  for (int vi = 0; vi < ring_size; ++vi) {
305  tristrips->add_vertex(last_ring_vertex + vi);
306  tristrips->add_vertex(ring_vertex + vi);
307  }
308  tristrips->add_vertex(last_ring_vertex);
309  tristrips->add_vertex(ring_vertex);
310  tristrips->close_primitive();
311  }
312  }
313 
314  if (tristrips->get_num_vertices() != 0) {
315  geom->add_primitive(tristrips);
316  }
317  if (geom->get_num_primitives() != 0) {
318  if (prefers_triangle_strips) {
319  geom_node->add_geom(geom);
320  } else {
321  geom_node->add_geom(geom->decompose());
322  }
323  }
324 
325  return geom_node;
326 }
327 
328 /**
329  * Given a point defined by a radius and an angle in radians, compute the 2-d
330  * coordinates for the vertex as well as the 3-d texture coordinates, and add
331  * both to the VertexData.
332  */
333 void FisheyeMaker::
334 add_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
335  PN_stdfloat r, PN_stdfloat a) {
336  PN_stdfloat sina, cosa;
337  csincos(a, &sina, &cosa);
338 
339  // The 2-d point is just a point r units from the center of the circle.
340  LPoint3 point(r * cosa, 0.0f, r * sina);
341  vertex.add_data3(point);
342 
343  // The 3-d point is the same thing, bent through the third dimension around
344  // the surface of a sphere to the point in the back.
345  PN_stdfloat b = r * _half_fov_rad;
346  if (b >= MathNumbers::pi_f) {
347  // Special case: we want to stop at the back pole, not continue around it.
348  texcoord.add_data3(0, _reflect, 0);
349 
350  } else {
351  PN_stdfloat sinb, cosb;
352  csincos(b, &sinb, &cosb);
353  LPoint3 tc(sinb * cosa, cosb * _reflect, sinb * sina);
354  texcoord.add_data3(tc);
355  }
356 }
357 
358 /**
359  * Similar to add_vertex(), but it draws the vertex all the way out to the
360  * edge of the square we are inscribed within, and the texture coordinate is
361  * always the back pole.
362  *
363  * This is just for the purpose of drawing the inscribing square.
364  */
365 void FisheyeMaker::
366 add_square_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
367  PN_stdfloat a) {
368  PN_stdfloat sina, cosa;
369  csincos(a, &sina, &cosa);
370 
371  // Extend the 2-d point to the edge of the square of the indicated size.
372  if (cabs(sina) > cabs(cosa)) {
373  PN_stdfloat y = (sina > 0.0f) ? _square_radius : -_square_radius;
374  PN_stdfloat x = y * cosa / sina;
375  LPoint3 point(x, 0.0f, y);
376  vertex.add_data3(point);
377 
378  } else {
379  PN_stdfloat x = (cosa > 0.0f) ? _square_radius : -_square_radius;
380  PN_stdfloat y = x * sina / cosa;
381  LPoint3 point(x, 0.0f, y);
382  vertex.add_data3(point);
383  }
384 
385  texcoord.add_data3(0, _reflect, 0);
386 }
Geom
A container for geometry primitives.
Definition: geom.h:54
geomVertexWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
internalName.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
GeomVertexWriter::add_data3
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row.
Definition: geomVertexWriter.I:1132
GeomVertexWriter
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
Definition: geomVertexWriter.h:55
geomVertexArrayFormat.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cmath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomNode
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
displayRegion.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
FisheyeMaker::set_fov
void set_fov(PN_stdfloat fov)
Specifies the field of view of the fisheye projection.
Definition: fisheyeMaker.cxx:45
luse.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
fisheyeMaker.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT
PT(PandaNode) FisheyeMaker
Generates a GeomNode that renders the specified geometry.
Definition: fisheyeMaker.cxx:54
GeomVertexFormat
This class defines the physical layout of the vertex data stored within a Geom.
Definition: geomVertexFormat.h:55
FisheyeMaker::reset
void reset()
Resets all the parameters to their initial defaults.
Definition: fisheyeMaker.cxx:32
GeomTristrips
Defines a series of triangle strips.
Definition: geomTristrips.h:23
geomTristrips.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
FisheyeMaker::set_reflection
void set_reflection(bool reflection)
Sets the flag indicating whether the texture image should be mirrored (true) or normal (false).
Definition: fisheyeMaker.I:65
geomNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomVertexFormat.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PandaNode
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
GeomVertexArrayFormat
This describes the structure of a single array within a Geom data.
Definition: geomVertexArrayFormat.h:47
graphicsStateGuardian.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomPrimitive
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
mathNumbers.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.