Select Git revision
VertexArray.cpp
-
Christophe Geuzaine authoredChristophe Geuzaine authored
VertexArray.cpp 11.28 KiB
// Gmsh - Copyright (C) 1997-2019 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
#include <string.h>
#include <algorithm>
#include "GmshMessage.h"
#include "VertexArray.h"
#include "Context.h"
#include "Numeric.h"
#include "OS.h"
template<int N> float ElementDataLessThan<N>::tolerance = 0.0F;
float BarycenterLessThan::tolerance = 0.0F;
VertexArray::VertexArray(int numVerticesPerElement, int numElements)
: _numVerticesPerElement(numVerticesPerElement)
{
int nb = (numElements ? numElements : 1) * _numVerticesPerElement;
double memv = (nb * 3. * sizeof(float)) / 1024. / 1024.;
double memmax = TotalRam() / 3.;
if(memv > memmax){
int old = nb;
nb = memmax / (3. * sizeof(float)) * 1024 * 1024;
Msg::Debug("Reduce preallocation of vertex array (%d -> %d)", old, nb);
}
_vertices.reserve(nb * 3);
_normals.reserve(nb * 3);
_colors.reserve(nb * 4);
}
double VertexArray::getMemoryInMb()
{
int bytes = _vertices.size() * sizeof(float) +
_normals.size() * sizeof(normal_type) +
_colors.size() * sizeof(unsigned char);
return (double)bytes / 1024. / 1024.;
}
void VertexArray::_addVertex(float x, float y, float z)
{
_vertices.push_back(x);
_vertices.push_back(y);
_vertices.push_back(z);
}
void VertexArray::_addNormal(float nx, float ny, float nz)
{
#if defined(HAVE_VISUDEV)
_normals.push_back(nx);
_normals.push_back(ny);
_normals.push_back(nz);
#else
// storing the normals as bytes hurts rendering performance, but it
// significantly reduces the memory footprint
char cx = float2char(nx);
char cy = float2char(ny);
char cz = float2char(nz);
_normals.push_back(cx);
_normals.push_back(cy);
_normals.push_back(cz);
#endif
}
void VertexArray::_addColor(unsigned char r, unsigned char g, unsigned char b,
unsigned char a)
{
_colors.push_back(r);
_colors.push_back(g);
_colors.push_back(b);
_colors.push_back(a);
}
void VertexArray::_addElement(MElement *ele)
{
if(ele && CTX::instance()->pickElements) _elements.push_back(ele);
}
void VertexArray::add(double *x, double *y, double *z, SVector3 *n,
unsigned int *col, MElement *ele, bool unique, bool boundary)
{
if(col){
unsigned char r[100], g[100], b[100], a[100];
int npe = getNumVerticesPerElement();
for(int i = 0; i < npe; i++){
r[i] = CTX::instance()->unpackRed(col[i]);
g[i] = CTX::instance()->unpackGreen(col[i]);
b[i] = CTX::instance()->unpackBlue(col[i]);
a[i] = CTX::instance()->unpackAlpha(col[i]);
}
add(x, y, z, n, r, g, b, a, ele, unique, boundary);
}
else
add(x, y, z, n, 0, 0, 0, 0, ele, unique, boundary);
}
void VertexArray::add(double *x, double *y, double *z, SVector3 *n, unsigned char *r,
unsigned char *g, unsigned char *b, unsigned char *a,
MElement *ele, bool unique, bool boundary)
{
int npe = getNumVerticesPerElement();
if(boundary && npe == 3){
ElementData<3> e(x, y, z, n, r, g, b, a, ele);
ElementDataLessThan<3>::tolerance = (float)(CTX::instance()->lc * 1.e-12);
std::set<ElementData<3>, ElementDataLessThan<3> >::iterator it = _data3.find(e);
if(it == _data3.end())
_data3.insert(e);
else
_data3.erase(it);
return;
}
// enabling this will reduce memory and rendering time; but will increase the
// time it takes to create the vertex array
#if 0
if(unique){
Barycenter pc(0.0F, 0.0F, 0.0F);
for(int i = 0; i < npe; i++)
pc += Barycenter(x[i], y[i], z[i]);
BarycenterLessThan::tolerance = (float)(CTX::instance()->lc * 1.e-12);
if(_barycenters.find(pc) != _barycenters.end())
return;
_barycenters.insert(pc);
}
#endif
for(int i = 0; i < npe; i++){
_addVertex((float)x[i], (float)y[i], (float)z[i]);
if(n) _addNormal((float)n[i].x(), (float)n[i].y(), (float)n[i].z());
if(r && g && b && a) _addColor(r[i], g[i], b[i], a[i]);
_addElement(ele);
}
}
void VertexArray::finalize()
{
if(_data3.size()){
std::set<ElementData<3>, ElementDataLessThan<3> >::iterator it = _data3.begin();
for(; it != _data3.end(); it++){
for(int i = 0; i < 3; i++){
_addVertex(it->x(i), it->y(i), it->z(i));
_addNormal(it->nx(i), it->ny(i), it->nz(i));
_addColor(it->r(i), it->g(i), it->b(i), it->a(i));
_addElement(it->ele());
}
}
_data3.clear();
}
_barycenters.clear();
}
class AlphaElement {
public:
AlphaElement(float *vp, normal_type *np, unsigned char *cp) : v(vp), n(np), c(cp) {}
float *v;
normal_type *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];
}
return prosca(eye, cg1) < prosca(eye, cg2);
}
};
int AlphaElementLessThan::numVertices = 0;
double AlphaElementLessThan::eye[3] = {0., 0., 0.};
void VertexArray::sort(double x, double y, double z)
{
// 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.
int npe = getNumVerticesPerElement();
int n = getNumVertices() / npe;
AlphaElementLessThan::numVertices = npe;
AlphaElementLessThan::eye[0] = x;
AlphaElementLessThan::eye[1] = y;
AlphaElementLessThan::eye[2] = z;
std::vector<AlphaElement> elements;
elements.reserve(n);
for(int i = 0; i < n; i++){
float *vp = &_vertices[3 * npe * i];
normal_type *np = _normals.empty() ? 0 : &_normals[3 * npe * i];
unsigned char *cp = _colors.empty() ? 0 : &_colors[4 * npe * i];
elements.push_back(AlphaElement(vp, np, cp));
}
std::sort(elements.begin(), elements.end(), AlphaElementLessThan());
std::vector<float> sortedVertices;
std::vector<normal_type> 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 < npe; j++){
for(int k = 0; k < 3; k++)
sortedVertices.push_back(elements[i].v[3 * j + k]);
if(elements[i].n)
for(int k = 0; k < 3; k++)
sortedNormals.push_back(elements[i].n[3 * j + k]);
if(elements[i].c)
for(int k = 0; k < 4; k++)
sortedColors.push_back(elements[i].c[4 * j + k]);
}
}
_vertices = sortedVertices;
_normals = sortedNormals;
_colors = sortedColors;
}
char *VertexArray::toChar(int num, const std::string &name, int type,
double min, double max, int numsteps, double time,
const SBoundingBox3d &bbox, int &len)
{
int vn = _vertices.size(), nn = _normals.size(), cn = _colors.size();
int vs = vn * sizeof(float),
ns = nn * sizeof(normal_type),
cs = cn * sizeof(unsigned char);
int is = sizeof(int), ds = sizeof(double);
int ss = name.size();
double xmin = bbox.min().x(), ymin = bbox.min().y(), zmin = bbox.min().z();
double xmax = bbox.max().x(), ymax = bbox.max().y(), zmax = bbox.max().z();
len = ss + 7 * is + 9 * ds + vs + ns + cs;
char *bytes = new char[len];
int index = 0;
memcpy(&bytes[index], &num, is); index += is;
memcpy(&bytes[index], &ss, is); index += is;
memcpy(&bytes[index], name.c_str(), ss); index += ss;
memcpy(&bytes[index], &type, is); index += is;
memcpy(&bytes[index], &min, ds); index += ds;
memcpy(&bytes[index], &max, ds); index += ds;
memcpy(&bytes[index], &numsteps, is); index += is;
memcpy(&bytes[index], &time, ds); index += ds;
memcpy(&bytes[index], &xmin, ds); index += ds;
memcpy(&bytes[index], &ymin, ds); index += ds;
memcpy(&bytes[index], &zmin, ds); index += ds;
memcpy(&bytes[index], &xmax, ds); index += ds;
memcpy(&bytes[index], &ymax, ds); index += ds;
memcpy(&bytes[index], &zmax, ds); index += ds;
memcpy(&bytes[index], &vn, is); index += is;
if(vs){ memcpy(&bytes[index], &_vertices[0], vs); index += vs; }
memcpy(&bytes[index], &nn, is); index += is;
if(ns){ memcpy(&bytes[index], &_normals[0], ns); index += ns; }
memcpy(&bytes[index], &cn, is); index += is;
if(cs){ memcpy(&bytes[index], &_colors[0], cs); /* index += cs; */ }
return bytes;
}
int VertexArray::decodeHeader(int length, const char *bytes, int swap,
std::string &name, int &tag, int &type,
double &min, double &max, int &numSteps, double &time,
double &xmin, double &ymin, double &zmin,
double &xmax, double &ymax, double &zmax)
{
int is = sizeof(int), ds = sizeof(double);
if(length < 4 * is + 9 * ds){
Msg::Error("Too few bytes to create vertex array: %d", length);
return 0;
}
if(swap){
Msg::Error("Should swap bytes in vertex array--not implemented yet");
return 0;
}
int index = 0;
memcpy(&tag, &bytes[index], is); index += is;
int ss; memcpy(&ss, &bytes[index], is); index += is;
if(ss){
std::vector<char> n(ss);
memcpy(&n[0], &bytes[index], ss); index += ss;
for(std::size_t i = 0; i < n.size(); i++) name += n[i];
}
memcpy(&type, &bytes[index], is); index += is;
memcpy(&min, &bytes[index], ds); index += ds;
memcpy(&max, &bytes[index], ds); index += ds;
memcpy(&numSteps, &bytes[index], is); index += is;
memcpy(&time, &bytes[index], ds); index += ds;
memcpy(&xmin, &bytes[index], ds); index += ds;
memcpy(&ymin, &bytes[index], ds); index += ds;
memcpy(&zmin, &bytes[index], ds); index += ds;
memcpy(&xmax, &bytes[index], ds); index += ds;
memcpy(&ymax, &bytes[index], ds); index += ds;
memcpy(&zmax, &bytes[index], ds); index += ds;
return index;
}
void VertexArray::fromChar(int length, const char *bytes, int swap)
{
std::string name;
int tag, type, numSteps;
double min, max, time, xmin, ymin, zmin, xmax, ymax, zmax;
int index = decodeHeader(length, bytes, swap, name, tag, type, min, max,
numSteps, time, xmin, ymin, zmin, xmax, ymax, zmax);
if(!index) return;
int is = sizeof(int);
int vn; memcpy(&vn, &bytes[index], is); index += is;
if(vn){
_vertices.resize(vn); int vs = vn * sizeof(float);
memcpy(&_vertices[0], &bytes[index], vs); index += vs;
}
int nn; memcpy(&nn, &bytes[index], is); index += is;
if(nn){
_normals.resize(nn);
int ns = nn * sizeof(normal_type);
memcpy(&_normals[0], &bytes[index], ns); index += ns;
}
int cn; memcpy(&cn, &bytes[index], is); index += is;
if(cn){
_colors.resize(cn); int cs = cn * sizeof(unsigned char);
memcpy(&_colors[0], &bytes[index], cs); /* index += cs; */
}
}
void VertexArray::merge(VertexArray* va)
{
if(va->getNumVertices() != 0) {
_vertices.insert(_vertices.end(), va->firstVertex(), va->lastVertex());
_normals.insert(_normals.end(), va->firstNormal(), va->lastNormal());
_colors.insert(_colors.end(), va->firstColor(), va->lastColor());
_elements.insert(_elements.end(), va->firstElementPointer(),
va->lastElementPointer());
}
}