Commit 038b8c37 authored by Pierre Kraemer's avatar Pierre Kraemer

surface render: boundary + cage generation voxellisation update

parent 130a4ba4
......@@ -42,3 +42,4 @@ TARGET_LINK_LIBRARIES( SCHNAppsD
ADD_SUBDIRECTORY(${SCHNApps_ROOT_DIR}/Plugins PluginsD)
ADD_SUBDIRECTORY(${SCHNApps_ROOT_DIR}/../../SCHNAppsPlugins ExtPluginsD)
......@@ -86,7 +86,7 @@
<item row="2" column="0" colspan="2">
<widget class="Line" name="line"/>
</item>
<item row="8" column="0" colspan="2">
<item row="9" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
......@@ -131,6 +131,13 @@
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QCheckBox" name="check_renderBoundary">
<property name="text">
<string>render boundary</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
......
......@@ -41,6 +41,7 @@ struct MapParameters
bool renderVertices;
bool renderEdges;
bool renderFaces;
bool renderBoundary;
FaceShadingStyle faceStyle;
};
......@@ -86,6 +87,7 @@ private slots:
void vboAdded(Utils::VBO* vbo);
void vboRemoved(Utils::VBO* vbo);
public slots:
// slots for Python calls
void changePositionVBO(const QString& view, const QString& map, const QString& vbo);
......@@ -95,6 +97,7 @@ public slots:
void changeRenderEdges(const QString& view, const QString& map, bool b);
void changeRenderFaces(const QString& view, const QString& map, bool b);
void changeFacesStyle(const QString& view, const QString& map, MapParameters::FaceShadingStyle style);
void changeRenderBoundary(const QString& view, const QString& map, bool b);
protected:
Surface_Render_DockTab* m_dockTab;
......
......@@ -36,6 +36,7 @@ private slots:
void renderEdgesChanged(bool b);
void renderFacesChanged(bool b);
void faceStyleChanged(QAbstractButton* b);
void renderBoundaryChanged(bool b);
private:
void addPositionVBO(QString name);
......
......@@ -25,8 +25,6 @@ bool Surface_Render_Plugin::enable()
m_phongShader->setShininess(80.0f) ;
m_simpleColorShader = new CGoGN::Utils::ShaderSimpleColor();
CGoGN::Geom::Vec4f c(0.1f, 0.1f, 0.1f, 1.0f);
m_simpleColorShader->setColor(c);
m_pointSprite = new CGoGN::Utils::PointSprite();
......@@ -79,6 +77,8 @@ void Surface_Render_Plugin::drawMap(View* view, MapHandlerGen* map)
if(p.renderEdges)
{
glLineWidth(1.0f);
CGoGN::Geom::Vec4f c(0.1f, 0.1f, 0.1f, 1.0f);
m_simpleColorShader->setColor(c);
m_simpleColorShader->setAttributePosition(p.positionVBO);
map->draw(m_simpleColorShader, CGoGN::Algo::Render::GL2::LINES);
}
......@@ -105,6 +105,14 @@ void Surface_Render_Plugin::drawMap(View* view, MapHandlerGen* map)
}
glDisable(GL_POLYGON_OFFSET_FILL);
}
if(p.renderBoundary)
{
glLineWidth(5.0f);
CGoGN::Geom::Vec4f c(0.8f, 0.8f, 0.1f, 1.0f);
m_simpleColorShader->setColor(c);
m_simpleColorShader->setAttributePosition(p.positionVBO);
map->draw(m_simpleColorShader, CGoGN::Algo::Render::GL2::BOUNDARY);
}
}
}
......@@ -300,6 +308,21 @@ void Surface_Render_Plugin::changeFacesStyle(const QString& view, const QString&
}
}
void Surface_Render_Plugin::changeRenderBoundary(const QString& view, const QString& map, bool b)
{
View* v = m_schnapps->getView(view);
MapHandlerGen* m = m_schnapps->getMap(map);
if(v && m)
{
h_viewParameterSet[v][m].renderBoundary = b;
if(v->isSelectedView())
{
if(v->isLinkedToMap(m)) v->updateGL();
if(m->isSelectedMap()) m_dockTab->updateMapParameters();
}
}
}
#ifndef DEBUG
Q_EXPORT_PLUGIN2(Surface_Render_Plugin, Surface_Render_Plugin)
#else
......
......@@ -24,6 +24,7 @@ Surface_Render_DockTab::Surface_Render_DockTab(SCHNApps* s, Surface_Render_Plugi
connect(check_renderEdges, SIGNAL(toggled(bool)), this, SLOT(renderEdgesChanged(bool)));
connect(check_renderFaces, SIGNAL(toggled(bool)), this, SLOT(renderFacesChanged(bool)));
connect(group_faceShading, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(faceStyleChanged(QAbstractButton*)));
connect(check_renderBoundary, SIGNAL(toggled(bool)), this, SLOT(renderBoundaryChanged(bool)));
}
......@@ -131,6 +132,20 @@ void Surface_Render_DockTab::faceStyleChanged(QAbstractButton* b)
}
}
void Surface_Render_DockTab::renderBoundaryChanged(bool b)
{
if(!b_updatingUI)
{
View* view = m_schnapps->getSelectedView();
MapHandlerGen* map = m_schnapps->getSelectedMap();
if(view && map)
{
m_plugin->h_viewParameterSet[view][map].renderBoundary = b;
view->updateGL();
}
}
}
......
......@@ -42,3 +42,4 @@ TARGET_LINK_LIBRARIES( SCHNApps
ADD_SUBDIRECTORY(${SCHNApps_ROOT_DIR}/Plugins Plugins)
ADD_SUBDIRECTORY(${SCHNApps_ROOT_DIR}/../../SCHNAppsPlugins ExtPlugins)
......@@ -116,6 +116,7 @@ public:
m_render->setPrimitiveDirty(Algo::Render::GL2::POINTS);
m_render->setPrimitiveDirty(Algo::Render::GL2::LINES);
m_render->setPrimitiveDirty(Algo::Render::GL2::TRIANGLES);
m_render->setPrimitiveDirty(Algo::Render::GL2::BOUNDARY);
}
for(unsigned int orbit = 0; orbit < NB_ORBITS; ++orbit)
......
......@@ -32,6 +32,7 @@
#include <utility>
#include "Algo/Geometry/normal.h"
#include "Topology/generic/autoAttributeHandler.h"
namespace CGoGN
{
......
#ifndef MATRICE_H
#define MATRICE_H
#include <vector>
#include <map>
#include <stack>
#include "Geometry/bounding_box.h"
#include "Geometry/vector_gen.h"
#include "Algo/MC/image.h"
#include "types.h"
#include <vector>
#include <stack>
#include <map>
namespace CGoGN {
namespace Algo {
namespace Surface {
namespace Modelisation {
struct PFP: public PFP_STANDARD
{
// definition of the map
typedef EmbeddedMap2 MAP ;
typedef VEC3 VEC3;
};
template <typename T>
int sign(T val) {
return (T(0) < val) - (val < T(0));
}
template <typename T>
void swapMax(T& min, T& max) {
if(min>max) {
std::swap(min, max);
}
}
template <unsigned int DIM, typename T>
void swapVectorMax(Geom::Vector<DIM, T>& min, Geom::Vector<DIM, T>& max) {
if(min.dimension()==max.dimension()) {
for(unsigned int i=0; i<min.dimension(); i++) {
swapMax(min[i], max[i]);
}
}
}
class Voxellisation {
public:
Voxellisation(unsigned int taille_x, unsigned int taille_y, unsigned int taille_z, Geom::BoundingBox<Geom::Vec3f> bb)
: m_taille_x(taille_x+2), m_taille_y(taille_y+2), m_taille_z(taille_z+2),
Voxellisation(Geom::Vec3i resolutions= Geom::Vec3i(), Geom::BoundingBox<Geom::Vec3f> bb=Geom::BoundingBox<PFP::VEC3>())
: m_taille_x(resolutions[0]+2), m_taille_y(resolutions[1]+2), m_taille_z(resolutions[2]+2),
m_bb_min(bb.min()), m_bb_max(bb.max()), m_data(m_taille_x*m_taille_y*m_taille_z, 0),
m_indexes(), m_sommets(), m_faces()
m_indexes(), m_sommets(), m_faces(), m_transfo(3)
{
m_size = 0;
m_dilatations = 0;
m_transfo[0] = (m_bb_max[0]-m_bb_min[0])/(m_taille_x-2);
m_transfo[1] = (m_bb_max[1]-m_bb_min[1])/(m_taille_y-2);
m_transfo[2] = (m_bb_max[2]-m_bb_min[2])/(m_taille_z-2);
}
void removeVoxel(int x, int y, int z) {
if(this->m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y]!=0) {
if(x>=0 && y>=0 && z>=0 && x<m_taille_x-1 && y<m_taille_y-1 && z<m_taille_z-1) {
if(this->m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y]!=0) --m_size;
this->m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y] = 0;
--m_size;
}
}
void addVoxel(int x, int y, int z, int type=1) {
if(this->m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y]==0) {
if(x>=-1 && y>=-1 && z>=-1 && x<m_taille_x-1 && y<m_taille_y-1 && z<m_taille_z-1) {
if(this->m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y]==0 && type==1) ++m_size;
this->m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y] = type;
++m_size;
}
}
void addVoxelRaw(int x, int y, int z, int type=1) {
if(x>=0 && y>=0 && z>=0 && x<m_taille_x && y<m_taille_y && z<m_taille_z) {
if(this->m_data[x + y*m_taille_x + z*m_taille_x*m_taille_y]==0 && type==1) ++m_size;
this->m_data[x + y*m_taille_x + z*m_taille_x*m_taille_y] = type;
}
}
void addVoxel(Geom::Vec3i a, int type=1) {
if(this->m_data[(a[0]+1) + (a[1]+1)*m_taille_x + (a[2]+1)*m_taille_x*m_taille_y]==0) {
if(a[0]>=-1 && a[1]>=-1 && a[2]>=-1 && a[0]<m_taille_x-1 && a[1]<m_taille_y-1 && a[2]<m_taille_z-1) {
if(this->m_data[(a[0]+1) + (a[1]+1)*m_taille_x + (a[2]+1)*m_taille_x*m_taille_y]==0 && type==1) ++m_size;
this->m_data[(a[0]+1) + (a[1]+1)*m_taille_x + (a[2]+1)*m_taille_x*m_taille_y] = type;
++m_size;
}
}
void addVoxelRaw(Geom::Vec3i a, int type=1) {
if(a[0]>=0 && a[1]>=0 && a[2]>=0 && a[0]<m_taille_x && a[1]<m_taille_y && a[2]<m_taille_z) {
if(this->m_data[a[0] + a[1]*m_taille_x + a[2]*m_taille_x*m_taille_y]==0 && type==1) ++m_size;
this->m_data[a[0] + a[1]*m_taille_x + a[2]*m_taille_x*m_taille_y] = type;
}
}
int getVoxel(int x, int y, int z) {
if(x>=-1 && y>=-1 && z>=-1 && x<m_taille_x-1 && y<m_taille_y-1 && z<m_taille_z-1) {
return m_data[(x+1) + (y+1)*m_taille_x + (z+1)*m_taille_x*m_taille_y];
}
else
return 0;
}
int getVoxelRaw(int x, int y, int z) {
if(x>=0 && y>=0 && z>=0 && x<m_taille_x && y<m_taille_y && z<m_taille_z) {
return m_data[x + y*m_taille_x + z*m_taille_x*m_taille_y];
}
else
return -1;
}
int getVoxel(Geom::Vec3i a) {
if(a[0]>=-1 && a[1]>=-1 && a[2]>=-1 && a[0]<m_taille_x-1 && a[1]<m_taille_y-1 && a[1]<m_taille_z-1) {
return m_data[(a[0]+1) + (a[1]+1)*m_taille_x + (a[2]+1)*m_taille_x*m_taille_y];
}
else
return 0;
}
int getVoxelRaw(Geom::Vec3i a) {
if(a[0]>=0 && a[1]>=0 && a[2]>=0 && a[0]<m_taille_x && a[1]<m_taille_y && a[1]<m_taille_z) {
return m_data[a[0] + a[1]*m_taille_x + a[2]*m_taille_x*m_taille_y];
}
else
return 0;
}
void clear() {
m_size = 0;
......@@ -68,192 +148,266 @@ class Voxellisation {
return m_taille_z;
}
int getResolutionX() {
return m_taille_x-2;
}
int getResolutionY() {
return m_taille_y-2;
}
int getResolutionZ() {
return m_taille_z-2;
}
int getResolution(int resolution) {
int res = -1;
switch(resolution) {
case 0:
res = m_taille_x-2;
break;
case 1:
res = m_taille_y-2;
break;
case 2:
res = m_taille_z-2;
break;
}
return res;
}
int size() {
return m_size;
}
void check(int type=1) {
int voxels = 0;
for(int i=0; i<m_taille_x; ++i) {
for(int j=0; j<m_taille_y; ++j) {
for(int k=0; k<m_taille_z; ++k) {
voxels += m_data[i+ j*m_taille_x + k*m_taille_x*m_taille_y]==type?1:0;
/*
* Fonction qui ralise le remplissage d'un polygone convexe
*/
void voxellisePolygone(std::vector<Geom::Vec3i>& polygone) {
//On trie les artes selon leur Y dcroissant
Geom::Vec3i a, b, c = polygone.back();
int x, y, z, dx, dy, dz, swap, ddy, ddz, sx, sy, sz;
for(unsigned int i=0; i<polygone.size()-1;++i) {
a = polygone[i];
b = polygone[i+1];
if(a==b) voxelliseLine(a, c);
else {
x = a[0], y = a[1], z = a[2];
dx = abs(b[0]-a[0]);
dy = abs(b[1]-a[1]);
dz = abs(b[2]-a[2]);
swap=0;
if(dy > dx) {
if(dy > dz) { std::swap(dx,dy); swap=1; }
else { std::swap(dx,dz); swap=2; }
}
else {
if(dx < dz) { std::swap(dx,dz); swap=2; }
}
sx = sign(b[0]-a[0]);
sy = sign(b[1]-a[1]);
sz = sign(b[2]-a[2]);
ddy = (dy<<1)-dx;
ddz = (dz<<1)-dx;
voxelliseLine(Geom::Vec3i(x, y, z), c);
for(int i=0; i<dx; ++i) {
while(ddy>=0) {
ddy -= dx<<1;
if(swap==1) x+=sx;
else y+=sy;
}
voxelliseLine(Geom::Vec3i(x, y, z), c);
while(ddz>=0) {
ddz -= dx<<1;
if(swap==2) x+=sx;
else z+=sz;
}
voxelliseLine(Geom::Vec3i(x, y, z), c);
ddy += dy<<1;
ddz += dz<<1;
if(swap==1) y+=sy;
else if(swap==2) z+=sz;
else x+=sx;
voxelliseLine(Geom::Vec3i(x, y, z), c);
}
}
}
CGoGNout << "Il y a " << voxels << " voxel(s)" << CGoGNendl;
}
/*
* Fonction qui part d'un des sommets de la bounding box et qui va marquer les pixels non déjà marqués comme faisant partie de l'extérieur
* Utilisation de algorithme de croissance de région
* Fonction qui ralise un trac de droite discrte en 3D du voxel 'a' au voxel 'b'
* L'algorithme utilis est celui de Bresenham, adapt la 3D
*/
void marqueVoxelsExterieurs() {
CGoGNout << "Marquage des voxels extérieurs.." << CGoGNflush;
std::stack<Geom::Vec3i>* pile = new std::stack<Geom::Vec3i>();
Geom::Vec3i voxel_courant;
void voxelliseLine(Geom::Vec3i a, Geom::Vec3i b) {
int x, y, z, dx, dy, dz, swap, ddy, ddz, sx, sy, sz;
//Marquage du contour extérieur
for(int j=0; j<m_taille_y; ++j) {
for(int k=0; k<m_taille_z; ++k) {
m_data[j*m_taille_x + k*m_taille_x*m_taille_y] = 2;
m_data[m_taille_x-1 + j*m_taille_x + k*m_taille_x*m_taille_y] = 2;
if(a==b) addVoxel(a);
else {
x = a[0], y = a[1], z = a[2];
dx = abs(b[0]-a[0]);
dy = abs(b[1]-a[1]);
dz = abs(b[2]-a[2]);
swap=0;
if(dy > dx) {
if(dy > dz) { std::swap(dx,dy); swap=1; }
else { std::swap(dx,dz); swap=2; }
}
else {
if(dx < dz) { std::swap(dx,dz); swap=2; }
}
for(int i=0; i<m_taille_x; ++i) {
for(int k=0; k<m_taille_z; ++k) {
m_data[i + k*m_taille_x*m_taille_y] = 2;
m_data[i + (m_taille_y-1)*m_taille_x +k*m_taille_x*m_taille_y] = 2;
sx = sign(b[0]-a[0]);
sy = sign(b[1]-a[1]);
sz = sign(b[2]-a[2]);
ddy = (dy<<1)-dx;
ddz = (dz<<1)-dx;
addVoxel(x, y, z);
for(int i=0; i<dx; ++i) {
while(ddy>=0) {
ddy -= dx<<1;
if(swap==1) x+=sx;
else y+=sy;
}
addVoxel(x, y, z); //On affiche les points intermdiaires -> ligne 6-connexe
while(ddz>=0) {
ddz -= dx<<1;
if(swap==2) x+=sx;
else z+=sz;
}
addVoxel(x, y, z); //On affiche les points intermdiaires -> ligne 6-connexe
ddy += dy<<1;
ddz += dz<<1;
if(swap==1) y+=sy;
else if(swap==2) z+=sz;
else x+=sx;
addVoxel(x, y, z);
}
}
}
for(int i=0; i<m_taille_x; ++i) {
for(int j=0; j<m_taille_y; ++j) {
m_data[i + j*m_taille_x] = 2;
m_data[i + j*m_taille_x + (m_taille_z-1)*m_taille_x*m_taille_y] = 2;
}
}
if(getVoxel(0,0,0)==0)
pile->push(Geom::Vec3i(0,0,0));
else if(getVoxel(getTailleX()-2,0,0)==0)
pile->push(Geom::Vec3i(getTailleX()-2,0,0));
else if(getVoxel(0,getTailleY()-2,0)==0)
pile->push(Geom::Vec3i(0,getTailleY()-2,0));
else if(getVoxel(0,0,getTailleZ()-2)==0)
pile->push(Geom::Vec3i(0,0,getTailleZ()-2));
else if(getVoxel(getTailleX()-2,getTailleY()-2,0)==0)
pile->push(Geom::Vec3i(getTailleX()-2,getTailleY()-2,0));
else if(getVoxel(getTailleX()-2,0,getTailleZ()-2)==0)
pile->push(Geom::Vec3i(getTailleX()-2,0,getTailleZ()-2));
else if(getVoxel(0,getTailleY()-2,getTailleZ()-2)==0)
pile->push(Geom::Vec3i(0,getTailleY()-2,getTailleZ()-2));
else if(getVoxel(getTailleX()-2,getTailleY()-2,getTailleZ()-2)==0)
pile->push(Geom::Vec3i(getTailleX()-2,getTailleY()-2,getTailleZ()-2));
while(!pile->empty()) {
//Tant qu'il y a des voxels à traiter
voxel_courant = pile->top();
pile->pop();
addVoxel(voxel_courant,2);
if(getVoxel(voxel_courant[0]+1,voxel_courant[1], voxel_courant[2])==0)
pile->push(Geom::Vec3i(voxel_courant[0]+1, voxel_courant[1], voxel_courant[2]));
if(getVoxel(voxel_courant[0]-1, voxel_courant[1], voxel_courant[2])==0)
pile->push(Geom::Vec3i(voxel_courant[0]-1, voxel_courant[1], voxel_courant[2]));
if(getVoxel(voxel_courant[0], voxel_courant[1]+1, voxel_courant[2])==0)
pile->push(Geom::Vec3i(voxel_courant[0],voxel_courant[1]+1,voxel_courant[2]));
if(getVoxel(voxel_courant[0],voxel_courant[1]-1,voxel_courant[2])==0)
pile->push(Geom::Vec3i(voxel_courant[0],voxel_courant[1]-1,voxel_courant[2]));
if(getVoxel(voxel_courant[0],voxel_courant[1],voxel_courant[2]+1)==0)
pile->push(Geom::Vec3i(voxel_courant[0],voxel_courant[1],voxel_courant[2]+1));
if(getVoxel(voxel_courant[0],voxel_courant[1],voxel_courant[2]-1)==0)
pile->push(Geom::Vec3i(voxel_courant[0],voxel_courant[1],voxel_courant[2]-1));
}
delete pile;
CGoGNout << ".. fait" << CGoGNendl;
/*
* Fonction qui part d'un des sommets de la Bounding box et qui va marquer les pixels non dj marqus comme faisant partie de l'extrieur
* Utilisation de algorithme de croissance de rgion
*/
void marqueVoxelsExterieurs() {
CGoGNout << "Marquage des voxels extrieurs.." << CGoGNflush;
std::stack<Geom::Vec3i> pile;
Geom::Vec3i voxel_courant;
pile.push(Geom::Vec3i(0,0,0));
while(!pile.empty()) {
//Tant qu'il y a des voxels traiter
voxel_courant = pile.top();
pile.pop();
addVoxelRaw(voxel_courant,2);
if(getVoxelRaw(voxel_courant[0]+1,voxel_courant[1], voxel_courant[2])==0)
pile.push(Geom::Vec3i(voxel_courant[0]+1, voxel_courant[1], voxel_courant[2]));
if(getVoxelRaw(voxel_courant[0]-1, voxel_courant[1], voxel_courant[2])==0)
pile.push(Geom::Vec3i(voxel_courant[0]-1, voxel_courant[1], voxel_courant[2]));
if(getVoxelRaw(voxel_courant[0], voxel_courant[1]+1, voxel_courant[2])==0)
pile.push(Geom::Vec3i(voxel_courant[0],voxel_courant[1]+1,voxel_courant[2]));
if(getVoxelRaw(voxel_courant[0],voxel_courant[1]-1,voxel_courant[2])==0)
pile.push(Geom::Vec3i(voxel_courant[0],voxel_courant[1]-1,voxel_courant[2]));
if(getVoxelRaw(voxel_courant[0],voxel_courant[1],voxel_courant[2]+1)==0)
pile.push(Geom::Vec3i(voxel_courant[0],voxel_courant[1],voxel_courant[2]+1));
if(getVoxelRaw(voxel_courant[0],voxel_courant[1],voxel_courant[2]-1)==0)
pile.push(Geom::Vec3i(voxel_courant[0],voxel_courant[1],voxel_courant[2]-1));
}
CGoGNout << ".. fait." << CGoGNendl;
}
/*
* Fonction qui extrait les faces extrieu res des voxels en regardant quelles faces appartiennent un voxel extrieur et un voxel d'intersection
*/
void extractionBord() {
CGoGNout << "Extraction du bord.." << CGoGNflush;
int x, y, z;
float transfo_x = (m_bb_max[0]-m_bb_min[0])/(m_taille_x-2);
float transfo_y = (m_bb_max[1]-m_bb_min[1])/(m_taille_y-2);
float transfo_z = (m_bb_max[2]-m_bb_min[2])/(m_taille_z-2);
m_indexes.clear();
m_faces.clear();
m_faces.reserve(m_size*6); //Au maximum on a 6 fois plus de faces que le nombre de voxels (cas d'1 seul voxel qui intersecte la surface du maillage
m_sommets.clear();
m_sommets.reserve(m_size*6*4); //On a 4 sommets par face
int x, y, z;
std::map<int,int>::iterator index_sommet;
for(int i=0; i<m_taille_x-2; ++i) {
for(int j=0; j<m_taille_y-2; ++j) {
for(int k=0; k<m_taille_z-2; ++k) {
if(getVoxel(i,j,k)==1) {
//Si le voxel courant intersecte le bord du maillage de base
if(getVoxel(i-1,j,k)==2) {
//Si le voxel de gauche est un voxel de l'extérieur
//Si le voxel de gauche est un voxel de l'extrieur
//Sommets formant la face : 8, 5, 4, 1
x = i-1; y = j; z = k;
for(int l=0; l<4; ++l) {
switch(l) {
case 0 :
++x;
break;
case 1 :
++z;
break;
case 2 :