FisheyeMaker

This commit is contained in:
David Rose 2005-10-03 22:54:45 +00:00
parent 4cc9dba771
commit 180bf37cf5
5 changed files with 560 additions and 0 deletions

View File

@ -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 \

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

View File

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