Skip to content
Snippets Groups Projects
vertex_array.cc 38.73 KiB
//------------------------------------------------------------------------------
// This file is part of the OpenStructure project <www.openstructure.org>
//
// Copyright (C) 2008-2010 by the OpenStructure authors
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3.0 of the License, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//------------------------------------------------------------------------------
/*
  Author: Ansgar Philippsen
*/

#include <boost/format.hpp>

#include "vertex_array.hh"

#include "glext_include.hh"
#include "color.hh"
#include "material.hh"
#include "scene.hh"
#include "vertex_array_helper.hh"
#include "povray.hh"

#if OST_SHADER_SUPPORT_ENABLED
#include "shader.hh"
#endif


#define VERTEX_ARRAY_CHECK_GL_ERROR(m) \
  if((glerr=glGetError())!=0) {  \
    LOGN_VERBOSE("Error during va buffer prep: " << m << " : " << gluErrorString(glerr)); \
    return false; \
  }

namespace ost { 

using namespace geom;

namespace gfx {

IndexedVertexArray::Entry::Entry()
{
  v[0]=0.0; v[1]=0.0; v[2]=0.0;
  n[0]=0.0; n[1]=0.0; n[2]=1.0;
  c[0]=0.0; c[1]=0.0; c[2]=0.0; c[3]=0.0;
}

IndexedVertexArray::Entry::Entry(const Vec3& vv, const Vec3& nn, const Color& cc)
{
  v[0]=vv[0]; v[1]=vv[1]; v[2]=vv[2];
  n[0]=nn[0]; n[1]=nn[1]; n[2]=nn[2];
  c[0]=cc[0]; c[1]=cc[1]; c[2]=cc[2]; c[3]=cc[3];
}


IndexedVertexArray::IndexedVertexArray()
{
  initialized_=false;
  Clear(); // replaces ctor initialization list
}

IndexedVertexArray::~IndexedVertexArray()
{
}

IndexedVertexArray::IndexedVertexArray(const IndexedVertexArray& va)
{
  copy(va);
}

IndexedVertexArray& IndexedVertexArray::operator=(const IndexedVertexArray& va)
{
  copy(va);
  return *this;
}

unsigned int IndexedVertexArray::GetFormat()
{
  // hardcoded for now, may be refactored and moved into an impl
  return GL_C4F_N3F_V3F;
}


void IndexedVertexArray::Cleanup() 
{
  if(initialized_) {
    glDeleteLists(outline_mat_dlist_,1);
#if OST_SHADER_SUPPORT_ENABLED
    glDeleteBuffers(4,buffer_id_);
#endif
    initialized_=false;
  }
}

void IndexedVertexArray::SetMode(int m) {mode_=m;}
int IndexedVertexArray::GetMode() const {return mode_;}

void IndexedVertexArray::SetPolyMode(int m) {poly_mode_=m;}

void IndexedVertexArray::SetLighting(bool f) {lighting_=f;}
void IndexedVertexArray::SetTwoSided(bool f) {two_sided_=f;}
void IndexedVertexArray::SetCullFace(bool f) {cull_face_=f;}
void IndexedVertexArray::SetColorMaterial(bool f) {color_mat_=f;}
void IndexedVertexArray::SetLineWidth(float lw) {line_width_=lw;}
void IndexedVertexArray::SetAALines(bool f) {aalines_flag_=f;}
void IndexedVertexArray::SetPointSize(float ps) {point_size_=ps;}
void IndexedVertexArray::SetLineHalo(float lh) {line_halo_=lh;}
void IndexedVertexArray::SetOutlineMode(int m) {outline_mode_=m;}
void IndexedVertexArray::SetOutlineWidth(float f) {outline_width_=f;}
void IndexedVertexArray::SetOutlineMaterial(const Material& m) 
{
  outline_mat_=m;
  outline_mat_update_=true;
}
void IndexedVertexArray::SetOutlineExpandFactor(float f) { outline_exp_factor_=f;}
void IndexedVertexArray::SetOutlineExpandColor(const Color& c) {outline_exp_color_=c;}


VertexID IndexedVertexArray::Add(const Vec3& vert, 
                                 const Vec3& norm,
                                 const Color& col) 
{
  dirty_=true;
  entry_list_.push_back(Entry(vert,norm,col));
  return entry_list_.size()-1;
}

unsigned int IndexedVertexArray::GetVertexCount() const
{
  return entry_list_.size();
}

void IndexedVertexArray::DumpVertices() const
{
  for(uint i=0;i<entry_list_.size();++i) {
    LOGN_MESSAGE("id=" << i << " v=" << entry_list_[i].v << " n=" << entry_list_[i].n << " c=" << entry_list_[i].c);
  }
}

LineID IndexedVertexArray::AddLine(VertexID id0, VertexID id1) 
{
  assert(id0<entry_list_.size() && id1<entry_list_.size());
  dirty_=true;
  line_index_list_.push_back(id0);
  line_index_list_.push_back(id1);
  return line_index_list_.size()-2;
}

TriID IndexedVertexArray::AddTri(VertexID id0, VertexID id1, VertexID id2) 
{
  assert(id0<entry_list_.size() && id1<entry_list_.size() && id2<entry_list_.size());
  dirty_=true;
  tri_index_list_.push_back(id0);
  tri_index_list_.push_back(id1);
  tri_index_list_.push_back(id2);
  return tri_index_list_.size()-3;
}

TriID IndexedVertexArray::AddTriN(VertexID id0, VertexID id1, VertexID id2)
{
  TriID tid = AddTri(id0,id1,id2);
  Vec3 d1= Vec3(entry_list_[id1].v)-Vec3(entry_list_[id0].v);
  Vec3 d2= Vec3(entry_list_[id2].v)-Vec3(entry_list_[id0].v);
  Vec3 x = Cross(d1,d2);
  NormalizerTriEntry nte = {
    tid, id0,id1,id2,
    Normalize(x),
#if 0
    1.0/(1.0+Length(x))
#else
    1.0
#endif
  };
  ntentry_list_.push_back(nte);
  return tid;
}

QuadID IndexedVertexArray::AddQuad(VertexID id0, VertexID id1, VertexID id2, VertexID id3) 
{
  assert(id0<entry_list_.size() && id1<entry_list_.size() && id2<entry_list_.size() && id3<entry_list_.size());
  dirty_=true;
  quad_index_list_.push_back(id0);
  quad_index_list_.push_back(id1);
  quad_index_list_.push_back(id2);
  quad_index_list_.push_back(id3);
  return quad_index_list_.size()-4;
}

void IndexedVertexArray::AddSphere(const SpherePrim& prim, unsigned int detail) 
{
  dirty_=true;

  unsigned int level= std::min(VA_SPHERE_MAX_DETAIL,detail);

  const detail::PrebuildSphereEntry &se = detail::GetPrebuildSphere(level);

  // use prebuild list of vertices which define given unit sphere
  std::vector<VertexID> vid_table;
  for(std::vector<Vec3>::const_iterator it=se.vlist.begin();it!=se.vlist.end();++it) {
    VertexID id = Add(prim.radius*(*it)+prim.position,(*it),prim.color);
    vid_table.push_back(id);
  }
  for(unsigned int cc=0;cc<se.ilist.size();cc+=3) {
    AddTri(vid_table[se.ilist[cc+0]],vid_table[se.ilist[cc+1]],vid_table[se.ilist[cc+2]]);
  }
}

// add an icosahedral based sphere with the given params to the va
void IndexedVertexArray::AddIcoSphere(const SpherePrim& prim, unsigned int detail) 
{
  dirty_=true;
  
  unsigned int level= std::min(VA_ICO_SPHERE_MAX_DETAIL,detail);
  
  std::vector<Vec3> vlist = detail::GetPrebuildIcoSphere(level);
  
  for(std::vector<Vec3>::const_iterator it=vlist.begin();it!=vlist.end();it+=3) {
    VertexID id1 = Add(prim.radius*(*(it+0))+prim.position,(*(it+0)),prim.color);
    VertexID id2 = Add(prim.radius*(*(it+1))+prim.position,(*(it+1)),prim.color);
    VertexID id3 = Add(prim.radius*(*(it+2))+prim.position,(*(it+2)),prim.color);
    AddTri(id1,id2,id3);
  }
}

void IndexedVertexArray::AddCylinder(const CylinderPrim& prim, unsigned int detail)
{
  dirty_=true;
  
  unsigned int level = std::min(VA_CYL_MAX_DETAIL,detail);
  
  const std::vector<Vec3>& vlist = detail::GetPrebuildCyl(level);
  
  Vec3 off(0.0,0.0,prim.length);
  
  // prepare first vertices to add
  std::vector<Vec3>::const_iterator it=vlist.begin();
  Vec3 v0 = (*it);
  Vec3 n0 = prim.rotmat * v0; 
  v0*=prim.radius;
  VertexID id1 = Add(prim.rotmat * v0 + prim.start, n0, prim.color);
  VertexID id2 = Add(prim.rotmat * (v0+off) + prim.start, n0, prim.color);
  
  // now for the loop around the circle
  VertexID id3=id1;
  VertexID id4=id2;
  ++it;
  for(;it!=vlist.end();++it) {
    v0 = (*it);
    n0 = prim.rotmat * v0; 
    v0 *= prim.radius;
    VertexID id5 = Add(prim.rotmat * v0 + prim.start, n0, prim.color);
    VertexID id6 = Add(prim.rotmat * (v0+off) + prim.start, n0, prim.color);
    AddTri(id3,id5,id4);
    AddTri(id5,id6,id4);
    id3=id5;
    id4=id6;
  }
  // and finally close the circle
  AddTri(id3,id1,id4);
  AddTri(id1,id2,id4);
}

Vec3 IndexedVertexArray::GetVert(VertexID id) const
{
  Vec3 nrvo;
  if(id>=entry_list_.size()) return nrvo;
  nrvo = Vec3(entry_list_[id].v);
  return nrvo;
} 
void IndexedVertexArray::SetVert(VertexID id, const Vec3& v) 
{
  if(id>=entry_list_.size()) return;
  entry_list_[id].v[0]=v[0];
  entry_list_[id].v[1]=v[1];
  entry_list_[id].v[2]=v[2];
}

Vec3 IndexedVertexArray::GetNormal(VertexID id) const
{
  Vec3 nrvo;
  if(id>=entry_list_.size()) return nrvo;
  nrvo = Vec3(entry_list_[id].n);
  return nrvo;
} 

void IndexedVertexArray::SetNormal(VertexID id, const Vec3& n) 
{
  if(id>=entry_list_.size()) return;
  entry_list_[id].n[0]=n[0];
  entry_list_[id].n[1]=n[1];
  entry_list_[id].n[2]=n[2];
}

Color IndexedVertexArray::GetColor(VertexID id) const
{
  Color nrvo;
  if(id>=entry_list_.size()) return nrvo;
  nrvo = Color(entry_list_[id].c[0],
               entry_list_[id].c[1],
               entry_list_[id].c[2],
               entry_list_[id].c[3]);
  return nrvo;
} 

void IndexedVertexArray::SetColor(VertexID id, const Color& c) 
{
  if(id>=entry_list_.size()) return;
  entry_list_[id].c[0]=c[0];
  entry_list_[id].c[1]=c[1];
  entry_list_[id].c[2]=c[2];
  entry_list_[id].c[3]=c[3];
}

void IndexedVertexArray::RenderGL() 
{
  static bool use_buff=false;
  
  if(!initialized_) {
    LOGN_DUMP("initializing vertex array lists");
#if OST_SHADER_SUPPORT_ENABLED
    glGenBuffers(4,buffer_id_);
#endif
    outline_mat_dlist_=glGenLists(1);
    initialized_=true;
  }

  if(dirty_) {
    dirty_=false;
#if OST_SHADER_SUPPORT_ENABLED
    LOGN_DUMP("checking buffer object availability");
    if(mode_&0x2 && aalines_flag_) {
      use_buff=false;
    } else {
      use_buff=prep_buff();
    }
    if(!use_buff) {
      glBindBuffer(GL_ARRAY_BUFFER,0);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    } else {
      LOGN_DUMP("using buffer objects for vertex array");
    }
#endif
  }
  
  glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_COLOR_BUFFER_BIT | GL_LIGHTING_BIT);
  glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
  glPushMatrix();

