Panda3D
 All Classes Functions Variables Enumerations
fisheyeMaker.cxx
00001 // Filename: fisheyeMaker.cxx
00002 // Created by:  drose (3Oct05)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "fisheyeMaker.h"
00016 #include "geomNode.h"
00017 #include "geom.h"
00018 #include "geomTristrips.h"
00019 #include "geomVertexWriter.h"
00020 #include "geomVertexFormat.h"
00021 #include "geomVertexArrayFormat.h"
00022 #include "internalName.h"
00023 #include "luse.h"
00024 #include "cmath.h"
00025 #include "mathNumbers.h"
00026 #include "graphicsStateGuardian.h"
00027 #include "displayRegion.h"
00028 
00029 ////////////////////////////////////////////////////////////////////
00030 //     Function: FisheyeMaker::reset
00031 //       Access: Public
00032 //  Description: Resets all the parameters to their initial defaults.
00033 ////////////////////////////////////////////////////////////////////
00034 void FisheyeMaker::
00035 reset() {
00036   set_fov(360.0);
00037   _num_vertices = 1000;
00038   _square_inscribed = false;
00039   _square_radius = 1.0f;
00040   set_reflection(false);
00041 }
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: FisheyeMaker::set_fov
00045 //       Access: Public
00046 //  Description: Specifies the field of view of the fisheye
00047 //               projection.  A sphere map will have a 360-degree
00048 //               field of view (and this is the default).
00049 ////////////////////////////////////////////////////////////////////
00050 void FisheyeMaker::
00051 set_fov(PN_stdfloat fov) {
00052   _fov = fov;
00053   _half_fov_rad =  deg_2_rad(_fov * 0.5f);
00054 }
00055 
00056 
00057 ////////////////////////////////////////////////////////////////////
00058 //     Function: FisheyeMaker::generate
00059 //       Access: Public
00060 //  Description: Generates a GeomNode that renders the specified
00061 //               geometry.
00062 ////////////////////////////////////////////////////////////////////
00063 PT(PandaNode) FisheyeMaker::
00064 generate() {
00065   // Get some system-imposed limits.
00066   int max_vertices_per_array = 100;
00067   int max_vertices_per_primitive = 10000;
00068   bool prefers_triangle_strips = true;
00069 
00070   /*
00071   GraphicsStateGuardian *global_gsg = GraphicsStateGuardian::get_global_gsg();
00072   if (global_gsg != (GraphicsStateGuardian *)NULL) {
00073     max_vertices_per_array = global_gsg->get_max_vertices_per_array();
00074     max_vertices_per_primitive = global_gsg->get_max_vertices_per_primitive();
00075     prefers_triangle_strips = global_gsg->prefers_triangle_strips();
00076   }
00077   */
00078 
00079   // We will generate a rose of radius 1, with vertices approximately
00080   // evenly distributed throughout.
00081 
00082   // Since we will have _num_vertices filling the circle, and the area
00083   // of a circle of radius 1 is A = pi*r^2 = pi, it follows that the
00084   // number of vertices per square unit is (_num_vertices / pi), and
00085   // thus the number of vertices per linear unit is the square root of
00086   // that.
00087   PN_stdfloat vertices_per_unit = csqrt(_num_vertices / MathNumbers::pi_f);
00088   PN_stdfloat two_pi = 2.0f * MathNumbers::pi_f;
00089 
00090   // The rose will be made up of concentric rings, originating from
00091   // the center, to a radius of 1.0.
00092   int num_rings = (int)floor(vertices_per_unit + 0.5f);
00093 
00094   CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
00095     (new GeomVertexArrayFormat
00096      (InternalName::get_vertex(), 3, 
00097       Geom::NT_stdfloat, Geom::C_point,
00098       InternalName::get_texcoord(), 3,
00099       Geom::NT_stdfloat, Geom::C_texcoord));
00100 
00101   PT(GeomVertexData) vdata = 
00102     new GeomVertexData(get_name(), format, Geom::UH_static);
00103   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00104   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00105 
00106   PT(Geom) geom = new Geom(vdata);
00107   PT(GeomPrimitive) tristrips = new GeomTristrips(Geom::UH_static);
00108   tristrips->set_shade_model(Geom::SM_uniform);
00109 
00110   PT(GeomNode) geom_node = new GeomNode(get_name());
00111 
00112   int last_ring_size = 3;
00113   int last_ring_vertex = 0;
00114   PN_stdfloat last_r = 1.0f / (PN_stdfloat)num_rings;
00115 
00116   // Make the first triangle.  We actually make a one-triangle strip,
00117   // but that seems more sensible than making a single isolated
00118   // triangle.
00119   for (int vi = 0; vi < last_ring_size; ++vi) {
00120     add_vertex(vertex, texcoord, last_r, 
00121                two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size);
00122     tristrips->add_vertex(vi);
00123   }
00124   // Actually, we need to add one more degenerate triangle to make it
00125   // an even-length tristrip.
00126   tristrips->add_vertex(2);
00127   tristrips->close_primitive();
00128 
00129   // Now make all of the rings.
00130   for (int ri = 1; ri < num_rings; ++ri) {
00131     PN_stdfloat r = (PN_stdfloat)(ri + 1) / (PN_stdfloat)num_rings;
00132     
00133     // The circumference of a ring of radius r is 2*pi*r.
00134     PN_stdfloat c = two_pi * r;
00135     int ring_size = (int)floor(c * vertices_per_unit + 0.5f);
00136     
00137     // Each ring must either have exactly the same number of vertices
00138     // as the previous ring, or exactly double.
00139     if (ring_size < last_ring_size * 2) {
00140       // This one will be the same.
00141       ring_size = last_ring_size;
00142     } else {
00143       // This one will be double.
00144       ring_size = last_ring_size * 2;
00145     }
00146 
00147     if (vdata->get_num_rows() + ring_size > max_vertices_per_array) {
00148       // Too many vertices; we need to start a new VertexData.
00149       if (tristrips->get_num_vertices() != 0) {
00150         geom->add_primitive(tristrips);
00151       }
00152       if (geom->get_num_primitives() != 0) {
00153         if (prefers_triangle_strips) {
00154           geom_node->add_geom(geom);
00155         } else {
00156           geom_node->add_geom(geom->decompose());
00157         }
00158       }
00159 
00160       vdata = new GeomVertexData(get_name(), format, Geom::UH_static);
00161       vertex = GeomVertexWriter(vdata, InternalName::get_vertex());
00162       texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord());
00163       geom = new Geom(vdata);
00164       tristrips = new GeomTristrips(Geom::UH_static);
00165       tristrips->set_shade_model(Geom::SM_uniform);
00166 
00167       // Now we need to re-make the previous ring in this VertexData.
00168       last_ring_vertex = 0;
00169       for (int vi = 0; vi < last_ring_size; ++vi) {
00170         add_vertex(vertex, texcoord, last_r, 
00171                    two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size);
00172       }
00173     }
00174 
00175     // Now make this ring.
00176     int ring_vertex = vdata->get_num_rows();
00177     for (int vi = 0; vi < ring_size; ++vi) {
00178       add_vertex(vertex, texcoord, r, 
00179                  two_pi * (PN_stdfloat)vi / (PN_stdfloat)ring_size);
00180     }
00181 
00182     // Now draw the triangle strip to connect the rings.
00183     if (ring_size == last_ring_size) {
00184       // Exactly the same size ring.  This one is easy.
00185       if ((ring_size + 1) * 2 > max_vertices_per_primitive) {
00186         // Actually, we need to subdivide the ring to fit within the
00187         // GSG's advertised limits.
00188         int piece_size = max_vertices_per_primitive / 2 - 1;
00189         int vi = 0;
00190         while (vi < ring_size) {
00191           int piece_end = min(ring_size + 1, piece_size + 1 + vi);
00192           for (int pi = vi; pi < piece_end; ++pi) {
00193             tristrips->add_vertex(last_ring_vertex + pi % last_ring_size);
00194             tristrips->add_vertex(ring_vertex + pi % ring_size);
00195           }
00196           tristrips->close_primitive();
00197           vi += piece_size;
00198         }
00199 
00200       } else {
00201         // We can fit the entire ring.
00202         if (tristrips->get_num_vertices() > 0 &&
00203             tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) {
00204           geom->add_primitive(tristrips);
00205           tristrips = new GeomTristrips(Geom::UH_static);
00206           tristrips->set_shade_model(Geom::SM_uniform);
00207         }
00208         for (int vi = 0; vi < ring_size; ++vi) {
00209           tristrips->add_vertex(last_ring_vertex + vi);
00210           tristrips->add_vertex(ring_vertex + vi);
00211         }
00212         tristrips->add_vertex(last_ring_vertex);
00213         tristrips->add_vertex(ring_vertex);
00214         tristrips->close_primitive();
00215       }
00216 
00217     } else {
00218       // Exactly double size ring.  This is harder; we can't make a
00219       // single tristrip that goes all the way around the ring.
00220       // Instead, we'll make an alternating series of four-triangle
00221       // strips and two-triangle strips around the ring.
00222       int vi = 0;
00223       while (vi < last_ring_size) {
00224         if (tristrips->get_num_vertices() + 10 > max_vertices_per_primitive) {
00225           geom->add_primitive(tristrips);
00226           tristrips = new GeomTristrips(Geom::UH_static);
00227           tristrips->set_shade_model(Geom::SM_uniform);
00228         }
00229         tristrips->add_vertex(ring_vertex + (vi * 2 + 1) % ring_size);
00230         tristrips->add_vertex(ring_vertex + (vi * 2 + 2) % ring_size);
00231         tristrips->add_vertex(last_ring_vertex + (vi + 1) % last_ring_size);
00232         tristrips->add_vertex(ring_vertex + (vi * 2 + 3) % ring_size);
00233         tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size);
00234         tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size);
00235         tristrips->close_primitive();
00236 
00237         tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size);
00238         tristrips->add_vertex(ring_vertex + (vi * 2 + 5) % ring_size);
00239         tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size);
00240         tristrips->add_vertex(last_ring_vertex + (vi + 3) % last_ring_size);
00241         tristrips->close_primitive();
00242 
00243         vi += 2;
00244       }
00245     }
00246 
00247     last_ring_size = ring_size;
00248     last_ring_vertex = ring_vertex;
00249     last_r = r;
00250   }
00251 
00252   if (_square_inscribed) {
00253     // Make one more "ring", which extends out to the edges of a squre.
00254     int ring_size = last_ring_size;
00255 
00256     if (vdata->get_num_rows() + ring_size > max_vertices_per_array) {
00257       // Too many vertices; we need to start a new VertexData.
00258       if (tristrips->get_num_vertices() != 0) {
00259         geom->add_primitive(tristrips);
00260       }
00261       if (geom->get_num_primitives() != 0) {
00262         if (prefers_triangle_strips) {
00263           geom_node->add_geom(geom);
00264         } else {
00265           geom_node->add_geom(geom->decompose());
00266         }
00267       }
00268 
00269       vdata = new GeomVertexData(get_name(), format, Geom::UH_static);
00270       vertex = GeomVertexWriter(vdata, InternalName::get_vertex());
00271       texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord());
00272       geom = new Geom(vdata);
00273       tristrips = new GeomTristrips(Geom::UH_static);
00274       tristrips->set_shade_model(Geom::SM_uniform);
00275       
00276       // Now we need to re-make the previous ring in this VertexData.
00277       last_ring_vertex = 0;
00278       for (int vi = 0; vi < last_ring_size; ++vi) {
00279         add_vertex(vertex, texcoord, last_r, 
00280                    two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size);
00281       }
00282     }
00283 
00284     // Now make this ring.
00285     int ring_vertex = vdata->get_num_rows();
00286     for (int vi = 0; vi < ring_size; ++vi) {
00287       add_square_vertex(vertex, texcoord,
00288                         two_pi * (PN_stdfloat)vi / (PN_stdfloat)ring_size);
00289     }
00290 
00291     // Now draw the triangle strip to connect the rings.
00292     if ((ring_size + 1) * 2 > max_vertices_per_primitive) {
00293       // Actually, we need to subdivide the ring to fit within the
00294       // GSG's advertised limits.
00295       int piece_size = max_vertices_per_primitive / 2 - 1;
00296       int vi = 0;
00297       while (vi < ring_size) {
00298         int piece_end = min(ring_size + 1, piece_size + 1 + vi);
00299         for (int pi = vi; pi < piece_end; ++pi) {
00300           tristrips->add_vertex(last_ring_vertex + pi % last_ring_size);
00301           tristrips->add_vertex(ring_vertex + pi % ring_size);
00302         }
00303         tristrips->close_primitive();
00304         vi += piece_size;
00305       }
00306       
00307     } else {
00308       // We can fit the entire ring.
00309       if (tristrips->get_num_vertices() > 0 &&
00310           tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) {
00311         geom->add_primitive(tristrips);
00312         tristrips = new GeomTristrips(Geom::UH_static);
00313         tristrips->set_shade_model(Geom::SM_uniform);
00314       }
00315       for (int vi = 0; vi < ring_size; ++vi) {
00316         tristrips->add_vertex(last_ring_vertex + vi);
00317         tristrips->add_vertex(ring_vertex + vi);
00318       }
00319       tristrips->add_vertex(last_ring_vertex);
00320       tristrips->add_vertex(ring_vertex);
00321       tristrips->close_primitive();
00322     }
00323   }
00324 
00325   if (tristrips->get_num_vertices() != 0) {
00326     geom->add_primitive(tristrips);
00327   }
00328   if (geom->get_num_primitives() != 0) {
00329     if (prefers_triangle_strips) {
00330       geom_node->add_geom(geom);
00331     } else {
00332       geom_node->add_geom(geom->decompose());
00333     }
00334   }
00335 
00336   return geom_node.p();
00337 }
00338 
00339 ////////////////////////////////////////////////////////////////////
00340 //     Function: FisheyeMaker::add_vertex
00341 //       Access: Private
00342 //  Description: Given a point defined by a radius and an angle in
00343 //               radians, compute the 2-d coordinates for the vertex
00344 //               as well as the 3-d texture coordinates, and add both
00345 //               to the VertexData.
00346 ////////////////////////////////////////////////////////////////////
00347 void FisheyeMaker::
00348 add_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
00349            PN_stdfloat r, PN_stdfloat a) {
00350   PN_stdfloat sina, cosa;
00351   csincos(a, &sina, &cosa);
00352   
00353   // The 2-d point is just a point r units from the center of the
00354   // circle.
00355   LPoint3 point(r * cosa, 0.0f, r * sina);
00356   vertex.add_data3(point);
00357 
00358   // The 3-d point is the same thing, bent through the third dimension
00359   // around the surface of a sphere to the point in the back.
00360   PN_stdfloat b = r * _half_fov_rad;
00361   if (b >= MathNumbers::pi_f) {
00362     // Special case: we want to stop at the back pole, not continue
00363     // around it.
00364     texcoord.add_data3(0, _reflect, 0);
00365 
00366   } else {
00367     PN_stdfloat sinb, cosb;
00368     csincos(b, &sinb, &cosb);
00369     LPoint3 tc(sinb * cosa, cosb * _reflect, sinb * sina);
00370     texcoord.add_data3(tc);
00371   }
00372 }
00373   
00374 ////////////////////////////////////////////////////////////////////
00375 //     Function: FisheyeMaker::add_square_vertex
00376 //       Access: Private
00377 //  Description: Similar to add_vertex(), but it draws the vertex all
00378 //               the way out to the edge of the square we are
00379 //               inscribed within, and the texture coordinate is
00380 //               always the back pole.
00381 //
00382 //               This is just for the purpose of drawing the
00383 //               inscribing square.
00384 ////////////////////////////////////////////////////////////////////
00385 void FisheyeMaker::
00386 add_square_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
00387                   PN_stdfloat a) {
00388   PN_stdfloat sina, cosa;
00389   csincos(a, &sina, &cosa);
00390   
00391   // Extend the 2-d point to the edge of the square of the indicated
00392   // size.
00393   if (cabs(sina) > cabs(cosa)) {
00394     PN_stdfloat y = (sina > 0.0f) ? _square_radius : -_square_radius;
00395     PN_stdfloat x = y * cosa / sina;
00396     LPoint3 point(x, 0.0f, y);
00397     vertex.add_data3(point);
00398 
00399   } else {
00400     PN_stdfloat x = (cosa > 0.0f) ? _square_radius : -_square_radius;
00401     PN_stdfloat y = x * sina / cosa;
00402     LPoint3 point(x, 0.0f, y);
00403     vertex.add_data3(point);
00404   }
00405 
00406   texcoord.add_data3(0, _reflect, 0);
00407 }
00408   
 All Classes Functions Variables Enumerations