// Filename: sceneGraphReducer.h
// Created by:  drose (14Mar02)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////

#ifndef SCENEGRAPHREDUCER_H
#define SCENEGRAPHREDUCER_H

#include "pandabase.h"
#include "transformState.h"
#include "renderAttrib.h"
#include "renderState.h"
#include "accumulatedAttribs.h"
#include "geomTransformer.h"
#include "pStatCollector.h"
#include "pStatTimer.h"
#include "typedObject.h"
#include "pointerTo.h"
#include "graphicsStateGuardianBase.h"

class PandaNode;

////////////////////////////////////////////////////////////////////
//       Class : SceneGraphReducer
// Description : An interface for simplifying ("flattening") scene
//               graphs by eliminating unneeded nodes and collapsing
//               out unneeded state changes and transforms.
//
//               This class is designed so that it may be inherited
//               from and specialized, if needed, to fine-tune the
//               flattening behavior, but normally the default
//               behavior is sufficient.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PGRAPH SceneGraphReducer {
PUBLISHED:
  INLINE SceneGraphReducer(GraphicsStateGuardianBase *gsg = NULL);
  INLINE ~SceneGraphReducer();

  enum AttribTypes {
    TT_transform           = 0x001,
    TT_color               = 0x002,
    TT_color_scale         = 0x004,
    TT_tex_matrix          = 0x008,
    TT_clip_plane          = 0x010,
    TT_cull_face           = 0x020,
    TT_apply_texture_color = 0x040,
    TT_other               = 0x080,
  };

  enum CombineSiblings {
    CS_geom_node       = 0x001,
    CS_within_radius   = 0x002,
    CS_other           = 0x004,
    CS_recurse         = 0x008,
  };

  enum CollectVertexData {
    // If set, two GeomVertexDatas with different names will not be
    // collected together.
    CVD_name           = 0x001,

    // If set, a ModelNode begins a subgraph of nodes whose
    // GeomVertexDatas will not be collected with nodes outside the
    // subgraph.
    CVD_model          = 0x002,

    // If set, a non-identity transform begins a subgraph of nodes
    // whose GeomVertexDatas will not be collected with nodes outside
    // the subgraph.
    CVD_transform      = 0x004,

    // If set, GeomVertexDatas with any usage_hint other than
    // UH_static will not be collected with any other Geoms in a
    // different GeomNode.  However, two different dynamic Geoms
    // within the same node might still be collected together.
    CVD_avoid_dynamic  = 0x008,

    // If set, only those GeomVertexDatas within the same node might
    // be collected together.
    CVD_one_node_only  = 0x010,

    // If set, two GeomVertexDatas with different formats will not be
    // collected together.  If not set, GeomVertexDatas of different
    // formats may be combined by expanding all GeomVertexDatas to the
    // union of all defined columns.
    CVD_format         = 0x020,

    // If set, two GeomVertexDatas with different usage hints (for
    // instance, UH_static vs. UH_dynamic) will not be collected
    // together.
    CVD_usage_hint     = 0x040,

    // If set, GeomVertexDatas with unanimated vertices will not be
    // combined with GeomVertexDatas with animated vertices.
    CVD_animation_type = 0x080, 
  };

  enum MakeNonindexed {
    // If set, only composite primitives such as tristrips and trifans
    // will be made nonindexed; simple primitives such as triangles
    // will be left indexed.
    MN_composite_only  = 0x001,

    // If set any GeomVertexData with any animation indication will
    // not be adjusted, whether the animation is to be performed on
    // the CPU or on the graphics pipe.
    MN_avoid_animated  = 0x002,

    // If set, any GeomVertexData or Geom with a usage_hint other than
    // UH_static will not be made nonindexed.
    MN_avoid_dynamic   = 0x004,
  };

  void set_gsg(GraphicsStateGuardianBase *gsg);
  void clear_gsg();
  INLINE GraphicsStateGuardianBase *get_gsg() const;

  INLINE void set_combine_radius(PN_stdfloat combine_radius);
  INLINE PN_stdfloat get_combine_radius() const;

  INLINE void apply_attribs(PandaNode *node, int attrib_types = ~(TT_clip_plane | TT_cull_face | TT_apply_texture_color));
  INLINE void apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
                            int attrib_types, GeomTransformer &transformer);

  int flatten(PandaNode *root, int combine_siblings_bits);

  int remove_column(PandaNode *root, const InternalName *column);

  int make_compatible_state(PandaNode *root);
  
  INLINE int make_compatible_format(PandaNode *root, int collect_bits = ~0);
  void decompose(PandaNode *root);

  INLINE int collect_vertex_data(PandaNode *root, int collect_bits = ~0);
  INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0);
  void unify(PandaNode *root, bool preserve_order);
  void remove_unused_vertices(PandaNode *root);

  INLINE void premunge(PandaNode *root, const RenderState *initial_state);
  bool check_live_flatten(PandaNode *node);

protected:
  void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
                       int attrib_types, GeomTransformer &transformer);

  int r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
                int combine_siblings_bits);
  int flatten_siblings(PandaNode *parent_node,
                       int combine_siblings_bits);

  bool consider_child(PandaNode *grandparent_node,
                      PandaNode *parent_node, PandaNode *child_node);
  bool consider_siblings(PandaNode *parent_node, PandaNode *child1,
                         PandaNode *child2);

  bool do_flatten_child(PandaNode *grandparent_node, 
                        PandaNode *parent_node, PandaNode *child_node);

  PandaNode *do_flatten_siblings(PandaNode *parent_node, 
                                 PandaNode *child1, PandaNode *child2);

  PT(PandaNode) collapse_nodes(PandaNode *node1, PandaNode *node2, 
                               bool siblings);
  void choose_name(PandaNode *preserve, PandaNode *source1, 
                   PandaNode *source2);

  int r_remove_column(PandaNode *node, const InternalName *column,
                      GeomTransformer &transformer);

  int r_make_compatible_state(PandaNode *node, GeomTransformer &transformer);

  int r_collect_vertex_data(PandaNode *node, int collect_bits,
                            GeomTransformer &transformer, bool format_only);
  int r_make_nonindexed(PandaNode *node, int collect_bits);
  void r_unify(PandaNode *node, int max_indices, bool preserve_order);
  void r_register_vertices(PandaNode *node, GeomTransformer &transformer);
  void r_decompose(PandaNode *node);

  void r_premunge(PandaNode *node, const RenderState *state);

private:
  PT(GraphicsStateGuardianBase) _gsg;
  PN_stdfloat _combine_radius;
  GeomTransformer _transformer;

  static PStatCollector _flatten_collector;
  static PStatCollector _apply_collector;
  static PStatCollector _remove_column_collector;
  static PStatCollector _compatible_state_collector;
  static PStatCollector _collect_collector;
  static PStatCollector _make_nonindexed_collector;
  static PStatCollector _unify_collector;
  static PStatCollector _remove_unused_collector;
  static PStatCollector _premunge_collector;
};

#include "sceneGraphReducer.I"

#endif
