445 lines
14 KiB
C++
445 lines
14 KiB
C++
/**
|
|
* 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."
|
|
*
|
|
* @file eggCompositePrimitive.cxx
|
|
* @author drose
|
|
* @date 2005-03-13
|
|
*/
|
|
|
|
#include "eggCompositePrimitive.h"
|
|
#include "eggGroupNode.h"
|
|
#include "eggVertexPool.h"
|
|
|
|
TypeHandle EggCompositePrimitive::_type_handle;
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
EggCompositePrimitive::
|
|
~EggCompositePrimitive() {
|
|
// Every derived class of EggCompositePrimitive must call clear() in its
|
|
// destructor.
|
|
nassertv(_components.empty());
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* get_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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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(LColor(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(LColor(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(LColor(1.0f, 1.0f, 1.0f, 1.0f));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case S_unknown:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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";
|
|
}
|
|
}
|
|
}
|