Browse Source

FisheyeMaker

David Rose 20 years ago
parent
commit
180bf37cf5

+ 3 - 0
panda/src/grutil/Sources.pp

@@ -12,6 +12,7 @@
 
   #define SOURCES \
     cardMaker.I cardMaker.h \
+    fisheyeMaker.I fisheyeMaker.h \
     config_grutil.h \
     frameRateMeter.I frameRateMeter.h \
     lineSegs.I lineSegs.h \
@@ -20,6 +21,7 @@
     
   #define INCLUDED_SOURCES \
     cardMaker.cxx \
+    fisheyeMaker.cxx \
     config_grutil.cxx \
     frameRateMeter.cxx \
     openCVTexture.cxx \    	 
@@ -28,6 +30,7 @@
 
   #define INSTALL_HEADERS \
     cardMaker.I cardMaker.h \
+    fisheyeMaker.I fisheyeMaker.h \
     frameRateMeter.I frameRateMeter.h \
     lineSegs.I lineSegs.h \
     multitexReducer.I multitexReducer.h \

+ 74 - 0
panda/src/grutil/fisheyeMaker.I

@@ -0,0 +1,74 @@
+// Filename: fisheyeMaker.I
+// Created by:  drose (3Oct05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE FisheyeMaker::
+FisheyeMaker(const string &name) : Namable(name) {
+  reset();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE FisheyeMaker::
+~FisheyeMaker() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::set_num_vertices
+//       Access: Public
+//  Description: Specifies the approximate number of vertices to be
+//               used to generate the rose.  This is the approximate
+//               number of vertices that will be located within the
+//               rose's unit circle, not counting the inscribing
+//               square (if any).  The actual number of vertices used
+//               may be +/- 25% of this value.
+////////////////////////////////////////////////////////////////////
+INLINE void FisheyeMaker::
+set_num_vertices(int num_vertices) {
+  _num_vertices = num_vertices;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::set_square_inscribed
+//       Access: Public
+//  Description: Sets the flag that indicates whether the rose should
+//               be inscribed within a square.  When this is true, an
+//               additional square is generated to inscribed the
+//               circular rose, with the indicated "radius" (the sides
+//               of the square will be 2 * square_radius).  The
+//               texture coordinates of the square will uniformly map
+//               to the back pole of the cube map.
+//
+//               This is mainly useful to provide a good uniform
+//               background color for a sphere map so that it does not
+//               have a sharp circular edge that might produce
+//               artifacts due to numerical imprecision when mapping.
+////////////////////////////////////////////////////////////////////
+INLINE void FisheyeMaker::
+set_square_inscribed(bool square_inscribed, float square_radius) {
+  _square_inscribed = true;
+  _square_radius = square_radius;
+}

+ 410 - 0
panda/src/grutil/fisheyeMaker.cxx

@@ -0,0 +1,410 @@
+// Filename: fisheyeMaker.cxx
+// Created by:  drose (3Oct05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "fisheyeMaker.h"
+#include "geomNode.h"
+#include "geom.h"
+#include "geomTristrips.h"
+#include "geomVertexWriter.h"
+#include "geomVertexFormat.h"
+#include "geomVertexArrayFormat.h"
+#include "internalName.h"
+#include "luse.h"
+#include "cmath.h"
+#include "mathNumbers.h"
+#include "graphicsStateGuardian.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::reset
+//       Access: Public
+//  Description: Resets all the parameters to their initial defaults.
+////////////////////////////////////////////////////////////////////
+void FisheyeMaker::
+reset() {
+  set_fov(360.0);
+  _num_vertices = 1000;
+  _square_inscribed = false;
+  _square_radius = 1.0f;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::set_fov
+//       Access: Public
+//  Description: Specifies the field of view of the fisheye
+//               projection.  A sphere map will have a 360-degree
+//               field of view (and this is the default).
+////////////////////////////////////////////////////////////////////
+void FisheyeMaker::
+set_fov(float fov) {
+  _fov = fov;
+  _half_fov_rad =  deg_2_rad(_fov * 0.5f);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::generate
+//       Access: Public
+//  Description: Generates a GeomNode that renders the specified
+//               geometry.
+////////////////////////////////////////////////////////////////////
+PT(PandaNode) FisheyeMaker::
+generate() {
+  // Get some system-imposed limits.
+  int max_vertices_per_array = 100;
+  int max_vertices_per_primitive = 10000;
+  bool prefers_triangle_strips = true;
+
+  /*
+  GraphicsStateGuardian *global_gsg = GraphicsStateGuardian::get_global_gsg();
+  if (global_gsg != (GraphicsStateGuardian *)NULL) {
+    max_vertices_per_array = global_gsg->get_max_vertices_per_array();
+    max_vertices_per_primitive = global_gsg->get_max_vertices_per_primitive();
+    prefers_triangle_strips = global_gsg->prefers_triangle_strips();
+  }
+  */
+
+  // We will generate a rose of radius 1, with vertices approximately
+  // evenly distributed throughout.
+
+  // Since we will have _num_vertices filling the circle, and the area
+  // of a circle of radius 1 is A = pi*r^2 = pi, it follows that the
+  // number of vertices per square unit is (_num_vertices / pi), and
+  // thus the number of vertices per linear unit is the square root of
+  // that.
+  float vertices_per_unit = csqrt(_num_vertices / MathNumbers::pi_f);
+  float two_pi = 2.0f * MathNumbers::pi_f;
+
+  // The rose will be made up of concentric rings, originating from
+  // the center, to a radius of 1.0.
+  int num_rings = (int)floor(vertices_per_unit + 0.5f);
+
+  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
+    (new GeomVertexArrayFormat
+     (InternalName::get_vertex(), 3, 
+      Geom::NT_float32, Geom::C_point,
+      InternalName::get_texcoord(), 3,
+      Geom::NT_float32, Geom::C_texcoord));
+
+  PT(GeomVertexData) vdata = 
+    new GeomVertexData(get_name(), format, Geom::UH_static);
+  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
+
+  PT(Geom) geom = new Geom(vdata);
+  PT(GeomPrimitive) tristrips = new GeomTristrips(Geom::UH_static);
+  tristrips->set_shade_model(Geom::SM_uniform);
+
+  PT(GeomNode) geom_node = new GeomNode(get_name());
+
+  int last_ring_size = 3;
+  int last_ring_vertex = 0;
+  float last_r = 1.0f / (float)num_rings;
+
+  // Make the first triangle.  We actually make a one-triangle strip,
+  // but that seems more sensible than making a single isolated
+  // triangle.
+  for (int vi = 0; vi < last_ring_size; ++vi) {
+    add_vertex(vertex, texcoord, last_r, 
+               two_pi * (float)vi / (float)last_ring_size);
+    tristrips->add_vertex(vi);
+  }
+  // Actually, we need to add one more degenerate triangle to make it
+  // an even-length tristrip.
+  tristrips->add_vertex(2);
+  tristrips->close_primitive();
+
+  // Now make all of the rings.
+  for (int ri = 1; ri < num_rings; ++ri) {
+    float r = (float)(ri + 1) / (float)num_rings;
+    
+    // The circumference of a ring of radius r is 2*pi*r.
+    float c = two_pi * r;
+    int ring_size = (int)floor(c * vertices_per_unit + 0.5f);
+    
+    // Each ring must either have exactly the same number of vertices
+    // as the previous ring, or exactly double.
+    if (ring_size < last_ring_size * 2) {
+      // This one will be the same.
+      ring_size = last_ring_size;
+    } else {
+      // This one will be double.
+      ring_size = last_ring_size * 2;
+    }
+
+    if (vdata->get_num_rows() + ring_size > max_vertices_per_array) {
+      // Too many vertices; we need to start a new VertexData.
+      if (tristrips->get_num_vertices() != 0) {
+        geom->add_primitive(tristrips);
+      }
+      if (geom->get_num_primitives() != 0) {
+        if (prefers_triangle_strips) {
+          geom_node->add_geom(geom);
+        } else {
+          geom_node->add_geom(geom->decompose());
+        }
+      }
+
+      vdata = new GeomVertexData(get_name(), format, Geom::UH_static);
+      vertex = GeomVertexWriter(vdata, InternalName::get_vertex());
+      texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord());
+      geom = new Geom(vdata);
+      tristrips = new GeomTristrips(Geom::UH_static);
+      tristrips->set_shade_model(Geom::SM_uniform);
+
+      // Now we need to re-make the previous ring in this VertexData.
+      last_ring_vertex = 0;
+      for (int vi = 0; vi < last_ring_size; ++vi) {
+        add_vertex(vertex, texcoord, last_r, 
+                   two_pi * (float)vi / (float)last_ring_size);
+      }
+    }
+
+    // Now make this ring.
+    int ring_vertex = vdata->get_num_rows();
+    for (int vi = 0; vi < ring_size; ++vi) {
+      add_vertex(vertex, texcoord, r, 
+                 two_pi * (float)vi / (float)ring_size);
+    }
+
+    // Now draw the triangle strip to connect the rings.
+    if (ring_size == last_ring_size) {
+      // Exactly the same size ring.  This one is easy.
+      if ((ring_size + 1) * 2 > max_vertices_per_primitive) {
+        // Actually, we need to subdivide the ring to fit within the
+        // GSG's advertised limits.
+        int piece_size = max_vertices_per_primitive / 2 - 1;
+        int vi = 0;
+        while (vi < ring_size) {
+          int piece_end = min(ring_size + 1, piece_size + 1 + vi);
+          for (int pi = vi; pi < piece_end; ++pi) {
+            tristrips->add_vertex(last_ring_vertex + pi % last_ring_size);
+            tristrips->add_vertex(ring_vertex + pi % ring_size);
+          }
+          tristrips->close_primitive();
+          vi += piece_size;
+        }
+
+      } else {
+        // We can fit the entire ring.
+        if (tristrips->get_num_vertices() > 0 &&
+            tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) {
+          geom->add_primitive(tristrips);
+          tristrips = new GeomTristrips(Geom::UH_static);
+          tristrips->set_shade_model(Geom::SM_uniform);
+        }
+        for (int vi = 0; vi < ring_size; ++vi) {
+          tristrips->add_vertex(last_ring_vertex + vi);
+          tristrips->add_vertex(ring_vertex + vi);
+        }
+        tristrips->add_vertex(last_ring_vertex);
+        tristrips->add_vertex(ring_vertex);
+        tristrips->close_primitive();
+      }
+
+    } else {
+      // Exactly double size ring.  This is harder; we can't make a
+      // single tristrip that goes all the way around the ring.
+      // Instead, we'll make an alternating series of four-triangle
+      // strips and two-triangle strips around the ring.
+      int vi = 0;
+      while (vi < last_ring_size) {
+        if (tristrips->get_num_vertices() + 10 > max_vertices_per_primitive) {
+          geom->add_primitive(tristrips);
+          tristrips = new GeomTristrips(Geom::UH_static);
+          tristrips->set_shade_model(Geom::SM_uniform);
+        }
+        tristrips->add_vertex(ring_vertex + (vi * 2 + 1) % ring_size);
+        tristrips->add_vertex(ring_vertex + (vi * 2 + 2) % ring_size);
+        tristrips->add_vertex(last_ring_vertex + (vi + 1) % last_ring_size);
+        tristrips->add_vertex(ring_vertex + (vi * 2 + 3) % ring_size);
+        tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size);
+        tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size);
+        tristrips->close_primitive();
+
+        tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size);
+        tristrips->add_vertex(ring_vertex + (vi * 2 + 5) % ring_size);
+        tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size);
+        tristrips->add_vertex(last_ring_vertex + (vi + 3) % last_ring_size);
+        tristrips->close_primitive();
+
+        vi += 2;
+      }
+    }
+
+    last_ring_size = ring_size;
+    last_ring_vertex = ring_vertex;
+    last_r = r;
+  }
+
+  if (_square_inscribed) {
+    // Make one more "ring", which extends out to the edges of a squre.
+    int ring_size = last_ring_size;
+
+    if (vdata->get_num_rows() + ring_size > max_vertices_per_array) {
+      // Too many vertices; we need to start a new VertexData.
+      if (tristrips->get_num_vertices() != 0) {
+        geom->add_primitive(tristrips);
+      }
+      if (geom->get_num_primitives() != 0) {
+        if (prefers_triangle_strips) {
+          geom_node->add_geom(geom);
+        } else {
+          geom_node->add_geom(geom->decompose());
+        }
+      }
+
+      vdata = new GeomVertexData(get_name(), format, Geom::UH_static);
+      vertex = GeomVertexWriter(vdata, InternalName::get_vertex());
+      texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord());
+      geom = new Geom(vdata);
+      tristrips = new GeomTristrips(Geom::UH_static);
+      tristrips->set_shade_model(Geom::SM_uniform);
+      
+      // Now we need to re-make the previous ring in this VertexData.
+      last_ring_vertex = 0;
+      for (int vi = 0; vi < last_ring_size; ++vi) {
+        add_vertex(vertex, texcoord, last_r, 
+                   two_pi * (float)vi / (float)last_ring_size);
+      }
+    }
+
+    // Now make this ring.
+    int ring_vertex = vdata->get_num_rows();
+    for (int vi = 0; vi < ring_size; ++vi) {
+      add_square_vertex(vertex, texcoord,
+                        two_pi * (float)vi / (float)ring_size);
+    }
+
+    // Now draw the triangle strip to connect the rings.
+    if ((ring_size + 1) * 2 > max_vertices_per_primitive) {
+      // Actually, we need to subdivide the ring to fit within the
+      // GSG's advertised limits.
+      int piece_size = max_vertices_per_primitive / 2 - 1;
+      int vi = 0;
+      while (vi < ring_size) {
+        int piece_end = min(ring_size + 1, piece_size + 1 + vi);
+        for (int pi = vi; pi < piece_end; ++pi) {
+          tristrips->add_vertex(last_ring_vertex + pi % last_ring_size);
+          tristrips->add_vertex(ring_vertex + pi % ring_size);
+        }
+        tristrips->close_primitive();
+        vi += piece_size;
+      }
+      
+    } else {
+      // We can fit the entire ring.
+      if (tristrips->get_num_vertices() > 0 &&
+          tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) {
+        geom->add_primitive(tristrips);
+        tristrips = new GeomTristrips(Geom::UH_static);
+        tristrips->set_shade_model(Geom::SM_uniform);
+      }
+      for (int vi = 0; vi < ring_size; ++vi) {
+        tristrips->add_vertex(last_ring_vertex + vi);
+        tristrips->add_vertex(ring_vertex + vi);
+      }
+      tristrips->add_vertex(last_ring_vertex);
+      tristrips->add_vertex(ring_vertex);
+      tristrips->close_primitive();
+    }
+  }
+
+  if (tristrips->get_num_vertices() != 0) {
+    geom->add_primitive(tristrips);
+  }
+  if (geom->get_num_primitives() != 0) {
+    if (prefers_triangle_strips) {
+      geom_node->add_geom(geom);
+    } else {
+      geom_node->add_geom(geom->decompose());
+    }
+  }
+
+  return geom_node.p();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::add_vertex
+//       Access: Private
+//  Description: Given a point defined by a radius and an angle in
+//               radians, compute the 2-d coordinates for the vertex
+//               as well as the 3-d texture coordinates, and add both
+//               to the VertexData.
+////////////////////////////////////////////////////////////////////
+void FisheyeMaker::
+add_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
+           float r, float a) {
+  float sina, cosa;
+  csincos(a, &sina, &cosa);
+  
+  // The 2-d point is just a point r units from the center of the
+  // circle.
+  LPoint3f point(r * cosa, 0.0f, r * sina);
+  vertex.add_data3f(point);
+
+  // The 3-d point is the same thing, bent through the third dimension
+  // around the surface of a sphere to the point in the back.
+  float b = r * _half_fov_rad;
+  if (b >= MathNumbers::pi_f) {
+    // Special case: we want to stop at the back pole, not continue
+    // around it.
+    texcoord.add_data3f(0, -1, 0);
+
+  } else {
+    float sinb, cosb;
+    csincos(b, &sinb, &cosb);
+    LPoint3f tc(sinb * cosa, cosb, sinb * sina);
+    texcoord.add_data3f(tc);
+  }
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: FisheyeMaker::add_square_vertex
+//       Access: Private
+//  Description: Similar to add_vertex(), but it draws the vertex all
+//               the way out to the edge of the square we are
+//               inscribed within, and the texture coordinate is
+//               always the back pole.
+//
+//               This is just for the purpose of drawing the
+//               inscribing square.
+////////////////////////////////////////////////////////////////////
+void FisheyeMaker::
+add_square_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
+                  float a) {
+  float sina, cosa;
+  csincos(a, &sina, &cosa);
+  
+  // Extend the 2-d point to the edge of the square of the indicated
+  // size.
+  if (cabs(sina) > cabs(cosa)) {
+    float y = (sina > 0.0f) ? _square_radius : -_square_radius;
+    float x = y * cosa / sina;
+    LPoint3f point(x, 0.0f, y);
+    vertex.add_data3f(point);
+
+  } else {
+    float x = (cosa > 0.0f) ? _square_radius : -_square_radius;
+    float y = x * sina / cosa;
+    LPoint3f point(x, 0.0f, y);
+    vertex.add_data3f(point);
+  }
+
+  texcoord.add_data3f(0, -1, 0);
+}
+  