  if(outline_mode_>0) {
    LOGN_TRACE("outline rendering");
    if(outline_mat_update_) {
      glNewList(outline_mat_dlist_,GL_COMPILE);
      outline_mat_.RenderGL();
      glEndList();
      outline_mat_update_=false;
    }
    glDisable(GL_LIGHTING);
    glDisable(GL_COLOR_MATERIAL);
    glCullFace(GL_FRONT);
    glEnable(GL_CULL_FACE);
    glShadeModel(GL_FLAT);
    if(outline_mode_==1) {
      glCallList(outline_mat_dlist_);
      glEnable(GL_POLYGON_OFFSET_LINE);
      glEnable(GL_POLYGON_OFFSET_POINT);
      glPolygonOffset(10.0,1.0);
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_LINE_SMOOTH);
      glDisable(GL_POINT_SMOOTH); // kills selfx fragment shader if enabled
      glDisable(GL_POLYGON_SMOOTH);
    } else if(outline_mode_==2) {
      glCallList(outline_mat_dlist_);
      glEnable(GL_POLYGON_OFFSET_LINE);
      glPolygonOffset(10.0,1.0);
      glDisable(GL_BLEND);
      glDisable(GL_LINE_SMOOTH);
      glDisable(GL_POINT_SMOOTH);
      glDisable(GL_POLYGON_SMOOTH);
      glCullFace(GL_FRONT);
    } else {
      glDisable(GL_POLYGON_OFFSET_LINE);
      glDisable(GL_POLYGON_OFFSET_POINT);
      glDisable(GL_LINE_SMOOTH);
      glDisable(GL_BLEND);
    }
  } else {
    if(lighting_) {
      glEnable(GL_LIGHTING); 
    } else {
      glDisable(GL_LIGHTING);
    }
    if(two_sided_) {
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    } else {
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    }
    if(color_mat_) {
      glEnable(GL_COLOR_MATERIAL); 
    } else {
      glDisable(GL_COLOR_MATERIAL);
    }
    if(cull_face_) {
      glEnable(GL_CULL_FACE);
    } else { 
      glDisable(GL_CULL_FACE); 
    }
  }
  
