// Filename: indexedFaceSet.cxx
// Created by:  drose (24Jun99)
// 
////////////////////////////////////////////////////////////////////
// Copyright (C) 1992,93,94,95,96,97  Walt Disney Imagineering, Inc.
// 
// These  coded  instructions,  statements,  data   structures   and
// computer  programs contain unpublished proprietary information of
// Walt Disney Imagineering and are protected by  Federal  copyright
// law.  They may  not be  disclosed to third  parties  or copied or
// duplicated in any form, in whole or in part,  without  the  prior
// written consent of Walt Disney Imagineering Inc.
////////////////////////////////////////////////////////////////////

#include "indexedFaceSet.h"
#include "vrmlAppearance.h"
#include "vrmlNodeType.h"
#include "vrmlNode.h"
#include "pnotify.h"

#include "eggGroup.h"
#include "eggVertex.h"
#include "eggVertexPool.h"
#include "eggPolygon.h"

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::Constructor
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
IndexedFaceSet::
IndexedFaceSet(const VrmlNode *geometry, const VRMLAppearance &appearance) :
  _geometry(geometry), _appearance(appearance)
{
  get_coord_values();
  get_polys();
  get_colors();
  _has_normals = get_normals();
  if (!_per_vertex_normals.empty()) {
    assign_per_vertex_normals();
  }
  get_uvs();
  if (!_per_vertex_uvs.empty()) {
    assign_per_vertex_uvs();
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::convert_to_egg
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
convert_to_egg(EggGroup *group, const LMatrix4d &net_transform) {
  EggVertexPool *vpool = new EggVertexPool(group->get_name());
  group->add_child(vpool);

  make_polys(vpool, group, net_transform);
  if (!_has_normals && _appearance._has_material) {
    compute_normals(group);
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_coord_values
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
get_coord_values() {
  const VrmlNode *coord = _geometry->get_value("coord")._sfnode._p;

  if (coord != NULL) {
    const MFArray *point = coord->get_value("point")._mf;
    MFArray::const_iterator ci;
    for (ci = point->begin(); ci != point->end(); ++ci) {
      const double *p = (*ci)._sfvec;
      _coord_values.push_back(Vertexd(p[0], p[1], p[2]));
    }
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_polys
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
get_polys() {
  const MFArray *coordIndex = _geometry->get_value("coordIndex")._mf;
  VrmlPolygon poly;

  MFArray::const_iterator ci;
  for (ci = coordIndex->begin(); ci != coordIndex->end(); ++ci) {
    if ((*ci)._sfint32 < 0) {
      _polys.push_back(poly);
      poly._verts.clear();
    } else {
      const Vertexd &p = _coord_values[(*ci)._sfint32];
      VrmlVertex vert;
      vert._index = (*ci)._sfint32;
      vert._pos = p;
      poly._verts.push_back(vert);
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_vrml_colors
//       Access: Private
//  Description: Builds up a vector of Colorf pointers corresponding
//               to the VRML color node.
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
get_vrml_colors(const VrmlNode *color_node, double transparency,
                pvector<Colorf> &color_list) {
  const MFArray *color = color_node->get_value("color")._mf;
  MFArray::const_iterator ci;
  for (ci = color->begin(); ci != color->end(); ++ci) {
    const double *p = (*ci)._sfvec;
    Colorf color(p[0], p[1], p[2], 1.0 - transparency);
    color_list.push_back(color);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_vrml_normals
//       Access: Private
//  Description: Builds up a vector of double array pointers corresponding
//               to the VRML normal node.
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
get_vrml_normals(const VrmlNode *normal_node, 
                 pvector<Normald> &normal_list) {
  const MFArray *point = normal_node->get_value("vector")._mf;
  MFArray::const_iterator ci;
  for (ci = point->begin(); ci != point->end(); ++ci) {
    const double *p = (*ci)._sfvec;
    Normald normal(p[0], p[1], p[2]);
    normal_list.push_back(normal);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_vrml_uvs
//       Access: Private
//  Description: Builds up a vector of double array pointers corresponding
//               to the VRML texCoord node.
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
get_vrml_uvs(const VrmlNode *texCoord_node, 
             pvector<TexCoordd> &uv_list) {
  const MFArray *point = texCoord_node->get_value("point")._mf;
  MFArray::const_iterator ci;
  for (ci = point->begin(); ci != point->end(); ++ci) {
    const double *p = (*ci)._sfvec;
    TexCoordd uv(p[0], p[1]);
    uv_list.push_back(uv);
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_colors
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
bool IndexedFaceSet::
get_colors() {
  const VrmlNode *color = _geometry->get_value("color")._sfnode._p;
  if (color != NULL) {
    // Vertex or face colors.
    pvector<Colorf> color_list;
    get_vrml_colors(color, _appearance._transparency, color_list);
    
    bool colorPerVertex = _geometry->get_value("colorPerVertex")._sfbool;
    MFArray *colorIndex = _geometry->get_value("colorIndex")._mf;
    if (colorPerVertex) {
      MFArray::const_iterator ci;
      size_t pi = 0;
      size_t pv = 0;
      for (ci = colorIndex->begin(); ci != colorIndex->end(); ++ci) {
        if ((*ci)._sfint32 < 0) {
          // End of poly.
          if (pv != _polys[pi]._verts.size()) {
            cerr << "Color indices don't match up!\n";
            return false;
          }
          pi++;
          pv = 0;
        } else {
          if (pi >= _polys.size() || pv >= _polys[pi]._verts.size()) {
            cerr << "Color indices don't match up!\n";
            return false;
          }
          _polys[pi]._verts[pv]._attrib.set_color(color_list[(*ci)._sfint32]);
          pv++;
        }
      }
      if (pi != _polys.size()) {
        cerr << "Not enough color indices!\n";
        return false;
      }
    } else {
      if (!colorIndex->empty()) {
        MFArray::const_iterator ci;
        size_t pi = 0;
        if (colorIndex->size() != _polys.size()) {
          cerr << "Wrong number of color indices!\n";
          return false;
        }
        for (ci = colorIndex->begin(); ci != colorIndex->end(); ++ci) {
          if ((*ci)._sfint32 < 0 || (*ci)._sfint32 >= (int)color_list.size()) {
            cerr << "Invalid color index!\n";
            return false;
          }
          _polys[pi]._attrib.set_color(color_list[(*ci)._sfint32]);
          pi++;
        }
      } else {
        if (color_list.size() != _polys.size()) {
          cerr << "Wrong number of colors!\n";
          return false;
        }
        for (size_t pi = 0; pi < color_list.size(); pi++) {
          _polys[pi]._attrib.set_color(color_list[pi]);
        }
      }
    }
    return true;
  }
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_normals
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
bool IndexedFaceSet::
get_normals() {
  const VrmlNode *normal = _geometry->get_value("normal")._sfnode._p;
  if (normal != NULL) {
    // Vertex or face normals.
    pvector<Normald> normal_list;
    get_vrml_normals(normal, normal_list);
    
    bool normalPerVertex = _geometry->get_value("normalPerVertex")._sfbool;
    MFArray *normalIndex = _geometry->get_value("normalIndex")._mf;
    MFArray::const_iterator ci;

    if (normalPerVertex &&
        normal_list.size() == _polys.size() &&
        normalIndex->empty()) {
      // Here's an interesting formZ bug.  We end up with a VRML file
      // that claims to have normals per vertex, yet there is no
      // normal index list, and there are exactly enough normals in
      // the list to indicate one normal per face.  Silly formZ.
      normalPerVertex = false;
    }

    if (normalPerVertex) {

      if (normalIndex->empty()) {
        // If we have *no* normal index array, but we do have
        // per-vertex normals, assume the VRML writer meant to imply a
        // one-to-one mapping.  This works around a broken formZ VRML
        // file writer.
        for (size_t i = 0; i < normal_list.size(); i++) {
          VrmlFieldValue fv;
          fv._sfint32 = i;
          (*normalIndex).push_back(fv);
        }
      }

      // It's possible that this .wrl file indexes normals directly
      // into the vertex array, instead of into the polygon list.
      // Check for this possibility.  This can only happen if the
      // number of normal indices exactly matches the number of
      // vertices, and none of the indices is -1.
      bool linear_list = (normalIndex->size() == _coord_values.size());
      for (ci = normalIndex->begin(); 
           ci != normalIndex->end() && linear_list; 
           ++ci) {
        linear_list = ((*ci)._sfint32 >= 0);
      }
      
      if (linear_list) {
        // Ok, we do have such a list.  This .wrl file seems to store
        // its texture coordinates one per vertex, instead of one per
        // polygon vertex.
        _per_vertex_normals.reserve(_coord_values.size());

        for (ci = normalIndex->begin(); ci != normalIndex->end(); ++ci) {
          size_t vi = (*ci)._sfint32;
          nassertr(vi >= 0, false);
          if (vi >= normal_list.size()) {
            cerr << "Invalid normal index: " << vi << "\n";
            return false;
          }
          _per_vertex_normals.push_back(normal_list[vi]);
        }
        nassertr(_per_vertex_normals.size() == _coord_values.size(), false);

      } else {
        // This is a "correct" .wrl file that stores its texture
        // coordinates one per polygon vertex.  This allows a shared
        // vertex to contain two different normal values in differing
        // polygons (meaning it's not actually shared).

        MFArray::const_iterator ci;
        size_t pi = 0;
        size_t pv = 0;
        for (ci = normalIndex->begin(); ci != normalIndex->end(); ++ci) {
          if ((*ci)._sfint32 < 0) {
            // End of poly.
            if (pv != _polys[pi]._verts.size()) {
              cerr << "Normal indices don't match up!\n";
              return false;
            }
            pi++;
            pv = 0;
          } else {
            if (pi >= _polys.size() || pv >= _polys[pi]._verts.size()) {
              cerr << "Normal indices don't match up!\n";
              return false;
            }
            const Normald &d = normal_list[(*ci)._sfint32];
            _polys[pi]._verts[pv]._attrib.set_normal(d);
            pv++;
          }
        }
        if (pi != _polys.size()) {
          cerr << "Not enough normal indices!\n";
          return false;
        }
      }
    } else {
      if (!normalIndex->empty()) {
        size_t pi = 0;
        if (normalIndex->size() != _polys.size()) {
          cerr << "Wrong number of normal indices!\n";
          return false;
        }
        for (ci = normalIndex->begin(); ci != normalIndex->end(); ++ci) {
          if ((*ci)._sfint32 < 0 || (*ci)._sfint32 >= (int)normal_list.size()) {
            cerr << "Invalid normal index!\n";
            return false;
          }
          const Normald &d = normal_list[(*ci)._sfint32];
          _polys[pi]._attrib.set_normal(d);
          pi++;
        }
      } else {
        if (normal_list.size() != _polys.size()) {
          cerr << "Wrong number of normals!\n";
          return false;
        }
        for (size_t pi = 0; pi < normal_list.size(); pi++) {
          const Normald &d = normal_list[pi];
          _polys[pi]._attrib.set_normal(d);
        }
      }
    }
    return true;
  }
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::assign_per_vertex_normals
//       Access: Private
//  Description: Once the array of _per_vertex_normals has been filled
//               (by a broken .wrl file that indexes the normal's
//               directly into the vertex array instead of per polygon
//               vertex), go back through the polygons and assign the
//               normals by index number.
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
assign_per_vertex_normals() {
  for (size_t pi = 0; pi < _polys.size(); pi++) {
    for (size_t pv = 0; pv < _polys[pi]._verts.size(); pv++) {
      VrmlVertex &vv = _polys[pi]._verts[pv];
      if (vv._index >= 0 && vv._index < (int)_per_vertex_normals.size()) {
        const Normald &d = _per_vertex_normals[vv._index];
        vv._attrib.set_normal(d);
      }
    }
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::get_uvs
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
bool IndexedFaceSet::
get_uvs() {
  const VrmlNode *texCoord = _geometry->get_value("texCoord")._sfnode._p;
  if (texCoord != NULL) {
    // Vertex or face texCoords.
    pvector<TexCoordd> uv_list;
    get_vrml_uvs(texCoord, uv_list);
    
    MFArray *texCoordIndex = _geometry->get_value("texCoordIndex")._mf;
    MFArray::const_iterator ci;

    if (texCoordIndex->empty()) {
      // If we have *no* texture coordinate index array, but we do
      // have texture coordinates, assume the VRML writer meant to
      // imply a one-to-one mapping.  This works around a broken formZ
      // VRML file writer.
      for (size_t i = 0; i < uv_list.size(); i++) {
        VrmlFieldValue fv;
        fv._sfint32 = i;
        (*texCoordIndex).push_back(fv);
      }
    }

    // It's possible that this .wrl file indexes texture coordinates
    // directly into the vertex array, instead of into the polygon
    // list.  Check for this possibility.  This can only happen if the
    // number of texture coordinate indices exactly matches the number
    // of vertices, and none of the indices is -1.
    bool linear_list = (texCoordIndex->size() == _coord_values.size());
    for (ci = texCoordIndex->begin(); 
         ci != texCoordIndex->end() && linear_list; 
         ++ci) {
      linear_list = ((*ci)._sfint32 >= 0);
    }

    if (linear_list) {
      // Ok, we do have such a list.  This .wrl file seems to store
      // its texture coordinates one per vertex, instead of one per
      // polygon vertex.
      _per_vertex_uvs.reserve(_coord_values.size());

      for (ci = texCoordIndex->begin(); ci != texCoordIndex->end(); ++ci) {
        size_t vi = (*ci)._sfint32;
        nassertr(vi >= 0, false);
        if (vi >= uv_list.size()) {
          cerr << "Invalid texCoord index: " << vi << "\n";
          return false;
        }
        _per_vertex_uvs.push_back(uv_list[vi]);
      }
      nassertr(_per_vertex_uvs.size() == _coord_values.size(), false);

    } else {
      // This is a "correct" .wrl file that stores its texture
      // coordinates one per polygon vertex.  This allows a shared
      // vertex to contain two different texture coordinate values in
      // differing polygons (meaning it's not actually shared).

      size_t pi = 0;
      size_t pv = 0;
      for (ci = texCoordIndex->begin(); ci != texCoordIndex->end(); ++ci) {
        if ((*ci)._sfint32 < 0) {
          // End of poly.
          if (pv != _polys[pi]._verts.size()) {
            cerr << "texCoord indices don't match up!\n";
            return false;
          }
          pi++;
          pv = 0;
        } else {
          if (pi >= _polys.size() || pv >= _polys[pi]._verts.size()) {
            cerr << "texCoord indices don't match up!\n";
            return false;
          }
          _polys[pi]._verts[pv]._attrib.set_uv(uv_list[(*ci)._sfint32]);
          pv++;
        }
      }
      if (pi != _polys.size()) {
        cerr << "Not enough texCoord indices!\n";
        return false;
      }
    }
    return true;
  }
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::assign_per_vertex_uvs
//       Access: Private
//  Description: Once the array of _per_vertex_uvs has been filled (by
//               a broken .wrl file that indexes the uv's directly
//               into the vertex array instead of per polygon vertex),
//               go back through the polygons and assign the UV's by
//               index number.
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
assign_per_vertex_uvs() {
  for (size_t pi = 0; pi < _polys.size(); pi++) {
    for (size_t pv = 0; pv < _polys[pi]._verts.size(); pv++) {
      VrmlVertex &vv = _polys[pi]._verts[pv];
      if (vv._index >= 0 && vv._index < (int)_per_vertex_uvs.size()) {
        const TexCoordd &d = _per_vertex_uvs[vv._index];
        vv._attrib.set_uv(d);
      }
    }
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::make_polys
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
make_polys(EggVertexPool *vpool, EggGroup *group, 
           const LMatrix4d &net_transform) {
  bool ccw = _geometry->get_value("ccw")._sfbool;
  bool solid = _geometry->get_value("solid")._sfbool;

  for (size_t pi = 0; pi < _polys.size(); pi++) {
    EggPolygon *poly = new EggPolygon;
    group->add_child(poly);
    poly->copy_attributes(_polys[pi]._attrib);

    if (!poly->has_color() && _appearance._has_material) {
      poly->set_color(_appearance._color);
    }

    if (_appearance._tex != (EggTexture *)NULL) {
      poly->set_texture(_appearance._tex);
    }

    if (!solid) {
      poly->set_bface_flag(true);
    }

    if (ccw) {
      // The vertices are counterclockwise, same as Egg.
      for (int pv = 0; pv < (int)_polys[pi]._verts.size(); pv++) {
        EggVertex vert(_polys[pi]._verts[pv]._attrib);
        Vertexd pos = 
          _polys[pi]._verts[pv]._pos * net_transform;
        vert.set_pos(pos);

        poly->add_vertex(vpool->create_unique_vertex(vert));
      }
    } else {
      // The vertices are clockwise, so add 'em in reverse order.
      for (int pv = (int)_polys[pi]._verts.size() - 1; pv >= 0; pv--) {
        EggVertex vert(_polys[pi]._verts[pv]._attrib);
        Vertexd pos = 
          _polys[pi]._verts[pv]._pos * net_transform;
        vert.set_pos(pos);

        poly->add_vertex(vpool->create_unique_vertex(vert));
      }
    }
  }
}


////////////////////////////////////////////////////////////////////
//     Function: IndexedFaceSet::compute_normals
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
void IndexedFaceSet::
compute_normals(EggGroup *group) {
  const VrmlNode *normal = _geometry->get_value("normal")._sfnode._p;
  if (normal == NULL) {
    // Compute normals.
    double creaseAngle = _geometry->get_value("creaseAngle")._sffloat;
    if (creaseAngle == 0.0) {
      group->recompute_polygon_normals();
    } else {
      group->recompute_vertex_normals(rad_2_deg(creaseAngle));
    }
  }
}