+ 72 - 0
panda/src/grutil/fisheyeMaker.h

@@ -0,0 +1,72 @@
+// Filename: fisheyeMaker.h
+// Created by:  drose (3Oct05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef FISHEYEMAKER_H
+#define FISHEYEMAKER_H
+
+#include "pandabase.h"
+
+#include "pandaNode.h"
+#include "pointerTo.h"
+#include "namable.h"
+
+class GeomVertexWriter;
+
+////////////////////////////////////////////////////////////////////
+//       Class : FisheyeMaker
+// Description : This class is similar to CardMaker, but instead of
+//               generating ordinary cards, it generates a circular
+//               rose that represents the projection of a 3-D scene
+//               through a fisheye lens.  The texture coordinates of
+//               the rose are defined so that each 2-D vertex has a
+//               3-D UVW that reflects the corresponding position in
+//               3-D space of that particular vertex.
+//
+//               This class is particularly suited for converting cube
+//               maps to sphere maps.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA FisheyeMaker : public Namable {
+PUBLISHED:
+  INLINE FisheyeMaker(const string &name);
+  INLINE ~FisheyeMaker();
+
+  void reset();
+  void set_fov(float fov);
+  INLINE void set_num_vertices(int num_vertices);
+  INLINE void set_square_inscribed(bool square_inscribed, float square_radius);
+
+  PT(PandaNode) generate();
+
+private:
+  void add_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
+                  float r, float a);
+
+  void add_square_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord,
+                         float a);
+
+  float _fov;
+  float _half_fov_rad;
+  int _num_vertices;
+  bool _square_inscribed;
+  float _square_radius;
+};
+
+#include "fisheyeMaker.I"
+
+#endif
+

+ 1 - 0
panda/src/grutil/grutil_composite1.cxx

@@ -1,6 +1,7 @@
 #include "cardMaker.cxx"
 #include "config_grutil.cxx"
 #include "lineSegs.cxx"
+#include "fisheyeMaker.cxx"
 #include "frameRateMeter.cxx"
 #include "openCVTexture.cxx"