  if(mode_&0x1) {
    if(outline_mode_>0) {
      glPointSize(outline_width_);
    } else {
      glPointSize(point_size_);
    }
#if OST_SHADER_SUPPORT_ENABLED      
    Shader::Instance().UpdateState();
#endif      
    draw_p(use_buff);
  }
  if(mode_&0x6) { // 0x2 and 0x4
    if(outline_mode_==1) {
      // draw points beneath lines to reduce aliasing
      glLineWidth(outline_width_);
      glPointSize(outline_width_-1);
      glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
      draw_ltq(use_buff);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      draw_ltq(use_buff);
    } else if(outline_mode_==2) {
      glLineWidth(outline_width_);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      draw_ltq(use_buff);
    } else if(outline_mode_==3) {
#if OST_SHADER_SUPPORT_ENABLED
      Shader::Instance().PushProgram();
      Shader::Instance().Activate("outline");
      glUniform1f(glGetUniformLocation(Shader::Instance().GetCurrentProgram(),"scalef"),outline_exp_factor_);
      glUniform4f(glGetUniformLocation(Shader::Instance().GetCurrentProgram(),"color"),
                  outline_exp_color_[0],outline_exp_color_[1],
                  outline_exp_color_[2],outline_exp_color_[3]);
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      draw_ltq(use_buff);

      Shader::Instance().PopProgram();
#endif
    } else {
      glLineWidth(line_width_);
      if(poly_mode_==0) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
      } else if(poly_mode_==1) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      } else {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      }
#if OST_SHADER_SUPPORT_ENABLED
      Shader::Instance().UpdateState();
      if(aalines_flag_) {
        draw_aalines();
      } else {
#endif
        if((mode_&0x2 || (mode_&0x4 && poly_mode_==1)) && line_halo_>0.0) {
          draw_line_halo(use_buff);
        }
        draw_ltq(use_buff);

#if OST_SHADER_SUPPORT_ENABLED
      }
#endif
    }
  }

  if(draw_normals_) {
    glColor3f(1,0,0);
    glBegin(GL_LINES);
    for(EntryList::const_iterator it=entry_list_.begin();it!=entry_list_.end();++it) {
      glNormal3fv(it->n);
      glVertex3fv(it->v);
      glNormal3fv(it->n);
      glVertex3f(it->v[0]+it->n[0]*0.75,
                 it->v[1]+it->n[1]*0.75,
                 it->v[2]+it->n[2]*0.75);
    }
    glEnd();
  }

  glPopMatrix();
  glPopClientAttrib();
  glPopAttrib();
}

namespace {
  unsigned int col_to_index(float* c)
  {
    return static_cast<unsigned int>(c[0]*7.0)*64+static_cast<unsigned int>(c[1]*7.0)*8+static_cast<unsigned int>(c[2]*7.0);
  }
}

