// Filename: ropeNode.h
// Created by:  drose (04Dec02)
//
////////////////////////////////////////////////////////////////////
//
// 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 ROPENODE_H
#define ROPENODE_H

#include "pandabase.h"
#include "nurbsCurveEvaluator.h"
#include "pandaNode.h"
#include "pStatCollector.h"
#include "geomVertexFormat.h"

class GeomVertexData;

////////////////////////////////////////////////////////////////////
//       Class : RopeNode
// Description : This class draws a visible representation of the
//               NURBS curve stored in its NurbsCurveEvaluator.  It
//               automatically recomputes the curve every frame.
//
//               This is not related to NurbsCurve, CubicCurveseg or
//               any of the ParametricCurve-derived objects in this
//               module.  It is a completely parallel implementation
//               of NURBS curves, and will probably eventually replace
//               the whole ParametricCurve class hierarchy.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PARAMETRICS RopeNode : public PandaNode {
PUBLISHED:
  RopeNode(const string &name);

protected:
  RopeNode(const RopeNode &copy);
public:
  virtual void output(ostream &out) const;
  virtual void write(ostream &out, int indent_level = 0) const;

  virtual PandaNode *make_copy() const;

  virtual bool safe_to_transform() const;
  virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
  virtual bool is_renderable() const;

PUBLISHED:
  enum RenderMode {
    // Render the rope as a one-pixel thread using a linestrip.
    RM_thread,

    // Render the rope as a triangle strip oriented to be
    // perpendicular to the tube_up vector.
    RM_tape,

    // Render the rope as a triangle strip oriented to be
    // perpendicular to the view vector.
    RM_billboard,

    // Render the rope as a hollow tube extruded along its length.
    RM_tube
  };

  enum UVMode {
    // Don't generate UV's along the curve.
    UV_none,

    // Generate UV's based on the parametric coordinates along the
    // curve.
    UV_parametric,

    // Generate UV's in proportion to spatial distance along the
    // curve, by using the distance function to compute the length of
    // each segment.
    UV_distance,

    // As above, but don't bother to take the square root of each
    // segment.  The distance is then in proportion to the
    // sum-of-squares of the segments along the rope.  If the segments
    // are similar in length, this approximates the proportion of
    // UV_distance while avoiding hundreds of square root operations.
    UV_distance2,
  };

  enum NormalMode {
    // Don't generate normals.
    NM_none,

    // Generate vertex (smooth-shaded) normals.
    NM_vertex
  };

  INLINE void set_curve(NurbsCurveEvaluator *curve);
  INLINE NurbsCurveEvaluator *get_curve() const;

  INLINE void set_render_mode(RenderMode render_mode);
  INLINE RenderMode get_render_mode() const;

  INLINE void set_uv_mode(UVMode uv_mode);
  INLINE UVMode get_uv_mode() const;

  INLINE void set_uv_direction(bool u_dominant);
  INLINE bool get_uv_direction() const;

  INLINE void set_uv_scale(float scale);
  INLINE float get_uv_scale() const;

  INLINE void set_normal_mode(NormalMode normal_mode);
  INLINE NormalMode get_normal_mode() const;

  INLINE void set_tube_up(const LVector3f &tube_up);
  INLINE const LVector3f &get_tube_up() const;

  INLINE void set_use_vertex_color(bool flag);
  INLINE bool get_use_vertex_color() const;
  INLINE static int get_vertex_color_dimension();

  INLINE void set_num_subdiv(int num_subdiv);
  INLINE int get_num_subdiv() const;

  INLINE void set_num_slices(int num_slices);
  INLINE int get_num_slices() const;

  INLINE void set_use_vertex_thickness(bool flag);
  INLINE bool get_use_vertex_thickness() const;
  INLINE static int get_vertex_thickness_dimension();

  INLINE void set_thickness(float thickness);
  INLINE float get_thickness() const;

  INLINE void set_matrix(const LMatrix4f &matrix);
  INLINE void clear_matrix();
  INLINE bool has_matrix() const;
  INLINE const LMatrix4f &get_matrix() const;

  void reset_bound(const NodePath &rel_to);

