From 1375560cb4b55a2e990b0e206688c22099d8c542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 28 Jan 2018 23:03:11 +0100 Subject: [PATCH 01/36] Add first version of a Makefile --- Makefile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2074d0d --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +CXX=g++ +CXXFLAGS=-Wall -Wextra -O2 -std=c++14 +CXXLIBS= + +# In `TARGET`, list the names of the `main_[stuff].cpp` you'd like to compile +# into a `[stuff].bin`. +TARGETS= + +OBJS=Implicit.o \ + Mesh.o + +all: $(TARGETS:=.bin) + +%.bin: main_%.cpp $(OBJS) + $(CXX) $(CXXFLAGS) $(CXXLIBS) $(OBJS) $< -o $@ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +############################################################ + +.PHONY: clean + +clean: + rm -rf $(OBJS) From 5c19700e42bd7b639c83f92f6c31aef38ed84373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sat, 27 Jan 2018 18:33:50 +0100 Subject: [PATCH 02/36] =?UTF-8?q?Create=20a=20basic=20glut=20renderer=20?= =?UTF-8?q?=E2=80=94=20yet=20untested?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common_structures.hpp | 16 ++++++++++ render/GlutRender.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++ render/GlutRender.hpp | 31 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 render/GlutRender.cpp create mode 100644 render/GlutRender.hpp diff --git a/common_structures.hpp b/common_structures.hpp index 3993966..b38e2d6 100644 --- a/common_structures.hpp +++ b/common_structures.hpp @@ -3,10 +3,22 @@ **/ #include // Hash +#include +#include struct Point { Point(double x, double y, double z) : x(x), y(y), z(z) {} double x, y, z; + + double operator[](unsigned i) const { + assert(0 <= i && i < 3); + switch(i) { + case 0: return x; + case 1: return y; + case 2: return z; + default: return 0; + } + } }; struct Face { @@ -19,6 +31,10 @@ struct Face { int operator[](unsigned i) const { return vert[i % 3]; // dodge errors } + const Point& pt(int id, const std::vector& pts) const { + assert(0 <= id && id < 3); + return pts[vert[id]]; + } }; namespace std { diff --git a/render/GlutRender.cpp b/render/GlutRender.cpp new file mode 100644 index 0000000..b9632ab --- /dev/null +++ b/render/GlutRender.cpp @@ -0,0 +1,71 @@ +#include "GlutRender.hpp" + +#include +#include +#include + +GlutRender& GlutRender::get_instance() { + static GlutRender instance; + return instance; +} + +GlutRender::GlutRender() { } + +void GlutRender::init(int* argc, char** argv, + int wid, int hei, const char* win_name) +{ + glutInit(argc, argv); + glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); + glutInitWindowSize(wid, hei); + glutCreateWindow(win_name); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glShadeModel(GL_FLAT); + + glutDisplayFunc(display_handle); + glutReshapeFunc(reshape_handle); +} + +void GlutRender::cleanup() { +} + +void GlutRender::add_mesh(const Mesh* mesh) { + meshes.insert(mesh); +} + +void GlutRender::remove_mesh(const Mesh* mesh) { + meshes.erase(mesh); +} + +void GlutRender::run() { +} + +void GlutRender::reshape(int wid, int hei) { + glViewport(0, 0, (GLsizei) wid, (GLsizei) hei); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.0, (GLdouble) wid, 0.0, (GLdouble) hei); +} + +void GlutRender::display() { + for(const Mesh* mesh: meshes) { + const std::vector& points = mesh->get_vertices(); + for(const Face& face: mesh->get_faces()) { + const Point& p0 = face.pt(0, points), + p1 = face.pt(2, points), + p2 = face.pt(2, points); + glBegin(GL_TRIANGLES); + glVertex3d(p0[0], p0[1], p0[2]); + glVertex3d(p1[0], p1[1], p1[2]); + glVertex3d(p2[0], p2[1], p2[2]); + glEnd(); + } + } +} + +void GlutRender::reshape_handle(int wid, int hei) { + get_instance().reshape(wid, hei); +} +void GlutRender::display_handle() { + get_instance().display(); +} diff --git a/render/GlutRender.hpp b/render/GlutRender.hpp new file mode 100644 index 0000000..699a26a --- /dev/null +++ b/render/GlutRender.hpp @@ -0,0 +1,31 @@ +/** The most basic renderer — a stupid glut application */ + +#include "../Mesh.hpp" +#include + +class GlutRender { + public: + static GlutRender& get_instance(); + + GlutRender(GlutRender const&) = delete; + void operator=(GlutRender const&) = delete; + + void init(int* argc, char** argv, + int wid, int hei, const char* win_name); + void cleanup(); + void add_mesh(const Mesh* mesh); + void remove_mesh(const Mesh* mesh); + void run(); + + private: //meth + GlutRender(); + + protected: + void reshape(int wid, int hei); + void display(); + + static void reshape_handle(int wid, int hei); + static void display_handle(); + private: + std::set meshes; +}; From 6c55d262edb28cbaf37988107e3ba51a2faa8f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 28 Jan 2018 23:03:57 +0100 Subject: [PATCH 03/36] Add parser for obj mesh format --- Makefile | 3 ++- util/ObjParser.cpp | 32 ++++++++++++++++++++++++++++++++ util/ObjParser.hpp | 16 ++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 util/ObjParser.cpp create mode 100644 util/ObjParser.hpp diff --git a/Makefile b/Makefile index 2074d0d..7b08b8f 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ CXXLIBS= TARGETS= OBJS=Implicit.o \ - Mesh.o + Mesh.o \ + util/ObjParser.o all: $(TARGETS:=.bin) diff --git a/util/ObjParser.cpp b/util/ObjParser.cpp new file mode 100644 index 0000000..c62bfc1 --- /dev/null +++ b/util/ObjParser.cpp @@ -0,0 +1,32 @@ +#include "ObjParser.hpp" + +ObjParser::ObjParser(const std::string& path) + : path(path) +{} + +Mesh ObjParser::parse() { + std::ifstream handle(path); + Mesh output; + + if(!handle.is_open()) + throw std::runtime_error(std::string("Cannot open the file ") + path); + + while(!handle.eof()) { + char type; + handle >> type; + if(type == 'v') { // vertice + double x, y, z; + handle >> x >> y >> z; + output.add_vertice(Point(x, y, z)); + } + else { + int v1, v2, v3; + handle >> v1 >> v2 >> v3; + output.add_face(Face(v1, v2, v3)); + } + + handle.ignore('\n'); + } + + return output; +} diff --git a/util/ObjParser.hpp b/util/ObjParser.hpp new file mode 100644 index 0000000..5fa697b --- /dev/null +++ b/util/ObjParser.hpp @@ -0,0 +1,16 @@ +/** Parses .obj mesh files, outputting a `Mesh` */ + +#include +#include +#include + +#include "../Mesh.hpp" + +class ObjParser { + public: + ObjParser(const std::string& path); + Mesh parse(); + + private: + std::string path; +}; From 4d944d0e756c9cf308869e4128981b509618e233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 28 Jan 2018 23:05:02 +0100 Subject: [PATCH 04/36] Add basic main for a glut visualization --- .gitignore | 1 - Makefile | 2 +- main_glut.cpp | 19 +++++++++++++++++++ mesh/cube.obj | 20 ++++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 main_glut.cpp create mode 100644 mesh/cube.obj diff --git a/.gitignore b/.gitignore index 14b117c..5206ef6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ *.slo *.lo *.o -*.obj # Precompiled Headers *.gch diff --git a/Makefile b/Makefile index 7b08b8f..6869ad7 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CXXLIBS= # In `TARGET`, list the names of the `main_[stuff].cpp` you'd like to compile # into a `[stuff].bin`. -TARGETS= +TARGETS=glut OBJS=Implicit.o \ Mesh.o \ diff --git a/main_glut.cpp b/main_glut.cpp new file mode 100644 index 0000000..767a0eb --- /dev/null +++ b/main_glut.cpp @@ -0,0 +1,19 @@ +/** An entry-point file using render/GlutRender as a renderer + * As of now, mostly for testing purposes. + **/ + +#include "render/GlutRender.hpp" +#include "util/ObjParser.hpp" +#include "Mesh.hpp" + +int main(int argc, char** argv) { + GlutRender& render = GlutRender::get_instance(); + render.init(&argc, argv, 640, 480, "Bouncing stuff"); + + Mesh mesh = ObjParser("mesh/cube.obj").parse(); + render.add_mesh(&mesh); + + render.run(); + + return 0; +} diff --git a/mesh/cube.obj b/mesh/cube.obj new file mode 100644 index 0000000..61185dc --- /dev/null +++ b/mesh/cube.obj @@ -0,0 +1,20 @@ +v -0.5 -0.5 0.5 +v 0.5 -0.5 0.5 +v -0.5 0.5 0.5 +v 0.5 0.5 0.5 +v -0.5 0.5 -0.5 +v 0.5 0.5 -0.5 +v -0.5 -0.5 -0.5 +v 0.5 -0.5 -0.5 +f 1 2 4 +f 1 4 3 +f 3 4 6 +f 3 6 5 +f 5 6 8 +f 5 8 7 +f 7 8 2 +f 7 2 1 +f 2 8 6 +f 2 6 4 +f 7 1 3 +f 7 3 5 From d1c3cbe498daa3744056747c91ae9bb02ad8e3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 19:07:07 +0100 Subject: [PATCH 05/36] Add missing pragma once on headers --- Implicit.hpp | 2 ++ Mesh.hpp | 2 ++ common_structures.hpp | 2 ++ render/GlutRender.hpp | 2 ++ util/ObjParser.hpp | 2 ++ 5 files changed, 10 insertions(+) diff --git a/Implicit.hpp b/Implicit.hpp index 94f3bd8..8a45776 100644 --- a/Implicit.hpp +++ b/Implicit.hpp @@ -1,3 +1,5 @@ +#pragma once + class ImplicitSurface { public: virtual double operator() (double x, double y, double z); diff --git a/Mesh.hpp b/Mesh.hpp index 63ea5f8..e46c127 100644 --- a/Mesh.hpp +++ b/Mesh.hpp @@ -2,6 +2,8 @@ * Defines a mesh, ready to be OpenGL-rendered **/ +#pragma once + #include #include #include // size_t diff --git a/common_structures.hpp b/common_structures.hpp index b38e2d6..fd6d841 100644 --- a/common_structures.hpp +++ b/common_structures.hpp @@ -2,6 +2,8 @@ * Defines a few widely used, widely spread structures. Imported pervasively. **/ +#pragma once + #include // Hash #include #include diff --git a/render/GlutRender.hpp b/render/GlutRender.hpp index 699a26a..688a1d6 100644 --- a/render/GlutRender.hpp +++ b/render/GlutRender.hpp @@ -1,5 +1,7 @@ /** The most basic renderer — a stupid glut application */ +#pragma once + #include "../Mesh.hpp" #include diff --git a/util/ObjParser.hpp b/util/ObjParser.hpp index 5fa697b..02ddcb2 100644 --- a/util/ObjParser.hpp +++ b/util/ObjParser.hpp @@ -1,5 +1,7 @@ /** Parses .obj mesh files, outputting a `Mesh` */ +#pragma once + #include #include #include From 804031dc81b10a3649519e619023b5e22090051d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 19:08:17 +0100 Subject: [PATCH 06/36] Fix obj file parser --- util/ObjParser.cpp | 39 +++++++++++++++++++++++++++++++-------- util/ObjParser.hpp | 14 ++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/util/ObjParser.cpp b/util/ObjParser.cpp index c62bfc1..90a24b4 100644 --- a/util/ObjParser.cpp +++ b/util/ObjParser.cpp @@ -1,5 +1,8 @@ #include "ObjParser.hpp" +#include +#include + ObjParser::ObjParser(const std::string& path) : path(path) {} @@ -11,22 +14,42 @@ Mesh ObjParser::parse() { if(!handle.is_open()) throw std::runtime_error(std::string("Cannot open the file ") + path); - while(!handle.eof()) { + for(std::string line; std::getline(handle, line); ) { + std::istringstream lineStream(strip(line)); char type; - handle >> type; + lineStream >> type; + if(type == 'v') { // vertice double x, y, z; - handle >> x >> y >> z; + lineStream >> x >> y >> z; output.add_vertice(Point(x, y, z)); } - else { + else if(type == 'f') { int v1, v2, v3; - handle >> v1 >> v2 >> v3; - output.add_face(Face(v1, v2, v3)); + lineStream >> v1 >> v2 >> v3; + output.add_face(Face(v1 - 1, v2 - 1, v3 - 1)); + } + else { + throw BadObj(path); } - - handle.ignore('\n'); } return output; } + +std::string ObjParser::strip(const std::string& str) { + size_t fPos, lPos; + for(fPos=0; fPos < str.size(); ++fPos) { + if(!isspace(str[fPos])) + break; + } + if(fPos == str.size()) + return ""; + + for(lPos=str.size() - 1; lPos > 0; --lPos) { + if(!isspace(str[lPos])) + break; + } + + return str.substr(fPos, lPos - fPos + 1); +} diff --git a/util/ObjParser.hpp b/util/ObjParser.hpp index 02ddcb2..2338ff5 100644 --- a/util/ObjParser.hpp +++ b/util/ObjParser.hpp @@ -10,9 +10,23 @@ class ObjParser { public: + class BadObj : public std::exception { + public: + BadObj(const std::string& path): path(path) {} + const char* what() const noexcept { + return (std::string("Badly formed obj file ") + + path).c_str(); + } + + private: + std::string path; + }; + ObjParser(const std::string& path); Mesh parse(); + private: //meth + static std::string strip(const std::string& str); private: std::string path; }; From 7e3df70df6e2733f2a5391e979d148e5d8818154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 19:09:02 +0100 Subject: [PATCH 07/36] Fix compilation process --- .gitignore | 1 + Makefile | 9 +++++---- common_structures.hpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 5206ef6..6769bd9 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ *.out *.app +*.bin diff --git a/Makefile b/Makefile index 6869ad7..51ce6f3 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXX=g++ CXXFLAGS=-Wall -Wextra -O2 -std=c++14 -CXXLIBS= +CXXLIBS=-lGL -lGLU -lglut # In `TARGET`, list the names of the `main_[stuff].cpp` you'd like to compile # into a `[stuff].bin`. @@ -8,11 +8,12 @@ TARGETS=glut OBJS=Implicit.o \ Mesh.o \ - util/ObjParser.o + util/ObjParser.o \ + render/GlutRender.o all: $(TARGETS:=.bin) -%.bin: main_%.cpp $(OBJS) +%.bin: main_%.o $(OBJS) $(CXX) $(CXXFLAGS) $(CXXLIBS) $(OBJS) $< -o $@ %.o: %.cpp @@ -23,4 +24,4 @@ all: $(TARGETS:=.bin) .PHONY: clean clean: - rm -rf $(OBJS) + rm -rf $(OBJS) $(TARGETS:=.bin) diff --git a/common_structures.hpp b/common_structures.hpp index fd6d841..8c88a13 100644 --- a/common_structures.hpp +++ b/common_structures.hpp @@ -13,7 +13,7 @@ struct Point { double x, y, z; double operator[](unsigned i) const { - assert(0 <= i && i < 3); + assert(i < 3); switch(i) { case 0: return x; case 1: return y; From 739d1da36fbd6c81485816cfc7b6780c230cf3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 19:09:26 +0100 Subject: [PATCH 08/36] Enrich test meshes --- mesh/cube.obj | 16 ++++++++-------- mesh/tet.obj | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 mesh/tet.obj diff --git a/mesh/cube.obj b/mesh/cube.obj index 61185dc..f7ed751 100644 --- a/mesh/cube.obj +++ b/mesh/cube.obj @@ -1,11 +1,11 @@ -v -0.5 -0.5 0.5 -v 0.5 -0.5 0.5 -v -0.5 0.5 0.5 -v 0.5 0.5 0.5 -v -0.5 0.5 -0.5 -v 0.5 0.5 -0.5 -v -0.5 -0.5 -0.5 -v 0.5 -0.5 -0.5 +v -1 -1 1 +v 1 -1 1 +v -1 1 1 +v 1 1 1 +v -1 1 -1 +v 1 1 -1 +v -1 -1 -1 +v 1 -1 -1 f 1 2 4 f 1 4 3 f 3 4 6 diff --git a/mesh/tet.obj b/mesh/tet.obj new file mode 100644 index 0000000..e7a3772 --- /dev/null +++ b/mesh/tet.obj @@ -0,0 +1,8 @@ +v 0 0 0 +v 1 0 0 +v 0 1 0 +v 0 0 1 +f 1 2 3 +f 1 2 4 +f 1 3 4 +f 2 3 4 From c179b1049f2c9725b4e50d4698492361011951e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 19:10:08 +0100 Subject: [PATCH 09/36] Make basic glut renderer work --- render/GlutRender.cpp | 50 +++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/render/GlutRender.cpp b/render/GlutRender.cpp index b9632ab..6f016dd 100644 --- a/render/GlutRender.cpp +++ b/render/GlutRender.cpp @@ -15,15 +15,20 @@ void GlutRender::init(int* argc, char** argv, int wid, int hei, const char* win_name) { glutInit(argc, argv); - glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); + glutInitDisplayMode(GLUT_DOUBLE); glutInitWindowSize(wid, hei); glutCreateWindow(win_name); - glClearColor(0.0, 0.0, 0.0, 0.0); - glShadeModel(GL_FLAT); - glutDisplayFunc(display_handle); glutReshapeFunc(reshape_handle); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Background color + glClearDepth(1.0f); // Set background depth to farthest + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glShadeModel(GL_SMOOTH); // Enable smooth shading + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + } void GlutRender::cleanup() { @@ -38,29 +43,46 @@ void GlutRender::remove_mesh(const Mesh* mesh) { } void GlutRender::run() { + glutMainLoop(); } void GlutRender::reshape(int wid, int hei) { - glViewport(0, 0, (GLsizei) wid, (GLsizei) hei); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(0.0, (GLdouble) wid, 0.0, (GLdouble) hei); + if (hei == 0) + hei = 1; + GLfloat aspect = (GLfloat)wid / (GLfloat)hei; + + glViewport(0, 0, wid, hei); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // Enable perspective projection with fovy, aspect, zNear and zFar + gluPerspective(45.0f, aspect, 0.1f, 100.0f); } void GlutRender::display() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + for(const Mesh* mesh: meshes) { + glLoadIdentity(); + glTranslatef(.0f, .0f, -6.0f); + const std::vector& points = mesh->get_vertices(); + + glBegin(GL_TRIANGLES); for(const Face& face: mesh->get_faces()) { const Point& p0 = face.pt(0, points), - p1 = face.pt(2, points), + p1 = face.pt(1, points), p2 = face.pt(2, points); - glBegin(GL_TRIANGLES); - glVertex3d(p0[0], p0[1], p0[2]); - glVertex3d(p1[0], p1[1], p1[2]); - glVertex3d(p2[0], p2[1], p2[2]); - glEnd(); + glColor3f(1.0f, 1.0f, 0.0f); + glVertex3f(p0[0], p0[1], p0[2]); + glVertex3f(p1[0], p1[1], p1[2]); + glVertex3f(p2[0], p2[1], p2[2]); } + glEnd(); } + + glutSwapBuffers(); } void GlutRender::reshape_handle(int wid, int hei) { From 8655e39fba09d2c0a0ab3d5f1545814d737a8089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 20:45:23 +0100 Subject: [PATCH 10/36] Add center to a mesh to allow easy translation --- Mesh.cpp | 27 +++++++++++++++++++++++++++ Mesh.hpp | 13 +++++++++++++ common_structures.hpp | 24 ++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/Mesh.cpp b/Mesh.cpp index 815aad2..7fd709d 100644 --- a/Mesh.cpp +++ b/Mesh.cpp @@ -1,6 +1,7 @@ #include "Mesh.hpp" Mesh::Mesh() + : center(Point(.0, .0, .0)) { } @@ -16,6 +17,32 @@ void Mesh::add_face(size_t f1, size_t f2, size_t f3) { add_face(Face(f1, f2, f3)); } +const Point& Mesh::get_center() const { + return center; +} + +void Mesh::set_center(const Point& pt) { + center = pt; +} +void Mesh::translate(const Point& tr) { + center += tr; +} + +void Mesh::normalize_center(bool keep_position) { + Point bary(0., 0., 0.); + for(const Point& vert: vertices) + bary += vert; + bary = (1. / ((double)vertices.size())) * bary; + + for(Point& vert: vertices) + vert -= bary; + + if(keep_position) + translate(bary); + else + set_center(Point(0, 0, 0)); +} + const std::vector& Mesh::get_vertices() const { return vertices; } diff --git a/Mesh.hpp b/Mesh.hpp index e46c127..43d668f 100644 --- a/Mesh.hpp +++ b/Mesh.hpp @@ -20,6 +20,17 @@ class Mesh { void add_face(const Face& face); void add_face(size_t f1, size_t f2, size_t f3); + /// Center manipulation + const Point& get_center() const; + void set_center(const Point& pt); + void translate(const Point& tr); ///< Translate by the vector `tr` + + /** Translates the vertices to make (0, 0, 0) the mesh's barycenter. + * If `keep_position == true`, this also sets the center to the + * previous barycenter. + */ + void normalize_center(bool keep_position=false); + /// Gets the various vertices const std::vector& get_vertices() const; @@ -29,4 +40,6 @@ class Mesh { private: std::vector vertices; std::vector faces; + + Point center; }; diff --git a/common_structures.hpp b/common_structures.hpp index 8c88a13..adf9d9f 100644 --- a/common_structures.hpp +++ b/common_structures.hpp @@ -21,6 +21,30 @@ struct Point { default: return 0; } } + + Point operator+(const Point& pt) const { + return Point( + x + pt.x, + y + pt.y, + z + pt.z); + } + Point& operator+=(const Point& pt) { + x += pt.x; + y += pt.y; + z += pt.z; + return *this; + } + + Point& operator-=(const Point& pt) { + return (*this += Point(-pt.x, -pt.y, -pt.z)); + } + + friend Point operator*(double scalar, const Point& pt) { + return Point( + scalar * pt.x, + scalar * pt.y, + scalar * pt.z); + } }; struct Face { From 6e892989509b00406b489ed7a09a6bb16ed879dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 21:05:45 +0100 Subject: [PATCH 11/36] Use mesh center everywhere else --- main_glut.cpp | 1 + mesh/cube_tr.obj | 20 ++++++++++++++++++++ render/GlutRender.cpp | 19 +++++++++++-------- render/GlutRender.hpp | 6 +++--- util/ObjParser.cpp | 2 ++ 5 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 mesh/cube_tr.obj diff --git a/main_glut.cpp b/main_glut.cpp index 767a0eb..d51a926 100644 --- a/main_glut.cpp +++ b/main_glut.cpp @@ -11,6 +11,7 @@ int main(int argc, char** argv) { render.init(&argc, argv, 640, 480, "Bouncing stuff"); Mesh mesh = ObjParser("mesh/cube.obj").parse(); + mesh.translate(Point(2.5, -1, 0.)); render.add_mesh(&mesh); render.run(); diff --git a/mesh/cube_tr.obj b/mesh/cube_tr.obj new file mode 100644 index 0000000..4b0065d --- /dev/null +++ b/mesh/cube_tr.obj @@ -0,0 +1,20 @@ +v -3 -1 1 +v -1 -1 1 +v -3 1 1 +v -1 1 1 +v -3 1 -1 +v -1 1 -1 +v -3 -1 -1 +v -1 -1 -1 +f 1 2 4 +f 1 4 3 +f 3 4 6 +f 3 6 5 +f 5 6 8 +f 5 8 7 +f 7 8 2 +f 7 2 1 +f 2 8 6 +f 2 6 4 +f 7 1 3 +f 7 3 5 diff --git a/render/GlutRender.cpp b/render/GlutRender.cpp index 6f016dd..7107b30 100644 --- a/render/GlutRender.cpp +++ b/render/GlutRender.cpp @@ -34,11 +34,11 @@ void GlutRender::init(int* argc, char** argv, void GlutRender::cleanup() { } -void GlutRender::add_mesh(const Mesh* mesh) { +void GlutRender::add_mesh(Mesh* mesh) { meshes.insert(mesh); } -void GlutRender::remove_mesh(const Mesh* mesh) { +void GlutRender::remove_mesh(Mesh* mesh) { meshes.erase(mesh); } @@ -63,17 +63,20 @@ void GlutRender::display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); - for(const Mesh* mesh: meshes) { - glLoadIdentity(); - glTranslatef(.0f, .0f, -6.0f); + // Camera position and orientation + glLoadIdentity(); + glTranslatef(0., 0., -6.); + + for(Mesh* mesh: meshes) { + const Point& mesh_center = mesh->get_center(); const std::vector& points = mesh->get_vertices(); glBegin(GL_TRIANGLES); for(const Face& face: mesh->get_faces()) { - const Point& p0 = face.pt(0, points), - p1 = face.pt(1, points), - p2 = face.pt(2, points); + Point p0 = face.pt(0, points) + mesh_center, + p1 = face.pt(1, points) + mesh_center, + p2 = face.pt(2, points) + mesh_center; glColor3f(1.0f, 1.0f, 0.0f); glVertex3f(p0[0], p0[1], p0[2]); glVertex3f(p1[0], p1[1], p1[2]); diff --git a/render/GlutRender.hpp b/render/GlutRender.hpp index 688a1d6..eb85680 100644 --- a/render/GlutRender.hpp +++ b/render/GlutRender.hpp @@ -15,8 +15,8 @@ class GlutRender { void init(int* argc, char** argv, int wid, int hei, const char* win_name); void cleanup(); - void add_mesh(const Mesh* mesh); - void remove_mesh(const Mesh* mesh); + void add_mesh(Mesh* mesh); + void remove_mesh(Mesh* mesh); void run(); private: //meth @@ -29,5 +29,5 @@ class GlutRender { static void reshape_handle(int wid, int hei); static void display_handle(); private: - std::set meshes; + std::set meshes; }; diff --git a/util/ObjParser.cpp b/util/ObjParser.cpp index 90a24b4..d6d0088 100644 --- a/util/ObjParser.cpp +++ b/util/ObjParser.cpp @@ -34,6 +34,8 @@ Mesh ObjParser::parse() { } } + output.normalize_center(false); + return output; } From 66d6d89aebd810d86ae2a1a3f57ab0eed27107be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 21:26:18 +0100 Subject: [PATCH 12/36] Randomizing face colour for each face Allows to easily identify each face. --- render/GlutRender.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/render/GlutRender.cpp b/render/GlutRender.cpp index 7107b30..28a4d74 100644 --- a/render/GlutRender.cpp +++ b/render/GlutRender.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include GlutRender& GlutRender::get_instance() { static GlutRender instance; @@ -67,6 +69,10 @@ void GlutRender::display() { glLoadIdentity(); glTranslatef(0., 0., -6.); + std::default_random_engine rand_engine(time(NULL)); + std::uniform_real_distribution distribution; + auto rand_color = std::bind(distribution, rand_engine); + for(Mesh* mesh: meshes) { const Point& mesh_center = mesh->get_center(); @@ -77,7 +83,7 @@ void GlutRender::display() { Point p0 = face.pt(0, points) + mesh_center, p1 = face.pt(1, points) + mesh_center, p2 = face.pt(2, points) + mesh_center; - glColor3f(1.0f, 1.0f, 0.0f); + glColor3f(rand_color(), rand_color(), rand_color()); glVertex3f(p0[0], p0[1], p0[2]); glVertex3f(p1[0], p1[1], p1[2]); glVertex3f(p2[0], p2[1], p2[2]); From ae95da9cd86ea8be40cc8d3c7e806890ddacc208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 21:29:32 +0100 Subject: [PATCH 13/36] Fancify scene, adding a tet --- main_glut.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/main_glut.cpp b/main_glut.cpp index d51a926..29ea72b 100644 --- a/main_glut.cpp +++ b/main_glut.cpp @@ -10,9 +10,13 @@ int main(int argc, char** argv) { GlutRender& render = GlutRender::get_instance(); render.init(&argc, argv, 640, 480, "Bouncing stuff"); - Mesh mesh = ObjParser("mesh/cube.obj").parse(); - mesh.translate(Point(2.5, -1, 0.)); - render.add_mesh(&mesh); + Mesh cube = ObjParser("mesh/cube.obj").parse(); + cube.translate(Point(2.5, -1, 0.)); + render.add_mesh(&cube); + + Mesh tet = ObjParser("mesh/tet.obj").parse(); + tet.translate(Point(-2.5, 0.5, 0.)); + render.add_mesh(&tet); render.run(); From 7e797980ac6823507e3016bc2d0c61b6be4f8bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 22:15:19 +0100 Subject: [PATCH 14/36] Enable depth buffering --- render/GlutRender.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/GlutRender.cpp b/render/GlutRender.cpp index 28a4d74..fdc4fae 100644 --- a/render/GlutRender.cpp +++ b/render/GlutRender.cpp @@ -17,7 +17,7 @@ void GlutRender::init(int* argc, char** argv, int wid, int hei, const char* win_name) { glutInit(argc, argv); - glutInitDisplayMode(GLUT_DOUBLE); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(wid, hei); glutCreateWindow(win_name); From b614979c7aa3fbc0d04c7f217ea835f7e67a312d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 6 Feb 2018 22:15:35 +0100 Subject: [PATCH 15/36] GlutRender.cpp: fix tab indentation --- render/GlutRender.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/render/GlutRender.cpp b/render/GlutRender.cpp index fdc4fae..31268d6 100644 --- a/render/GlutRender.cpp +++ b/render/GlutRender.cpp @@ -49,21 +49,21 @@ void GlutRender::run() { } void GlutRender::reshape(int wid, int hei) { - if (hei == 0) + if (hei == 0) hei = 1; - GLfloat aspect = (GLfloat)wid / (GLfloat)hei; + GLfloat aspect = (GLfloat)wid / (GLfloat)hei; - glViewport(0, 0, wid, hei); + glViewport(0, 0, wid, hei); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - // Enable perspective projection with fovy, aspect, zNear and zFar - gluPerspective(45.0f, aspect, 0.1f, 100.0f); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // Enable perspective projection with fovy, aspect, zNear and zFar + gluPerspective(45.0f, aspect, 0.1f, 100.0f); } void GlutRender::display() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glMatrixMode(GL_MODELVIEW); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); // Camera position and orientation glLoadIdentity(); @@ -91,7 +91,7 @@ void GlutRender::display() { glEnd(); } - glutSwapBuffers(); + glutSwapBuffers(); } void GlutRender::reshape_handle(int wid, int hei) { From 6c095dba0bcf8513bf89c0099d7bb01badba4518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Wed, 7 Feb 2018 17:57:49 +0100 Subject: [PATCH 16/36] Fix missing semicolon --- Implicit.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Implicit.hpp b/Implicit.hpp index 8a45776..9e1a341 100644 --- a/Implicit.hpp +++ b/Implicit.hpp @@ -3,4 +3,4 @@ class ImplicitSurface { public: virtual double operator() (double x, double y, double z); -} +}; From 4e41e88462100d33fcc26585400791fa6cec0e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Wed, 7 Feb 2018 17:59:41 +0100 Subject: [PATCH 17/36] Add a cuboid structure --- common_structures.cpp | 34 ++++++++++++++++++++++++++++++++++ common_structures.hpp | 31 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 common_structures.cpp diff --git a/common_structures.cpp b/common_structures.cpp new file mode 100644 index 0000000..37efd04 --- /dev/null +++ b/common_structures.cpp @@ -0,0 +1,34 @@ +#include "common_structures.hpp" + +Cuboid::Cuboid(Point bd1, Point bd2) + : lowBound(0, 0, 0), highBound(0, 0, 0) +{ + lowBound = Point( + std::min(bd1.x, bd2.x), + std::min(bd1.y, bd2.y), + std::min(bd1.z, bd2.z)); + highBound = Point( + std::max(bd1.x, bd2.x), + std::max(bd1.y, bd2.y), + std::max(bd1.z, bd2.z)); +} + +double Cuboid::low(unsigned dim) const { + assert(dim < 3); + return lowBound[dim]; +} + +double Cuboid::high(unsigned dim) const { + assert(dim < 3); + return highBound[dim]; +} + +double Cuboid::volume() const { + return (high(0) - low(0)) + * (high(1) - low(1)) + * (high(2) - low(2)); +} + +bool Cuboid::is_empty() const { + return volume() < 1e-8; +} diff --git a/common_structures.hpp b/common_structures.hpp index adf9d9f..9a8971e 100644 --- a/common_structures.hpp +++ b/common_structures.hpp @@ -45,6 +45,16 @@ struct Point { scalar * pt.y, scalar * pt.z); } + + bool operator<(const Point& pt) const { + /// Lexicographic order on (x, y, z) + if(x == pt.x) { + if(y == pt.y) + return z < pt.z; + return y < pt.y; + } + return x < pt.x; + } }; struct Face { @@ -63,6 +73,27 @@ struct Face { } }; +class Cuboid { + /// A 3D box + + public: + static Cuboid empty() { + return Cuboid(Point(0, 0, 0), Point(0, 0, 0)); + } + + Cuboid(Point bd1, Point bd2); + Cuboid(const Cuboid& oth) + : lowBound(oth.lowBound), highBound(oth.highBound) {} + + double low(unsigned dim) const; ///< Lower bound for a dimension + double high(unsigned dim) const; ///< Higher bound for a dimension + + double volume() const; + bool is_empty() const; + private: + Point lowBound, highBound; +}; + namespace std { template<> struct hash { From 8b98f373bc90293522dad0666fa3e54c11e26e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Wed, 7 Feb 2018 18:00:01 +0100 Subject: [PATCH 18/36] Add a context to implement marching cubes --- Makefile | 2 ++ MarchingCubes.cpp | 33 ++++++++++++++++++++++++++++++++ MarchingCubes.hpp | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 MarchingCubes.cpp create mode 100644 MarchingCubes.hpp diff --git a/Makefile b/Makefile index 51ce6f3..e19eac1 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,10 @@ CXXLIBS=-lGL -lGLU -lglut TARGETS=glut OBJS=Implicit.o \ + common_structures.o \ Mesh.o \ util/ObjParser.o \ + MarchingCubes.o \ render/GlutRender.o all: $(TARGETS:=.bin) diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp new file mode 100644 index 0000000..6988d10 --- /dev/null +++ b/MarchingCubes.cpp @@ -0,0 +1,33 @@ +#include "MarchingCubes.hpp" + +MarchingCubes::MarchingCubes( + const ImplicitSurface& surface, + const Cuboid& box, + double resolution): + surface(surface), + box(box), + resolution(resolution) +{} + +void MarchingCubes::add_hint(const Cuboid& hint) { + hints.push_back(hint); +} + +Mesh MarchingCubes::operator()() { + Mesh output; + + for(double x = box.low(0); x < box.high(0) + resolution; x += resolution) + { + for(double y = box.low(1); y < box.high(1) + resolution; + y += resolution) + { + for(double z = box.low(2); z < box.high(2) + resolution; + z += resolution) + { + //TODO apply marching cube indeed + } + } + } + + return output; +} diff --git a/MarchingCubes.hpp b/MarchingCubes.hpp new file mode 100644 index 0000000..95db829 --- /dev/null +++ b/MarchingCubes.hpp @@ -0,0 +1,48 @@ +#pragma once + +/** Implement the Marching Cubes algorithm + * + * Marching cubes: + * W. E. Lorensen, H. E. Cline, 1987 + * ``Marching cubes: a high resulution 3D surface construction algorithm'' + * + */ + +#include +#include "Mesh.hpp" +#include "Implicit.hpp" +#include "common_structures.hpp" + +class MarchingCubes { + private: + typedef unsigned char intersect_t; + + public: + MarchingCubes( + const ImplicitSurface& surface, + const Cuboid& box=Cuboid( + Point(-20, -20, -20), + Point(20, 20, 20)), + double resolution=.25); + + /** Add a starting point hint + * + * A hint is a cuboid that should intersect at least once the surface, + * such that the marching cube will find the surface there and will be + * able to follow it. + * If at least a hint is given, the algorithm will expect that at least + * a hint per disjoint surface is given, ie. that it is safe to only + * follow the surface starting from the hints, and ignoring the parts + * of the grid that are "far" from the hints. + */ + void add_hint(const Cuboid& hint); + + Mesh operator()(); + + private: + const ImplicitSurface& surface; + Cuboid box; + double resolution; + + std::vector hints; +}; From 776303b18df6cbc3a34346da7f349e8ecb4192a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Thu, 8 Feb 2018 12:22:19 +0100 Subject: [PATCH 19/36] Make operator() const for implicit surfaces --- Implicit.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Implicit.hpp b/Implicit.hpp index 9e1a341..02fcae2 100644 --- a/Implicit.hpp +++ b/Implicit.hpp @@ -2,5 +2,5 @@ class ImplicitSurface { public: - virtual double operator() (double x, double y, double z); + virtual double operator() (double x, double y, double z) const; }; From 39d12ef8a48f227890efff3668809fdcad861622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 00:29:16 +0100 Subject: [PATCH 20/36] Marching cubes: tentative configurations generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First attempt to generate the configurations of the marching cubes algorithm, following naively the definition — using the 15 base cases and generating all rotations and symmetries. This implementation does not try to be parcimonious, it bruteforces the configurations, since the search space is quite small anyway. The plan is to implement a python generator that outputs a valid .cpp file containing the pre-computed (at compile time) array for configurations. --- .gitignore | 1 + tools/__init__.py | 0 tools/gen_marching_cubes_conf.py | 556 +++++++++++++++++++++++++++++++ 3 files changed, 557 insertions(+) create mode 100644 tools/__init__.py create mode 100644 tools/gen_marching_cubes_conf.py diff --git a/.gitignore b/.gitignore index 6769bd9..2530b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ *.app *.bin +__pycache__ diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py new file mode 100644 index 0000000..899a462 --- /dev/null +++ b/tools/gen_marching_cubes_conf.py @@ -0,0 +1,556 @@ +#!/usr/bin/env python3 + +""" Generates Marching Cubes' algorithm array of triangles + + Generates the array of triangles for a given intersection pattern of a unit + cube. +""" + +from functools import reduce + + +class Vert: + frnt_bot_l = (0, 0, 1) + frnt_bot_r = (1, 0, 1) + frnt_top_l = (0, 1, 1) + frnt_top_r = (1, 1, 1) + back_bot_l = (0, 0, 0) + back_bot_r = (1, 0, 0) + back_top_l = (0, 1, 0) + back_top_r = (1, 1, 0) + + +class Edge: + def __init__(self, v1, v2): + self.vert = [ + v1 if v1 < v2 else v2, + v2 if v1 < v2 else v1, + ] + if v1 == v2: + raise ValueError((v1, v2)) + + def same_as(self, oth): + return self.vert == oth.vert + + def transform(self, transf): + return Edge( + transf(self.vert[0]), + transf(self.vert[1])) + + def dump(self): + def cbool(val): + return "true" if val else "false" + + return "MarchingCubes::CubeEdge({}, {}, {}, {}, {}, {})".format( + cbool(self.vert[0][0]), + cbool(self.vert[0][1]), + cbool(self.vert[0][2]), + cbool(self.vert[1][0]), + cbool(self.vert[1][1]), + cbool(self.vert[1][2])) + + +class Edg: + frnt_l = Edge(Vert.frnt_bot_l, Vert.frnt_top_l) + frnt_r = Edge(Vert.frnt_bot_r, Vert.frnt_top_r) + frnt_top = Edge(Vert.frnt_top_l, Vert.frnt_top_r) + frnt_bot = Edge(Vert.frnt_bot_l, Vert.frnt_bot_r) + back_l = Edge(Vert.back_bot_l, Vert.back_top_l) + back_r = Edge(Vert.back_bot_r, Vert.back_top_r) + back_top = Edge(Vert.back_top_l, Vert.back_top_r) + back_bot = Edge(Vert.back_bot_l, Vert.back_bot_r) + top_l = Edge(Vert.frnt_top_l, Vert.back_top_l) + top_r = Edge(Vert.frnt_top_r, Vert.back_top_r) + bot_l = Edge(Vert.frnt_bot_l, Vert.back_bot_l) + bot_r = Edge(Vert.frnt_bot_r, Vert.back_bot_r) + + +class TriangulatedCube: + def __init__(self, activated, triangles=None): + self.triangles = [] if triangles is None else triangles + self.activated = activated + + def transform(self, transf): + n_tri = [] + for tri in self.triangles: + n_edge = [] + for edge in tri: + n_edge.append(edge.transform(transf)) + n_tri.append(n_edge) + + n_act = set() + for act in self.activated: + n_act.add(transf(act)) + return TriangulatedCube(n_act, n_tri) + + def activated_code(self): + out = 0 + for act in self.activated: + val = (act[0] + + (act[1] << 1) + + (act[2] << 2)) + out |= (1 << val) + return out + + +def dump_tri(tri): + return ("MarchingCubes::CubeTri({{{}, {}, {}}})".format( + tri[0].dump(), tri[1].dump(), tri[2].dump)) + + +def rot_general(vert, fixed, ax1, ax2): + cases = { + (0, 0): (0, 1), + (0, 1): (1, 1), + (1, 1): (1, 0), + (1, 0): (0, 0), + } + moved = cases[(vert[ax1], vert[ax2])] + out = [0] * 3 + out[fixed] = vert[fixed] + out[ax1] = moved[0] + out[ax2] = moved[1] + return (out[0], out[1], out[2]) + + +def rot_x(vert): + return rot_general(vert, 0, 1, 2) + + +def rot_y(vert): + return rot_general(vert, 1, 2, 0) + + +def rot_z(vert): + return rot_general(vert, 2, 0, 1) + + +def sym_general(vert, normal): + return ( + vert[0] if normal != 0 else 1-vert[0], + vert[1] if normal != 1 else 1-vert[1], + vert[2] if normal != 2 else 1-vert[2] + ) + + +def sym_x(vert): + return sym_general(vert, 0) + + +def sym_y(vert): + return sym_general(vert, 1) + + +def sym_z(vert): + return sym_general(vert, 2) + + +def all_transforms(): + def compose2(fun1, fun2): + return lambda x: fun1(fun2(x)) + + def compose(*fs): + return reduce(compose2, fs, lambda x: x) + + def funcpow(fun, exp): + return compose(*([fun] * exp)) + + output = [] + + for num_rx in range(4): + for num_ry in range(4): + for num_rz in range(4): + for syms in range(8): + cur = compose( + funcpow(rot_x, num_rx), + funcpow(rot_y, num_ry), + funcpow(rot_z, num_rz), + sym_x if (syms & 0x1) else lambda x: x, + sym_y if (syms & 0x2) else lambda x: x, + sym_z if (syms & 0x4) else lambda x: x) + output.append(cur) + return output + + +def do_main(base_cases): + transforms = all_transforms() + index = [None for x in range(256)] + for case in base_cases: + code = case.activated_code() + assert index[code] is None + index[code] = case + + for transf in transforms: + for case in base_cases: + tr_case = case.transform(transf) + code = tr_case.activated_code() + if index[code] is None: + index[code] = tr_case + + for (val, tri) in enumerate(index): + if tri is None: + print(">> UNBOUND {}".format(val)) + + print(("static const std::vector " + "edges_of_intersection[256] = {")) + + for (case_id, case) in enumerate(index): + print("\tstd::vector({") + + for (tri_id, tri) in enumerate(case.triangles): + print(dump_tri(tri), + end=",\n" if tri_id == len(case.triangles) - 1 else '\n') + print('})', end=',\n' if case_id == len(base_cases) - 1 else '\n') + print("};") + + +def main(): + base_cases = [ + # Source: + # -- 1 -- + TriangulatedCube(set(), []), + # -- 2 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + }, + [ + [ + Edg.frnt_bot, + Edg.bot_l, + Edg.frnt_l, + ], + ]), + # -- 3 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_bot_r, + }, + [ + [ + Edg.frnt_l, + Edg.bot_l, + Edg.bot_r, + ], + [ + Edg.frnt_l, + Edg.frnt_r, + Edg.bot_r, + ], + ]), + # -- 4 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_top_r, + }, + [ + [ + Edg.frnt_bot, + Edg.bot_l, + Edg.frnt_l, + ], + [ + Edg.frnt_top, + Edg.top_r, + Edg.frnt_r, + ], + ]), + + # -- 5 -- + TriangulatedCube( + { + Vert.frnt_bot_r, + Vert.back_bot_r, + Vert.back_bot_l, + }, + [ + [ + Edg.frnt_r, + Edg.back_r, + Edg.back_l, + ], + [ + Edg.bot_l, + Edg.frnt_bot, + Edg.frnt_r, + ], + [ + Edg.frnt_r, + Edg.back_l, + Edg.bot_l, + ], + ]), + # -- 6 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_bot_l, + Vert.frnt_bot_r, + Vert.back_bot_r, + }, + [ + [ + Edg.frnt_l, + Edg.frnt_r, + Edg.back_r, + ], + [ + Edg.back_r, + Edg.back_l, + Edg.frnt_l, + ], + ]), + # -- 7 -- + TriangulatedCube( + { + Vert.frnt_top_l, + Vert.back_bot_l, + Vert.frnt_bot_r, + Vert.back_bot_r, + }, + [ + [ + Edg.frnt_r, + Edg.back_r, + Edg.back_l, + ], + [ + Edg.bot_l, + Edg.frnt_bot, + Edg.frnt_r, + ], + [ + Edg.frnt_r, + Edg.back_l, + Edg.bot_l, + ], + [ + Edg.frnt_l, + Edg.frnt_top, + Edg.top_l, + ], + ]), + # -- 8 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_bot_r, + Vert.frnt_top_r, + Vert.back_top_l, + }, + [ + [ + Edg.frnt_bot, + Edg.bot_l, + Edg.frnt_l, + ], + [ + Edg.frnt_top, + Edg.top_r, + Edg.frnt_r, + ], + [ + Edg.back_bot, + Edg.bot_r, + Edg.back_r, + ], + [ + Edg.back_top, + Edg.top_l, + Edg.back_l, + ], + ]), + # -- 9 -- + TriangulatedCube( + { + Vert.back_bot_l, + Vert.back_bot_r, + Vert.back_top_l, + Vert.frnt_bot_l, + }, + [ + [ + Edg.top_l, + Edg.frnt_l, + Edg.back_top, + ], + [ + Edg.frnt_l, + Edg.back_top, + Edg.frnt_bot, + ], + [ + Edg.back_top, + Edg.frnt_bot, + Edg.back_r, + ], + [ + Edg.frnt_bot, + Edg.back_r, + Edg.bot_r, + ], + ]), + # -- 10 -- + TriangulatedCube( + { + Vert.back_top_l, + Vert.back_bot_l, + Vert.back_bot_r, + Vert.frnt_bot_r, + }, + [ + [ + Edg.bot_l, + Edg.frnt_bot, + Edg.top_l, + ], + [ + Edg.frnt_bot, + Edg.top_l, + Edg.back_r, + ], + [ + Edg.top_l, + Edg.back_r, + Edg.back_top, + ], + [ + Edg.frnt_bot, + Edg.back_r, + Edg.frnt_r, + ], + ]), + # -- 11 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_bot, + Edg.frnt_l, + Edg.bot_l, + ], + [ + Edg.back_top, + Edg.back_r, + Edg.top_r, + ], + ]), + # -- 12 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_l, + Edg.bot_l, + Edg.bot_r, + ], + [ + Edg.frnt_l, + Edg.frnt_r, + Edg.bot_r, + ], + [ + Edg.back_top, + Edg.back_r, + Edg.top_r, + ], + ]), + # -- 13 -- + TriangulatedCube( + { + Vert.frnt_top_l, + Vert.frnt_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_l, + Edg.frnt_top, + Edg.top_l, + ], + [ + Edg.frnt_r, + Edg.frnt_bot, + Edg.bot_r, + ], + [ + Edg.back_r, + Edg.back_top, + Edg.top_r, + ], + ]), + # -- 14 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_top_l, + Vert.back_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_top, + Edg.frnt_bot, + Edg.top_l, + ], + [ + Edg.frnt_bot, + Edg.top_l, + Edg.bot_l, + ], + [ + Edg.back_bot, + Edg.back_top, + Edg.bot_r, + ], + [ + Edg.back_top, + Edg.bot_r, + Edg.top_r, + ], + ]), + # -- 15 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_bot_l, + Vert.back_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_l, + Edg.frnt_bot, + Edg.back_l, + ], + [ + Edg.frnt_bot, + Edg.back_l, + Edg.top_r, + ], + [ + Edg.back_l, + Edg.top_r, + Edg.back_top, + ], + [ + Edg.frnt_bot, + Edg.top_r, + Edg.bot_r, + ], + ]), + ] + do_main(base_cases) + + +if __name__ == "__main__": + main() From 1dece061a3e2a1199464f80ad77011cb2d820e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 00:53:47 +0100 Subject: [PATCH 21/36] march generator: add reverse activated points --- tools/gen_marching_cubes_conf.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index 899a462..8c76a14 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -83,6 +83,19 @@ class TriangulatedCube: n_act.add(transf(act)) return TriangulatedCube(n_act, n_tri) + def reverse_activated(self): + all_elts = { + Vert.frnt_bot_l, + Vert.frnt_bot_r, + Vert.frnt_top_l, + Vert.frnt_top_r, + Vert.back_bot_l, + Vert.back_bot_r, + Vert.back_top_l, + Vert.back_top_r, + } + self.activated = all_elts - self.activated + def activated_code(self): out = 0 for act in self.activated: @@ -181,11 +194,14 @@ def do_main(base_cases): index[code] = case for transf in transforms: - for case in base_cases: - tr_case = case.transform(transf) - code = tr_case.activated_code() - if index[code] is None: - index[code] = tr_case + for rev_activated in [False, True]: + for case in base_cases: + tr_case = case.transform(transf) + if rev_activated: + tr_case.reverse_activated() + code = tr_case.activated_code() + if index[code] is None: + index[code] = tr_case for (val, tri) in enumerate(index): if tri is None: From 65cb03487d33706c590a82604800c8d22ffeb8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 00:58:09 +0100 Subject: [PATCH 22/36] March generator: enhance error handling --- tools/gen_marching_cubes_conf.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index 8c76a14..be40cad 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -7,6 +7,7 @@ """ from functools import reduce +import sys class Vert: @@ -203,9 +204,13 @@ def do_main(base_cases): if index[code] is None: index[code] = tr_case + has_unbound = False for (val, tri) in enumerate(index): if tri is None: - print(">> UNBOUND {}".format(val)) + print(">> UNBOUND {}".format(val), file=sys.stderr) + has_unbound = True + if has_unbound: + raise RuntimeError("Some cases were not generated.") print(("static const std::vector " "edges_of_intersection[256] = {")) From b5ae4b546304a2bd357567b3e36d528b73079c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 01:28:06 +0100 Subject: [PATCH 23/36] March generator: fix pretty printing Fix various display/valid C++ generation problems. --- tools/gen_marching_cubes_conf.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index be40cad..bcfe01d 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -10,6 +10,15 @@ from functools import reduce import sys +PREAMBLE = """ +#include "MarchingCubes.hpp" + +typedef MarchingCubes::CubeTri Tri; +typedef std::vector TriVect; +typedef MarchingCubes::CubeEdge Edge; +""" + + class Vert: frnt_bot_l = (0, 0, 1) frnt_bot_r = (1, 0, 1) @@ -40,9 +49,9 @@ class Edge: def dump(self): def cbool(val): - return "true" if val else "false" + return "1" if val else "0" - return "MarchingCubes::CubeEdge({}, {}, {}, {}, {}, {})".format( + return "Edge({}, {}, {}, {}, {}, {})".format( cbool(self.vert[0][0]), cbool(self.vert[0][1]), cbool(self.vert[0][2]), @@ -108,8 +117,8 @@ class TriangulatedCube: def dump_tri(tri): - return ("MarchingCubes::CubeTri({{{}, {}, {}}})".format( - tri[0].dump(), tri[1].dump(), tri[2].dump)) + return ("Tri({}, {}, {})".format( + tri[0].dump(), tri[1].dump(), tri[2].dump())) def rot_general(vert, fixed, ax1, ax2): @@ -212,16 +221,16 @@ def do_main(base_cases): if has_unbound: raise RuntimeError("Some cases were not generated.") - print(("static const std::vector " - "edges_of_intersection[256] = {")) + print(PREAMBLE) + print(("static const TriVect edges_of_intersection[256] = {")) for (case_id, case) in enumerate(index): - print("\tstd::vector({") + print("\tTriVect({") for (tri_id, tri) in enumerate(case.triangles): - print(dump_tri(tri), - end=",\n" if tri_id == len(case.triangles) - 1 else '\n') - print('})', end=',\n' if case_id == len(base_cases) - 1 else '\n') + print('\t\t' + dump_tri(tri), + end=",\n" if tri_id != len(case.triangles) - 1 else '\n') + print('\t\t})', end=',\n' if case_id != len(index) - 1 else '\n') print("};") From 0f60c717f01fd0083ab295a3a04a2412bd42a35d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 01:39:49 +0100 Subject: [PATCH 24/36] March gen: symmetries do not seem to be needed This would be backed by the base cases 10 and 15 being the same up to planar symmetry with a plane of normal vector x. --- tools/gen_marching_cubes_conf.py | 34 +++++--------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index bcfe01d..949b2ed 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -148,26 +148,6 @@ def rot_z(vert): return rot_general(vert, 2, 0, 1) -def sym_general(vert, normal): - return ( - vert[0] if normal != 0 else 1-vert[0], - vert[1] if normal != 1 else 1-vert[1], - vert[2] if normal != 2 else 1-vert[2] - ) - - -def sym_x(vert): - return sym_general(vert, 0) - - -def sym_y(vert): - return sym_general(vert, 1) - - -def sym_z(vert): - return sym_general(vert, 2) - - def all_transforms(): def compose2(fun1, fun2): return lambda x: fun1(fun2(x)) @@ -183,15 +163,11 @@ def all_transforms(): for num_rx in range(4): for num_ry in range(4): for num_rz in range(4): - for syms in range(8): - cur = compose( - funcpow(rot_x, num_rx), - funcpow(rot_y, num_ry), - funcpow(rot_z, num_rz), - sym_x if (syms & 0x1) else lambda x: x, - sym_y if (syms & 0x2) else lambda x: x, - sym_z if (syms & 0x4) else lambda x: x) - output.append(cur) + cur = compose( + funcpow(rot_x, num_rx), + funcpow(rot_y, num_ry), + funcpow(rot_z, num_rz)) + output.append(cur) return output From f15151ff571d867a54bcd7d3e17a8a70a98652a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 01:52:00 +0100 Subject: [PATCH 25/36] March gen: refactor for easier testing --- tools/gen_marching_cubes_conf.py | 720 ++++++++++++++++--------------- 1 file changed, 367 insertions(+), 353 deletions(-) diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index 949b2ed..da4eebe 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -171,7 +171,7 @@ def all_transforms(): return output -def do_main(base_cases): +def gen_index(base_cases): transforms = all_transforms() index = [None for x in range(256)] for case in base_cases: @@ -197,365 +197,379 @@ def do_main(base_cases): if has_unbound: raise RuntimeError("Some cases were not generated.") - print(PREAMBLE) - print(("static const TriVect edges_of_intersection[256] = {")) + return index + + +def pretty_print(index): + output = "" + output += PREAMBLE + output += "static const TriVect edges_of_intersection[256] = {\n" for (case_id, case) in enumerate(index): - print("\tTriVect({") + output += "\tTriVect({\n" for (tri_id, tri) in enumerate(case.triangles): - print('\t\t' + dump_tri(tri), - end=",\n" if tri_id != len(case.triangles) - 1 else '\n') - print('\t\t})', end=',\n' if case_id != len(index) - 1 else '\n') - print("};") + output += '\t\t{}{}'.format( + dump_tri(tri), + ",\n" if tri_id != len(case.triangles) - 1 else '\n') + output += '\t\t})' + (',\n' if case_id != len(index) - 1 else '\n') + output += "};\n" + + return output + + +def do_main(base_cases): + print(pretty_print(gen_index(base_cases))) + + +BASE_CASES = [ + # Source: + # -- 1 -- + TriangulatedCube(set(), []), + # -- 2 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + }, + [ + [ + Edg.frnt_bot, + Edg.bot_l, + Edg.frnt_l, + ], + ]), + # -- 3 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_bot_r, + }, + [ + [ + Edg.frnt_l, + Edg.bot_l, + Edg.bot_r, + ], + [ + Edg.frnt_l, + Edg.frnt_r, + Edg.bot_r, + ], + ]), + # -- 4 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_top_r, + }, + [ + [ + Edg.frnt_bot, + Edg.bot_l, + Edg.frnt_l, + ], + [ + Edg.frnt_top, + Edg.top_r, + Edg.frnt_r, + ], + ]), + + # -- 5 -- + TriangulatedCube( + { + Vert.frnt_bot_r, + Vert.back_bot_r, + Vert.back_bot_l, + }, + [ + [ + Edg.frnt_r, + Edg.back_r, + Edg.back_l, + ], + [ + Edg.bot_l, + Edg.frnt_bot, + Edg.frnt_r, + ], + [ + Edg.frnt_r, + Edg.back_l, + Edg.bot_l, + ], + ]), + # -- 6 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_bot_l, + Vert.frnt_bot_r, + Vert.back_bot_r, + }, + [ + [ + Edg.frnt_l, + Edg.frnt_r, + Edg.back_r, + ], + [ + Edg.back_r, + Edg.back_l, + Edg.frnt_l, + ], + ]), + # -- 7 -- + TriangulatedCube( + { + Vert.frnt_top_l, + Vert.back_bot_l, + Vert.frnt_bot_r, + Vert.back_bot_r, + }, + [ + [ + Edg.frnt_r, + Edg.back_r, + Edg.back_l, + ], + [ + Edg.bot_l, + Edg.frnt_bot, + Edg.frnt_r, + ], + [ + Edg.frnt_r, + Edg.back_l, + Edg.bot_l, + ], + [ + Edg.frnt_l, + Edg.frnt_top, + Edg.top_l, + ], + ]), + # -- 8 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_bot_r, + Vert.frnt_top_r, + Vert.back_top_l, + }, + [ + [ + Edg.frnt_bot, + Edg.bot_l, + Edg.frnt_l, + ], + [ + Edg.frnt_top, + Edg.top_r, + Edg.frnt_r, + ], + [ + Edg.back_bot, + Edg.bot_r, + Edg.back_r, + ], + [ + Edg.back_top, + Edg.top_l, + Edg.back_l, + ], + ]), + # -- 9 -- + TriangulatedCube( + { + Vert.back_bot_l, + Vert.back_bot_r, + Vert.back_top_l, + Vert.frnt_bot_l, + }, + [ + [ + Edg.top_l, + Edg.frnt_l, + Edg.back_top, + ], + [ + Edg.frnt_l, + Edg.back_top, + Edg.frnt_bot, + ], + [ + Edg.back_top, + Edg.frnt_bot, + Edg.back_r, + ], + [ + Edg.frnt_bot, + Edg.back_r, + Edg.bot_r, + ], + ]), + # -- 10 -- + TriangulatedCube( + { + Vert.back_top_l, + Vert.back_bot_l, + Vert.back_bot_r, + Vert.frnt_bot_r, + }, + [ + [ + Edg.bot_l, + Edg.frnt_bot, + Edg.top_l, + ], + [ + Edg.frnt_bot, + Edg.top_l, + Edg.back_r, + ], + [ + Edg.top_l, + Edg.back_r, + Edg.back_top, + ], + [ + Edg.frnt_bot, + Edg.back_r, + Edg.frnt_r, + ], + ]), + # -- 11 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_bot, + Edg.frnt_l, + Edg.bot_l, + ], + [ + Edg.back_top, + Edg.back_r, + Edg.top_r, + ], + ]), + # -- 12 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_l, + Edg.bot_l, + Edg.bot_r, + ], + [ + Edg.frnt_l, + Edg.frnt_r, + Edg.bot_r, + ], + [ + Edg.back_top, + Edg.back_r, + Edg.top_r, + ], + ]), + # -- 13 -- + TriangulatedCube( + { + Vert.frnt_top_l, + Vert.frnt_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_l, + Edg.frnt_top, + Edg.top_l, + ], + [ + Edg.frnt_r, + Edg.frnt_bot, + Edg.bot_r, + ], + [ + Edg.back_r, + Edg.back_top, + Edg.top_r, + ], + ]), + # -- 14 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.frnt_top_l, + Vert.back_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_top, + Edg.frnt_bot, + Edg.top_l, + ], + [ + Edg.frnt_bot, + Edg.top_l, + Edg.bot_l, + ], + [ + Edg.back_bot, + Edg.back_top, + Edg.bot_r, + ], + [ + Edg.back_top, + Edg.bot_r, + Edg.top_r, + ], + ]), + # -- 15 -- + TriangulatedCube( + { + Vert.frnt_bot_l, + Vert.back_bot_l, + Vert.back_bot_r, + Vert.back_top_r, + }, + [ + [ + Edg.frnt_l, + Edg.frnt_bot, + Edg.back_l, + ], + [ + Edg.frnt_bot, + Edg.back_l, + Edg.top_r, + ], + [ + Edg.back_l, + Edg.top_r, + Edg.back_top, + ], + [ + Edg.frnt_bot, + Edg.top_r, + Edg.bot_r, + ], + ]), +] def main(): - base_cases = [ - # Source: - # -- 1 -- - TriangulatedCube(set(), []), - # -- 2 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - }, - [ - [ - Edg.frnt_bot, - Edg.bot_l, - Edg.frnt_l, - ], - ]), - # -- 3 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.frnt_bot_r, - }, - [ - [ - Edg.frnt_l, - Edg.bot_l, - Edg.bot_r, - ], - [ - Edg.frnt_l, - Edg.frnt_r, - Edg.bot_r, - ], - ]), - # -- 4 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.frnt_top_r, - }, - [ - [ - Edg.frnt_bot, - Edg.bot_l, - Edg.frnt_l, - ], - [ - Edg.frnt_top, - Edg.top_r, - Edg.frnt_r, - ], - ]), - - # -- 5 -- - TriangulatedCube( - { - Vert.frnt_bot_r, - Vert.back_bot_r, - Vert.back_bot_l, - }, - [ - [ - Edg.frnt_r, - Edg.back_r, - Edg.back_l, - ], - [ - Edg.bot_l, - Edg.frnt_bot, - Edg.frnt_r, - ], - [ - Edg.frnt_r, - Edg.back_l, - Edg.bot_l, - ], - ]), - # -- 6 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.back_bot_l, - Vert.frnt_bot_r, - Vert.back_bot_r, - }, - [ - [ - Edg.frnt_l, - Edg.frnt_r, - Edg.back_r, - ], - [ - Edg.back_r, - Edg.back_l, - Edg.frnt_l, - ], - ]), - # -- 7 -- - TriangulatedCube( - { - Vert.frnt_top_l, - Vert.back_bot_l, - Vert.frnt_bot_r, - Vert.back_bot_r, - }, - [ - [ - Edg.frnt_r, - Edg.back_r, - Edg.back_l, - ], - [ - Edg.bot_l, - Edg.frnt_bot, - Edg.frnt_r, - ], - [ - Edg.frnt_r, - Edg.back_l, - Edg.bot_l, - ], - [ - Edg.frnt_l, - Edg.frnt_top, - Edg.top_l, - ], - ]), - # -- 8 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.back_bot_r, - Vert.frnt_top_r, - Vert.back_top_l, - }, - [ - [ - Edg.frnt_bot, - Edg.bot_l, - Edg.frnt_l, - ], - [ - Edg.frnt_top, - Edg.top_r, - Edg.frnt_r, - ], - [ - Edg.back_bot, - Edg.bot_r, - Edg.back_r, - ], - [ - Edg.back_top, - Edg.top_l, - Edg.back_l, - ], - ]), - # -- 9 -- - TriangulatedCube( - { - Vert.back_bot_l, - Vert.back_bot_r, - Vert.back_top_l, - Vert.frnt_bot_l, - }, - [ - [ - Edg.top_l, - Edg.frnt_l, - Edg.back_top, - ], - [ - Edg.frnt_l, - Edg.back_top, - Edg.frnt_bot, - ], - [ - Edg.back_top, - Edg.frnt_bot, - Edg.back_r, - ], - [ - Edg.frnt_bot, - Edg.back_r, - Edg.bot_r, - ], - ]), - # -- 10 -- - TriangulatedCube( - { - Vert.back_top_l, - Vert.back_bot_l, - Vert.back_bot_r, - Vert.frnt_bot_r, - }, - [ - [ - Edg.bot_l, - Edg.frnt_bot, - Edg.top_l, - ], - [ - Edg.frnt_bot, - Edg.top_l, - Edg.back_r, - ], - [ - Edg.top_l, - Edg.back_r, - Edg.back_top, - ], - [ - Edg.frnt_bot, - Edg.back_r, - Edg.frnt_r, - ], - ]), - # -- 11 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.back_top_r, - }, - [ - [ - Edg.frnt_bot, - Edg.frnt_l, - Edg.bot_l, - ], - [ - Edg.back_top, - Edg.back_r, - Edg.top_r, - ], - ]), - # -- 12 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.frnt_bot_r, - Vert.back_top_r, - }, - [ - [ - Edg.frnt_l, - Edg.bot_l, - Edg.bot_r, - ], - [ - Edg.frnt_l, - Edg.frnt_r, - Edg.bot_r, - ], - [ - Edg.back_top, - Edg.back_r, - Edg.top_r, - ], - ]), - # -- 13 -- - TriangulatedCube( - { - Vert.frnt_top_l, - Vert.frnt_bot_r, - Vert.back_top_r, - }, - [ - [ - Edg.frnt_l, - Edg.frnt_top, - Edg.top_l, - ], - [ - Edg.frnt_r, - Edg.frnt_bot, - Edg.bot_r, - ], - [ - Edg.back_r, - Edg.back_top, - Edg.top_r, - ], - ]), - # -- 14 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.frnt_top_l, - Vert.back_bot_r, - Vert.back_top_r, - }, - [ - [ - Edg.frnt_top, - Edg.frnt_bot, - Edg.top_l, - ], - [ - Edg.frnt_bot, - Edg.top_l, - Edg.bot_l, - ], - [ - Edg.back_bot, - Edg.back_top, - Edg.bot_r, - ], - [ - Edg.back_top, - Edg.bot_r, - Edg.top_r, - ], - ]), - # -- 15 -- - TriangulatedCube( - { - Vert.frnt_bot_l, - Vert.back_bot_l, - Vert.back_bot_r, - Vert.back_top_r, - }, - [ - [ - Edg.frnt_l, - Edg.frnt_bot, - Edg.back_l, - ], - [ - Edg.frnt_bot, - Edg.back_l, - Edg.top_r, - ], - [ - Edg.back_l, - Edg.top_r, - Edg.back_top, - ], - [ - Edg.frnt_bot, - Edg.top_r, - Edg.bot_r, - ], - ]), - ] - do_main(base_cases) + do_main(BASE_CASES) if __name__ == "__main__": From fc2f17b4538c254b797e6537e8949d0937fcb6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 20:07:38 +0100 Subject: [PATCH 26/36] March gen: refactor for easier testing --- tools/gen_marching_cubes_conf.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index da4eebe..6d96965 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -76,9 +76,21 @@ class Edg: class TriangulatedCube: + ALL_ELTS = { + Vert.frnt_bot_l, + Vert.frnt_bot_r, + Vert.frnt_top_l, + Vert.frnt_top_r, + Vert.back_bot_l, + Vert.back_bot_r, + Vert.back_top_l, + Vert.back_top_r, + } + def __init__(self, activated, triangles=None): self.triangles = [] if triangles is None else triangles self.activated = activated + self.non_activated = TriangulatedCube.ALL_ELTS - activated def transform(self, transf): n_tri = [] @@ -94,17 +106,8 @@ class TriangulatedCube: return TriangulatedCube(n_act, n_tri) def reverse_activated(self): - all_elts = { - Vert.frnt_bot_l, - Vert.frnt_bot_r, - Vert.frnt_top_l, - Vert.frnt_top_r, - Vert.back_bot_l, - Vert.back_bot_r, - Vert.back_top_l, - Vert.back_top_r, - } - self.activated = all_elts - self.activated + self.activated, self.non_activated = \ + self.non_activated, self.activated def activated_code(self): out = 0 From 62730a03b44bb17fbb5ae0949db0b78f21470889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 20:07:54 +0100 Subject: [PATCH 27/36] March gen: add some testing and viz capabilities This allows to visualize easily a generated configuration, thus allowing testing --- tools/test_marching_cubes_generator.py | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tools/test_marching_cubes_generator.py diff --git a/tools/test_marching_cubes_generator.py b/tools/test_marching_cubes_generator.py new file mode 100644 index 0000000..1bb5c20 --- /dev/null +++ b/tools/test_marching_cubes_generator.py @@ -0,0 +1,52 @@ +import gen_marching_cubes_conf as gen +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import axes3d +import random + + +def split_pt_list(pts): + splitted = [[], [], []] + for point in pts: + for i in range(3): + splitted[i].append(point[i]) + return splitted + + +def pt_of_edge(edge): + def avg(val0, val1): + return (val0 + val1) / 2 + + vert0 = edge.vert[0] + vert1 = edge.vert[1] + + return (avg(vert0[0], vert1[0]), + avg(vert0[1], vert1[1]), + avg(vert0[2], vert1[2])) + + +def tri_repr(tri, subplt): + pts = [pt_of_edge(tri[i]) for i in range(3)] + + x_val, y_val, z_val = split_pt_list(pts) + x_val = [val + random.random() / 10**5 for val in x_val] + y_val = [val + random.random() / 10**5 for val in y_val] + + subplt.plot_trisurf(x_val, y_val, z_val) + + +def display_case(tri_cube): + figure = plt.figure() + subplt = figure.add_subplot(111, projection='3d') + + actives = split_pt_list(list(tri_cube.activated)) + inactives = split_pt_list(list(tri_cube.non_activated)) + + subplt.scatter3D(actives[0], actives[1], actives[2], + c='r', marker='o') + subplt.scatter3D(inactives[0], inactives[1], inactives[2], + c='b', marker='.') + + for triangle in tri_cube.triangles: + tri_repr(triangle, subplt) + + plt.show() From 480430918292f35cd940798f6bbf1a0a98984ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 14:50:41 +0100 Subject: [PATCH 28/36] Marching: write C++ data structures --- MarchingCubes.cpp | 17 +++++++++++- MarchingCubes.hpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index 6988d10..7fe4946 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -1,5 +1,9 @@ #include "MarchingCubes.hpp" +const std::vector + MarchingCubes::edges_of_intersection[256] = { + }; + MarchingCubes::MarchingCubes( const ImplicitSurface& surface, const Cuboid& box, @@ -24,7 +28,18 @@ Mesh MarchingCubes::operator()() { for(double z = box.low(2); z < box.high(2) + resolution; z += resolution) { - //TODO apply marching cube indeed + Intersections intersections; + for(int dx=0; dx <= 1; dx++) { + for(int dy=0; dy <= 1; dy++) { + for(int dz=0; dz <= 1; dz++) { + double cx = x + resolution * dx; + double cy = y + resolution * dy; + double cz = z + resolution * dz; + intersections.set_corner(cx, cy, cz, + surface(cx, cy, cz)); + } + } + } } } } diff --git a/MarchingCubes.hpp b/MarchingCubes.hpp index 95db829..ced47cc 100644 --- a/MarchingCubes.hpp +++ b/MarchingCubes.hpp @@ -15,7 +15,40 @@ class MarchingCubes { private: - typedef unsigned char intersect_t; + class Intersections { + public: + typedef unsigned char intersect_t; + Intersections() : inters(0) {} + + intersect_t value() const { + return inters; + } + + /** The corners are indexed with three booleans, one for each + * axis (x, y, z). A false value means a lower coordinate along + * this axis. + * Eg., (false, true, false) means the corner (0, 1, 0) for a + * cube of side 1 placed at (0, 0, 0). + */ + + void set_corner(bool x, bool y, bool z, bool val) { + intersect_t mask = 1 << (shift(x, y, z)); + if(val) + inters |= mask; + else + inters &= ~mask; + } + bool get_corner(bool x, bool y, bool z) const { + return (inters & (1 << shift(x, y, z))) == 1; + } + + private: + intersect_t inters; + + int shift(bool x, bool y, bool z) const { + return x + (y << 1) + (z << 2); + } + }; public: MarchingCubes( @@ -39,7 +72,38 @@ class MarchingCubes { Mesh operator()(); + struct CubeEdge { + CubeEdge() {} + CubeEdge(bool x0, bool y0, bool z0, + bool x1, bool y1, bool z1) + { + x[0] = x0; + y[0] = y0; + z[0] = z0; + x[1] = x1; + y[1] = y1; + z[1] = z1; + } + bool x[2], y[2], z[2]; + }; + struct CubeTri { + CubeTri() {} + CubeTri(const CubeEdge* edge_) { + for(size_t i=0; i < 3; ++i) + edge[i] = edge_[i]; + } + CubeTri(const CubeEdge e0, const CubeEdge e1, const CubeEdge e2) { + edge[0] = e0; + edge[1] = e1; + edge[2] = e2; + } + CubeEdge edge[3]; + }; + private: + + static const std::vector edges_of_intersection[256]; + const ImplicitSurface& surface; Cuboid box; double resolution; From 998c77f6437dac53490239c9666d29253792ae37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 16:46:55 +0100 Subject: [PATCH 29/36] Marching: add dichitomic search of intersection --- Implicit.hpp | 5 ++++ MarchingCubes.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++- MarchingCubes.hpp | 9 +++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/Implicit.hpp b/Implicit.hpp index 02fcae2..b0abfac 100644 --- a/Implicit.hpp +++ b/Implicit.hpp @@ -1,6 +1,11 @@ #pragma once +#include "common_structures.hpp" + class ImplicitSurface { public: virtual double operator() (double x, double y, double z) const; + double operator()(const Point& pt) const { + return operator()(pt.x, pt.y, pt.z); + } }; diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index 7fe4946..b6e6222 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -1,5 +1,7 @@ #include "MarchingCubes.hpp" +#include + const std::vector MarchingCubes::edges_of_intersection[256] = { }; @@ -29,6 +31,15 @@ Mesh MarchingCubes::operator()() { z += resolution) { Intersections intersections; + /* ===== + * NOTE: this code currently computes 8 times the surface value + * at each corner point of the inspected space. This is okay + * for now, because such computations are ultra light and + * actually better than storing values. + * If a time comes when this computation is heavier, because we + * are looking at more complex implicit surfaces, + * IT WILL BE NECESSARY TO ENHANCE THIS CODE! + * ==== */ for(int dx=0; dx <= 1; dx++) { for(int dy=0; dy <= 1; dy++) { for(int dz=0; dz <= 1; dz++) { @@ -36,13 +47,60 @@ Mesh MarchingCubes::operator()() { double cy = y + resolution * dy; double cz = z + resolution * dz; intersections.set_corner(cx, cy, cz, - surface(cx, cy, cz)); + surface(cx, cy, cz) > 0); } } } + + const std::vector& cur_triangles = + edges_of_intersection[intersections.value()]; + // TODO } } } return output; } + +Point MarchingCubes::CubeEdge::at(double pos, + double bx, double by, double bz, double resolution) const +{ + Point p1( + bx + x[0] * resolution, + by + y[0] * resolution, + bz + z[0] * resolution); + Point p2( + bx + x[1] * resolution, + by + y[1] * resolution, + bz + z[1] * resolution); + + return Point( + 0.5 * (p1.x + p2.x), + 0.5 * (p1.y + p2.y), + 0.5 * (p1.z + p2.z)); +} + +Point MarchingCubes::intersect_location(const CubeEdge& edge, + double bx, double by, double bz) const +{ + Point p1 = edge.at(0, bx, by, bz, resolution); + Point p2 = edge.at(1, bx, by, bz, resolution); + + std::function compute = + [&](double low_prop, double high_prop) + { + double med_prop = (low_prop + high_prop) / 2; + Point med = edge.at(med_prop, bx, by, bz, resolution); + + if(high_prop - low_prop < 1e-8) + return med; + + assert(surface(p1) * surface(p2) <= 0); // Can still binary search + + if(surface(p1) * surface(med) <= 0) + return compute(low_prop, med_prop); + return compute(med_prop, high_prop); + }; + + return compute(0, 1); +} diff --git a/MarchingCubes.hpp b/MarchingCubes.hpp index ced47cc..0e0c155 100644 --- a/MarchingCubes.hpp +++ b/MarchingCubes.hpp @@ -85,6 +85,11 @@ class MarchingCubes { z[1] = z1; } bool x[2], y[2], z[2]; + + /** Get the space point at a given position of [0,1] along the edge + * when the base of the cube (ie. (0, 0, 0)) is given. */ + Point at(double pos, + double bx, double by, double bz, double resolution) const; }; struct CubeTri { CubeTri() {} @@ -100,6 +105,10 @@ class MarchingCubes { CubeEdge edge[3]; }; + private: //methods + Point intersect_location(const CubeEdge& edge, + double bx, double by, double bz) const; + private: static const std::vector edges_of_intersection[256]; From 3100ef520f9f721bcca3b8c47553a3a426d47c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 17:54:44 +0100 Subject: [PATCH 30/36] Marching: implement algorithm, still untested --- MarchingCubes.cpp | 22 +++++++++++++++++++++- MarchingCubes.hpp | 6 ++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index b6e6222..bccdb6c 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -54,7 +54,27 @@ Mesh MarchingCubes::operator()() { const std::vector& cur_triangles = edges_of_intersection[intersections.value()]; - // TODO + for(const CubeTri& cube_tri: cur_triangles) { + Point verts[3] = { + intersect_location(cube_tri.edge[0], + x, y, z), + intersect_location(cube_tri.edge[1], + x, y, z), + intersect_location(cube_tri.edge[2], + x, y, z), + }; + + size_t vert_ids[3]; + for(int i=0; i < 3; ++i) + vert_ids[i] = output.add_vertice(verts[i]); + + for(int i=0; i < 3; ++i) { + output.add_face( + vert_ids[i], + vert_ids[(i+1) % 3], + vert_ids[(i+2) % 3]); + } + } } } } diff --git a/MarchingCubes.hpp b/MarchingCubes.hpp index 0e0c155..97d1855 100644 --- a/MarchingCubes.hpp +++ b/MarchingCubes.hpp @@ -86,6 +86,12 @@ class MarchingCubes { } bool x[2], y[2], z[2]; + const bool operator==(const CubeEdge& oth) const { + return x[0] == oth.x[0] && x[1] == oth.x[1] + && y[0] == oth.y[0] && y[1] == oth.y[1] + && z[0] == oth.z[0] && z[1] == oth.z[1]; + } + /** Get the space point at a given position of [0,1] along the edge * when the base of the cube (ie. (0, 0, 0)) is given. */ Point at(double pos, From 8d11d49b813040af8e02796d4467637a77e921f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 18:34:00 +0100 Subject: [PATCH 31/36] Marching: add compilation process Also fix minor bugs --- .gitignore | 1 + Makefile | 9 ++++++++- MarchingCubes.cpp | 6 +++--- MarchingCubes.hpp | 2 +- tools/gen_marching_cubes_conf.py | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 2530b6d..98d9c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ *.bin __pycache__ +_gen diff --git a/Makefile b/Makefile index e19eac1..9452c5f 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ OBJS=Implicit.o \ Mesh.o \ util/ObjParser.o \ MarchingCubes.o \ + _gen/marching_cubes_data.o \ render/GlutRender.o all: $(TARGETS:=.bin) @@ -18,12 +19,18 @@ all: $(TARGETS:=.bin) %.bin: main_%.o $(OBJS) $(CXX) $(CXXFLAGS) $(CXXLIBS) $(OBJS) $< -o $@ +_gen/marching_cubes_data.cpp: tools/gen_marching_cubes_conf.py _gen + python3 $< > $@ + %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@ +_gen: + mkdir -p _gen + ############################################################ .PHONY: clean clean: - rm -rf $(OBJS) $(TARGETS:=.bin) + rm -rf $(OBJS) $(TARGETS:=.bin) _gen diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index bccdb6c..49f41ab 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -95,9 +95,9 @@ Point MarchingCubes::CubeEdge::at(double pos, bz + z[1] * resolution); return Point( - 0.5 * (p1.x + p2.x), - 0.5 * (p1.y + p2.y), - 0.5 * (p1.z + p2.z)); + pos * (p1.x + p2.x), + pos * (p1.y + p2.y), + pos * (p1.z + p2.z)); } Point MarchingCubes::intersect_location(const CubeEdge& edge, diff --git a/MarchingCubes.hpp b/MarchingCubes.hpp index 97d1855..f47dab5 100644 --- a/MarchingCubes.hpp +++ b/MarchingCubes.hpp @@ -86,7 +86,7 @@ class MarchingCubes { } bool x[2], y[2], z[2]; - const bool operator==(const CubeEdge& oth) const { + bool operator==(const CubeEdge& oth) const { return x[0] == oth.x[0] && x[1] == oth.x[1] && y[0] == oth.y[0] && y[1] == oth.y[1] && z[0] == oth.z[0] && z[1] == oth.z[1]; diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index 6d96965..3d80016 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -11,7 +11,7 @@ import sys PREAMBLE = """ -#include "MarchingCubes.hpp" +#include "../MarchingCubes.hpp" typedef MarchingCubes::CubeTri Tri; typedef std::vector TriVect; From 4a3a1b9d7675c1ea998e0e8dbe0102964df5684d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 20:19:26 +0100 Subject: [PATCH 32/36] Fix: ImplicitSurface.operator() is pure virtual --- Implicit.cpp | 5 +++++ Implicit.hpp | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Implicit.cpp b/Implicit.cpp index e69de29..43462df 100644 --- a/Implicit.cpp +++ b/Implicit.cpp @@ -0,0 +1,5 @@ +#include "Implicit.hpp" + +double ImplicitSurface::operator()(const Point& pt) const { + return operator()(pt.x, pt.y, pt.z); +} diff --git a/Implicit.hpp b/Implicit.hpp index b0abfac..ce8264d 100644 --- a/Implicit.hpp +++ b/Implicit.hpp @@ -4,8 +4,6 @@ class ImplicitSurface { public: - virtual double operator() (double x, double y, double z) const; - double operator()(const Point& pt) const { - return operator()(pt.x, pt.y, pt.z); - } + virtual double operator() (double x, double y, double z) const = 0; + double operator()(const Point& pt) const; }; From 52ca47cde697877c5b56153807290b6445a5dbec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 20:23:07 +0100 Subject: [PATCH 33/36] Marching: add test implicit surface and main The test surface is the sphere TestImplicitSphere --- Makefile | 1 + main_test_sphere.cpp | 33 +++++++++++++++++++++++++++++++++ tests/TestImplicitSphere.cpp | 9 +++++++++ tests/TestImplicitSphere.hpp | 14 ++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 main_test_sphere.cpp create mode 100644 tests/TestImplicitSphere.cpp create mode 100644 tests/TestImplicitSphere.hpp diff --git a/Makefile b/Makefile index 9452c5f..d02f502 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ OBJS=Implicit.o \ util/ObjParser.o \ MarchingCubes.o \ _gen/marching_cubes_data.o \ + tests/TestImplicitSphere.o \ render/GlutRender.o all: $(TARGETS:=.bin) diff --git a/main_test_sphere.cpp b/main_test_sphere.cpp new file mode 100644 index 0000000..61fb823 --- /dev/null +++ b/main_test_sphere.cpp @@ -0,0 +1,33 @@ +/** An entry-point file using render/GlutRender as a renderer, displaying a + * simple sphere. + **/ + +#include "render/GlutRender.hpp" +#include "util/ObjParser.hpp" +#include "tests/TestImplicitSphere.hpp" +#include "Mesh.hpp" +#include "MarchingCubes.hpp" + +int main(int argc, char** argv) { + GlutRender& render = GlutRender::get_instance(); + render.init(&argc, argv, 640, 480, "Bouncing stuff"); + + TestImplicitSphere sph1(Point(2.5, -1, 0), 1); + TestImplicitSphere sph2(Point(-4, 0.5, 0), 1.4); + Mesh m_sph1 = MarchingCubes(sph1, + Cuboid(Point(1, -2, -2), Point(3, 0, 2)))(); + Mesh m_sph2 = MarchingCubes(sph2, + Cuboid(Point(-6, -2, -2), Point(2, 3, 2)))(); + render.add_mesh(&m_sph1); + render.add_mesh(&m_sph2); + + puts("=== All set! ==="); + + printf("Sph1 has %ld vertices, %ld faces.\n", + m_sph1.get_vertices().size(), + m_sph1.get_faces().size()); + + render.run(); + + return 0; +} diff --git a/tests/TestImplicitSphere.cpp b/tests/TestImplicitSphere.cpp new file mode 100644 index 0000000..575dc56 --- /dev/null +++ b/tests/TestImplicitSphere.cpp @@ -0,0 +1,9 @@ +#include "TestImplicitSphere.hpp" + +double TestImplicitSphere::operator()(double x, double y, double z) const { + auto sq = [](double x) { return x*x; }; + return - (sq(center.x - x) + + sq(center.y - y) + + sq(center.z - z)) + + sq(radius); +} diff --git a/tests/TestImplicitSphere.hpp b/tests/TestImplicitSphere.hpp new file mode 100644 index 0000000..09b1fb9 --- /dev/null +++ b/tests/TestImplicitSphere.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "../Implicit.hpp" + +class TestImplicitSphere: public ImplicitSurface { + public: + TestImplicitSphere(const Point& center, double r): + center(center), radius(r) {} + virtual double operator()(double x, double y, double z) const; + + private: + Point center; + double radius; +}; From 0e026ccad1e76174597256c871c1d1d0cf682dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sun, 11 Feb 2018 20:39:59 +0100 Subject: [PATCH 34/36] Marching: fix static integration process --- MarchingCubes.cpp | 4 ---- tools/gen_marching_cubes_conf.py | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index 49f41ab..1a20b55 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -2,10 +2,6 @@ #include -const std::vector - MarchingCubes::edges_of_intersection[256] = { - }; - MarchingCubes::MarchingCubes( const ImplicitSurface& surface, const Cuboid& box, diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index 3d80016..db2ccf3 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -16,6 +16,8 @@ PREAMBLE = """ typedef MarchingCubes::CubeTri Tri; typedef std::vector TriVect; typedef MarchingCubes::CubeEdge Edge; + +const TriVect MarchingCubes::edges_of_intersection[] = { """ @@ -206,7 +208,6 @@ def gen_index(base_cases): def pretty_print(index): output = "" output += PREAMBLE - output += "static const TriVect edges_of_intersection[256] = {\n" for (case_id, case) in enumerate(index): output += "\tTriVect({\n" From c527ecc61178cb822944be309a5cd309dd0cd166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Mon, 12 Feb 2018 00:30:06 +0100 Subject: [PATCH 35/36] Marching: various fixes --- MarchingCubes.cpp | 30 +++++++++++++++++--------- MarchingCubes.hpp | 1 - tools/gen_marching_cubes_conf.py | 2 +- tools/test_marching_cubes_generator.py | 3 +++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index 1a20b55..1bbc12f 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -42,7 +42,7 @@ Mesh MarchingCubes::operator()() { double cx = x + resolution * dx; double cy = y + resolution * dy; double cz = z + resolution * dz; - intersections.set_corner(cx, cy, cz, + intersections.set_corner(dx, dy, dz, surface(cx, cy, cz) > 0); } } @@ -50,6 +50,7 @@ Mesh MarchingCubes::operator()() { const std::vector& cur_triangles = edges_of_intersection[intersections.value()]; + for(const CubeTri& cube_tri: cur_triangles) { Point verts[3] = { intersect_location(cube_tri.edge[0], @@ -70,6 +71,7 @@ Mesh MarchingCubes::operator()() { vert_ids[(i+1) % 3], vert_ids[(i+2) % 3]); } + } } } @@ -81,6 +83,10 @@ Mesh MarchingCubes::operator()() { Point MarchingCubes::CubeEdge::at(double pos, double bx, double by, double bz, double resolution) const { + auto bary = [pos](double x, double y) { + return pos * x + (1.-pos) * y; + }; + Point p1( bx + x[0] * resolution, by + y[0] * resolution, @@ -91,29 +97,33 @@ Point MarchingCubes::CubeEdge::at(double pos, bz + z[1] * resolution); return Point( - pos * (p1.x + p2.x), - pos * (p1.y + p2.y), - pos * (p1.z + p2.z)); + bary(p1.x, p2.x), + bary(p1.y, p2.y), + bary(p1.z, p2.z)); } Point MarchingCubes::intersect_location(const CubeEdge& edge, double bx, double by, double bz) const { - Point p1 = edge.at(0, bx, by, bz, resolution); - Point p2 = edge.at(1, bx, by, bz, resolution); - std::function compute = [&](double low_prop, double high_prop) { double med_prop = (low_prop + high_prop) / 2; - Point med = edge.at(med_prop, bx, by, bz, resolution); + Point med = edge.at(med_prop, bx, by, bz, resolution), + low = edge.at(low_prop, bx, by, bz, resolution), + high = edge.at(high_prop, bx, by, bz, resolution); if(high_prop - low_prop < 1e-8) return med; - assert(surface(p1) * surface(p2) <= 0); // Can still binary search + double sLow = surface(low), + sMed = surface(med), + sHigh = surface(high); - if(surface(p1) * surface(med) <= 0) + assert(sLow * sHigh <= 0); + // ^ Can still binary search + + if(sLow * sMed <= 0) return compute(low_prop, med_prop); return compute(med_prop, high_prop); }; diff --git a/MarchingCubes.hpp b/MarchingCubes.hpp index f47dab5..30076bf 100644 --- a/MarchingCubes.hpp +++ b/MarchingCubes.hpp @@ -116,7 +116,6 @@ class MarchingCubes { double bx, double by, double bz) const; private: - static const std::vector edges_of_intersection[256]; const ImplicitSurface& surface; diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py index db2ccf3..3eed3a6 100644 --- a/tools/gen_marching_cubes_conf.py +++ b/tools/gen_marching_cubes_conf.py @@ -210,7 +210,7 @@ def pretty_print(index): output += PREAMBLE for (case_id, case) in enumerate(index): - output += "\tTriVect({\n" + output += "\tTriVect({{ // == {}\n".format(case_id) for (tri_id, tri) in enumerate(case.triangles): output += '\t\t{}{}'.format( diff --git a/tools/test_marching_cubes_generator.py b/tools/test_marching_cubes_generator.py index 1bb5c20..1a31bb5 100644 --- a/tools/test_marching_cubes_generator.py +++ b/tools/test_marching_cubes_generator.py @@ -50,3 +50,6 @@ def display_case(tri_cube): tri_repr(triangle, subplt) plt.show() + + +index = gen.gen_index(gen.BASE_CASES) From d1d2811978e81e9c6d6460b4a1c00dbcb3b9e997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Mon, 12 Feb 2018 00:56:35 +0100 Subject: [PATCH 36/36] Enhance test for marching cubes --- main_test_sphere.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/main_test_sphere.cpp b/main_test_sphere.cpp index 61fb823..45a2e14 100644 --- a/main_test_sphere.cpp +++ b/main_test_sphere.cpp @@ -12,12 +12,14 @@ int main(int argc, char** argv) { GlutRender& render = GlutRender::get_instance(); render.init(&argc, argv, 640, 480, "Bouncing stuff"); - TestImplicitSphere sph1(Point(2.5, -1, 0), 1); - TestImplicitSphere sph2(Point(-4, 0.5, 0), 1.4); + TestImplicitSphere sph1(Point(0, 0, 0), 1); + TestImplicitSphere sph2(Point(0, 0, 0), 1.6); Mesh m_sph1 = MarchingCubes(sph1, - Cuboid(Point(1, -2, -2), Point(3, 0, 2)))(); + Cuboid(Point(-2.2, -2.2, -2.2), Point(2.2, 2.2, 2.2)))(); Mesh m_sph2 = MarchingCubes(sph2, - Cuboid(Point(-6, -2, -2), Point(2, 3, 2)))(); + Cuboid(Point(-2.2, -2.2, -2.2), Point(2.2, 2.2, 2.2)))(); + m_sph1.translate(Point(-2, 0, 0)); + m_sph2.translate(Point(2, 0, 1)); render.add_mesh(&m_sph1); render.add_mesh(&m_sph2); @@ -26,6 +28,9 @@ int main(int argc, char** argv) { printf("Sph1 has %ld vertices, %ld faces.\n", m_sph1.get_vertices().size(), m_sph1.get_faces().size()); + printf("Sph2 has %ld vertices, %ld faces.\n", + m_sph2.get_vertices().size(), + m_sph2.get_faces().size()); render.run();