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