protected:
  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
                                       int &internal_vertices,
                                       int pipeline_stage,
                                       Thread *current_thread) const;

private:
  CPT(GeomVertexFormat) get_format(bool support_normals) const;

  PT(BoundingVolume) do_recompute_bounds(const NodePath &rel_to,
					 int pipeline_stage, 
                                         Thread *current_thread) const;
  void render_thread(CullTraverser *trav, CullTraverserData &data, 
                     NurbsCurveResult *result) const;
  void render_tape(CullTraverser *trav, CullTraverserData &data, 
                   NurbsCurveResult *result) const;
  void render_billboard(CullTraverser *trav, CullTraverserData &data, 
                        NurbsCurveResult *result) const;
  void render_tube(CullTraverser *trav, CullTraverserData &data, 
                   NurbsCurveResult *result) const;

  class CurveVertex {
  public:
    LPoint3f _p;
    Colorf _c;
    float _thickness;
    float _t;
  };
  typedef pvector<CurveVertex> CurveSegment;
  typedef pvector<CurveSegment> CurveSegments;

  void get_connected_segments(CurveSegments &curve_segments,
                              const NurbsCurveResult *result) const;

  void compute_thread_vertices(GeomVertexData *vdata,
                               const CurveSegments &curve_segments) const;
  void compute_billboard_vertices(GeomVertexData *vdata,
                                  const LVector3f &camera_vec,
                                  const CurveSegments &curve_segments,
                                  NurbsCurveResult *result) const;
  void compute_tube_vertices(GeomVertexData *vdata,
                             int &num_verts_per_slice,
                             const CurveSegments &curve_segments,
                             NurbsCurveResult *result) const;

  static void compute_tangent(LVector3f &tangent, const CurveSegment &segment,
                              size_t j, NurbsCurveResult *result);
  static float compute_uv_t(float &dist, const UVMode &uv_mode,
                            float uv_scale, const CurveSegment &segment,
                            size_t j);


private:
  // This is the data that must be cycled between pipeline stages.
  class EXPCL_PANDA_PARAMETRICS CData : public CycleData {
  public:
    INLINE CData();
    INLINE CData(const CData &copy);
    virtual CycleData *make_copy() const;
    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
    virtual void fillin(DatagramIterator &scan, BamReader *manager);
    virtual TypeHandle get_parent_type() const {
      return RopeNode::get_class_type();
    }

    PT(NurbsCurveEvaluator) _curve;
    RenderMode _render_mode;
    UVMode _uv_mode;
    bool _u_dominant;
    float _uv_scale;
    NormalMode _normal_mode;
    LVector3f _tube_up;
    LMatrix4f _matrix;
    bool _has_matrix;
    bool _use_vertex_color;
    int _num_subdiv;
    int _num_slices;
    bool _use_vertex_thickness;
    float _thickness;
  };

  PipelineCycler<CData> _cycler;
  typedef CycleDataReader<CData> CDReader;
  typedef CycleDataWriter<CData> CDWriter;

  static PStatCollector _rope_node_pcollector;

public:
  static void register_with_read_factory();
  virtual void write_datagram(BamWriter *manager, Datagram &dg);

protected:
  static TypedWritable *make_from_bam(const FactoryParams &params);
  void fillin(DatagramIterator &scan, BamReader *manager);

public:
  static TypeHandle get_class_type() {
    return _type_handle;
  }
  static void init_type() {
    PandaNode::init_type();
    register_type(_type_handle, "RopeNode",
                  PandaNode::get_class_type());
  }
  virtual TypeHandle get_type() const {
    return get_class_type();
  }
  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}

private:
  static TypeHandle _type_handle;
};

#include "ropeNode.I"

#endif
