// Filename: geoMipTerrain.h
// Created by:  rdb (29Jun07)
//
////////////////////////////////////////////////////////////////////
//
// 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 GEOMIPTERRAIN_H
#define GEOMIPTERRAIN_H

#include "pandabase.h"

#include "luse.h"
#include "pandaNode.h"
#include "pointerTo.h"

#include "pnmImage.h"
#include "nodePath.h"

#include "texture.h"

////////////////////////////////////////////////////////////////////
//       Class : GeoMipTerrain
// Description : GeoMipTerrain, meaning Panda3D GeoMipMapping, can convert
//               a heightfield image into a 3D terrain, consisting
//               of several GeomNodes. It uses the GeoMipMapping
//               algorithm, or Geometrical MipMapping, based on
//               the LOD (Level of Detail) algorithm. For more
//               information about the GeoMipMapping algoritm, see
//               this paper, written by Willem H. de Boer:
//               http://flipcode.com/articles/article_geomipmaps.pdf
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GRUTIL GeoMipTerrain : public TypedObject {
PUBLISHED:
  INLINE GeoMipTerrain(const string &name);
  INLINE ~GeoMipTerrain();

  INLINE PNMImage &heightfield();
  bool set_heightfield(const Filename &filename, PNMFileType *type = NULL);
  INLINE bool set_heightfield(const PNMImage &image);
  INLINE bool set_heightfield(const string &path);
  INLINE PNMImage &color_map();
  INLINE bool set_color_map(const Filename &filename,
                                  PNMFileType *type = NULL);
  INLINE bool set_color_map(const PNMImage &image);
  INLINE bool set_color_map(const Texture *image);
  INLINE bool set_color_map(const string &path);
  INLINE bool has_color_map() const;
  INLINE void clear_color_map();
  void calc_ambient_occlusion(PN_stdfloat radius = 32, PN_stdfloat contrast = 2.0f, PN_stdfloat brightness = 0.75f);
  double get_elevation(double x, double y);
  LVector3 get_normal(int x, int y);
  INLINE LVector3 get_normal(unsigned short mx, unsigned short my,
                                                          int x,int y);
  INLINE void set_bruteforce(bool bf);
  INLINE bool get_bruteforce();

  // The flatten mode specifies whether the terrain nodes are flattened
  // together after each terrain update.
  enum AutoFlattenMode {
    // FM_off: don't ever flatten the terrain.
    AFM_off     = 0,
    // FM_light: the terrain is flattened using flatten_light.
    AFM_light   = 1,
    // FM_medium: the terrain is flattened using flatten_medium.
    AFM_medium  = 2,
    // FM_strong: the terrain is flattened using flatten_strong.
    AFM_strong  = 3,
  };

  INLINE void set_auto_flatten(int mode);

  // The focal point is the point at which the terrain will have the
  // highest quality (lowest level of detail). Parts farther away from
  // the focal point will have a lower quality (higher level of detail).
  // The focal point is not taken in respect if bruteforce is set true.
  INLINE void set_focal_point(const LPoint2d &fp);
  INLINE void set_focal_point(const LPoint2f &fp);
  INLINE void set_focal_point(const LPoint3d &fp);
  INLINE void set_focal_point(const LPoint3f &fp);
  INLINE void set_focal_point(double x, double y);
  INLINE void set_focal_point(NodePath fnp);
  INLINE NodePath get_focal_point() const;
  INLINE NodePath get_root() const;

  INLINE void set_block_size(unsigned short newbs);
  INLINE unsigned short get_block_size();
  INLINE unsigned short get_max_level();
  INLINE void set_min_level(unsigned short minlevel);
  INLINE unsigned short get_min_level();
  INLINE bool is_dirty();
  INLINE void set_factor(PN_stdfloat factor);
  INLINE void set_near_far(double input_near, double input_far);
  INLINE void set_near(double input_near);
  INLINE void set_far(double input_far);
  INLINE const NodePath get_block_node_path(unsigned short mx,
                                            unsigned short my);
  INLINE LVecBase2 get_block_from_pos(double x, double y);
  INLINE void set_border_stitching(bool stitching);
  INLINE bool get_border_stitching();
  INLINE double get_far();
  INLINE double get_near();
  INLINE int get_flatten_mode();

  PNMImage make_slope_image();
  void generate();
  bool update();

private:

  PT(GeomNode) generate_block(unsigned short mx, unsigned short my, unsigned short level);
  bool update_block(unsigned short mx, unsigned short my,
                    signed short level = -1, bool forced = false);
  void calc_levels();
  void auto_flatten();
  bool root_flattened();

  INLINE bool is_power_of_two(unsigned int i);
  INLINE float f_part(float i);
  INLINE double f_part(double i);
  INLINE int sfav(int n, int powlevel, int mypowlevel);
  INLINE double get_pixel_value(int x, int y);
  INLINE double get_pixel_value(unsigned short mx, unsigned short my, int x, int y);
  INLINE unsigned short lod_decide(unsigned short mx, unsigned short my);
  unsigned short get_neighbor_level(unsigned short mx, unsigned short my, short dmx, short dmy);

  NodePath _root;
  int _auto_flatten;
  bool _root_flattened;
  PNMImage _heightfield;
  PNMImage _color_map;
  bool _is_dirty;
  bool _has_color_map;
  unsigned int _xsize;
  unsigned int _ysize;
  PN_stdfloat _factor;
  double _near;
  double _far;
  bool _use_near_far; // False to use the _factor, True to use the _near and _far values.
  unsigned short _block_size;
  unsigned short _max_level; // Highest level possible for this block size
  bool _bruteforce;
  NodePath _focal_point;
  bool _focal_is_temporary;
  unsigned short _min_level;
  bool _stitching;
  pvector<pvector<NodePath> > _blocks;
  pvector<pvector<unsigned short> > _levels;
  pvector<pvector<unsigned short> > _old_levels;

public:
  static TypeHandle get_class_type() {
    return _type_handle;
  }
  static void init_type() {
    TypedObject::init_type();
    register_type(_type_handle, "GeoMipTerrain",
                  TypedObject::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 "geoMipTerrain.I"

#endif /*GEOMIPTERRAIN_H*/

