panda3d/panda/src/pgraph/portalClipper.cxx
2004-05-27 22:47:39 +00:00

439 lines
15 KiB
C++
Executable File

// Filename: portalClipper.cxx
// Created by: masad (4May04)
//
////////////////////////////////////////////////////////////////////
//
// 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 "portalClipper.h"
#include "cullTraverser.h"
#include "cullTraverserData.h"
#include "transformState.h"
#include "renderState.h"
#include "fogAttrib.h"
#include "cullHandler.h"
#include "dcast.h"
#include "geomNode.h"
#include "config_pgraph.h"
#include "boundingSphere.h"
#include "geomSphere.h"
#include "colorAttrib.h"
#include "renderModeAttrib.h"
#include "cullFaceAttrib.h"
#include "depthOffsetAttrib.h"
TypeHandle PortalClipper::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PortalClipper::
PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup) {
_previous = new GeomNode("my_frustum");
_geom_line = new GeomLine;
_geom_point = new GeomPoint;
_geom_linestrip = new GeomLinestrip;
_hex_frustum = DCAST(BoundingHexahedron, frustum);
_scene_setup = scene_setup;
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PortalClipper::
~PortalClipper() {
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::move_to
// Access: Public
// Description: Moves the pen to the given point without drawing a
// line. When followed by draw_to(), this marks the
// first point of a line segment; when followed by
// move_to() or create(), this creates a single point.
////////////////////////////////////////////////////////////////////
void PortalClipper::
move_to(const LVecBase3f &v) {
// We create a new SegmentList with the initial point in it.
SegmentList segs;
segs.push_back(Point(v, _color));
// And add this list to the list of segments.
_list.push_back(segs);
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::draw_to
// Access: Public
// Description: Draws a line segment from the pen's last position
// (the last call to move_to or draw_to) to the
// indicated point. move_to() and draw_to() only update
// tables; the actual drawing is performed when create()
// is called.
////////////////////////////////////////////////////////////////////
void PortalClipper::
draw_to(const LVecBase3f &v) {
if (_list.empty()) {
// Let our first call to draw_to() be an implicit move_to().
move_to(v);
} else {
// Get the current SegmentList, which was the last one we added to
// the LineList.
SegmentList &segs = _list.back();
// Add the new point.
segs.push_back(Point(v, _color));
}
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::draw a portal frustum
// Access: Public
// Description: Given the BoundingHexahedron draw it using lines
//
////////////////////////////////////////////////////////////////////
void PortalClipper::
draw_hexahedron(BoundingHexahedron *frustum) {
/*
pgraph_cat.debug() << "frustum points " << frustum->get_num_points() << endl;
pgraph_cat.debug() << frustum->get_point(0) << endl;
pgraph_cat.debug() << frustum->get_point(1) << endl;
pgraph_cat.debug() << frustum->get_point(2) << endl;
pgraph_cat.debug() << frustum->get_point(3) << endl;
pgraph_cat.debug() << frustum->get_point(4) << endl;
pgraph_cat.debug() << frustum->get_point(5) << endl;
pgraph_cat.debug() << frustum->get_point(6) << endl;
pgraph_cat.debug() << frustum->get_point(7) << endl;
*/
// walk the view frustum as it should be drawn
move_to(frustum->get_point(0));
draw_to(frustum->get_point(1));
draw_to(frustum->get_point(2));
draw_to(frustum->get_point(3));
move_to(frustum->get_point(4));
draw_to(frustum->get_point(0));
draw_to(frustum->get_point(3));
draw_to(frustum->get_point(7));
move_to(frustum->get_point(5));
draw_to(frustum->get_point(4));
draw_to(frustum->get_point(7));
draw_to(frustum->get_point(6));
move_to(frustum->get_point(1));
draw_to(frustum->get_point(5));
draw_to(frustum->get_point(6));
draw_to(frustum->get_point(2));
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::draw the lines
// Access: Public
// Description: Draw all the lines in the buffer
//
////////////////////////////////////////////////////////////////////
void PortalClipper::
draw_lines()
{
if (!_list.empty()) {
_created_verts.clear();
_created_colors.clear();
// One array each for the indices into these arrays for points
// and lines, and one for our line-segment lengths array.
PTA_ushort point_index;
PTA_ushort line_index;
PTA_int lengths;
// Now fill up the arrays.
int v = 0;
LineList::const_iterator ll;
SegmentList::const_iterator sl;
for (ll = _list.begin(); ll != _list.end(); ll++) {
const SegmentList &segs = (*ll);
if (segs.size() < 2) {
point_index.push_back(v);
} else {
lengths.push_back(segs.size());
}
for (sl = segs.begin(); sl != segs.end(); sl++) {
if (segs.size() >= 2) {
line_index.push_back(v);
}
_created_verts.push_back((*sl)._point);
_created_colors.push_back((*sl)._color);
v++;
//nassertr(v == (int)_created_verts.size(), previous);
}
}
// Now create the lines.
Geom *geom;
if (line_index.size() > 0) {
// Create a new Geom and add the line segments.
if (line_index.size() <= 2) {
// Here's a special case: just one line segment.
_geom_line->set_num_prims(1);
_geom_line->set_width(_thick);
geom = _geom_line;
} else {
// The more normal case: multiple line segments, connected
// end-to-end like a series of linestrips.
_geom_linestrip->set_num_prims(lengths.size());
_geom_linestrip->set_lengths(lengths);
_geom_linestrip->set_width(_thick);
geom = _geom_linestrip;
}
geom->set_colors(_created_colors, G_PER_VERTEX, line_index);
geom->set_coords(_created_verts, line_index);
//geom->write_verbose(cerr, 0);
_previous->add_geom(geom);
pgraph_cat.debug() << "added geometry" << endl;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::prepare the portal
// Access: Public
// Description: Given the portal draw the frustum with line segs
// for now. More functionalities coming up
////////////////////////////////////////////////////////////////////
void PortalClipper::
prepare_portal(int idx)
{
SegmentList segs;
char portal_name[128];
// print some messages to see if i am getting to this part
pgraph_cat.debug() << "creating portal clipper " << idx << endl;
// walk the portal
_num_vert = 0;
sprintf(portal_name, "**/portal%d", idx);
NodePath portal_nodepath = _scene_setup->get_scene_root().find(portal_name);
if (!portal_nodepath.is_empty()) {
pgraph_cat.debug() << "portal nodepath " << portal_nodepath << endl;
/*
// Get the World transformation matrix
CPT(TransformState) wtransform = portal_nodepath.get_transform(_scene_setup->get_scene_root());
LMatrix4f wmat = wtransform->get_mat();
pgraph_cat.debug() << wmat << endl;
*/
// Get the camera transformation matrix
CPT(TransformState) ctransform = portal_nodepath.get_transform(_scene_setup->get_cull_center());
//CPT(TransformState) ctransform = portal_nodepath.get_transform(_scene_setup->get_camera_path());
LMatrix4f cmat = ctransform->get_mat();
pgraph_cat.debug() << cmat << endl;
// Get the geometry from the portal
PandaNode *node = portal_nodepath.node();
if (node->is_of_type(PortalNode::get_class_type())) {
PortalNode *portal_node = DCAST(PortalNode, node);
pgraph_cat.debug() << *portal_node << endl;
_coords[0] = portal_node->get_vertex(0)*cmat;
_coords[1] = portal_node->get_vertex(1)*cmat;
_coords[2] = portal_node->get_vertex(2)*cmat;
_coords[3] = portal_node->get_vertex(3)*cmat;
pgraph_cat.debug() << "after transformation to camera space" << endl;
pgraph_cat.debug() << _coords[0] << endl;
pgraph_cat.debug() << _coords[1] << endl;
pgraph_cat.debug() << _coords[2] << endl;
pgraph_cat.debug() << _coords[3] << endl;
// check if facing camera
if (is_facing_camera()) {
// ok, now lets add the near plane to this portal
_color = Colorf(1,0,0,1);
move_to(_coords[0]);
draw_to(_coords[1]);
draw_to(_coords[2]);
draw_to(_coords[3]);
draw_to(_coords[0]);
pgraph_cat.debug() << "assembled portal" << idx << " frustum points" << endl;
_num_vert = portal_node->get_num_vertices();
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::clip the portal
// Access: Public
// Description: From the frustum clip the portal against the frustum
// and form the new planes of the reduced view frustum
////////////////////////////////////////////////////////////////////
void PortalClipper::
clip_portal(int idx)
{
int num_planes = _hex_frustum->get_num_planes();
if (!_num_vert)
return;
/*
pgraph_cat.debug() << "Number of planes " << num_planes << endl;
// print out the planes. plane 0 should be far and plane 5 should be near
// so we are only concerned with the 4 side planes.
for (int i=0; i<num_planes; ++i) {
Planef plane = _hex_frustum->get_plane(i);
plane.output(pgraph_cat.debug());
pgraph_cat.debug() << endl;
}
*/
for (int i=1; i<num_planes-1; ++i) {
Planef plane = _hex_frustum->get_plane(i);
for (int j=0; j<_num_vert; ++j) {
float t;
LPoint3f from_origin = _coords[j];
LVector3f from_direction = _coords[(j+1)%_num_vert] - _coords[j];
bool is_intersect = plane.intersects_line(t, from_origin, from_direction);
if (is_intersect) {
pgraph_cat.debug() << "plane " << i << " intersected segement " << j << "->" << (j+1)%_num_vert << " at t=" << t << endl;
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PortalClipper::get_reduced_frustum
// Access: Public
// Description: After clipping the portal, form the new sides and
// fill in the new frustum. Return true if success
////////////////////////////////////////////////////////////////////
PT(BoundingVolume) PortalClipper::
get_reduced_frustum(int idx)
{
int num_planes = 6;
LPoint3f intersect_points[4];
#if 0
// calculate the new side planes
for (int i=0; i<_num_vert; ++i) {
// get the vectors, Vi+1 and Vi
LVector3f front(_coords[(i+1)%_num_vert]);
LVector3f back(_coords[i]);
// get the cross product of these two vectors
LVector3f normal = front.cross(back);
normal.normalize();
frustum_planes[i+1] = Planef(normal, LPoint3f(0,0,0));
frustum_planes[i+1].output(pgraph_cat.debug());
pgraph_cat.debug() << endl;
}
#else
// another approach to actually finding the points, so that
// I can reuse the current BoundingHexahedron object. Apparently,
// it is better to construct this BH with bounding points, rather
// than bounding planes (which I might have to implement soon)
if (!_num_vert)
return NULL;
float t;
bool visible = true;
// find intersection of 7->0 with far
LPoint3f from_origin = _hex_frustum->get_point(7);
LVector3f from_direction = _coords[0] - from_origin;
bool is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 7->0 at t=" << t << endl;
intersect_points[0] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[0] << endl;
}
else
visible = false;
// find intersection of 4->1 with far
from_origin = _hex_frustum->get_point(4);
from_direction = _coords[1] - from_origin;
is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 4->1 at t=" << t << endl;
intersect_points[1] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[1] << endl;
}
else
visible = false;
// find intersection of 5->2 with far
from_origin = _hex_frustum->get_point(5);
from_direction = _coords[2] - from_origin;
is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 5->2 at t=" << t << endl;
intersect_points[2] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[2] << endl;
}
else
visible = false;
// find intersection of 6->3 with far
from_origin = _hex_frustum->get_point(6);
from_direction = _coords[3] - from_origin;
is_intersect = _hex_frustum->get_plane(0).intersects_line(t, from_origin, from_direction);
if (is_intersect && t >= 0.0) { // has to be positive, else camera is not looking at the portal
pgraph_cat.debug() << "far plane intersected 6->3 at t=" << t << endl;
intersect_points[3] = from_origin + t*from_direction;
pgraph_cat.debug() << intersect_points[3] << endl;
}
else
visible = false;
if (!visible) {
pgraph_cat.debug() << "portal" << idx << " is not visible from current camera look at" << endl;
return NULL;
}
// With these intersect_points, construct the new reduced frustum
PT(BoundingVolume) reduced_frustum = new
BoundingHexahedron(intersect_points[1], intersect_points[2],
intersect_points[3], intersect_points[0],
_hex_frustum->get_point(4), _hex_frustum->get_point(5),
_hex_frustum->get_point(6), _hex_frustum->get_point(7));
pgraph_cat.debug() << *reduced_frustum << endl;
// draw this hexahedron
_color = Colorf(0,0,1,1);
draw_hexahedron(DCAST(BoundingHexahedron, reduced_frustum));
#endif
return reduced_frustum;
}