void IndexedVertexArray::RenderPov(PovState& pov, const std::string& name)
{
  if(entry_list_.empty()) return;
  pov.inc() << "mesh2 {\n";

  pov.inc() << " vertex_vectors { " << entry_list_.size() << ",\n";
  for(unsigned int i=0;i<entry_list_.size();++i) {
    pov.inc() << "  " << pov.write_coord(entry_list_[i].v);
    if(i+1<entry_list_.size()) pov.inc() << ",";
    pov.inc() << "\n";
  }
  pov.inc() << " }\n";

  pov.inc() << " normal_vectors { " << entry_list_.size() << ",\n";
  for(unsigned int i=0;i<entry_list_.size();++i) {
    pov.inc() << "  " << pov.write_norm(entry_list_[i].n);
    if(i+1<entry_list_.size()) pov.inc() << ",";
    pov.inc() << "\n";
  }
  pov.inc() << " }\n";

  pov.inc() << " texture_list { " << 8*8*8 << ",\n";
  for(unsigned int r=0;r<8;++r) {
    float fr=static_cast<float>(r)/7.0;
    for(unsigned int g=0;g<8;++g) {
      float fg=static_cast<float>(g)/7.0;
      for(unsigned int b=0;b<8;++b) {
        float fb=static_cast<float>(b)/7.0;
        pov.inc() << boost::format("  texture {_%s_tex pigment {color rgbft <%f,%f,%f,_%s_fi,_%s_tp>}}") % name % fr % fg % fb % name %name ;
        pov.inc() << ((r<7 && g<7 && b<7) ? ",\n" : "\n");
      }
    }
  }
  pov.inc() << " }\n";

  pov.inc() << " face_indices { " << tri_index_list_.size()/3 + quad_index_list_.size()/2;
  for(unsigned int c=0;c<tri_index_list_.size();c+=3) {
    pov.inc() << ",\n";
    unsigned int i0 = tri_index_list_[c];
    unsigned int i1 = tri_index_list_[c+1];
    unsigned int i2 = tri_index_list_[c+2];
    unsigned int x0 = col_to_index(entry_list_[i0].c);
    unsigned int x1 = col_to_index(entry_list_[i1].c);
    unsigned int x2 = col_to_index(entry_list_[i2].c);
    pov.inc() << boost::format("  <%u,%u,%u>, %u, %u, %u") % i0 % i1 % i2 % x0 % x1 % x2;
  }
  for(unsigned int c=0;c<quad_index_list_.size();c+=4) {
    pov.inc() << ",\n";
    unsigned int i0 = tri_index_list_[c];
    unsigned int i1 = tri_index_list_[c+1];
    unsigned int i2 = tri_index_list_[c+2];
    unsigned int i3 = tri_index_list_[c+3];
    unsigned int x0 = col_to_index(entry_list_[i0].c);
    unsigned int x1 = col_to_index(entry_list_[i1].c);
    unsigned int x2 = col_to_index(entry_list_[i2].c);
    unsigned int x3 = col_to_index(entry_list_[i3].c);
    pov.inc() << boost::format("  <%u,%u,%u>, %u, %u, %u") % i0 % i1 % i2 % x0 % x1 % x2;
    pov.inc() << boost::format("  <%u,%u,%u>, %u, %u, %u") % i1 % i2 % i3 % x1 % x2 % x3;
  }
  pov.inc() << "\n }\n";

  pov.inc() << "}\n";
}

void IndexedVertexArray::Clear() 
{
  dirty_=true;
  entry_list_.clear();
  quad_index_list_.clear();
  tri_index_list_.clear();
  line_index_list_.clear();
  ntentry_list_.clear();
  mode_=0x4;
  poly_mode_=2;
  lighting_=true;
  two_sided_=false;
  cull_face_=true;;
  color_mat_=true;
  line_width_=2.0;
  aalines_flag_=false;
  point_size_=2.0;
  line_halo_=0.0;
  outline_mode_=0;
  outline_width_=4.0;
  outline_mat_=Material(0.3,1.0,0.0,0.0,0.0);
  outline_mat_update_=true;
  outline_exp_factor_=0.1;
  outline_exp_color_=Color(0,0,0);
  draw_normals_=false;
}

void IndexedVertexArray::FlagRefresh()
{
  dirty_=true;
}

void IndexedVertexArray::CalcNormals(float smoothf) 
{
  if(ntentry_list_.empty()) return;

  std::vector<std::pair<bool,Vec3> > norm_list(entry_list_.size());

  // is this necessary or is the zeroing handled by std::vector ctor
  for(uint c=0;c<norm_list.size();++c) {
    norm_list[c].first=false;
  }

  for(NTEntryList::const_iterator it=ntentry_list_.begin();it!=ntentry_list_.end();++it) {
    norm_list[it->id0].first=true;
    norm_list[it->id1].first=true;
    norm_list[it->id2].first=true;
    // is this necessary or is the zeroing handled by std::vector ctor
    norm_list[it->id0].second=Vec3(0,0,0);
    norm_list[it->id1].second=Vec3(0,0,0);
    norm_list[it->id2].second=Vec3(0,0,0);
  }

  for(NTEntryList::const_iterator it=ntentry_list_.begin();it!=ntentry_list_.end();++it) {
    norm_list[it->id0].second+=it->weight*it->norm;
    norm_list[it->id1].second+=it->weight*it->norm;
    norm_list[it->id2].second+=it->weight*it->norm;
  }

  if(smoothf>0.0) {
    std::vector<std::pair<bool,Vec3> > norm_list2=norm_list;
    for(NTEntryList::const_iterator it=ntentry_list_.begin();it!=ntentry_list_.end();++it) {
      norm_list[it->id0].second+=smoothf*norm_list2[it->id1].second;
      norm_list[it->id0].second+=smoothf*norm_list2[it->id2].second;
      norm_list[it->id1].second+=smoothf*norm_list2[it->id0].second;
      norm_list[it->id1].second+=smoothf*norm_list2[it->id2].second;
      norm_list[it->id2].second+=smoothf*norm_list2[it->id0].second;
      norm_list[it->id2].second+=smoothf*norm_list2[it->id1].second;
    }
  }

  for(uint c=0;c<norm_list.size();++c) {
    if(norm_list[c].first) {
      Vec3 n=Normalize(norm_list[c].second);
      entry_list_[c].n[0]=n[0];
      entry_list_[c].n[1]=n[1];
      entry_list_[c].n[2]=n[2];
    }
  }

}

