From 8fff42deda80974665fd401ed5e29f8aa0794370 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Tue, 22 Aug 2006 15:34:34 +0000
Subject: [PATCH] reimplemented VertexArrays with std::vector (it's a bit
 faster)

---
 Common/VertexArray.cpp | 232 ++++++++++++++++++-----------------------
 Common/VertexArray.h   |  29 ++++--
 Graphics/Mesh.cpp      |  20 ++--
 Graphics/Post.cpp      |  26 ++---
 4 files changed, 150 insertions(+), 157 deletions(-)

diff --git a/Common/VertexArray.cpp b/Common/VertexArray.cpp
index 2b610dcd87..8b874065cf 100644
--- a/Common/VertexArray.cpp
+++ b/Common/VertexArray.cpp
@@ -1,4 +1,4 @@
-// $Id: VertexArray.cpp,v 1.13 2006-08-22 01:58:32 geuzaine Exp $
+// $Id: VertexArray.cpp,v 1.14 2006-08-22 15:34:34 geuzaine Exp $
 //
 // Copyright (C) 1997-2006 C. Geuzaine, J.-F. Remacle
 //
@@ -19,7 +19,7 @@
 // 
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
-#include "Gmsh.h"
+#include <algorithm>
 #include "VertexArray.h"
 #include "Context.h"
 #include "Numeric.h"
@@ -27,166 +27,142 @@
 extern Context_T CTX;
 
 VertexArray::VertexArray(int numNodesPerElement, int numElements) 
+  : fill(0), _type(numNodesPerElement)
 {
-  type = numNodesPerElement;
-  if(type < 1 || type > 4){
-    Msg(GERROR, "Vertex arrays not done for %d-node element", type);
-    type = 3;
-  }
-  fill = 0;
-  if(!numElements)
-    numElements = 1;
-  int nb = numElements * numNodesPerElement;
-  vertices = List_Create(nb * 3, 3000, sizeof(float));
-  normals = List_Create(nb * 3, 3000, sizeof(char));
-  colors = List_Create(nb * 4, 4000, sizeof(unsigned char));
-}
-
-VertexArray::~VertexArray()
-{
-  List_Delete(vertices);
-  List_Delete(normals);
-  List_Delete(colors);
-}
-
-int VertexArray::n()
-{
-  return List_Nbr(vertices) / 3;
-}
-
-template<class T>
-inline void List_Add_3(List_T * liste, T *data1, T *data2, T *data3)
-{
-  liste->n += 3;
-  List_Realloc(liste, liste->n);
-  liste->isorder = 0;
-  T* dest1 = (T*)&liste->array[(liste->n - 3) * liste->size];
-  T* dest2 = (T*)&liste->array[(liste->n - 2) * liste->size];
-  T* dest3 = (T*)&liste->array[(liste->n - 1) * liste->size];
-  *dest1 = *data1;
-  *dest2 = *data2;
-  *dest3 = *data3;
-}
-
-template<class T>
-inline void List_Add_4(List_T * liste, T *data1, T *data2, T *data3, T *data4)
-{
-  liste->n += 4;
-  List_Realloc(liste, liste->n);
-  liste->isorder = 0;
-  T* dest1 = (T*)&liste->array[(liste->n - 4) * liste->size];
-  T* dest2 = (T*)&liste->array[(liste->n - 3) * liste->size];
-  T* dest3 = (T*)&liste->array[(liste->n - 2) * liste->size];
-  T* dest4 = (T*)&liste->array[(liste->n - 1) * liste->size];
-  *dest1 = *data1;
-  *dest2 = *data2;
-  *dest3 = *data3;
-  *dest4 = *data4;
+  int nb = (numElements ? numElements : 1) * numNodesPerElement;
+  _vertices.reserve(nb * 3);
+  _normals.reserve(nb * 3);
+  _colors.reserve(nb *4);
 }
 
 void VertexArray::add(float x, float y, float z, 
 		      float n0, float n1, float n2, unsigned int col)
 {
-  List_Add_3(vertices, &x, &y, &z);
+  _vertices.push_back(x);
+  _vertices.push_back(y);
+  _vertices.push_back(z);
 
+  // Warning: storing the normals as bytes WILL hurt rendering
+  // performance... but it reduces significantly the memory footprint.
   char c0 = float2char(n0);
   char c1 = float2char(n1);
   char c2 = float2char(n2);
-  List_Add_3(normals, &c0, &c1, &c2);
+  _normals.push_back(c0);
+  _normals.push_back(c1);
+  _normals.push_back(c2);
 
   unsigned char r = CTX.UNPACK_RED(col);
   unsigned char g = CTX.UNPACK_GREEN(col);
   unsigned char b = CTX.UNPACK_BLUE(col);
   unsigned char a = CTX.UNPACK_ALPHA(col);
-  List_Add_4(colors, &r, &g, &b, &a);
+  _colors.push_back(r);
+  _colors.push_back(g);
+  _colors.push_back(b);
+  _colors.push_back(a);
 }
 
 void VertexArray::add(float x, float y, float z, unsigned int col)
 {
-  List_Add_3(vertices, &x, &y, &z);
+  _vertices.push_back(x);
+  _vertices.push_back(y);
+  _vertices.push_back(z);
 
   unsigned char r = CTX.UNPACK_RED(col);
   unsigned char g = CTX.UNPACK_GREEN(col);
   unsigned char b = CTX.UNPACK_BLUE(col);
   unsigned char a = CTX.UNPACK_ALPHA(col);
-  List_Add_4(colors, &r, &g, &b, &a);
+  _colors.push_back(r);
+  _colors.push_back(g);
+  _colors.push_back(b);
+  _colors.push_back(a);
 }
 
