mirror of
				https://github.com/panda3d/panda3d.git
				synced 2025-11-03 20:13:57 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			1893 lines
		
	
	
		
			67 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1893 lines
		
	
	
		
			67 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Filename: eggGroupNode.cxx
 | 
						|
// Created by:  drose (16Jan99)
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// 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 "eggGroupNode.h"
 | 
						|
#include "eggCoordinateSystem.h"
 | 
						|
#include "eggData.h"
 | 
						|
#include "eggFilenameNode.h"
 | 
						|
#include "eggExternalReference.h"
 | 
						|
#include "eggPrimitive.h"
 | 
						|
#include "eggPolygon.h"
 | 
						|
#include "eggCompositePrimitive.h"
 | 
						|
#include "eggMesher.h"
 | 
						|
#include "eggVertexPool.h"
 | 
						|
#include "eggVertex.h"
 | 
						|
#include "eggTextureCollection.h"
 | 
						|
#include "eggMaterialCollection.h"
 | 
						|
#include "pt_EggTexture.h"
 | 
						|
#include "pt_EggMaterial.h"
 | 
						|
#include "config_egg.h"
 | 
						|
 | 
						|
#include "dSearchPath.h"
 | 
						|
#include "deg_2_rad.h"
 | 
						|
#include "dcast.h"
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
TypeHandle EggGroupNode::_type_handle;
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::Copy constructor
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::
 | 
						|