void IndexedVertexArray::CalcFullNormals() 
{
  LOGN_DUMP("calculating normals for vertex array");
  
  // book keeping setup
  static NormalizerTriEntry nte={0,0,0,0,Vec3(),1.0};
  
  NVEntryList nv_entry_list(entry_list_.size());
  unsigned int count=0;
  for(EntryList::const_iterator it = entry_list_.begin();
      it!=entry_list_.end(); ++it) {
    NormalizerVertexEntry nve;
    nve.pos=Vec3(it->v[0],it->v[1],it->v[2]);
    nve.tri_count=0;
    nve.weight=0.0;
    nv_entry_list[count++] = nve;
  }
  
  NTEntryList nt_entry_list(tri_index_list_.size()/3);
  count=0;
  for(IndexList::const_iterator it = tri_index_list_.begin();
      it!=tri_index_list_.end();) {
    nte.id0=*(it++);
    nte.id1=*(it++);
    nte.id2=*(it++);
    unsigned int nt_entry_id = count;
    nt_entry_list[count++]=nte;
    nv_entry_list[nte.id0].tri_list[nv_entry_list[nte.id0].tri_count++]=nt_entry_id;
    nv_entry_list[nte.id1].tri_list[nv_entry_list[nte.id1].tri_count++]=nt_entry_id;
    nv_entry_list[nte.id2].tri_list[nv_entry_list[nte.id2].tri_count++]=nt_entry_id;
  }
  
  // normals for all faces
  for(NTEntryList::iterator it=nt_entry_list.begin();
      it!=nt_entry_list.end();++it) {
    Vec3& p0=nv_entry_list[it->id0].pos;
    Vec3& p1=nv_entry_list[it->id1].pos;
    Vec3& p2=nv_entry_list[it->id2].pos;
    Vec3 d=Cross(Normalize(p1-p0),Normalize(p2-p0));
    //it->norm=Normalize(d);
    it->norm=d;
    it->weight=Dot(d,Cross(p0,p1)+Cross(p1,p2)+Cross(p2,p0));
    nv_entry_list[it->id0].weight+=acos(Dot(Normalize(p1-p0),Normalize(p2-p0)));
    nv_entry_list[it->id1].weight+=acos(Dot(Normalize(p0-p1),Normalize(p2-p1)));
    nv_entry_list[it->id2].weight+=acos(Dot(Normalize(p0-p2),Normalize(p1-p2)));
  }
  
  // now the normals for each vertex
  for(uint vc=0;vc<nv_entry_list.size();++vc) {
    NormalizerVertexEntry& nve=nv_entry_list[vc];
    if(nve.tri_count>0) {
      Vec3 norm(0.0,0.0,0.0);
      for(uint tc=0;tc<nve.tri_count;++tc) {
        TriID id = nve.tri_list[tc];
        if(id<nt_entry_list.size()) {
          NormalizerTriEntry& nte=nt_entry_list[id];
          //norm+=nte.norm;
          norm+=nte.norm*nte.weight;
          //norm+=nte.norm*nve.weight;
        } else {
          LOGN_DUMP("faulty vertex lookup in VA Normalize");
        }
      }
      norm=Normalize(norm);
      entry_list_[vc].n[0]=norm[0];
      entry_list_[vc].n[1]=norm[1];
      entry_list_[vc].n[2]=norm[2];
    }
  }
}

void IndexedVertexArray::DrawNormals(bool f)
{
  draw_normals_=f;
}


void IndexedVertexArray::SmoothNormals(float smoothf)
{
  if(smoothf==0.0) return;
  std::vector<std::pair<Vec3, int> > nlist(entry_list_.size());
#if 1
  // smooth by adjoining face normals
  for(uint c=0;c<tri_index_list_.size();) {
    VertexID id0 = tri_index_list_[c++];
    VertexID id1 = tri_index_list_[c++];
    VertexID id2 = tri_index_list_[c++];

    Vec3 n= Cross(Vec3(entry_list_[id2].v)-Vec3(entry_list_[id1].v),
                  Vec3(entry_list_[id0].v)-Vec3(entry_list_[id1].v));
    nlist[id0].first+=n;
    nlist[id0].second+=1;
    nlist[id1].first+=n;
    nlist[id1].second+=1;
    nlist[id2].first+=n;
    nlist[id2].second+=1;
  }
  for(uint c=0;c<quad_index_list_.size();) {
    VertexID id0 = quad_index_list_[c++];
    VertexID id1 = quad_index_list_[c++];
    VertexID id2 = quad_index_list_[c++];
    VertexID id3 = quad_index_list_[c++];
    Vec3 n= Cross(Vec3(entry_list_[id2].v)-Vec3(entry_list_[id1].v),
                  Vec3(entry_list_[id0].v)-Vec3(entry_list_[id1].v));
    nlist[id0].first+=n;
    nlist[id0].second+=1;
    nlist[id1].first+=n;
    nlist[id1].second+=1;
    nlist[id2].first+=n;
    nlist[id2].second+=1;
    nlist[id3].first+=n;
    nlist[id3].second+=1;
  }
#else
  // smooth by adjoining vertice normals
  for(uint c=0;c<tri_index_list_.size();) {
    VertexID id0 = tri_index_list_[c++];
    VertexID id1 = tri_index_list_[c++];
    VertexID id2 = tri_index_list_[c++];
    nlist[id0].first += Vec3(entry_list_[id1].n);
    nlist[id0].first += Vec3(entry_list_[id2].n);
    nlist[id0].second+=2;
    nlist[id1].first += Vec3(entry_list_[id2].n);
    nlist[id1].first += Vec3(entry_list_[id0].n);
    nlist[id1].second+=2;
    nlist[id2].first += Vec3(entry_list_[id0].n);
    nlist[id2].first += Vec3(entry_list_[id1].n);
    nlist[id2].second+=2;
  }
  for(uint c=0;c<quad_index_list_.size();) {
    VertexID id0 = quad_index_list_[c++];
    VertexID id1 = quad_index_list_[c++];
    VertexID id2 = quad_index_list_[c++];
    VertexID id3 = quad_index_list_[c++];
    nlist[id0].first += Vec3(entry_list_[id1].n);
    nlist[id0].first += Vec3(entry_list_[id2].n);
    nlist[id0].first += Vec3(entry_list_[id3].n);
    nlist[id0].second+=3;
    nlist[id1].first += Vec3(entry_list_[id2].n);
    nlist[id1].first += Vec3(entry_list_[id3].n);
    nlist[id1].first += Vec3(entry_list_[id0].n);
    nlist[id1].second+=3;
    nlist[id2].first += Vec3(entry_list_[id3].n);
    nlist[id2].first += Vec3(entry_list_[id0].n);
    nlist[id2].first += Vec3(entry_list_[id1].n);
    nlist[id2].second+=3;
    nlist[id3].first += Vec3(entry_list_[id0].n);
    nlist[id3].first += Vec3(entry_list_[id1].n);
    nlist[id3].first += Vec3(entry_list_[id2].n);
    nlist[id3].second+=3;
  }
#endif
  for(uint c=0;c<entry_list_.size();++c) {
    if(nlist[c].second>0) {
      float f=1.0/static_cast<float>(nlist[c].second);
      Vec3 n=Normalize((1.0-smoothf)*Vec3(entry_list_[c].n)+f*smoothf*nlist[c].first);
      entry_list_[c].n[0]=n[0];
      entry_list_[c].n[1]=n[1];
      entry_list_[c].n[2]=n[2];
    }
  }
}

