Panda3D
Loading...
Searching...
No Matches
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"
21#include "internalName.h"
22#include "luse.h"
23#include "cmath.h"
24#include "mathNumbers.h"
26#include "displayRegion.h"
27
28/**
29 * Resets all the parameters to their initial defaults.
30 */
32reset() {
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 */
45set_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 */
54PT(PandaNode) FisheyeMaker::
55generate() {
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 */
333void FisheyeMaker::
334add_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 */
365void FisheyeMaker::
366add_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}
void set_fov(PN_stdfloat fov)
Specifies the field of view of the fisheye projection.
void set_reflection(bool reflection)
Sets the flag indicating whether the texture image should be mirrored (true) or normal (false).
void reset()
Resets all the parameters to their initial defaults.
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Defines a series of triangle strips.
This describes the structure of a single array within a Geom data.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
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.
A container for geometry primitives.
Definition geom.h:54
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.