From 180bf37cf50fde85c302e87bd411162f52b160a1 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 3 Oct 2005 22:54:45 +0000 Subject: [PATCH] FisheyeMaker --- panda/src/grutil/Sources.pp | 3 + panda/src/grutil/fisheyeMaker.I | 74 +++++ panda/src/grutil/fisheyeMaker.cxx | 410 +++++++++++++++++++++++++ panda/src/grutil/fisheyeMaker.h | 72 +++++ panda/src/grutil/grutil_composite1.cxx | 1 + 5 files changed, 560 insertions(+) create mode 100644 panda/src/grutil/fisheyeMaker.I create mode 100644 panda/src/grutil/fisheyeMaker.cxx create mode 100644 panda/src/grutil/fisheyeMaker.h diff --git a/panda/src/grutil/Sources.pp b/panda/src/grutil/Sources.pp index 0d57ed2a8d..9846a4ebed 100644 --- a/panda/src/grutil/Sources.pp +++ b/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 \ diff --git a/panda/src/grutil/fisheyeMaker.I b/panda/src/grutil/fisheyeMaker.I new file mode 100644 index 0000000000..6519c2b563 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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; +} diff --git a/panda/src/grutil/fisheyeMaker.cxx b/panda/src/grutil/fisheyeMaker.cxx new file mode 100644 index 0000000000..fd37bc3af7 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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); +} + diff --git a/panda/src/grutil/fisheyeMaker.h b/panda/src/grutil/fisheyeMaker.h new file mode 100644 index 0000000000..fbb8f2f692 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + diff --git a/panda/src/grutil/grutil_composite1.cxx b/panda/src/grutil/grutil_composite1.cxx index c59c0816d3..69a8aef32a 100644 --- a/panda/src/grutil/grutil_composite1.cxx +++ b/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"