void IndexedVertexArray::SmoothVertices(float smoothf)
{
  if(smoothf==0.0) return;
  std::vector<std::pair<Vec3, int> > dlist(entry_list_.size());

  for(uint c=0;c<tri_index_list_.size();) {
    VertexID id0 = tri_index_list_[c++];
    VertexID id1 = tri_index_list_[c++];
    VertexID id2 = tri_index_list_[c++];
    Vec3 p0(entry_list_[id0].v);
    Vec3 p1(entry_list_[id1].v);
    Vec3 p2(entry_list_[id2].v);
    dlist[id0].first+=p1-p0;
    dlist[id0].first+=p2-p0;
    dlist[id0].second+=2;
    dlist[id1].first+=p0-p1;
    dlist[id1].first+=p2-p1;
    dlist[id1].second+=2;
    dlist[id2].first+=p0-p2;
    dlist[id2].first+=p1-p2;
    dlist[id2].second+=2;
  }
  for(uint c=0;c<quad_index_list_.size();) {
    VertexID id0 = quad_index_list_[c++];
    VertexID id1 = quad_index_list_[c++];
    VertexID id2 = quad_index_list_[c++];
    VertexID id3 = quad_index_list_[c++];
    Vec3 p0(entry_list_[id0].v);
    Vec3 p1(entry_list_[id1].v);
    Vec3 p2(entry_list_[id2].v);
    Vec3 p3(entry_list_[id3].v);
    dlist[id0].first+=p1-p0;
    dlist[id0].first+=p3-p0;
    dlist[id0].second+=2;
    dlist[id1].first+=p0-p1;
    dlist[id1].first+=p2-p1;
    dlist[id1].second+=2;
    dlist[id2].first+=p3-p2;
    dlist[id2].first+=p1-p2;
    dlist[id2].second+=2;
    dlist[id3].first+=p2-p3;
    dlist[id3].first+=p0-p3;
    dlist[id3].second+=2;
  }
  for(uint c=0;c<entry_list_.size();++c) {
    Vec3 d = smoothf*dlist[c].first/static_cast<float>(dlist[c].second);
    entry_list_[c].v[0]+=d[0];
    entry_list_[c].v[1]+=d[1];
    entry_list_[c].v[2]+=d[2];
  }
}


namespace {

uint npatch_tab_id(uint u, uint v, uint N)
{
  return (N+1)*(N+2)/2-(N+1-v)*(N+2-v)/2+u;
}

}

void IndexedVertexArray::NPatch()
{
  uint N=3;

  EntryList entry_list;
  IndexList tri_index_list;
  IndexList line_index_list;

  entry_list.push_back(Entry(Vec3(),Vec3(),Color(1,0,0)));

  for(uint c=0;c<tri_index_list_.size();) {
    VertexID id0 = tri_index_list_[c++];
    VertexID id1 = tri_index_list_[c++];
    VertexID id2 = tri_index_list_[c++];
    Vec3 p300(entry_list_[id0].v);
    Vec3 p030(entry_list_[id1].v);
    Vec3 p003(entry_list_[id2].v);
    Vec3 n200(entry_list_[id0].n);
    Vec3 n020(entry_list_[id1].n);
    Vec3 n002(entry_list_[id2].n);
    
    Vec3 p210 = (2.0*p300+p030-Dot(n200,p030-p300)*n200)/3.0;
    Vec3 p120 = (p300+2.0*p030-Dot(n020,p300-p030)*n020)/3.0;
    Vec3 p201 = (2.0*p300+p003-Dot(n200,p003-p300)*n200)/3.0;
    Vec3 p102 = (p003+2.0*p300-Dot(n002,p300-p003)*n002)/3.0;
    Vec3 p021 = (2.0*p030+p003-Dot(n020,p003-p030)*n020)/3.0;
    Vec3 p012 = (p030+2.0*p003-Dot(n002,p030-p003)*n002)/3.0;
    Vec3 p111 = (p210+p120+p102+p201+p021+p012)/4.0-(p300+p030+p003)/6.0;

    Vec3 d330 = p030-p300;
    Vec3 d033 = p003-p030;
    Vec3 d303 = p300-p003;
    Vec3 n110 = Normalize(n200+n020-2.0*Dot(d330,n200+n020)/Dot(d330,d330)*d330);
    Vec3 n011 = Normalize(n020+n002-2.0*Dot(d033,n020+n002)/Dot(d033,d033)*d033);
    Vec3 n101 = Normalize(n002+n200-2.0*Dot(d303,n002+n200)/Dot(d303,d303)*d303);

    std::vector<TriID> tab((N+1)*(N+2)/2);
    for(uint vc=0;vc<=N;++vc) {
      float v = static_cast<float>(vc)/static_cast<float>(N);
      for(uint uc=0;uc<=N-vc;++uc) {
        float u = static_cast<float>(uc)/static_cast<float>(N);
        float w = 1.0-u-v;
        Vec3 p = u*u*u*p300+v*v*v*p030+w*w*w*p003+
          3.0*(u*u*v*p210+u*u*w*p201+u*v*v*p120+v*v*w*p021+v*w*w*p012+u*w*w*p102)+
          6.0*u*v*w*p111;
        Vec3 n = Normalize(u*u*n200+v*v*n020+w*w*n002+u*v*n110+u*w*n101+v*w*n011);
        entry_list.push_back(Entry(p,n,Color()));
        tab[npatch_tab_id(uc,vc,N)] = entry_list.size()-1;
      }
    }

    line_index_list.push_back(tab[npatch_tab_id(0,0,N)]);
    line_index_list.push_back(tab[npatch_tab_id(0,N,N)]);

    line_index_list.push_back(tab[npatch_tab_id(0,N,N)]);
    line_index_list.push_back(tab[npatch_tab_id(N,0,N)]);

    line_index_list.push_back(tab[npatch_tab_id(N,0,N)]);
    line_index_list.push_back(tab[npatch_tab_id(0,0,N)]);

    for(uint vc=0;vc<N;++vc) {
      for(uint uc=0;uc<N-vc;++uc) {
        tri_index_list.push_back(tab[npatch_tab_id(uc,vc,N)]);
        tri_index_list.push_back(tab[npatch_tab_id(uc+1,vc,N)]);
        tri_index_list.push_back(tab[npatch_tab_id(uc,vc+1,N)]);
        if(uc+vc<N-1) {
          tri_index_list.push_back(tab[npatch_tab_id(uc+1,vc,N)]);
          tri_index_list.push_back(tab[npatch_tab_id(uc+1,vc+1,N)]);
          tri_index_list.push_back(tab[npatch_tab_id(uc,vc+1,N)]);
        }
      }
    }
  }

  entry_list_.swap(entry_list);
  line_index_list_.swap(line_index_list);
  tri_index_list_.swap(tri_index_list);
  quad_index_list_.clear();

  FlagRefresh();
}