EggGroupNode(const EggGroupNode ©) : EggNode(copy) {
 | 
						|
  if (!copy.empty()) {
 | 
						|
    egg_cat.warning()
 | 
						|
      << "The EggGroupNode copy constructor does not copy children!\n";
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::Copy assignment operator
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode &EggGroupNode::
 | 
						|
operator =(const EggGroupNode ©) {
 | 
						|
  if (!copy.empty()) {
 | 
						|
    egg_cat.warning()
 | 
						|
      << "The EggGroupNode copy assignment does not copy children!\n";
 | 
						|
  }
 | 
						|
  EggNode::operator =(copy);
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::Destructor
 | 
						|
//       Access: Published, Virtual
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::
 | 
						|
~EggGroupNode() {
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::write
 | 
						|
//       Access: Published, Virtual
 | 
						|
//  Description: Writes the group and all of its children to the
 | 
						|
//               indicated output stream in Egg format.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
write(ostream &out, int indent_level) const {
 | 
						|
  iterator i;
 | 
						|
 | 
						|
  // Since joints tend to reference vertex pools, which sometimes
 | 
						|
  // appear later in the file, and since generally non-joints don't
 | 
						|
  // reference joints, we try to maximize our chance of writing out a
 | 
						|
  // one-pass readable egg file by writing joints at the end of the
 | 
						|
  // list of children of a particular node.
 | 
						|
 | 
						|
  for (i = begin(); i != end(); ++i) {
 | 
						|
    PT(EggNode) child = (*i);
 | 
						|
    if (!child->is_joint()) {
 | 
						|
      child->write(out, indent_level);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (i = begin(); i != end(); ++i) {
 | 
						|
    PT(EggNode) child = (*i);
 | 
						|
    if (child->is_joint()) {
 | 
						|
      child->write(out, indent_level);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::begin
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::iterator EggGroupNode::
 | 
						|
begin() const {
 | 
						|
  return _children.begin();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::end
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::iterator EggGroupNode::
 | 
						|
end() const {
 | 
						|
  return _children.end();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::rbegin
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::reverse_iterator EggGroupNode::
 | 
						|
rbegin() const {
 | 
						|
  return _children.rbegin();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::rend
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::reverse_iterator EggGroupNode::
 | 
						|
rend() const {
 | 
						|
  return _children.rend();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::insert
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::iterator EggGroupNode::
 | 
						|
insert(iterator position, PT(EggNode) x) {
 | 
						|
  prepare_add_child(x);
 | 
						|
  return _children.insert((Children::iterator &)position, x);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::erase
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::iterator EggGroupNode::
 | 
						|
erase(iterator position) {
 | 
						|
  prepare_remove_child(*position);
 | 
						|
  return _children.erase((Children::iterator &)position);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::erase
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::iterator EggGroupNode::
 | 
						|
erase(iterator first, iterator last) {
 | 
						|
  iterator i;
 | 
						|
  for (i = first; i != last; ++i) {
 | 
						|
    prepare_remove_child(*i);
 | 
						|
  }
 | 
						|
  return _children.erase((Children::iterator &)first,
 | 
						|
                         (Children::iterator &)last);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::replace
 | 
						|
//       Access: Published
 | 
						|
//  Description: Replaces the node at the indicated position with
 | 
						|
//               the indicated node.  It is an error to call this
 | 
						|
//               with an invalid position iterator (e.g. end()).
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
replace(iterator position, PT(EggNode) x) {
 | 
						|
  nassertv(position != end());
 | 
						|
 | 
						|
  prepare_remove_child(*position);
 | 
						|
  prepare_add_child(x);
 | 
						|
  *(Children::iterator &)position = x;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::empty
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
bool EggGroupNode::
 | 
						|
empty() const {
 | 
						|
  return _children.empty();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::size
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggGroupNode::size_type EggGroupNode::
 | 
						|
size() const {
 | 
						|
  return _children.size();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::clear
 | 
						|
//       Access: Published
 | 
						|
//  Description:
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
clear() {
 | 
						|
  erase(begin(), end());
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::get_first_child
 | 
						|
//       Access: Published
 | 
						|
//  Description: Returns the first child in the group's list of
 | 
						|
//               children, or NULL if the list of children is empty.
 | 
						|
//               Can be used with get_next_child() to return the
 | 
						|
//               complete list of children without using the iterator
 | 
						|
//               class; however, this is non-thread-safe, and so is
 | 
						|
//               not recommended except for languages other than C++
 | 
						|
//               which cannot use the iterators.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggNode *EggGroupNode::
 | 
						|
get_first_child() {
 | 
						|
  _gnc_iterator = begin();
 | 
						|
  return get_next_child();
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::get_next_child
 | 
						|
//       Access: Published
 | 
						|
//  Description: Returns the next child in the group's list of
 | 
						|
//               children since the last call to get_first_child() or
 | 
						|
//               get_next_child(), or NULL if the last child has been
 | 
						|
//               returned.  Can be used with get_first_child() to
 | 
						|
//               return the complete list of children without using
 | 
						|
//               the iterator class; however, this is non-thread-safe,
 | 
						|
//               and so is not recommended except for languages other
 | 
						|
//               than C++ which cannot use the iterators.
 | 
						|
//
 | 
						|
//               It is an error to call this without previously
 | 
						|
//               calling get_first_child().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggNode *EggGroupNode::
 | 
						|
get_next_child() {
 | 
						|
  if (_gnc_iterator != end()) {
 | 
						|
    return *_gnc_iterator++;
 | 
						|
  }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::add_child
 | 
						|
//       Access: Published
 | 
						|
//  Description: Adds the indicated child to the group and returns it.
 | 
						|
//               If the child node is already a child of some other
 | 
						|
//               node, removes it first.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggNode *EggGroupNode::
 | 
						|
add_child(EggNode *node) {
 | 
						|
  test_ref_count_integrity();
 | 
						|
  PT(EggNode) ptnode = node;
 | 
						|
  if (node->_parent != NULL) {
 | 
						|
    node->_parent->remove_child(node);
 | 
						|
  }
 | 
						|
  prepare_add_child(node);
 | 
						|
  _children.push_back(node);
 | 
						|
  return node;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::remove_child
 | 
						|
//       Access: Published
 | 
						|
//  Description: Removes the indicated child node from the group and
 | 
						|
//               returns it.  If the child was not already in the
 | 
						|
//               group, does nothing and returns NULL.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
PT(EggNode) EggGroupNode::
 | 
						|
remove_child(EggNode *node) {
 | 
						|
  PT(EggNode) ptnode = node;
 | 
						|
  iterator i = find(begin(), end(), ptnode);
 | 
						|
  if (i == end()) {
 | 
						|
    return PT(EggNode)();
 | 
						|
  } else {
 | 
						|
    // erase() calls prepare_remove_child().
 | 
						|
    erase(i);
 | 
						|
    return ptnode;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::steal_children
 | 
						|
//       Access: Published
 | 
						|
//  Description: Moves all the children from the other node to this
 | 
						|
//               one.  This is especially useful because the group
 | 
						|
//               node copy assignment operator does not copy children.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
steal_children(EggGroupNode &other) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = other._children.begin();
 | 
						|
       ci != other._children.end();
 | 
						|
       ++ci) {
 | 
						|
    other.prepare_remove_child(*ci);
 | 
						|
    prepare_add_child(*ci);
 | 
						|
  }
 | 
						|
 | 
						|
  _children.splice(_children.end(), other._children);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::find_child
 | 
						|
//       Access: Published
 | 
						|
//  Description: Returns the child of this node whose name is the
 | 
						|
//               indicated string, or NULL if there is no child of
 | 
						|
//               this node by that name.  Does not search recursively.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
EggNode *EggGroupNode::
 | 
						|
find_child(const string &name) const {
 | 
						|
  Children::const_iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = (*ci);
 | 
						|
    if (child->get_name() == name) {
 | 
						|
      return child;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::has_absolute_pathnames
 | 
						|
//       Access: Published
 | 
						|
//  Description: Returns true if any nodes at this level and below
 | 
						|
//               include a reference to a file via an absolute
 | 
						|
//               pathname, or false if all references are relative.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
bool EggGroupNode::
 | 
						|
has_absolute_pathnames() const {
 | 
						|
  Children::const_iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
    if (child->is_of_type(EggTexture::get_class_type())) {
 | 
						|
      EggTexture *tex = DCAST(EggTexture, child);
 | 
						|
      if (!tex->get_filename().is_local()) {
 | 
						|
        if (egg_cat.is_debug()) {
 | 
						|
          egg_cat.debug()
 | 
						|
            << "Absolute pathname: " << tex->get_filename()
 | 
						|
            << "\n";
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
 | 
						|
      if (tex->has_alpha_filename()) {
 | 
						|
        if (!tex->get_alpha_filename().is_local()) {
 | 
						|
          if (egg_cat.is_debug()) {
 | 
						|
            egg_cat.debug()
 | 
						|
              << "Absolute pathname: " << tex->get_alpha_filename()
 | 
						|
              << "\n";
 | 
						|
          }
 | 
						|
          return true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggFilenameNode::get_class_type())) {
 | 
						|
      EggFilenameNode *fnode = DCAST(EggFilenameNode, child);
 | 
						|
      if (!fnode->get_filename().is_local()) {
 | 
						|
        if (egg_cat.is_debug()) {
 | 
						|
          egg_cat.debug()
 | 
						|
            << "Absolute pathname: " << fnode->get_filename()
 | 
						|
            << "\n";
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (DCAST(EggGroupNode, child)->has_absolute_pathnames()) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::resolve_filenames
 | 
						|
//       Access: Published
 | 
						|
//  Description: Walks the tree and attempts to resolve any filenames
 | 
						|
//               encountered.  This looks up filenames in the search
 | 
						|
//               path, etc.  It does not automatically search the
 | 
						|
//               egg_path for missing files.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
resolve_filenames(const DSearchPath &searchpath) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
    if (child->is_of_type(EggTexture::get_class_type())) {
 | 
						|
      EggTexture *tex = DCAST(EggTexture, child);
 | 
						|
      Filename tex_filename = tex->get_filename();
 | 
						|
      tex_filename.resolve_filename(searchpath);
 | 
						|
      tex->set_filename(tex_filename);
 | 
						|
      
 | 
						|
      if (tex->has_alpha_filename()) {
 | 
						|
        Filename alpha_filename = tex->get_alpha_filename();
 | 
						|
        alpha_filename.resolve_filename(searchpath);
 | 
						|
        tex->set_alpha_filename(alpha_filename);
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggFilenameNode::get_class_type())) {
 | 
						|
      EggFilenameNode *fnode = DCAST(EggFilenameNode, child);
 | 
						|
      Filename filename = fnode->get_filename();
 | 
						|
      filename.resolve_filename(searchpath, fnode->get_default_extension());
 | 
						|
      fnode->set_filename(filename);
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->resolve_filenames(searchpath);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::force_filenames
 | 
						|
//       Access: Published
 | 
						|
//  Description: Similar to resolve_filenames, but each non-absolute
 | 
						|
//               filename encountered is arbitrarily taken to be in
 | 
						|
//               the indicated directory, whether or not the so-named
 | 
						|
//               filename exists.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
force_filenames(const Filename &directory) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
    if (child->is_of_type(EggTexture::get_class_type())) {
 | 
						|
      EggTexture *tex = DCAST(EggTexture, child);
 | 
						|
      Filename tex_filename = tex->get_filename();
 | 
						|
      if (tex_filename.is_local()) {
 | 
						|
        tex->set_filename(Filename(directory, tex_filename));
 | 
						|
      }
 | 
						|
 | 
						|
      if (tex->has_alpha_filename()) {
 | 
						|
        Filename alpha_filename = tex->get_alpha_filename();
 | 
						|
        if (alpha_filename.is_local()) {
 | 
						|
          tex->set_alpha_filename(Filename(directory, alpha_filename));
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggFilenameNode::get_class_type())) {
 | 
						|
      EggFilenameNode *fnode = DCAST(EggFilenameNode, child);
 | 
						|
      Filename filename = fnode->get_filename();
 | 
						|
      if (filename.is_local()) {
 | 
						|
        fnode->set_filename(Filename(directory, filename));
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->force_filenames(directory);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::reverse_vertex_ordering
 | 
						|
//       Access: Published
 | 
						|
//  Description: Reverses the vertex ordering of all polygons defined
 | 
						|
//               at this node and below.  Does not change the surface
 | 
						|
//               normals, if any.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
reverse_vertex_ordering() {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->reverse_vertex_ordering();
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->reverse_vertex_ordering();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::recompute_vertex_normals
 | 
						|
//       Access: Published
 | 
						|
//  Description: Recomputes all the vertex normals for polygon
 | 
						|
//               geometry at this group node and below so that they
 | 
						|
//               accurately reflect the vertex positions.  A shared
 | 
						|
//               edge between two polygons (even in different groups)
 | 
						|
//               is considered smooth if the angle between the two
 | 
						|
//               edges is less than threshold degrees.
 | 
						|
//
 | 
						|
//               This function also removes degenerate polygons that
 | 
						|
//               do not have enough vertices to define a normal.  It
 | 
						|
//               does not affect normals for other kinds of primitives
 | 
						|
//               like Nurbs or Points.
 | 
						|
//
 | 
						|
//               This function does not remove or adjust vertices in
 | 
						|
//               the vertex pool; it only adds new vertices with the
 | 
						|
//               correct normals.  Thus, it is a good idea to call
 | 
						|
//               remove_unused_vertices() after calling this.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
recompute_vertex_normals(double threshold, CoordinateSystem cs) {
 | 
						|
  // First, collect all the vertices together with their shared
 | 
						|
  // polygons.
 | 
						|
  NVertexCollection collection;
 | 
						|
  r_collect_vertex_normals(collection, threshold, cs);
 | 
						|
 | 
						|
  // Now bust them into separate groups according to the edge
 | 
						|
  // threshold.  Two polygons that share a vertex belong in the same
 | 
						|
  // group only if the angle between their normals is within the
 | 
						|
  // threshold.
 | 
						|
 | 
						|
  double cos_angle = cos(deg_2_rad(threshold));
 | 
						|
 | 
						|
  NVertexCollection::iterator ci;
 | 
						|
  for (ci = collection.begin(); ci != collection.end(); ++ci) {
 | 
						|
    NVertexGroup &group = (*ci).second;
 | 
						|
 | 
						|
    // Here's a group of polygons that share a vertex.  Build up a new
 | 
						|
    // group that consists of just the first polygon and all the ones
 | 
						|
    // that are within threshold degrees from it.
 | 
						|
    NVertexGroup::iterator gi;
 | 
						|
    gi = group.begin();
 | 
						|
    while (gi != group.end()) {
 | 
						|
      const NVertexReference &base_ref = (*gi);
 | 
						|
      NVertexGroup new_group;
 | 
						|
      NVertexGroup leftover_group;
 | 
						|
      new_group.push_back(base_ref);
 | 
						|
      ++gi;
 | 
						|
 | 
						|
      while (gi != group.end()) {
 | 
						|
        const NVertexReference &ref = (*gi);
 | 
						|
        double dot = base_ref._normal.dot(ref._normal);
 | 
						|
        if (dot > cos_angle) {
 | 
						|
          // These polygons are close enough to the same angle.
 | 
						|
          new_group.push_back(ref);
 | 
						|
        } else {
 | 
						|
          // These polygons are not.
 | 
						|
          leftover_group.push_back(ref);
 | 
						|
        }
 | 
						|
        ++gi;
 | 
						|
      }
 | 
						|
 | 
						|
      // Now new_group is a collection of connected polygons and the
 | 
						|
      // vertices that connect them.  Smooth these vertices.
 | 
						|
      do_compute_vertex_normals(new_group);
 | 
						|
 | 
						|
      // And reset the group of remaining polygons.
 | 
						|
      group.swap(leftover_group);
 | 
						|
      gi = group.begin();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::recompute_polygon_normals
 | 
						|
//       Access: Published
 | 
						|
//  Description: Recomputes all the polygon normals for polygon
 | 
						|
//               geometry at this group node and below so that they
 | 
						|
//               accurately reflect the vertex positions.  Normals are
 | 
						|
//               removed from the vertices and defined only on
 | 
						|
//               polygons, giving the geometry a faceted appearance.
 | 
						|
//
 | 
						|
//               This function also removes degenerate polygons that
 | 
						|
//               do not have enough vertices to define a normal.  It
 | 
						|
//               does not affect normals for other kinds of primitives
 | 
						|
//               like Nurbs or Points.
 | 
						|
//
 | 
						|
//               This function does not remove or adjust vertices in
 | 
						|
//               the vertex pool; it only adds new vertices with the
 | 
						|
//               normals removed.  Thus, it is a good idea to call
 | 
						|
//               remove_unused_vertices() after calling this.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
recompute_polygon_normals(CoordinateSystem cs) {
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPolygon::get_class_type())) {
 | 
						|
      EggPolygon *polygon = DCAST(EggPolygon, child);
 | 
						|
 | 
						|
      if (!polygon->recompute_polygon_normal(cs)) {
 | 
						|
        // The polygon is degenerate.  Remove it.
 | 
						|
        prepare_remove_child(child);
 | 
						|
        _children.erase(ci);
 | 
						|
 | 
						|
      } else {
 | 
						|
        // Remove the normal from each polygon vertex.
 | 
						|
        size_t num_vertices = polygon->size();
 | 
						|
        for (size_t i = 0; i < num_vertices; i++) {
 | 
						|
          EggVertex *vertex = polygon->get_vertex(i);
 | 
						|
          EggVertexPool *pool = vertex->get_pool();
 | 
						|
 | 
						|
          if (vertex->has_normal()) {
 | 
						|
            EggVertex new_vertex(*vertex);
 | 
						|
            new_vertex.clear_normal();
 | 
						|
            EggVertex *unique = pool->create_unique_vertex(new_vertex);
 | 
						|
            unique->copy_grefs_from(*vertex);
 | 
						|
 | 
						|
            polygon->set_vertex(i, unique);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->recompute_polygon_normals(cs);
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::strip_normals
 | 
						|
//       Access: Published
 | 
						|
//  Description: Removes all normals from primitives, and the vertices
 | 
						|
//               they reference, at this node and below.
 | 
						|
//
 | 
						|
//               This function does not remove or adjust vertices in
 | 
						|
//               the vertex pool; it only adds new vertices with the
 | 
						|
//               normal removed.  Thus, it is a good idea to call
 | 
						|
//               remove_unused_vertices() after calling this.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
strip_normals() {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->clear_normal();
 | 
						|
 | 
						|
      // Remove the normal from each prim vertex.
 | 
						|
      size_t num_vertices = prim->size();
 | 
						|
      for (size_t i = 0; i < num_vertices; i++) {
 | 
						|
        EggVertex *vertex = prim->get_vertex(i);
 | 
						|
        EggVertexPool *pool = vertex->get_pool();
 | 
						|
 | 
						|
        if (vertex->has_normal()) {
 | 
						|
          EggVertex new_vertex(*vertex);
 | 
						|
          new_vertex.clear_normal();
 | 
						|
          EggVertex *unique = pool->create_unique_vertex(new_vertex);
 | 
						|
          unique->copy_grefs_from(*vertex);
 | 
						|
 | 
						|
          prim->set_vertex(i, unique);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->strip_normals();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::recompute_tangent_binormal
 | 
						|
//       Access: Published
 | 
						|
//  Description: This function recomputes the tangent and binormal for
 | 
						|
//               the named texture coordinate set for all vertices at
 | 
						|
//               this level and below.  Use the empty string for the
 | 
						|
//               default texture coordinate set.
 | 
						|
//
 | 
						|
//               It is necessary for each vertex to already have a
 | 
						|
//               normal (or at least a polygon normal), as well as a
 | 
						|
//               texture coordinate in the named texture coordinate
 | 
						|
//               set, before calling this function.  You might precede
 | 
						|
//               this with recompute_vertex_normals() to ensure that
 | 
						|
//               the normals exist.
 | 
						|
//
 | 
						|
//               Like recompute_vertex_normals(), this function does
 | 
						|
//               not remove or adjust vertices in the vertex pool; it
 | 
						|
//               only adds new vertices with the new tangents and
 | 
						|
//               binormals computed.  Thus, it is a good idea to call
 | 
						|
//               remove_unused_vertices() after calling this.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
recompute_tangent_binormal(const GlobPattern &uv_name) {
 | 
						|
  // First, collect all the vertices together with their shared
 | 
						|
  // polygons.
 | 
						|
  TBNVertexCollection collection;
 | 
						|
  r_collect_tangent_binormal(uv_name, collection);
 | 
						|
 | 
						|
  // Now compute the tangent and binormal separately for each common
 | 
						|
  // group of vertices.
 | 
						|
  TBNVertexCollection::const_iterator ci;
 | 
						|
  for (ci = collection.begin(); ci != collection.end(); ++ci) {
 | 
						|
    const TBNVertexValue &value = (*ci).first;
 | 
						|
    const TBNVertexGroup &group = (*ci).second;
 | 
						|
 | 
						|
    do_compute_tangent_binormal(value, group);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::triangulate_polygons
 | 
						|
//       Access: Published
 | 
						|
//  Description: Replace all higher-order polygons at this point in
 | 
						|
//               the scene graph and below with triangles.  Returns
 | 
						|
//               the total number of new triangles produced, less
 | 
						|
//               degenerate polygons removed.
 | 
						|
//
 | 
						|
//               If flags contains T_polygon and T_convex, both
 | 
						|
//               concave and convex polygons will be subdivided into
 | 
						|
//               triangles; with only T_polygon, only concave polygons
 | 
						|
//               will be subdivided, and convex polygons will be
 | 
						|
//               largely unchanged.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
int EggGroupNode::
 | 
						|
triangulate_polygons(int flags) {
 | 
						|
  int num_produced = 0;
 | 
						|
 | 
						|
  Children children_copy = _children;
 | 
						|
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = children_copy.begin();
 | 
						|
       ci != children_copy.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = (*ci);
 | 
						|
 | 
						|
    if (child->is_of_type(EggPolygon::get_class_type())) {
 | 
						|
      if ((flags & T_polygon) != 0) {
 | 
						|
        EggPolygon *poly = DCAST(EggPolygon, child);
 | 
						|
        poly->triangulate_in_place((flags & T_convex) != 0);
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggCompositePrimitive::get_class_type())) {
 | 
						|
      if ((flags & T_composite) != 0) {
 | 
						|
        EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, child);
 | 
						|
        comp->triangulate_in_place();
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if ((flags & T_recurse) != 0) {
 | 
						|
        num_produced += DCAST(EggGroupNode, child)->triangulate_polygons(flags);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  num_produced += max(0, (int)(_children.size() - children_copy.size()));
 | 
						|
  return num_produced;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::mesh_triangles
 | 
						|
//       Access: Published
 | 
						|
//  Description: Combine triangles together into triangle strips, at
 | 
						|
//               this group and below.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
mesh_triangles(int flags) {
 | 
						|
  EggMesher mesher;
 | 
						|
  mesher.mesh(this, (flags & T_flat_shaded) != 0);
 | 
						|
 | 
						|
  if ((flags & T_recurse) != 0) {
 | 
						|
    EggGroupNode::iterator ci;
 | 
						|
    for (ci = begin(); ci != end(); ++ci) {
 | 
						|
      if ((*ci)->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
        EggGroupNode *group_child = DCAST(EggGroupNode, *ci);
 | 
						|
        group_child->mesh_triangles(flags);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::rename_nodes
 | 
						|
//       Access: Published
 | 
						|
//  Description: Rename by stripping out the prefix
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
int EggGroupNode::
 | 
						|
rename_nodes(vector_string strip_prefix, bool recurse) {
 | 
						|
  int num_renamed = 0;
 | 
						|
  for (unsigned int ni = 0; ni < strip_prefix.size(); ++ni) {
 | 
						|
    string axe_name = strip_prefix[ni];
 | 
						|
    if (this->get_name().substr(0, axe_name.size()) == axe_name) {
 | 
						|
      string new_name = this->get_name().substr(axe_name.size());
 | 
						|
      //cout << "renaming " << this->get_name() << "->" << new_name << endl;
 | 
						|
      this->set_name(new_name);
 | 
						|
      num_renamed += 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (recurse) {
 | 
						|
    EggGroupNode::iterator ci;
 | 
						|
    for (ci = begin(); ci != end(); ++ci) {
 | 
						|
      if ((*ci)->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
        EggGroupNode *group_child = DCAST(EggGroupNode, *ci);
 | 
						|
        num_renamed += group_child->rename_nodes(strip_prefix, recurse);
 | 
						|
      }
 | 
						|
      else if ((*ci)->is_of_type(EggNode::get_class_type())) {
 | 
						|
        EggNode *node_child = DCAST(EggNode, *ci);
 | 
						|
        num_renamed += node_child->rename_node(strip_prefix);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return num_renamed;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::remove_unused_vertices
 | 
						|
//       Access: Published
 | 
						|
//  Description: Removes all vertices from VertexPools within this
 | 
						|
//               group or below that are not referenced by at least
 | 
						|
//               one primitive.  Also collapses together equivalent
 | 
						|
//               vertices, and renumbers all vertices after the
 | 
						|
//               operation so their indices are consecutive, beginning
 | 
						|
//               at zero.  Returns the total number of vertices
 | 
						|
//               removed.
 | 
						|
//
 | 
						|
//               Note that this operates on the VertexPools within
 | 
						|
//               this group level, without respect to primitives that
 | 
						|
//               reference these vertices (unlike other functions like
 | 
						|
//               strip_normals()).  It is therefore most useful to
 | 
						|
//               call this on the EggData root, rather than on a
 | 
						|
//               subgroup within the hierarchy, since a VertexPool may
 | 
						|
//               appear anywhere in the hierarchy.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
int EggGroupNode::
 | 
						|
remove_unused_vertices(bool recurse) {
 | 
						|
  int num_removed = 0;
 | 
						|
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggVertexPool::get_class_type())) {
 | 
						|
      EggVertexPool *vpool = DCAST(EggVertexPool, child);
 | 
						|
      num_removed += vpool->remove_unused_vertices();
 | 
						|
 | 
						|
      if (vpool->empty()) {
 | 
						|
        // If, after removing all the vertices, there's nothing left
 | 
						|
        // in the vertex pool, then remove the whole vertex pool.
 | 
						|
        _children.erase(ci);
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        num_removed += DCAST(EggGroupNode, child)->remove_unused_vertices(recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
 | 
						|
  return num_removed;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::remove_invalid_primitives
 | 
						|
//       Access: Published
 | 
						|
//  Description: Removes primitives at this level and below which
 | 
						|
//               appear to be degenerate; e.g. polygons with fewer
 | 
						|
//               than 3 vertices, etc.  Returns the number of
 | 
						|
//               primitives removed.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
int EggGroupNode::
 | 
						|
remove_invalid_primitives(bool recurse) {
 | 
						|
  int num_removed = 0;
 | 
						|
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      if (!prim->cleanup()) {
 | 
						|
        _children.erase(ci);
 | 
						|
        num_removed++;
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        num_removed += DCAST(EggGroupNode, child)->remove_invalid_primitives(recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
 | 
						|
  return num_removed;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::clear_connected_shading
 | 
						|
//       Access: Published
 | 
						|
//  Description: Resets the connected_shading information on all
 | 
						|
//               primitives at this node and below, so that it may be
 | 
						|
//               accurately rederived by the next call to
 | 
						|
//               get_connected_shading().
 | 
						|
//
 | 
						|
//               It may be a good idea to call
 | 
						|
//               remove_unused_vertices() as well, to establish the
 | 
						|
//               correct connectivity between common vertices.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
clear_connected_shading() {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->clear_connected_shading();
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->clear_connected_shading();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::get_connected_shading
 | 
						|
//       Access: Published
 | 
						|
//  Description: Queries the connected_shading information on all
 | 
						|
//               primitives at this node and below, to ensure that it
 | 
						|
//               has been completely filled in before we start mucking
 | 
						|
//               around with vertices.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
get_connected_shading() {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->get_connected_shading();
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      DCAST(EggGroupNode, child)->get_connected_shading();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::rebuild_vertex_pool
 | 
						|
//       Access: Published
 | 
						|
//  Description: Copies vertices used by the primitives at this group
 | 
						|
//               node (and below, if recurse is true) into the
 | 
						|
//               indicated vertex pool, and updates the primitives to
 | 
						|
//               reference this pool.  It is up to the caller to
 | 
						|
//               parent the new vertex pool somewhere appropriate in
 | 
						|
//               the egg hierarchy.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
rebuild_vertex_pool(EggVertexPool *vertex_pool, bool recurse) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggCompositePrimitive::get_class_type())) {
 | 
						|
      typedef pvector< PT(EggVertex) > Vertices;
 | 
						|
      typedef pvector<EggAttributes> Attributes;
 | 
						|
      Vertices vertices;
 | 
						|
      Attributes attributes;
 | 
						|
 | 
						|
      EggCompositePrimitive *prim = DCAST(EggCompositePrimitive, child);
 | 
						|
      EggPrimitive::const_iterator pi;
 | 
						|
      for (pi = prim->begin(); pi != prim->end(); ++pi) {
 | 
						|
        vertices.push_back(*pi);
 | 
						|
      }
 | 
						|
      int i;
 | 
						|
      int num_components = prim->get_num_components();
 | 
						|
      for (i = 0; i < num_components; i++) {
 | 
						|
        attributes.push_back(*prim->get_component(i));
 | 
						|
      }
 | 
						|
 | 
						|
      prim->clear();
 | 
						|
 | 
						|
      Vertices::const_iterator vi;
 | 
						|
      for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
 | 
						|
        EggVertex *vertex = (*vi);
 | 
						|
        EggVertex *new_vertex = vertex_pool->create_unique_vertex(*vertex);
 | 
						|
        new_vertex->copy_grefs_from(*vertex);
 | 
						|
        prim->add_vertex(new_vertex);
 | 
						|
      }
 | 
						|
      for (i = 0; i < num_components; i++) {
 | 
						|
        prim->set_component(i, &attributes[i]);
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      typedef pvector< PT(EggVertex) > Vertices;
 | 
						|
      Vertices vertices;
 | 
						|
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      EggPrimitive::const_iterator pi;
 | 
						|
      for (pi = prim->begin(); pi != prim->end(); ++pi) {
 | 
						|
        vertices.push_back(*pi);
 | 
						|
      }
 | 
						|
 | 
						|
      prim->clear();
 | 
						|
 | 
						|
      Vertices::const_iterator vi;
 | 
						|
      for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
 | 
						|
        EggVertex *vertex = (*vi);
 | 
						|
        EggVertex *new_vertex = vertex_pool->create_unique_vertex(*vertex);
 | 
						|
        new_vertex->copy_grefs_from(*vertex);
 | 
						|
        prim->add_vertex(new_vertex);
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        DCAST(EggGroupNode, child)->rebuild_vertex_pool(vertex_pool, recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::unify_attributes
 | 
						|
//       Access: Published
 | 
						|
//  Description: Applies per-vertex normal and color to all vertices,
 | 
						|
//               if they are in fact per-vertex (and different for
 | 
						|
//               each vertex), or moves them to the primitive if they
 | 
						|
//               are all the same.
 | 
						|
//
 | 
						|
//               After this call, either the primitive will have
 | 
						|
//               normals or its vertices will, but not both.  Ditto
 | 
						|
//               for colors.
 | 
						|
//
 | 
						|
//               If use_connected_shading is true, each polygon is
 | 
						|
//               considered in conjunction with all connected
 | 
						|
//               polygons; otherwise, each polygon is considered
 | 
						|
//               individually.
 | 
						|
//
 | 
						|
//               This may create redundant vertices in the vertex
 | 
						|
//               pool, so it may be a good idea to follow this up with
 | 
						|
//               remove_unused_vertices().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
unify_attributes(bool use_connected_shading, bool recurse) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      if (use_connected_shading) {
 | 
						|
        prim->unify_attributes(prim->get_connected_shading());
 | 
						|
      } else {
 | 
						|
        prim->unify_attributes(prim->get_shading());
 | 
						|
      }
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        DCAST(EggGroupNode, child)->unify_attributes(use_connected_shading, recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::apply_last_attribute
 | 
						|
//       Access: Published
 | 
						|
//  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 create redundant vertices in the vertex
 | 
						|
//               pool, so it may be a good idea to follow this up with
 | 
						|
//               remove_unused_vertices().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
apply_last_attribute(bool recurse) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->apply_last_attribute();
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        DCAST(EggGroupNode, child)->apply_last_attribute(recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::apply_first_attribute
 | 
						|
//       Access: Published
 | 
						|
//  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 create redundant vertices in the vertex
 | 
						|
//               pool, so it may be a good idea to follow this up with
 | 
						|
//               remove_unused_vertices().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
apply_first_attribute(bool recurse) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->apply_first_attribute();
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        DCAST(EggGroupNode, child)->apply_first_attribute(recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::post_apply_flat_attribute
 | 
						|
//       Access: Published
 | 
						|
//  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 EggGroupNode::
 | 
						|
post_apply_flat_attribute(bool recurse) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPrimitive::get_class_type())) {
 | 
						|
      EggPrimitive *prim = DCAST(EggPrimitive, child);
 | 
						|
      prim->post_apply_flat_attribute();
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      if (recurse) {
 | 
						|
        DCAST(EggGroupNode, child)->post_apply_flat_attribute(recurse);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::has_primitives
 | 
						|
//       Access: Published, Virtual
 | 
						|
//  Description: Returns true if there are any primitives
 | 
						|
//               (e.g. polygons) defined within this group or below,
 | 
						|
//               false otherwise.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
bool EggGroupNode::
 | 
						|
has_primitives() const {
 | 
						|
  Children::const_iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    if ((*ci)->has_primitives()) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::joint_has_primitives
 | 
						|
//       Access: Published, Virtual
 | 
						|
//  Description: Returns true if there are any primitives
 | 
						|
//               (e.g. polygons) defined within this group or below,
 | 
						|
//               but the search does not include nested joints.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
bool EggGroupNode::
 | 
						|
joint_has_primitives() const {
 | 
						|
  Children::const_iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = (*ci);
 | 
						|
 | 
						|
    if (!child->is_joint()) {
 | 
						|
      if (child->joint_has_primitives()) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::has_normals
 | 
						|
//       Access: Published, Virtual
 | 
						|
//  Description: Returns true if any of the primitives (e.g. polygons)
 | 
						|
//               defined within this group or below have either face
 | 
						|
//               or vertex normals defined, false otherwise.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
bool EggGroupNode::
 | 
						|
has_normals() const {
 | 
						|
  Children::const_iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    if ((*ci)->has_normals()) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::update_under
 | 
						|
//       Access: Protected, Virtual
 | 
						|
//  Description: This function is called from within EggGroupNode
 | 
						|
//               whenever the parentage of the node has changed.  It
 | 
						|
//               should update the depth and under_instance flags
 | 
						|
//               accordingly.
 | 
						|
//
 | 
						|
//               Offset is the difference between the old depth value
 | 
						|
//               and the new value.  It should be consistent with the
 | 
						|
//               supplied depth value.  If it is not, we have some
 | 
						|
//               error.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
update_under(int depth_offset) {
 | 
						|
  EggNode::update_under(depth_offset);
 | 
						|
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    nassertv((*ci)->get_parent() == this);
 | 
						|
    (*ci)->update_under(depth_offset);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_transform
 | 
						|
//       Access: Protected, Virtual
 | 
						|
//  Description: This is called from within the egg code by
 | 
						|
//               transform().  It applies a transformation matrix
 | 
						|
//               to the current node in some sensible way, then
 | 
						|
//               continues down the tree.
 | 
						|
//
 | 
						|
//               The first matrix is the transformation to apply; the
 | 
						|
//               second is its inverse.  The third parameter is the
 | 
						|
//               coordinate system we are changing to, or CS_default
 | 
						|
//               if we are not changing coordinate systems.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_transform(const LMatrix4d &mat, const LMatrix4d &inv,
 | 
						|
            CoordinateSystem to_cs) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    (*ci)->r_transform(mat, inv, to_cs);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_transform_vertices
 | 
						|
//       Access: Protected, Virtual
 | 
						|
//  Description: This is called from within the egg code by
 | 
						|
//               transform_vertices_only()().  It applies a
 | 
						|
//               transformation matrix to the current node in some
 | 
						|
//               sensible way (if the current node is a vertex pool
 | 
						|
//               with vertices), then continues down the tree.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_transform_vertices(const LMatrix4d &mat) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    (*ci)->r_transform_vertices(mat);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_mark_coordsys
 | 
						|
//       Access: Protected, Virtual
 | 
						|
//  Description: This is only called immediately after loading an egg
 | 
						|
//               file from disk, to propagate the value found in the
 | 
						|
//               CoordinateSystem entry (or the default Y-up
 | 
						|
//               coordinate system) to all nodes that care about what
 | 
						|
//               the coordinate system is.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_mark_coordsys(CoordinateSystem cs) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    (*ci)->r_mark_coordsys(cs);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_flatten_transforms
 | 
						|
//       Access: Protected, Virtual
 | 
						|
//  Description: The recursive implementation of flatten_transforms().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_flatten_transforms() {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    (*ci)->r_flatten_transforms();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_apply_texmats
 | 
						|
//       Access: Protected, Virtual
 | 
						|
//  Description: The recursive implementation of apply_texmats().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_apply_texmats(EggTextureCollection &textures) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    (*ci)->r_apply_texmats(textures);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::find_coordsys_entry()
 | 
						|
//       Access: Protected
 | 
						|
//  Description: Walks the tree, looking for an EggCoordinateSystem
 | 
						|
//               entry.  If one is found, extracts it and returns its
 | 
						|
//               value.  If multiple entries are found, extracts all
 | 
						|
//               of them and returns CS_invalid if they disagree.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
CoordinateSystem EggGroupNode::
 | 
						|
find_coordsys_entry() {
 | 
						|
  CoordinateSystem coordsys = CS_default;
 | 
						|
 | 
						|
  // We can do this ci/cnext iteration through the list as we modify
 | 
						|
  // it, only because we know this works with an STL list type
 | 
						|
  // container.  If this were a vector or a set, this wouldn't
 | 
						|
  // necessarily work.
 | 
						|
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggCoordinateSystem::get_class_type())) {
 | 
						|
      CoordinateSystem new_cs =
 | 
						|
        DCAST(EggCoordinateSystem, child)->get_value();
 | 
						|
 | 
						|
      // Now remove the CoordinateSystem entry from our child list.
 | 
						|
      prepare_remove_child(child);
 | 
						|
      _children.erase(ci);
 | 
						|
 | 
						|
      if (new_cs != CS_default) {
 | 
						|
        if (coordsys != CS_default && coordsys != new_cs) {
 | 
						|
          coordsys = CS_invalid;
 | 
						|
        } else {
 | 
						|
          coordsys = new_cs;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      CoordinateSystem new_cs =
 | 
						|
        DCAST(EggGroupNode, child)->find_coordsys_entry();
 | 
						|
      if (new_cs != CS_default) {
 | 
						|
        if (coordsys != CS_default && coordsys != new_cs) {
 | 
						|
          coordsys = CS_invalid;
 | 
						|
        } else {
 | 
						|
          coordsys = new_cs;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
 | 
						|
  return coordsys;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::find_textures()
 | 
						|
//       Access: Protected
 | 
						|
//  Description: Walks the tree, looking for EggTextures.  Each
 | 
						|
//               EggTexture that is found is removed from the
 | 
						|
//               hierarchy and added to the EggTextureCollection.
 | 
						|
//               Returns the number of EggTextures found.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
int EggGroupNode::
 | 
						|
find_textures(EggTextureCollection *collection) {
 | 
						|
  int num_found = 0;
 | 
						|
 | 
						|
  // We can do this ci/cnext iteration through the list as we modify
 | 
						|
  // it, only because we know this works with an STL list type
 | 
						|
  // container.  If this were a vector or a set, this wouldn't
 | 
						|
  // necessarily work.
 | 
						|
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggTexture::get_class_type())) {
 | 
						|
      PT_EggTexture tex = DCAST(EggTexture, child);
 | 
						|
 | 
						|
      // Now remove the EggTexture entry from our child list.
 | 
						|
      prepare_remove_child(tex);
 | 
						|
      _children.erase(ci);
 | 
						|
 | 
						|
      // And add it to the collection.
 | 
						|
      collection->add_texture(tex);
 | 
						|
      num_found++;
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      num_found +=
 | 
						|
        DCAST(EggGroupNode, child)->find_textures(collection);
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
 | 
						|
  return num_found;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::find_materials()
 | 
						|
//       Access: Protected
 | 
						|
//  Description: Walks the tree, looking for EggMaterials.  Each
 | 
						|
//               EggMaterial that is found is removed from the
 | 
						|
//               hierarchy and added to the EggMaterialCollection.
 | 
						|
//               Returns the number of EggMaterials found.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
int EggGroupNode::
 | 
						|
find_materials(EggMaterialCollection *collection) {
 | 
						|
  int num_found = 0;
 | 
						|
 | 
						|
  // We can do this ci/cnext iteration through the list as we modify
 | 
						|
  // it, only because we know this works with an STL list type
 | 
						|
  // container.  If this were a vector or a set, this wouldn't
 | 
						|
  // necessarily work.
 | 
						|
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggMaterial::get_class_type())) {
 | 
						|
      PT_EggMaterial tex = DCAST(EggMaterial, child);
 | 
						|
 | 
						|
      // Now remove the EggMaterial entry from our child list.
 | 
						|
      prepare_remove_child(tex);
 | 
						|
      _children.erase(ci);
 | 
						|
 | 
						|
      // And add it to the collection.
 | 
						|
      collection->add_material(tex);
 | 
						|
      num_found++;
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      num_found +=
 | 
						|
        DCAST(EggGroupNode, child)->find_materials(collection);
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
 | 
						|
  return num_found;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_load_externals
 | 
						|
//       Access: Protected
 | 
						|
//  Description: Walks the tree and locates unloaded external
 | 
						|
//               reference nodes, which it attempts to locate and load
 | 
						|
//               in.  The reference node is replaced with the entire
 | 
						|
//               subtree loaded.  This is intended to be called from
 | 
						|
//               EggData::load_externals().
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
bool EggGroupNode::
 | 
						|
r_load_externals(const DSearchPath &searchpath, CoordinateSystem coordsys) {
 | 
						|
  bool success = true;
 | 
						|
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin();
 | 
						|
       ci != _children.end();
 | 
						|
       ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
    if (child->is_of_type(EggExternalReference::get_class_type())) {
 | 
						|
      PT(EggExternalReference) ref = DCAST(EggExternalReference, child);
 | 
						|
 | 
						|
      // Replace the reference with an empty group node.  When we load
 | 
						|
      // the external file successfully, we'll put its contents here.
 | 
						|
      Filename filename = ref->get_filename();
 | 
						|
      EggGroupNode *new_node =
 | 
						|
        new EggGroupNode(filename.get_basename_wo_extension());
 | 
						|
      replace(ci, new_node);
 | 
						|
 | 
						|
      if (!EggData::resolve_egg_filename(filename, searchpath)) {
 | 
						|
        egg_cat.error()
 | 
						|
          << "Could not locate " << filename << " in "
 | 
						|
          << searchpath << "\n";
 | 
						|
      } else {
 | 
						|
        // Now define a new EggData structure to hold the external
 | 
						|
        // reference, and load it.
 | 
						|
        EggData ext_data;
 | 
						|
        ext_data.set_coordinate_system(coordsys);
 | 
						|
        ext_data.set_auto_resolve_externals(true);
 | 
						|
        if (ext_data.read(filename)) {
 | 
						|
          // The external file was read correctly.  Add its contents
 | 
						|
          // into the tree at this point.
 | 
						|
          success =
 | 
						|
            ext_data.load_externals(searchpath)
 | 
						|
            && success;
 | 
						|
          new_node->steal_children(ext_data);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      EggGroupNode *group_child = DCAST(EggGroupNode, child);
 | 
						|
      success =
 | 
						|
        group_child->r_load_externals(searchpath, coordsys)
 | 
						|
        && success;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return success;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::prepare_add_child
 | 
						|
//       Access: Private
 | 
						|
//  Description: Marks the node as a child of the group.  This is an
 | 
						|
//               internal function called by the STL-like functions
 | 
						|
//               push_back() and insert(), in preparation for actually
 | 
						|
//               adding the child.
 | 
						|
//
 | 
						|
//               It is an error to add a node that is already a child
 | 
						|
//               of this group or some other group.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
prepare_add_child(EggNode *node) {
 | 
						|
  nassertv(node != (EggNode *)NULL);
 | 
						|
  test_ref_count_integrity();
 | 
						|
  node->test_ref_count_integrity();
 | 
						|
  // Make sure the node is not already a child of some other group.
 | 
						|
  nassertv(node->get_parent() == NULL);
 | 
						|
  nassertv(node->get_depth() == 0);
 | 
						|
  node->_parent = this;
 | 
						|
 | 
						|
  node->update_under(get_depth() + 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::prepare_remove_child
 | 
						|
//       Access: Private
 | 
						|
//  Description: Marks the node as removed from the group.  This is an
 | 
						|
//               internal function called by the STL-like functions
 | 
						|
//               pop_back() and erase(), in preparation for actually
 | 
						|
//               doing the removal.
 | 
						|
//
 | 
						|
//               It is an error to attempt to remove a node that is
 | 
						|
//               not already a child of this group.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
prepare_remove_child(EggNode *node) {
 | 
						|
  nassertv(node != (EggNode *)NULL);
 | 
						|
  // Make sure the node is in fact a child of this group.
 | 
						|
  nassertv(node->get_parent() == this);
 | 
						|
  nassertv(node->get_depth() == get_depth() + 1);
 | 
						|
  node->_parent = NULL;
 | 
						|
 | 
						|
  node->update_under(-(get_depth() + 1));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_collect_vertex_normals
 | 
						|
//       Access: Private
 | 
						|
//  Description: This is part of the implementation of
 | 
						|
//               recompute_vertex_normals().  It walks the scene graph
 | 
						|
//               at this group node and below, identifying all the
 | 
						|
//               polygons and the vertices they have in common.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_collect_vertex_normals(EggGroupNode::NVertexCollection &collection,
 | 
						|
                         double threshold, CoordinateSystem cs) {
 | 
						|
  // We can do this ci/cnext iteration through the list as we modify
 | 
						|
  // it, only because we know this works with an STL list type
 | 
						|
  // container.  If this were a vector or a set, this wouldn't
 | 
						|
  // necessarily work.
 | 
						|
 | 
						|
  Children::iterator ci, cnext;
 | 
						|
  ci = _children.begin();
 | 
						|
  while (ci != _children.end()) {
 | 
						|
    cnext = ci;
 | 
						|
    ++cnext;
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPolygon::get_class_type())) {
 | 
						|
      EggPolygon *polygon = DCAST(EggPolygon, child);
 | 
						|
      polygon->clear_normal();
 | 
						|
 | 
						|
      NVertexReference ref;
 | 
						|
      ref._polygon = polygon;
 | 
						|
      if (!polygon->calculate_normal(ref._normal, cs)) {
 | 
						|
        // The polygon is degenerate.  Remove it.
 | 
						|
 | 
						|
        prepare_remove_child(child);
 | 
						|
        _children.erase(ci);
 | 
						|
 | 
						|
      } else {
 | 
						|
        // Now add each vertex from the polygon separately to our
 | 
						|
        // collection.
 | 
						|
        size_t num_vertices = polygon->size();
 | 
						|
        for (size_t i = 0; i < num_vertices; i++) {
 | 
						|
          EggVertex *vertex = polygon->get_vertex(i);
 | 
						|
          ref._vertex = i;
 | 
						|
          collection[vertex->get_pos3()].push_back(ref);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      EggGroupNode *group = DCAST(EggGroupNode, child);
 | 
						|
 | 
						|
      // We can't share vertices across an Instance node.  Don't
 | 
						|
      // even bother trying.  Instead, just restart.
 | 
						|
      if (group->is_under_instance()) {
 | 
						|
        group->recompute_vertex_normals(threshold, cs);
 | 
						|
      } else {
 | 
						|
        group->r_collect_vertex_normals(collection, threshold, cs);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ci = cnext;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::do_compute_vertex_normals
 | 
						|
//       Access: Private
 | 
						|
//  Description: This is part of the implementation of
 | 
						|
//               recompute_vertex_normals().  It accepts a group of
 | 
						|
//               polygons and their common normals, and computes the
 | 
						|
//               same normal for all their shared vertices.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
do_compute_vertex_normals(const NVertexGroup &group) {
 | 
						|
  nassertv(!group.empty());
 | 
						|
 | 
						|
  // Determine the common normal.  This is simply the average of all
 | 
						|
  // the polygon normals that share this vertex.
 | 
						|
  Normald normal(0.0, 0.0, 0.0);
 | 
						|
  NVertexGroup::const_iterator gi;
 | 
						|
  for (gi = group.begin(); gi != group.end(); ++gi) {
 | 
						|
    const NVertexReference &ref = (*gi);
 | 
						|
    normal += ref._normal;
 | 
						|
  }
 | 
						|
 | 
						|
  normal /= (double)group.size();
 | 
						|
  normal.normalize();
 | 
						|
 | 
						|
  // Now we have the common normal; apply it to all the vertices.
 | 
						|
 | 
						|
  for (gi = group.begin(); gi != group.end(); ++gi) {
 | 
						|
    const NVertexReference &ref = (*gi);
 | 
						|
    EggVertex *vertex = ref._polygon->get_vertex(ref._vertex);
 | 
						|
    EggVertexPool *pool = vertex->get_pool();
 | 
						|
 | 
						|
    EggVertex new_vertex(*vertex);
 | 
						|
    new_vertex.set_normal(normal);
 | 
						|
    EggVertex *unique = pool->create_unique_vertex(new_vertex);
 | 
						|
    unique->copy_grefs_from(*vertex);
 | 
						|
 | 
						|
    ref._polygon->set_vertex(ref._vertex, unique);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::r_collect_tangent_binormal
 | 
						|
//       Access: Private
 | 
						|
//  Description: This is part of the implementation of
 | 
						|
//               recompute_tangent_binormal().  It walks the scene
 | 
						|
//               graph at this group node and below, identifying all
 | 
						|
//               the polygons and the vertices they have in common.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
r_collect_tangent_binormal(const GlobPattern &uv_name,
 | 
						|
                           EggGroupNode::TBNVertexCollection &collection) {
 | 
						|
  Children::iterator ci;
 | 
						|
  for (ci = _children.begin(); ci != _children.end(); ++ci) {
 | 
						|
    EggNode *child = *ci;
 | 
						|
 | 
						|
    if (child->is_of_type(EggPolygon::get_class_type())) {
 | 
						|
      EggPolygon *polygon = DCAST(EggPolygon, child);
 | 
						|
 | 
						|
      TBNVertexReference ref;
 | 
						|
      ref._polygon = polygon;
 | 
						|
 | 
						|
      // Now add each vertex from the polygon separately to our
 | 
						|
      // collection.
 | 
						|
      size_t num_vertices = polygon->size();
 | 
						|
      for (size_t i = 0; i < num_vertices; i++) {
 | 
						|
        // We look at the triangle formed by each three consecutive
 | 
						|
        // vertices to determine the s direction and t direction at
 | 
						|
        // each vertex.  v1 is the key vertex, the one at position i;
 | 
						|
        // v2 is vertex i + 1, and v3 is vertex i - 1.
 | 
						|
        EggVertex *v1 = polygon->get_vertex(i);
 | 
						|
        EggVertex *v2 = polygon->get_vertex((i + 1) % num_vertices);
 | 
						|
        EggVertex *v3 = polygon->get_vertex((i + num_vertices - 1) % num_vertices);
 | 
						|
        if (v1->has_normal() || polygon->has_normal()) {
 | 
						|
          // Go through all of the UV names on the vertex, looking for
 | 
						|
          // one that matches the glob pattern.
 | 
						|
          EggVertex::const_uv_iterator uvi;
 | 
						|
          for (uvi = v1->uv_begin(); uvi != v1->uv_end(); ++uvi) {
 | 
						|
            EggVertexUV *uv_obj = (*uvi);
 | 
						|
            string name = uv_obj->get_name();
 | 
						|
            if (uv_name.matches(name) &&
 | 
						|
                v2->has_uv(name) && v3->has_uv(name)) {
 | 
						|
              TBNVertexValue value;
 | 
						|
              value._uv_name = name;
 | 
						|
              value._pos = v1->get_pos3();
 | 
						|
              if (v1->has_normal()) {
 | 
						|
                value._normal = v1->get_normal();
 | 
						|
              } else {
 | 
						|
                value._normal = polygon->get_normal();
 | 
						|
              }
 | 
						|
              value._uv = v1->get_uv(name);
 | 
						|
              
 | 
						|
              // Compute the s direction and t direction for this vertex.
 | 
						|
              LPoint3d p1 = v1->get_pos3();
 | 
						|
              LPoint3d p2 = v2->get_pos3();
 | 
						|
              LPoint3d p3 = v3->get_pos3();
 | 
						|
              
 | 
						|
              TexCoordd w1 = v1->get_uv(name);
 | 
						|
              TexCoordd w2 = v2->get_uv(name);
 | 
						|
              TexCoordd w3 = v3->get_uv(name);
 | 
						|
 | 
						|
              // Check the facing of the texture; we will have to
 | 
						|
              // split vertices whose UV's are mirrored along a seam.
 | 
						|
              // The facing is determined by the winding order of the
 | 
						|
              // texcoords on the polygon.  A front-facing polygon
 | 
						|
              // should not contribute to the tangent and binormal of
 | 
						|
              // a back-facing polygon, and vice-versa.
 | 
						|
              value._facing = is_right(w1 - w2, w3 - w1);
 | 
						|
 | 
						|
              double x1 = p2[0] - p1[0];
 | 
						|
              double x2 = p3[0] - p1[0];
 | 
						|
              double y1 = p2[1] - p1[1];
 | 
						|
              double y2 = p3[1] - p1[1];
 | 
						|
              double z1 = p2[2] - p1[2];
 | 
						|
              double z2 = p3[2] - p1[2];
 | 
						|
              
 | 
						|
              double s1 = w2[0] - w1[0];
 | 
						|
              double s2 = w3[0] - w1[0];
 | 
						|
              double t1 = w2[1] - w1[1];
 | 
						|
              double t2 = w3[1] - w1[1];
 | 
						|
              
 | 
						|
              double r = 1.0f / (s1 * t2 - s2 * t1);
 | 
						|
              ref._sdir.set((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
 | 
						|
                            (t2 * z1 - t1 * z2) * r);
 | 
						|
              ref._tdir.set((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
 | 
						|
                            (s1 * z2 - s2 * z1) * r);
 | 
						|
 | 
						|
              // Store the vertex referenced to the polygon.
 | 
						|
              ref._vertex = i;
 | 
						|
              collection[value].push_back(ref);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
 | 
						|
      EggGroupNode *group = DCAST(EggGroupNode, child);
 | 
						|
 | 
						|
      // We can't share vertices across an Instance node.  Don't
 | 
						|
      // even bother trying.  Instead, just restart.
 | 
						|
      if (group->is_under_instance()) {
 | 
						|
        group->recompute_tangent_binormal(uv_name);
 | 
						|
      } else {
 | 
						|
        group->r_collect_tangent_binormal(uv_name, collection);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
//     Function: EggGroupNode::do_compute_tangent_binormal
 | 
						|
//       Access: Private
 | 
						|
//  Description: This is part of the implementation of
 | 
						|
//               recompute_tangent_binormal().  It accepts a group of
 | 
						|
//               polygons and their common normals and UV's, and
 | 
						|
//               computes the tangent and binormal for all their
 | 
						|
//               shared vertices.
 | 
						|
////////////////////////////////////////////////////////////////////
 | 
						|
void EggGroupNode::
 | 
						|
do_compute_tangent_binormal(const TBNVertexValue &value,
 | 
						|
                            const TBNVertexGroup &group) {
 | 
						|
  nassertv(!group.empty());
 | 
						|
 | 
						|
  // Accumulate together all of the s vectors and t vectors computed
 | 
						|
  // for the different vertices that are together here.
 | 
						|
  Normald sdir(0.0, 0.0, 0.0);
 | 
						|
  Normald tdir(0.0, 0.0, 0.0);
 | 
						|
 | 
						|
  TBNVertexGroup::const_iterator gi;
 | 
						|
  for (gi = group.begin(); gi != group.end(); ++gi) {
 | 
						|
    const TBNVertexReference &ref = (*gi);
 | 
						|
    sdir += ref._sdir;
 | 
						|
    tdir += ref._tdir;
 | 
						|
  }
 | 
						|
 | 
						|
  Normald tangent = (sdir - value._normal * value._normal.dot(sdir));
 | 
						|
  tangent.normalize();
 | 
						|
 | 
						|
  Normald binormal = cross(value._normal, tangent);
 | 
						|
  if (dot(binormal, tdir) < 0.0f) {
 | 
						|
    binormal = -binormal;
 | 
						|
  }
 | 
						|
  // Shouldn't need to normalize this, but we do just for good measure.
 | 
						|
  binormal.normalize();
 | 
						|
 | 
						|
  // Now we have the common tangent and binormal; apply them to all
 | 
						|
  // the vertices.
 | 
						|
 | 
						|
  for (gi = group.begin(); gi != group.end(); ++gi) {
 | 
						|
    const TBNVertexReference &ref = (*gi);
 | 
						|
    EggVertex *vertex = ref._polygon->get_vertex(ref._vertex);
 | 
						|
    EggVertexPool *pool = vertex->get_pool();
 | 
						|
 | 
						|
    EggVertex new_vertex(*vertex);
 | 
						|
    EggVertexUV *uv_obj = new_vertex.modify_uv_obj(value._uv_name);
 | 
						|
    nassertv(uv_obj != (EggVertexUV *)NULL);
 | 
						|
    uv_obj->set_tangent(tangent);
 | 
						|
    uv_obj->set_binormal(binormal);
 | 
						|
 | 
						|
    EggVertex *unique = pool->create_unique_vertex(new_vertex);
 | 
						|
    unique->copy_grefs_from(*vertex);
 | 
						|
 | 
						|
    ref._polygon->set_vertex(ref._vertex, unique);
 | 
						|
  }
 | 
						|
}
 |