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  */
31 void FisheyeMaker::
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  */
44 void FisheyeMaker::
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Defines a series of triangle strips.
Definition: geomTristrips.h:23
void set_fov(PN_stdfloat fov)
Specifies the field of view of the fisheye projection.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
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.
void reset()
Resets all the parameters to their initial defaults.
This class defines the physical layout of the vertex data stored within a Geom.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_reflection(bool reflection)
Sets the flag indicating whether the texture image should be mirrored (true) or normal (false).
Definition: fisheyeMaker.I:65
This describes the structure of a single array within a Geom data.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(PandaNode) FisheyeMaker
Generates a GeomNode that renders the specified geometry.