-static double theeye[3];
 
-int compareTriEye(const void *a, const void *b)
-{
-  float *q = (float*)a;
-  float *w = (float*)b;
-  double d, dq, dw, cgq[3] = { 0., 0., 0. }, cgw[3] = { 0., 0., 0.};
-  for(int i = 0; i < 3; i++) {
-    cgq[0] += q[3*i];
-    cgq[1] += q[3*i + 1];
-    cgq[2] += q[3*i + 2];
-    cgw[0] += w[3*i];
-    cgw[1] += w[3*i + 1];
-    cgw[2] += w[3*i + 2];
+class AlphaElement {
+public:
+  AlphaElement(float *vp, char *np, unsigned char *cp) : v(vp), n(np), c(cp) {}
+  float *v;
+  char *n;
+  unsigned char *c;
+};
+
+class AlphaElementLessThan {
+ public:
+  static int numVertices;
+  static double eye[3];
+  bool operator()(const AlphaElement &e1, const AlphaElement &e2) const
+  {
+    double cg1[3] = { 0., 0., 0. }, cg2[3] = { 0., 0., 0.};
+    for(int i = 0; i < numVertices; i++) {
+      cg1[0] += e1.v[3 * i];
+      cg1[1] += e1.v[3 * i + 1];
+      cg1[2] += e1.v[3 * i + 2];
+      cg2[0] += e2.v[3 * i];
+      cg2[1] += e2.v[3 * i + 1];
+      cg2[2] += e2.v[3 * i + 2];
+    }
+    double d1, d2;
+    prosca(eye, cg1, &d1);
+    prosca(eye, cg2, &d2);
+    if(d1 < d2)
+      return true;
+    return false;
   }
-  prosca(theeye, cgq, &dq);
-  prosca(theeye, cgw, &dw);
-  d = dq - dw;
-  if(d > 0)
-    return 1;
-  if(d < 0)
-    return -1;
-  return 0;
-}
+};
+
+int AlphaElementLessThan::numVertices = 0;
+double AlphaElementLessThan::eye[3] = {0., 0., 0.};
 
 void VertexArray::sort(double eye[3])
 {
-  // sort assumes that the all the arrays are filled
-  if(!List_Nbr(vertices) || !List_Nbr(normals) || !List_Nbr(colors))
-    return;
-
-  if(type != 3){
-    Msg(GERROR, "VertexArray sort only implemented for triangles");
-    return;
-  }
-  
-  theeye[0] = eye[0];
-  theeye[1] = eye[1];
-  theeye[2] = eye[2];
-
-  int num = n() / 3;
-  int nb = List_Nbr(vertices) + List_Nbr(normals) + List_Nbr(colors);
-  float *tmp = new float[nb];
+  // This simplementation is pretty bad: it copies the whole data
+  // twice. We should think about a more efficient way to sort the
+  // three arrays in place.
+
+  AlphaElementLessThan::numVertices = getType();
+  AlphaElementLessThan::eye[0] = eye[0];
+  AlphaElementLessThan::eye[1] = eye[1];
+  AlphaElementLessThan::eye[2] = eye[2];
+
+  int n = getNumVertices() / getType();
+
+  std::vector<AlphaElement> elements;
+  elements.reserve(n);
+  if(_normals.size())
+    for(int i = 0; i < n; i++)
+      elements.push_back(AlphaElement(&_vertices[3 * getType() * i], 
+				      &_normals[3 * getType() * i], 
+				      &_colors[4 * getType() * i]));
+  else
+    for(int i = 0; i < n; i++)
+      elements.push_back(AlphaElement(&_vertices[3 * getType() * i], 
+				      0, 
+				      &_colors[4 * getType() * i]));
   
-  int iv = 0, in = 0, ic = 0, k = 0;
-  for(int i = 0; i < num; i++){
-    for(int j = 0; j < 9; j++)
-      List_Read(vertices, iv++, &tmp[k++]);
-    for(int j = 0; j < 9; j++)
-      List_Read(normals, in++, &tmp[k++]);
-    for(int j = 0; j < 12; j++){
-      unsigned char c;
-      List_Read(colors, ic++, &c);
-      tmp[k++] = c;
+  std::sort(elements.begin(), elements.end(), AlphaElementLessThan());
+
+  std::vector<float> sortedVertices;
+  std::vector<char> sortedNormals;
+  std::vector<unsigned char> sortedColors;
+  sortedVertices.reserve(_vertices.size());
+  sortedNormals.reserve(_normals.size());
+  sortedColors.reserve(_colors.size());
+
+  for(int i = 0; i < n; i++){
+    for(int j = 0; j < getType(); j++){
+      for(int k = 0; k < 3; k++){
+	sortedVertices.push_back(elements[i].v[3 * j + k]);
+	if(elements[i].v)
+	  sortedNormals.push_back(elements[i].n[3 * j + k]);
+      }
+      for(int k = 0; k < 4; k++){
+	sortedColors.push_back(elements[i].c[4 * j + k]);
+      }
     }
   }
 
-  List_Reset(vertices);
-  List_Reset(normals);
-  List_Reset(colors);
-
-  qsort(tmp, num, (9+9+12)*sizeof(float), compareTriEye);
-
-  k = 0;
-  for(int i = 0; i < num; i++){
-    for(int j = 0; j < 9; j++)
-      List_Add(vertices, &tmp[k++]);
-    for(int j = 0; j < 9; j++)
-      List_Add(normals, &tmp[k++]);
-    for(int j = 0; j < 12; j++){
-      unsigned char c = (unsigned char)tmp[k++];
-      List_Add(colors, &c);
-    }
-  }  
-
-  delete [] tmp;
+  _vertices = sortedVertices;
+  _normals = sortedNormals;
+  _colors = sortedColors;
 }
 
diff --git a/Common/VertexArray.h b/Common/VertexArray.h
index f53b030fab..01f5034889 100644
--- a/Common/VertexArray.h
+++ b/Common/VertexArray.h
@@ -20,21 +20,34 @@
 // 
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
-#include "List.h"
+#include "vector"
 
 class VertexArray{
  public:
-  int type, fill;
-  List_T *vertices, *normals, *colors;
+  int fill; // this must/will be removed
+ private:
+  int _type;
+  std::vector<float> _vertices;
+  std::vector<char> _normals;
+  std::vector<unsigned char> _colors;
+ public:
   VertexArray(int numNodesPerElement, int numElements);
-  ~VertexArray();
-  // return the number of nodes in the array
-  int n();
-  // add a node in the array
+  ~VertexArray(){}
+  // returns the number of nodes in the array
+  int getNumVertices() { return _vertices.size() / 3; }
+  // returns the type of the array
+  int getType() { return _type; }
+  // returns a pointer to the raw vertex array
+  float *getVertexArray(){ return &_vertices[0]; }
+  // returns a pointer to the raw normal array
+  char *getNormalArray(){ return &_normals[0]; }
+  // returns a pointer to the raw color array
+  unsigned char *getColorArray(){ return &_colors[0]; }
+  // adds a vertex in the arrays
   void add(float x, float y, float z, 
 	   float n0, float n1, float n2, unsigned int col);
   void add(float x, float y, float z, unsigned int col);
-  // sort the elements back to fron wrt the eye position
+  // sorts the elements back to front wrt the eye position
   void sort(double eye[3]);
 };
 
diff --git a/Graphics/Mesh.cpp b/Graphics/Mesh.cpp
index b24ef9b33b..01c53442cc 100644
--- a/Graphics/Mesh.cpp
+++ b/Graphics/Mesh.cpp
@@ -1,4 +1,4 @@
-// $Id: Mesh.cpp,v 1.182 2006-08-22 01:58:34 geuzaine Exp $
+// $Id: Mesh.cpp,v 1.183 2006-08-22 15:34:34 geuzaine Exp $
 //
 // Copyright (C) 1997-2006 C. Geuzaine, J.-F. Remacle
 //
@@ -435,9 +435,9 @@ static void drawArrays(GEntity *e, VertexArray *va, GLint type, bool useNormalAr
 {
   if(!va) return;
 
-  glVertexPointer(3, GL_FLOAT, 0, va->vertices->array);
-  glNormalPointer(GL_BYTE, 0, va->normals->array);
-  glColorPointer(4, GL_UNSIGNED_BYTE, 0, va->colors->array);
+  glVertexPointer(3, GL_FLOAT, 0, va->getVertexArray());
+  glNormalPointer(GL_BYTE, 0, va->getNormalArray());
+  glColorPointer(4, GL_UNSIGNED_BYTE, 0, va->getColorArray());
   
   glEnableClientState(GL_VERTEX_ARRAY);
 
@@ -462,13 +462,13 @@ static void drawArrays(GEntity *e, VertexArray *va, GLint type, bool useNormalAr
     glColor4ubv((GLubyte *) & color);
   }
   
-  if(va->type > 2 && !drawOutline && CTX.polygon_offset)
+  if(va->getType() > 2 && !drawOutline && CTX.polygon_offset)
     glEnable(GL_POLYGON_OFFSET_FILL);
   
   if(drawOutline) 
     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
   
-  glDrawArrays(type, 0, va->n());
+  glDrawArrays(type, 0, va->getNumVertices());
   
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   glDisable(GL_POLYGON_OFFSET_FILL);
@@ -591,7 +591,9 @@ class initMeshGFace {
 
     // Further optimizations are possible when useEdges is true:
     // 1) store the unique vertices in the vertex array and use
-    //    glDrawElements() instead of glDrawArrays().
+    //    glDrawElements() instead of glDrawArrays(). Question:
+    //    which normal do you choose for each vertex, and so how 
+    //    can you achieve accurate "flat shading" rendering?
     // 2) use tc to stripe the triangles and draw strips instead of 
     //    individual triangles
     
@@ -624,7 +626,7 @@ class drawMeshGFace {
     MRep *m = f->meshRep;
 
     if(CTX.mesh.surfaces_edges){
-      if(m->va_lines && m->va_lines->n()){
+      if(m->va_lines && m->va_lines->getNumVertices()){
 	drawArrays(f, m->va_lines, GL_LINES, CTX.mesh.light && CTX.mesh.light_lines, 
 		   CTX.mesh.surfaces_faces, CTX.color.mesh.line);
       }
@@ -732,7 +734,7 @@ class drawMeshGRegion {
     MRep *m = r->meshRep;
 
     if(CTX.mesh.volumes_edges){
-      if(m->va_lines && m->va_lines->n()){
+      if(m->va_lines && m->va_lines->getNumVertices()){
 	drawArrays(r, m->va_lines, GL_LINES, CTX.mesh.light && CTX.mesh.light_lines, 
 		   CTX.mesh.volumes_faces, CTX.color.mesh.line);
       }
diff --git a/Graphics/Post.cpp b/Graphics/Post.cpp
index 13c9be08ff..4c7d65b553 100644
--- a/Graphics/Post.cpp
+++ b/Graphics/Post.cpp
@@ -1,4 +1,4 @@
-// $Id: Post.cpp,v 1.111 2006-08-18 21:11:43 geuzaine Exp $
+// $Id: Post.cpp,v 1.112 2006-08-22 15:34:34 geuzaine Exp $
 //
 // Copyright (C) 1997-2006 C. Geuzaine, J.-F. Remacle
 //
@@ -749,25 +749,27 @@ void Draw_Post(void)
 
       pass_1:
 	if(v->TriVertexArray && v->TriVertexArray->fill){
-	  Msg(DEBUG, "View[%d]; %d tris in vertex array", v->Index, v->TriVertexArray->n()/3);
+	  Msg(DEBUG, "View[%d]; %d tris in vertex array", 
+	      v->Index, v->TriVertexArray->getNumVertices()/3);
 	  v->TriVertexArray->fill = 0;
 	}
 	if(v->LinVertexArray && v->LinVertexArray->fill){
-	  Msg(DEBUG, "View[%d]; %d segs in vertex array", v->Index, v->LinVertexArray->n()/2);
+	  Msg(DEBUG, "View[%d]; %d segs in vertex array",
+	      v->Index, v->LinVertexArray->getNumVertices()/2);
 	  v->LinVertexArray->fill = 0;
 	}
       }
 
-      if(v->TriVertexArray && v->TriVertexArray->n()){
+      if(v->TriVertexArray && v->TriVertexArray->getNumVertices()){
 	if(CTX.alpha && ColorTable_IsAlpha(&v->CT) && !v->FakeTransparency &&
 	   (changedEye() || v->Changed)){
 	  Msg(DEBUG, "Sorting View[%d] for transparency (WITH vertex array)", v->Index);
 	  v->TriVertexArray->sort(storedEye);
 	}
 
-	glVertexPointer(3, GL_FLOAT, 0, v->TriVertexArray->vertices->array);
-	glNormalPointer(GL_BYTE, 0, v->TriVertexArray->normals->array);
-	glColorPointer(4, GL_UNSIGNED_BYTE, 0, v->TriVertexArray->colors->array);
+	glVertexPointer(3, GL_FLOAT, 0, v->TriVertexArray->getVertexArray());
+	glNormalPointer(GL_BYTE, 0, v->TriVertexArray->getNormalArray());
+	glColorPointer(4, GL_UNSIGNED_BYTE, 0, v->TriVertexArray->getColorArray());
 
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_COLOR_ARRAY);
@@ -778,7 +780,7 @@ void Draw_Post(void)
 	else
 	  glDisableClientState(GL_NORMAL_ARRAY);
 	if(CTX.polygon_offset) glEnable(GL_POLYGON_OFFSET_FILL);
-	glDrawArrays(GL_TRIANGLES, 0, v->TriVertexArray->n());
+	glDrawArrays(GL_TRIANGLES, 0, v->TriVertexArray->getNumVertices());
 	glDisable(GL_POLYGON_OFFSET_FILL);
 	glDisable(GL_LIGHTING);
       
@@ -787,12 +789,12 @@ void Draw_Post(void)
 	glDisableClientState(GL_COLOR_ARRAY);
       }
 
-      if(v->LinVertexArray && v->LinVertexArray->n()){
-	glVertexPointer(3, GL_FLOAT, 0, v->LinVertexArray->vertices->array);
-	glColorPointer(4, GL_UNSIGNED_BYTE, 0, v->LinVertexArray->colors->array);
+      if(v->LinVertexArray && v->LinVertexArray->getNumVertices()){
+	glVertexPointer(3, GL_FLOAT, 0, v->LinVertexArray->getVertexArray());
+	glColorPointer(4, GL_UNSIGNED_BYTE, 0, v->LinVertexArray->getColorArray());
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_COLOR_ARRAY);
-	glDrawArrays(GL_LINES, 0, v->LinVertexArray->n());
+	glDrawArrays(GL_LINES, 0, v->LinVertexArray->getNumVertices());
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_COLOR_ARRAY);
       }
-- 
GitLab