////////////////////////////////////////
// private methods

    
void IndexedVertexArray::copy(const IndexedVertexArray& va)
{
  initialized_=false;
  entry_list_=va.entry_list_;
  quad_index_list_=va.quad_index_list_;
  tri_index_list_=va.tri_index_list_;
  line_index_list_=va.line_index_list_;
  ntentry_list_=va.ntentry_list_;
  dirty_=true;
  mode_=va.mode_;
  poly_mode_=va.poly_mode_;
  lighting_=va.lighting_;
  two_sided_=va.two_sided_;
  cull_face_=va.cull_face_;
  color_mat_=va.color_mat_;
  line_width_=va.line_width_;
  aalines_flag_=va.aalines_flag_;
  point_size_=va.point_size_;
  line_halo_=va.line_halo_;
  outline_mode_=va.outline_mode_;
  outline_width_=va.outline_width_;
  outline_mat_=va.outline_mat_;
  outline_mat_update_=true;
  outline_exp_factor_=va.outline_exp_factor_;
  outline_exp_color_=va.outline_exp_color_;
  draw_normals_=va.draw_normals_;
}
  
bool IndexedVertexArray::prep_buff()
{
#if OST_SHADER_SUPPORT_ENABLED
  int glerr=glGetError(); // clear error flag
  glBindBuffer(GL_ARRAY_BUFFER, buffer_id_[0]);
  VERTEX_ARRAY_CHECK_GL_ERROR("bind buf0");
  glBufferData(GL_ARRAY_BUFFER, 
               sizeof(Entry) * entry_list_.size(),
               &entry_list_[0],
               GL_STATIC_DRAW);
  VERTEX_ARRAY_CHECK_GL_ERROR("set buf0");
  
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id_[1]);
  VERTEX_ARRAY_CHECK_GL_ERROR("bind buf1");
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
               sizeof(unsigned int) * line_index_list_.size(),
               &line_index_list_[0],
               GL_STATIC_DRAW);
  VERTEX_ARRAY_CHECK_GL_ERROR("set buf1");
  
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id_[2]);
  VERTEX_ARRAY_CHECK_GL_ERROR("bind buf2");
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
               sizeof(unsigned int) * tri_index_list_.size(),
               &tri_index_list_[0],
               GL_STATIC_DRAW);
  VERTEX_ARRAY_CHECK_GL_ERROR("set buf2");
  
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id_[3]);
  VERTEX_ARRAY_CHECK_GL_ERROR("bind buf3");
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
               sizeof(unsigned int) * quad_index_list_.size(),
               &quad_index_list_[0],
               GL_STATIC_DRAW);
  VERTEX_ARRAY_CHECK_GL_ERROR("set buf3");
  
  return true;
#else
  return false;
#endif
}

void IndexedVertexArray::draw_ltq(bool use_buff)
{
  if(use_buff && !Scene::Instance().InOffscreenMode()) {
#if OST_SHADER_SUPPORT_ENABLED
    LOGN_TRACE("binding vertex array buffer");
    glBindBuffer(GL_ARRAY_BUFFER, buffer_id_[0]);
    LOGN_TRACE("setting up vertex array");
    glInterleavedArrays(GetFormat(),sizeof(Entry),NULL);
    
    if(!tri_index_list_.empty() && (mode_ & 0x4)) {
      LOGN_TRACE("binding and rendering vertex array tris");
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id_[2]);
      glDrawElements(GL_TRIANGLES,tri_index_list_.size(),GL_UNSIGNED_INT,NULL);
    }
    if(!quad_index_list_.empty() && (mode_ & 0x4)) {
      LOGN_TRACE("binding and rendering vertex arras quads");
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id_[3]);
      glDrawElements(GL_QUADS,quad_index_list_.size(),GL_UNSIGNED_INT,NULL);
    }
    if(!line_index_list_.empty() && (mode_ & 0x2)) {
      LOGN_TRACE("binding and rendering vertex array lines");
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_id_[1]);
      glDrawElements(GL_LINES,line_index_list_.size(),GL_UNSIGNED_INT,NULL);
    }
    
    LOGN_TRACE("unbinding vertex array buffer");
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
#endif
  } else {
    
    LOGN_TRACE("setting up vertex array");
    glInterleavedArrays(GetFormat(),sizeof(Entry),&entry_list_[0]);
    
    if(!tri_index_list_.empty() && (mode_ & 0x4)) {
      LOGN_TRACE("rendering vertex arras tris");
      glDrawElements(GL_TRIANGLES,tri_index_list_.size(),GL_UNSIGNED_INT,&tri_index_list_[0]);
    }
    if(!quad_index_list_.empty() && (mode_ & 0x4)) {
      LOGN_TRACE("rendering vertex arras quads");
      glDrawElements(GL_QUADS,quad_index_list_.size(),GL_UNSIGNED_INT,&quad_index_list_[0]);
    }
    if(!line_index_list_.empty() && (mode_ & 0x2)) {
      LOGN_TRACE("rendering vertex arras lines");
      glDrawElements(GL_LINES,line_index_list_.size(),GL_UNSIGNED_INT,&line_index_list_[0]);
    }
  }
}
    
void IndexedVertexArray::draw_p(bool use_buff)
{
  if(use_buff && !Scene::Instance().InOffscreenMode()) {
#if OST_SHADER_SUPPORT_ENABLED
    LOGN_TRACE("binding vertex array buffer");
    glBindBuffer(GL_ARRAY_BUFFER, buffer_id_[0]);
    LOGN_TRACE("calling vertex array");
    glInterleavedArrays(GetFormat(),sizeof(Entry),NULL);
    glDrawArrays(GL_POINTS,0,entry_list_.size());
#endif
  } else {
    LOGN_TRACE("calling vertex array");
    glInterleavedArrays(GetFormat(),sizeof(Entry),&entry_list_[0]);
    glDrawArrays(GL_POINTS,0,entry_list_.size());
  }
}

