409 lines
14 KiB
C++
409 lines
14 KiB
C++
// Filename: fisheyeMaker.cxx
|
|
// Created by: drose (3Oct05)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#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"
|
|
#include "displayRegion.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;
|
|
set_reflection(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// 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, _reflect, 0);
|
|
|
|
} else {
|
|
float sinb, cosb;
|
|
csincos(b, &sinb, &cosb);
|
|
LPoint3f tc(sinb * cosa, cosb * _reflect, 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, _reflect, 0);
|
|
}
|
|
|