open_toontown_panda3d/panda/src/egg/eggCompositePrimitive.cxx

501 lines
18 KiB
C++

// Filename: eggCompositePrimitive.cxx
// Created by: drose (13Mar05)
//
////////////////////////////////////////////////////////////////////
//
// 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 "eggCompositePrimitive.h"
#include "eggGroupNode.h"
TypeHandle EggCompositePrimitive::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::Destructor
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
EggCompositePrimitive::
~EggCompositePrimitive() {
// Every derived class of EggCompositePrimitive must call clear() in
// its destructor.
nassertv(_components.empty());
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::get_shading
// Access: Published, Virtual
// Description: Returns the shading properties apparent on this
// particular primitive. This returns S_per_vertex if
// the vertices have colors or normals (and they are not
// all the same values), or for a simple primitive,
// S_overall otherwise. A composite primitive may also
// return S_per_face if the individual component
// primitives have colors or normals that are not all
// the same values.
//
// To get the most accurate results, you should call
// clear_shading() on all connected primitives (or on
// all primitives in the egg file), followed by
// determine_shading() on each primitive. You may find
// it easiest to call these methods on the EggData root
// node (they are defined on EggGroupNode).
////////////////////////////////////////////////////////////////////
EggPrimitive::Shading EggCompositePrimitive::
get_shading() const {
Shading basic_shading = EggPrimitive::get_shading();
if (basic_shading == S_per_vertex) {
return basic_shading;
}
if (_components.empty()) {
return S_overall;
}
// Check if the components all have the same normal.
{
const EggAttributes *first_component = get_component(0);
if (!first_component->has_normal()) {
first_component = this;
}
for (int i = 1; i < get_num_components(); i++) {
const EggAttributes *component = get_component(i);
if (!component->has_normal()) {
component = this;
}
if (!component->matches_normal(*first_component)) {
return S_per_face;
}
}
}
// Check if the components all have the same color.
{
const EggAttributes *first_component = get_component(0);
if (!first_component->has_color()) {
first_component = this;
}
for (int i = 1; i < get_num_components(); i++) {
const EggAttributes *component = get_component(i);
if (!component->has_color()) {
component = this;
}
if (!component->matches_color(*first_component)) {
return S_per_face;
}
}
}
return S_overall;
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::triangulate_in_place
// Access: Published
// Description: Subdivides the composite primitive into triangles and
// adds those triangles to the parent group node in
// place of the original primitive. Returns a pointer
// to the original primitive, which is likely about to
// be destructed.
//
// If convex_also is true, both concave and convex
// polygons will be subdivided into triangles;
// otherwise, only concave polygons will be subdivided,
// and convex polygons will be copied unchanged into the
// container.
////////////////////////////////////////////////////////////////////
PT(EggCompositePrimitive) EggCompositePrimitive::
triangulate_in_place() {
EggGroupNode *parent = get_parent();
nassertr(parent != (EggGroupNode *)NULL, this);
PT(EggCompositePrimitive) save_me = this;
parent->remove_child(this);
do_triangulate(parent);
return save_me;
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::unify_attributes
// Access: Published, Virtual
// Description: If the shading property is S_per_vertex, ensures that
// all vertices have a normal and a color, and the
// overall primitive does not.
//
// If the shading property is S_per_face, and this is a
// composite primitive, ensures that all components have
// a normal and a color, and the vertices and overall
// primitive do not. (If this is a simple primitive,
// S_per_face works the same as S_overall, below).
//
// If the shading property is S_overall, ensures that no
// vertices or components have a normal or a color, and
// the overall primitive does (if any exists at all).
//
// After this call, either the primitive will have
// normals or its vertices will, but not both. Ditto
// for colors.
//
// This may create redundant vertices in the vertex
// pool.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
unify_attributes(EggPrimitive::Shading shading) {
if (shading == S_unknown) {
shading = get_shading();
}
switch (shading) {
case S_per_vertex:
// Propagate everything to the vertices.
{
Components::iterator ci;
for (ci = _components.begin(); ci != _components.end(); ++ci) {
EggAttributes *component = (*ci);
if (component->has_normal()) {
if (!has_normal()) {
copy_normal(*component);
}
component->clear_normal();
}
if (component->has_color()) {
if (!has_color()) {
copy_color(*component);
}
component->clear_color();
}
}
// Not having a color is implicitly white.
if (!has_color()) {
set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
}
iterator pi;
for (pi = begin(); pi != end(); ++pi) {
EggVertex *orig_vertex = (*pi);
PT(EggVertex) vertex = new EggVertex(*orig_vertex);
if (!vertex->has_normal() && has_normal()) {
vertex->copy_normal(*this);
}
if (!vertex->has_color() && has_color()) {
vertex->copy_color(*this);
}
EggVertexPool *vertex_pool = orig_vertex->get_pool();
nassertv(vertex_pool != (EggVertexPool *)NULL);
vertex = vertex_pool->create_unique_vertex(*vertex);
vertex->copy_grefs_from(*orig_vertex);
replace(pi, vertex);
}
clear_normal();
clear_color();
}
break;
case S_per_face:
// Propagate everything to the components.
{
iterator pi;
for (pi = begin(); pi != end(); ++pi) {
EggVertex *orig_vertex = (*pi);
if (orig_vertex->has_normal() || orig_vertex->has_color()) {
if (orig_vertex->has_normal() && !has_normal()) {
copy_normal(*orig_vertex);
}
if (orig_vertex->has_color() && !has_color()) {
copy_color(*orig_vertex);
}
PT(EggVertex) vertex = new EggVertex(*orig_vertex);
vertex->clear_normal();
vertex->clear_color();
EggVertexPool *vertex_pool = orig_vertex->get_pool();
nassertv(vertex_pool != (EggVertexPool *)NULL);
vertex = vertex_pool->create_unique_vertex(*vertex);
vertex->copy_grefs_from(*orig_vertex);
replace(pi, vertex);
}
}
// Not having a color is implicitly white.
if (!has_color()) {
set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
}
Components::iterator ci;
for (ci = _components.begin(); ci != _components.end(); ++ci) {
EggAttributes *component = (*ci);
if (!component->has_normal() && has_normal()) {
component->copy_normal(*this);
}
if (!component->has_color() && has_color()) {
component->copy_color(*this);
}
}
clear_normal();
clear_color();
}
break;
case S_overall:
// Remove everything from the vertices and components.
{
iterator pi;
for (pi = begin(); pi != end(); ++pi) {
EggVertex *orig_vertex = (*pi);
PT(EggVertex) vertex = new EggVertex(*orig_vertex);
if (vertex->has_normal()) {
if (!has_normal()) {
copy_normal(*vertex);
}
vertex->clear_normal();
}
if (vertex->has_color()) {
if (!has_color()) {
copy_color(*vertex);
}
vertex->clear_color();
}
EggVertexPool *vertex_pool = orig_vertex->get_pool();
nassertv(vertex_pool != (EggVertexPool *)NULL);
vertex = vertex_pool->create_unique_vertex(*vertex);
vertex->copy_grefs_from(*orig_vertex);
replace(pi, vertex);
}
Components::iterator ci;
for (ci = _components.begin(); ci != _components.end(); ++ci) {
EggAttributes *component = (*ci);
if (component->has_normal()) {
if (!has_normal()) {
copy_normal(*component);
}
component->clear_normal();
}
if (component->has_color()) {
if (!has_color()) {
copy_color(*component);
}
component->clear_color();
}
}
// Not having a color is implicitly white.
if (!has_color()) {
set_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
}
}
break;
case S_unknown:
break;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::apply_last_attribute
// Access: Published, Virtual
// Description: Sets the last vertex of the triangle (or each
// component) to the primitive normal and/or color, if
// the primitive is flat-shaded. This reflects the
// OpenGL convention of storing flat-shaded properties on
// the last vertex, although it is not usually a
// convention in Egg.
//
// This may introduce redundant vertices to the vertex
// pool.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
apply_last_attribute() {
// The first component gets applied to the third vertex, and so on
// from there.
int num_lead_vertices = get_num_lead_vertices();
for (int i = 0; i < get_num_components(); i++) {
EggAttributes *component = get_component(i);
do_apply_flat_attribute(i + num_lead_vertices, component);
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::apply_first_attribute
// Access: Published, Virtual
// Description: Sets the first vertex of the triangle (or each
// component) to the primitive normal and/or color, if
// the primitive is flat-shaded. This reflects the
// DirectX convention of storing flat-shaded properties
// on the first vertex, although it is not usually a
// convention in Egg.
//
// This may introduce redundant vertices to the vertex
// pool.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
apply_first_attribute() {
// The first component gets applied to the first vertex, and so on
// from there.
for (int i = 0; i < get_num_components(); i++) {
EggAttributes *component = get_component(i);
do_apply_flat_attribute(i, component);
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::post_apply_flat_attribute
// Access: Published, Virtual
// Description: Intended as a followup to apply_last_attribute(),
// this also sets an attribute on the first vertices of
// the primitive, if they don't already have an
// attribute set, just so they end up with *something*.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
post_apply_flat_attribute() {
if (!empty()) {
int num_lead_vertices = get_num_lead_vertices();
for (int i = 0; i < (int)size(); i++) {
EggVertex *vertex = get_vertex(i);
EggAttributes *component = get_component(max(i - num_lead_vertices, 0));
// Use set_normal() instead of copy_normal(), to avoid getting
// the morphs--we don't want them here, since we're just putting
// a bogus value on the normal anyway.
if (component->has_normal() && !vertex->has_normal()) {
vertex->set_normal(component->get_normal());
} else if (has_normal() && !vertex->has_normal()) {
vertex->set_normal(get_normal());
}
if (component->has_color() && !vertex->has_color()) {
vertex->set_color(component->get_color());
} else if (has_color() && !vertex->has_color()) {
vertex->set_color(get_color());
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::cleanup
// Access: Published, Virtual
// Description: Cleans up modeling errors in whatever context this
// makes sense. For instance, for a polygon, this calls
// remove_doubled_verts(true). For a point, it calls
// remove_nonunique_verts(). Returns true if the
// primitive is valid, or false if it is degenerate.
////////////////////////////////////////////////////////////////////
bool EggCompositePrimitive::
cleanup() {
return (int)size() >= get_num_lead_vertices() + 1;
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::prepare_add_vertex
// Access: Protected, Virtual
// Description: Marks the vertex as belonging to the primitive. This
// is an internal function called by the STL-like
// functions push_back() and insert(), in preparation
// for actually adding the vertex.
//
// i indicates the new position of the vertex in the
// list; n indicates the new number of vertices after
// the operation has completed.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
prepare_add_vertex(EggVertex *vertex, int i, int n) {
EggPrimitive::prepare_add_vertex(vertex, i, n);
int num_lead_vertices = get_num_lead_vertices();
if (n >= num_lead_vertices + 1) {
i = max(i - num_lead_vertices, 0);
nassertv(i <= (int)_components.size());
_components.insert(_components.begin() + i, new EggAttributes(*this));
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::prepare_remove_vertex
// Access: Protected, Virtual
// Description: Marks the vertex as removed from the primitive. This
// is an internal function called by the STL-like
// functions pop_back() and erase(), in preparation for
// actually doing the removal.
//
// i indicates the former position of the vertex in the
// list; n indicates the current number of vertices
// before the operation has completed.
//
// It is an error to attempt to remove a vertex that is
// not already a vertex of this primitive.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
prepare_remove_vertex(EggVertex *vertex, int i, int n) {
EggPrimitive::prepare_remove_vertex(vertex, i, n);
int num_lead_vertices = get_num_lead_vertices();
if (n >= num_lead_vertices + 1) {
i = max(i - num_lead_vertices, 0);
nassertv(i < (int)_components.size());
delete _components[i];
_components.erase(_components.begin() + i);
}
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::triangulate_poly
// Access: Protected, Virtual
// Description: Fills the container up with EggPolygons that
// represent the component triangles of this triangle
// strip.
//
// It is assumed that the EggCompositePrimitive is not
// already a child of any other group when this function
// is called.
//
// Returns true if the triangulation is successful, or
// false if there was some error (in which case the
// container may contain some partial triangulation).
////////////////////////////////////////////////////////////////////
bool EggCompositePrimitive::
do_triangulate(EggGroupNode *container) const {
container->add_child((EggCompositePrimitive *)this);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggCompositePrimitive::write_body
// Access: Protected
// Description: Writes the attributes and the vertices referenced by
// the primitive to the indicated output stream in Egg
// format.
////////////////////////////////////////////////////////////////////
void EggCompositePrimitive::
write_body(ostream &out, int indent_level) const {
EggPrimitive::write_body(out, indent_level);
for (int i = 0; i < get_num_components(); i++) {
const EggAttributes *attrib = get_component(i);
if (attrib->compare_to(*this) != 0 &&
(attrib->has_color() || attrib->has_normal())) {
indent(out, indent_level)
<< "<Component> " << i << " {\n";
attrib->write(out, indent_level + 2);
indent(out, indent_level) << "}\n";
}
}
}