namespace {

struct AALineEntry {
  float p0[3], p1[3];
  float edge0[3],edge1[3],edge2[3],edge3[3];
  float c0[4], c1[4];
  float z;
};

typedef std::vector<AALineEntry> AALineList;

struct AALineEntryLess
{
  bool operator()(const AALineEntry& e1, const AALineEntry& e2)
  {
    return e1.z<e2.z;
  }
};

/*
  Adapted after Chan and Durand, "Fast Prefiltered Lines",
  in GPU Gems 2
*/
void render_aalines(AALineList& line_list, float w, float r)
{
#if OST_SHADER_SUPPORT_ENABLED
  static GLuint table_tex_id=0;
  std::vector<unsigned char> tex_table(32);

  if(table_tex_id==0) {
    for(int i=0;i<32;++i) {
      float x=static_cast<float>(i)/31.0;
      tex_table[31-i]=static_cast<unsigned char>(255.0*exp(-4.0*x*x));
    }
    glGenTextures(1,&table_tex_id);
    glBindTexture(GL_TEXTURE_1D,table_tex_id);
    glPixelStorei(GL_UNPACK_ALIGNMENT,1);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage1D(GL_TEXTURE_1D,0,GL_LUMINANCE,32,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,&tex_table[0]);
  }
  std::sort(line_list.begin(),line_list.end(),AALineEntryLess());
  
  Shader::Instance().PushProgram();
  Shader::Instance().Activate("aaline");

  glLineWidth(ceil((2.0f*r+w)*1.5));

  glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
  glDisable(GL_NORMALIZE);
  glDisable(GL_CULL_FACE);
  glDisable(GL_LIGHTING);
  glDisable(GL_COLOR_MATERIAL);
  //glDisable(GL_DEPTH_TEST);
  glDisable(GL_LINE_SMOOTH);
  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  glDepthMask(0);
  Shader::Instance().UpdateState();
  
  GLuint cpr = Shader::Instance().GetCurrentProgram();
  GLint edge0_id = glGetUniformLocation(cpr,"edge0");
  GLint edge1_id = glGetUniformLocation(cpr,"edge1");
  GLint edge2_id = glGetUniformLocation(cpr,"edge2");
  GLint edge3_id = glGetUniformLocation(cpr,"edge3");
  GLint table_id = glGetUniformLocation(cpr,"table");

  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_TEXTURE_1D);
  glBindTexture(GL_TEXTURE_1D,table_tex_id);
  glUniform1i(table_id,0);

  for(AALineList::iterator it=line_list.begin();
      it!=line_list.end();++it) {
    glUniform3fv(edge0_id,1,it->edge0);
    glUniform3fv(edge1_id,1,it->edge1);
    glUniform3fv(edge2_id,1,it->edge2);
    glUniform3fv(edge3_id,1,it->edge3);
    // glUniform cannot be inside a glBegin / glEnd
    glBegin(GL_LINES);
    glColor4fv(it->c0);
    glVertex3fv(it->p0);
    glColor4fv(it->c1);
    glVertex3fv(it->p1); 
    glEnd();
  }

  glPopAttrib();

  Shader::Instance().PopProgram();
#endif
}

Vec3 make_aaline_edge(const Vec2& c1, const Vec2& c0, float s)
{
  Vec3 nrvo(c1[1]-c0[1],c0[0]-c1[0],c1[0]*c0[1]-c0[0]*c1[1]);
  nrvo*=1.0/(s*Length(c1-c0));
  return nrvo;
}

}
    
void IndexedVertexArray::draw_aalines()
{
#if OST_SHADER_SUPPORT_ENABLED
  float w=0.5*line_width_;
  float r=w;
  float hwr = 0.5*w+r;
  
  AALineList line_list;
  for(unsigned int i=0;i<line_index_list_.size();i+=2) {
    Entry& ve0 = entry_list_[line_index_list_[i]];
    Entry& ve1 = entry_list_[line_index_list_[i+1]];
    Vec3 q0(Scene::Instance().Project(Vec3(ve0.v),false));
    Vec3 q1(Scene::Instance().Project(Vec3(ve1.v),false));
    Vec2 p0(q0[0],q0[1]);
    Vec2 p1(q1[0],q1[1]);
    Vec2 d=Normalize(p1-p0);
    Vec2 n(d[1],-d[0]);
    Vec2 c0=p0-(r)*d+(hwr)*n;
    Vec2 c1=p1+(r)*d+(hwr)*n;
    Vec2 c2=p1+(r)*d-(hwr)*n;
    Vec2 c3=p0-(r)*d-(hwr)*n;
    Vec3 e0=make_aaline_edge(c0,c1,r);
    Vec3 e1=make_aaline_edge(c1,c2,r);
    Vec3 e2=make_aaline_edge(c2,c3,r);
    Vec3 e3=make_aaline_edge(c3,c0,r);
    
    // TODO: it should be possible to avoid the unproject and
    // pass the projected coordinates directly to GL, setting
    // modelview and projection to unity; requires handling of
    // the viewport though!
    Vec3 np1=Scene::Instance().UnProject(q0-Vec3(hwr*d),false);
    Vec3 np2=Scene::Instance().UnProject(q1+Vec3(hwr*d),false);
    
    AALineEntry le={{np1[0],np1[1],np1[2]},
                    {np2[0],np2[1],np2[2]},
                    {e0[0],e0[1],e0[2]},
                    {e1[0],e1[1],e1[2]},
                    {e2[0],e2[1],e2[2]},
                    {e3[0],e3[1],e3[2]},
                    {ve0.c[0],ve0.c[1],ve0.c[2],ve0.c[3]},
                    {ve1.c[0],ve1.c[1],ve1.c[2],ve1.c[3]},
                    -0.5*(q0[2]+q1[2])};
    line_list.push_back(le);
  }
  
  render_aalines(line_list,w,r);
#endif
}

void IndexedVertexArray::draw_line_halo(bool use_buff)
{
  glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
  glLineWidth(line_width_+line_halo_);
  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_LIGHTING);
  glDisable(GL_COLOR_MATERIAL);
  glEnable(GL_LINE_SMOOTH);
  glDisable(GL_POINT_SMOOTH);
  glDisable(GL_POLYGON_SMOOTH);
  glEnable(GL_BLEND);
  glDisable(GL_FOG);
  float epsilon=0.05;
  glDepthRange(0.0+epsilon,1.0+epsilon);

  draw_ltq(use_buff);

  glDepthRange(0.0,1.0);
  glPopAttrib();
  glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
  glLineWidth(line_width_);
}

}} // ns