From 88c7377320c397c4c6f388e058f723876c3deb72 Mon Sep 17 00:00:00 2001 From: Daniel Cheung Date: Wed, 20 Mar 2019 09:44:16 +0800 Subject: [PATCH 1/3] Metaball is viewable --- GLUtils.cpp | 28 +++ GLUtils.h | 19 +++ MarchingCube.cpp | 1 + MarchingCube.h | 7 + MarchingCubes.cpp | 68 ++++++++ MarchingCubes.h | 367 ++++++++++++++++++++++++++++++++++++++++ MetaBall.cpp | 111 ++++++++++++ MetaBall.h | 34 ++++ MyModel.cpp | 20 ++- modeler.sln.DotSettings | 2 + modeler.vcxproj | 6 + modeler.vcxproj.filters | 18 ++ modelerdraw.cpp | 36 +--- 13 files changed, 685 insertions(+), 32 deletions(-) create mode 100644 GLUtils.cpp create mode 100644 GLUtils.h create mode 100644 MarchingCube.cpp create mode 100644 MarchingCube.h create mode 100644 MarchingCubes.cpp create mode 100644 MarchingCubes.h create mode 100644 MetaBall.cpp create mode 100644 MetaBall.h create mode 100644 modeler.sln.DotSettings diff --git a/GLUtils.cpp b/GLUtils.cpp new file mode 100644 index 0000000..5e70d54 --- /dev/null +++ b/GLUtils.cpp @@ -0,0 +1,28 @@ +#include "GLUtils.h" + +void GLUtils::ravelTrigPush(std::vector& a, Eigen::Vector3d v) +{ + a.push_back(v[0]); + a.push_back(v[1]); + a.push_back(v[2]); +} + +void GLUtils::trigPush(std::vector& vertices, std::vector& normals, const Triangle& triangle) +{ + static const auto EPSILON = 1.0e-4; + + const auto a = triangle[0]; + const auto b = triangle[1]; + const auto c = triangle[2]; + + auto normal = (a - b).cross(c - b); + if (normal.norm() > EPSILON) { + normal.normalize(); + ravelTrigPush(vertices, c); + ravelTrigPush(vertices, a); + ravelTrigPush(vertices, b); + ravelTrigPush(normals, normal); + ravelTrigPush(normals, normal); + ravelTrigPush(normals, normal); + } +} diff --git a/GLUtils.h b/GLUtils.h new file mode 100644 index 0000000..5061cc1 --- /dev/null +++ b/GLUtils.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include + +class GLUtils +{ +public: + typedef std::array Triangle; + + static void ravelTrigPush(std::vector& a, Eigen::Vector3d v); + + /** + * @param vertices vertices used for GLDrawArray + * @param normals normals used for GLDrawArray + * @param triangle vertices of the triangle in anti-clockwise order, to be inputted into the corresponding vectors + */ + static void trigPush(std::vector& vertices, std::vector& normals, const Triangle& triangle); +}; diff --git a/MarchingCube.cpp b/MarchingCube.cpp new file mode 100644 index 0000000..37e90e2 --- /dev/null +++ b/MarchingCube.cpp @@ -0,0 +1 @@ +#include "MarchingCubes.h" diff --git a/MarchingCube.h b/MarchingCube.h new file mode 100644 index 0000000..03f5421 --- /dev/null +++ b/MarchingCube.h @@ -0,0 +1,7 @@ +#pragma once + +class MarchingCubes +{ +public: + +}; diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp new file mode 100644 index 0000000..65d7b4f --- /dev/null +++ b/MarchingCubes.cpp @@ -0,0 +1,68 @@ +#include "MarchingCubes.h" +#include + +void MarchingCubes::polygonizeDraw(const Grid& grid, const double threshold) +{ + static const auto EPSILON = 1.0e-4; + + std::array verticesOnEdges; + + unsigned int cubeIndex = 0; + if (grid.isoLevels[0] < threshold) cubeIndex |= 1u; + if (grid.isoLevels[1] < threshold) cubeIndex |= 2u; + if (grid.isoLevels[2] < threshold) cubeIndex |= 4u; + if (grid.isoLevels[3] < threshold) cubeIndex |= 8u; + if (grid.isoLevels[4] < threshold) cubeIndex |= 16u; + if (grid.isoLevels[5] < threshold) cubeIndex |= 32u; + if (grid.isoLevels[6] < threshold) cubeIndex |= 64u; + if (grid.isoLevels[7] < threshold) cubeIndex |= 128u; + + if (EDGE_TABLE[cubeIndex] == 0) return; + if (EDGE_TABLE[cubeIndex] & 1u) verticesOnEdges[0] = interpolate(threshold, grid, 0, 1); + if (EDGE_TABLE[cubeIndex] & 2u) verticesOnEdges[1] = interpolate(threshold, grid, 1, 2); + if (EDGE_TABLE[cubeIndex] & 4u) verticesOnEdges[2] = interpolate(threshold, grid, 2, 3); + if (EDGE_TABLE[cubeIndex] & 8u) verticesOnEdges[3] = interpolate(threshold, grid, 3, 0); + if (EDGE_TABLE[cubeIndex] & 16u) verticesOnEdges[4] = interpolate(threshold, grid, 4, 5); + if (EDGE_TABLE[cubeIndex] & 32u) verticesOnEdges[5] = interpolate(threshold, grid, 5, 6); + if (EDGE_TABLE[cubeIndex] & 64u) verticesOnEdges[6] = interpolate(threshold, grid, 6, 7); + if (EDGE_TABLE[cubeIndex] & 128u) verticesOnEdges[7] = interpolate(threshold, grid, 7, 4); + if (EDGE_TABLE[cubeIndex] & 256u) verticesOnEdges[8] = interpolate(threshold, grid, 0, 4); + if (EDGE_TABLE[cubeIndex] & 512u) verticesOnEdges[9] = interpolate(threshold, grid, 1, 5); + if (EDGE_TABLE[cubeIndex] & 1024u) verticesOnEdges[10] = interpolate(threshold, grid, 2, 6); + if (EDGE_TABLE[cubeIndex] & 2048u) verticesOnEdges[11] = interpolate(threshold, grid, 3, 7); + + for (auto i = 0; TRI_TABLE[cubeIndex][i] != -1; i+= 3) + { + + const auto a = verticesOnEdges[TRI_TABLE[cubeIndex][i]]; + const auto b = verticesOnEdges[TRI_TABLE[cubeIndex][i + 1]]; + const auto c = verticesOnEdges[TRI_TABLE[cubeIndex][i + 2]]; + + auto normal = (a - b).cross(c - b); + if (normal.norm() > EPSILON) { + normal.normalize(); + glBegin(GL_TRIANGLES); + glNormal3d(normal.x(), normal.y(), normal.z()); + glVertex3d(a.x(), a.y(), a.z()); + glVertex3d(b.x(), b.y(), b.z()); + glVertex3d(c.x(), c.y(), c.z()); + glEnd(); + } + } +} + +Eigen::Vector3d MarchingCubes::interpolate(const double threshold, const Grid& grid, const int i, const int j) +{ + static const auto EPSILON = 1.0e-5; + auto& i1 = grid.isoLevels[i]; + auto& i2 = grid.isoLevels[j]; + auto& p1 = grid.cornerPositions[i]; + auto& p2 = grid.cornerPositions[j]; + + + if (abs(threshold - i1) < EPSILON) return p1; + if (abs(threshold - i2) < EPSILON) return p2; + if (abs(i1 - i2) < EPSILON) return p1; + + return p1 + (threshold - i1) / (i2 - i1) * (p2 - p1); +} \ No newline at end of file diff --git a/MarchingCubes.h b/MarchingCubes.h new file mode 100644 index 0000000..0e2f117 --- /dev/null +++ b/MarchingCubes.h @@ -0,0 +1,367 @@ +#pragma once +#include +#include +#include "GLUtils.h" + +/** + * Constants acquired from https://web.archive.org/web/20070923102851/http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ + * + * + * Vertices Convention + * + * 4-------------5 + * /| /| + * / | / | + * / | / | + * 7-------------6 | + * | | | | + * | 0---------|---1 + * | / | / + * | / | / + * |/ |/ + * 3-------------2 + * + * + * Edges Convention + * + * +------4------+ + * /| /| + * 7 | 5 | + * / 8 / 9 + * +------6------+ | + * | | | | + * | +------0--|---+ + * 11 / 10 / + * | 3 | 1 + * |/ |/ + * +------2------+ + * + * + * For the TRI_TABLE, all entries are edge numbers that has an intersection. + * Groups of 3 edges forms triangles. The maximum number of triangles is 4. + * i.e. the 8th element creates the following. + * + * +-------------+ + * /| /| + * / | / | + * / | / | + * +-------------+ | + * | | | | Notice the triangle created from edges {3, 11, 2} + * | +---------|---+ Vertex #3 would be the only vertex whose iso-level is >= 1 + * ++++ | / + * | +++ | / + * |/ ++ |/ + * O-----+-------+ + */ + +class MarchingCubes +{ +public: + struct Grid + { + std::array cornerPositions; + std::array isoLevels{}; + }; + + static void polygonizeDraw(const Grid& grid, double threshold); + + static inline Eigen::Vector3d interpolate(double threshold, const Grid& grid, int i, int j); + + + inline static const int EDGE_TABLE[256] = { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + + inline static const int TRI_TABLE[256][16] = + { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; + + +}; diff --git a/MetaBall.cpp b/MetaBall.cpp new file mode 100644 index 0000000..1f1dda3 --- /dev/null +++ b/MetaBall.cpp @@ -0,0 +1,111 @@ +#include "MetaBall.h" +#include +#include +#include "MyModel.h" +#include +#include "GLUtils.h" + +double MetaBall::getIsoLevel(Vector3d pos) +{ + return std::transform_reduce( + std::execution::par_unseq, + spheres.begin(), + spheres.end(), + 0.0, + [](const double a, const double b) {return a + b;}, + [&](const Sphere& sphere) + { + return pow(sphere.r, 3) / (pow(pos.x() - sphere.p.x(), 2) + pow(pos.y() - sphere.p.y(), 2) + pow(pos.z() - sphere.p.z(), 2)); + } + ); +} + +void MetaBall::initializeGrids() +{ + const auto gs = getGridSize(); + const auto xSteps = unsigned int((DOMAIN_X_MAX - DOMAIN_X_MIN) / gs); + const auto ySteps = unsigned int((DOMAIN_Y_MAX - DOMAIN_Y_MIN) / gs); + const auto zSteps = unsigned int((DOMAIN_Z_MAX - DOMAIN_Z_MIN) / gs); + + if (grids.size() == xSteps * ySteps * zSteps) return; + grids.clear(); + + for (auto zi = 0u; zi < zSteps; zi++) + { + auto z = DOMAIN_Z_MIN + zi * gs; + for (auto yi = 0u; yi < ySteps; yi++) + { + auto y = DOMAIN_Y_MIN + yi * gs; + for (auto xi = 0u; xi < xSteps; xi++) + { + auto x = DOMAIN_X_MIN + xi * gs; + grids.push_back(MarchingCubes::Grid{ + { + Vector3d(x, y, z + gs), + Vector3d(x + gs, y, z + gs), + Vector3d(x + gs, y, z), + Vector3d(x, y, z), + Vector3d(x, y + gs, z + gs), + Vector3d(x + gs, y + gs, z + gs), + Vector3d(x + gs, y + gs, z), + Vector3d(x, y + gs, z) + }, + {} + }); + } + } + } +} + +void MetaBall::drawMetaBalls() +{ + initializeGrids(); + std::for_each( + std::execution::seq, + grids.begin(), + grids.end(), + [&](MarchingCubes::Grid& grid) + { + for (auto i = 0u; i < 8; i++) + { + grid.isoLevels[i] = getIsoLevel(grid.cornerPositions[i]); + } + + MarchingCubes::polygonizeDraw(grid, threshold); + } + ); +} + +double MetaBall::getGridSize() +{ + double gridSize; + switch (ModelerDrawState::Instance()->m_quality) + { + case HIGH: + gridSize = 0.5; + break; + case MEDIUM: + default: + gridSize = 1; + break; + case LOW: + gridSize = 2; + break; + case POOR: + gridSize = 3; + break; + } + + return gridSize; +} + +MetaBall* MetaBall::getInstancePtr() +{ + if (instancePtr == nullptr) + { + instancePtr = new MetaBall(); + instancePtr->initializeGrids(); + } + + return instancePtr; +} diff --git a/MetaBall.h b/MetaBall.h new file mode 100644 index 0000000..d1f70db --- /dev/null +++ b/MetaBall.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include "MarchingCubes.h" + +class MetaBall +{ +public: + struct Sphere + { + Eigen::Vector3d p; + GLdouble r; + }; + + inline static const double DOMAIN_X_MIN = -5.0; + inline static const double DOMAIN_X_MAX = 5.0; + inline static const double DOMAIN_Y_MIN = 0.0; + inline static const double DOMAIN_Y_MAX = 10.0; + inline static const double DOMAIN_Z_MIN = -5.0; + inline static const double DOMAIN_Z_MAX = 5.0; + + inline static double threshold = 1.0; + inline static MetaBall* instancePtr = nullptr; + + static double getGridSize(); + static MetaBall* getInstancePtr(); + + std::vector grids; + std::vector spheres; + + double getIsoLevel(Eigen::Vector3d pos); + void initializeGrids(); + void drawMetaBalls(); +}; diff --git a/MyModel.cpp b/MyModel.cpp index ad07e4e..161dd3d 100644 --- a/MyModel.cpp +++ b/MyModel.cpp @@ -2,6 +2,7 @@ #include "modelerapp.h" #include #include "IkSolver.h" +#include "MetaBall.h" extern double rad(double deg); @@ -151,8 +152,8 @@ void MyModel::draw() setDiffuseColor(0.5f, 0.9f, 0.9f); glTranslated(-4, 0.001, -1); drawLathe({ {0, 0.96}, {0.5, 1}, {0.84, 1.3}, {0.83, 1.84}, {0.5, 2.6}, {0.6, 2.6}, {0.94, 1.84}, {0.94, 1.25}, {0.54, 0.91}, {0.1, 0.86}, {0.1, 0.1}, {1, 0}, {0,0} }); //wine glass - setDiffuseColor(0.4f, 0.f, 0.f); - drawLathe({ {0, 0.96}, {0.5, 1}, {0.84, 1.3}, {0.83, 1.84}, {0, 1.84}}); + setDiffuseColor(0.2f, 0.f, 0.02f); + drawLathe({ {0, 1.84}, {0.83, 1.84}, {0.84, 1.3}, {0.5, 1}, {0, 0.96} }); //wine glPopMatrix(); if (VAL(SHOWPRISM)) { @@ -217,6 +218,21 @@ void MyModel::draw() glPopMatrix(); + + // meta ball + auto* metaBallPtr = MetaBall::getInstancePtr(); + if (metaBallPtr->spheres.empty()) + { + metaBallPtr->spheres.push_back({ + {0, 5, 0}, + 2 + }); + metaBallPtr->spheres.push_back({ + {0, 8, 0}, + 1 + }); + } + metaBallPtr->drawMetaBalls(); } void MyModel::drawArm(int levels, int curDept) diff --git a/modeler.sln.DotSettings b/modeler.sln.DotSettings new file mode 100644 index 0000000..02831a9 --- /dev/null +++ b/modeler.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/modeler.vcxproj b/modeler.vcxproj index 09262ad..e338128 100644 --- a/modeler.vcxproj +++ b/modeler.vcxproj @@ -158,7 +158,10 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -197,8 +200,11 @@ + + + diff --git a/modeler.vcxproj.filters b/modeler.vcxproj.filters index 0296673..6b44751 100644 --- a/modeler.vcxproj.filters +++ b/modeler.vcxproj.filters @@ -51,6 +51,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -95,5 +104,14 @@ Header Files + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/modelerdraw.cpp b/modelerdraw.cpp index 4ca66e6..fce6cdb 100644 --- a/modelerdraw.cpp +++ b/modelerdraw.cpp @@ -4,6 +4,7 @@ #include #include #include "bitmap.h" +#include "GLUtils.h" int textureCheckerW, textureCheckerH; @@ -458,15 +459,6 @@ void drawLathe(const std::vector>& xyPositions) std::vector glVertices; // array of triangles with groups of 3 numbers defining x, y, z positions std::vector glNormals; // array of triangles with groups of 3 numbers defining x, y, z normals - static const auto EPSILON = 1.0e-4; - - const auto ravelPush = [](std::vector& a, Vector3d v) - { - a.push_back(v[0]); - a.push_back(v[1]); - a.push_back(v[2]); - }; - for (auto i = 1u; i < bands.size(); i++) { for (auto d = 1u; d <= divisions; d++) @@ -476,29 +468,13 @@ void drawLathe(const std::vector>& xyPositions) const auto bl = bands[i][d - 1]; const auto br = bands[i][d]; - // A - auto normalA = (tr - bl).cross(tl - bl); - if (normalA.norm() > EPSILON) { - normalA.normalize(); - ravelPush(glVertices, tl); - ravelPush(glVertices, tr); - ravelPush(glVertices, bl); - ravelPush(glNormals, normalA); - ravelPush(glNormals, normalA); - ravelPush(glNormals, normalA); - } + GLUtils::Triangle trigA = { tl, tr, bl }; + GLUtils::Triangle trigB = { tr, br, bl }; + // A + GLUtils::trigPush(glVertices, glNormals, trigA); // B - auto normalB = (br - bl).cross(tr - bl); - if (normalB.norm() > EPSILON) { - normalB.normalize(); - ravelPush(glVertices, tr); - ravelPush(glVertices, br); - ravelPush(glVertices, bl); - ravelPush(glNormals, normalB); - ravelPush(glNormals, normalB); - ravelPush(glNormals, normalB); - } + GLUtils::trigPush(glVertices, glNormals, trigB); } } From 49ab8b73e1465f98f2d0cac85f1a5fa25a7af1c2 Mon Sep 17 00:00:00 2001 From: Daniel Cheung Date: Wed, 20 Mar 2019 10:18:00 +0800 Subject: [PATCH 2/3] Metaball arm --- MarchingCubes.cpp | 2 +- MetaBall.cpp | 20 ++++++++++---------- MetaBall.h | 12 ++++++------ MyModel.cpp | 43 ++++++++++++++++++++++++++----------------- main.cpp | 1 + modelerglobals.h | 2 +- 6 files changed, 45 insertions(+), 35 deletions(-) diff --git a/MarchingCubes.cpp b/MarchingCubes.cpp index 65d7b4f..0419a0c 100644 --- a/MarchingCubes.cpp +++ b/MarchingCubes.cpp @@ -38,7 +38,7 @@ void MarchingCubes::polygonizeDraw(const Grid& grid, const double threshold) const auto b = verticesOnEdges[TRI_TABLE[cubeIndex][i + 1]]; const auto c = verticesOnEdges[TRI_TABLE[cubeIndex][i + 2]]; - auto normal = (a - b).cross(c - b); + auto normal = (a - b).cross(b - c); if (normal.norm() > EPSILON) { normal.normalize(); glBegin(GL_TRIANGLES); diff --git a/MetaBall.cpp b/MetaBall.cpp index 1f1dda3..89dbf4f 100644 --- a/MetaBall.cpp +++ b/MetaBall.cpp @@ -23,22 +23,22 @@ double MetaBall::getIsoLevel(Vector3d pos) void MetaBall::initializeGrids() { const auto gs = getGridSize(); - const auto xSteps = unsigned int((DOMAIN_X_MAX - DOMAIN_X_MIN) / gs); - const auto ySteps = unsigned int((DOMAIN_Y_MAX - DOMAIN_Y_MIN) / gs); - const auto zSteps = unsigned int((DOMAIN_Z_MAX - DOMAIN_Z_MIN) / gs); + const auto xSteps = unsigned int((domainXMax - domainXMin) / gs); + const auto ySteps = unsigned int((domainYMax - domainYMin) / gs); + const auto zSteps = unsigned int((domainZMax - domainZMin) / gs); if (grids.size() == xSteps * ySteps * zSteps) return; grids.clear(); for (auto zi = 0u; zi < zSteps; zi++) { - auto z = DOMAIN_Z_MIN + zi * gs; + auto z = domainZMin + zi * gs; for (auto yi = 0u; yi < ySteps; yi++) { - auto y = DOMAIN_Y_MIN + yi * gs; + auto y = domainYMin + yi * gs; for (auto xi = 0u; xi < xSteps; xi++) { - auto x = DOMAIN_X_MIN + xi * gs; + auto x = domainXMin + xi * gs; grids.push_back(MarchingCubes::Grid{ { Vector3d(x, y, z + gs), @@ -82,17 +82,17 @@ double MetaBall::getGridSize() switch (ModelerDrawState::Instance()->m_quality) { case HIGH: - gridSize = 0.5; + gridSize = 0.25; break; case MEDIUM: default: - gridSize = 1; + gridSize = 0.5; break; case LOW: - gridSize = 2; + gridSize = 1; break; case POOR: - gridSize = 3; + gridSize = 2; break; } diff --git a/MetaBall.h b/MetaBall.h index d1f70db..c0e91cd 100644 --- a/MetaBall.h +++ b/MetaBall.h @@ -12,12 +12,12 @@ class MetaBall GLdouble r; }; - inline static const double DOMAIN_X_MIN = -5.0; - inline static const double DOMAIN_X_MAX = 5.0; - inline static const double DOMAIN_Y_MIN = 0.0; - inline static const double DOMAIN_Y_MAX = 10.0; - inline static const double DOMAIN_Z_MIN = -5.0; - inline static const double DOMAIN_Z_MAX = 5.0; + double domainXMin = -5.0; + double domainXMax = 5.0; + double domainYMin = 0.0; + double domainYMax = 10.0; + double domainZMin = -5.0; + double domainZMax = 5.0; inline static double threshold = 1.0; inline static MetaBall* instancePtr = nullptr; diff --git a/MyModel.cpp b/MyModel.cpp index 161dd3d..ae262ea 100644 --- a/MyModel.cpp +++ b/MyModel.cpp @@ -218,21 +218,6 @@ void MyModel::draw() glPopMatrix(); - - // meta ball - auto* metaBallPtr = MetaBall::getInstancePtr(); - if (metaBallPtr->spheres.empty()) - { - metaBallPtr->spheres.push_back({ - {0, 5, 0}, - 2 - }); - metaBallPtr->spheres.push_back({ - {0, 8, 0}, - 1 - }); - } - metaBallPtr->drawMetaBalls(); } void MyModel::drawArm(int levels, int curDept) @@ -247,6 +232,25 @@ void MyModel::drawArm(int levels, int curDept) return; } + static auto* metaBallPtr = new MetaBall(); + metaBallPtr->domainXMin = -3; + metaBallPtr->domainXMax = 3; + metaBallPtr->domainYMin = -3; + metaBallPtr->domainYMax = 5; + metaBallPtr->domainZMin = -3; + metaBallPtr->domainZMax = 3; + if (metaBallPtr->spheres.empty()) + { + metaBallPtr->spheres.push_back({ + {0, 0, 0}, + 1.0 + }); + metaBallPtr->spheres.push_back({ + {0, 2, 0}, + 1.0 + }); + } + glPushMatrix(); // set vertical axis rotation glRotated(VAL(ARM1V + curDept*2)+(mood==1)*moodTick, 0, 1, 0); @@ -256,14 +260,19 @@ void MyModel::drawArm(int levels, int curDept) //vertical arm cylinder glPushMatrix(); - if(VAL(BOXARMS)==0) + if(VAL(BOXARMS)==0 && VAL(METABALL) == 0) { glRotated(-90, 1, 0, 0); drawCylinder(2, 1, 1); - }else + } + else if (VAL(METABALL) == 0) { glTranslated(-1, 0, -0.5); drawBox(2, 2, 1); + } else + { + //Meta-ball + metaBallPtr->drawMetaBalls(); } glPopMatrix(); diff --git a/main.cpp b/main.cpp index f2c82ca..762bab2 100644 --- a/main.cpp +++ b/main.cpp @@ -64,6 +64,7 @@ int main() controls[LDECAYLENGTH] = ModelerControl("Lsystem: decay length", 0.01, 10, 0.01, 1); controls[BALLJOINTS] = ModelerControl("Ball joints", 0, 1,1, 1); controls[BOXARMS] = ModelerControl("Box Arms", 0, 1,1, 0); + controls[METABALL] = ModelerControl("Meta Ball Arms", 0, 1,1, 0); controls[IKENABLE] = ModelerControl("IK: enable", 0, 1,1, 0); controls[IKX] = ModelerControl("IK: X", -10, 10,0.01, 3); controls[IKY] = ModelerControl("IK: Y", -10, 10,0.01, 3); diff --git a/modelerglobals.h b/modelerglobals.h index 1a0350e..2b5b414 100644 --- a/modelerglobals.h +++ b/modelerglobals.h @@ -18,7 +18,7 @@ enum SampleModelControls LIGHTX, LIGHTY, LIGHTZ, LIGHT_3_PT, LEVELDETAILS, ENABLEANIM, CYCLINGMOOD, LDISP, LDEPTH, LANGLE, LLEN, LTHICKNESS, LDECAYTHICKNESS, LDECAYLENGTH, - BALLJOINTS, BOXARMS, + BALLJOINTS, BOXARMS, METABALL, IKENABLE, IKX, IKY, IKZ, IKMIN, IKMAX, SHOWPRISM, NUMCONTROLS }; From 1ccc31401c4590be0c28fe7b9bff84c5778e7f08 Mon Sep 17 00:00:00 2001 From: Daniel Cheung Date: Wed, 20 Mar 2019 10:22:17 +0800 Subject: [PATCH 3/3] Updated readme --- readme.md | 4 +++- screenshot.png | Bin 0 -> 61614 bytes 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 screenshot.png diff --git a/readme.md b/readme.md index 4c3ad47..e08faa5 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,8 @@ Programming project 2 of HKUST Computer Graphics course COMP4411 +![Screenshot](screenshot.png) + ## Basic Requirements - [x] Own character (10 primitive shapes, 4 level of hierarchy) @@ -33,7 +35,7 @@ B: Bell, W: Whistle, 1B=2W 4. Others base on coolness - [x] (2B) procedural modelling with parameter control [L-system](https://en.wikipedia.org/wiki/L-system) - [x] (2B) mood cycling: the model have mood reaction based on UI control -- [ ] (4B) Organic shape [metaballs](http://en.wikipedia.org/wiki/Metaballs) +- [x] (4B) Organic shape [metaballs](http://en.wikipedia.org/wiki/Metaballs) - [x] *(8B)* goal-oriented motion (inverse kinematics [here](https://course.cse.ust.hk/comp4411/Password_Only/projects/modeler/inverse-kinematics.pdf)) - [x] *(4B)* constrain on joints diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..9e235bfada43ca73881863420d6d828794d43c32 GIT binary patch literal 61614 zcmeGEg;!f&(>@MUqy!HX_u%eO+_kt%umFKlG&seI2X}{3ytqrz65I&!0vr z=gBvo-ky2tsmP<$O;hhbouE6&Y09CXG$!HPLolAsvD{TnJyB4Iy#5`}zT4CJp`e^9 zs42=B_*owHV7{X@_HTPoU0JAlSv_Icu@TkCKeR;7Cqk|-&s!cz1OUd4WdX$_3Q@3b zhyX=QRz&hRJ%22bVb6Tse`P{1>((=TjQ@U`miW`JbWw{~`8(&Fw(> zH~R>jOv$$~npS#~Ky`YwPE({WJ^5o-I6rKX1TYmRp$6%IO4eBz5Qjh+S)6! z)$R5}dbKCOcTN0hxg!7Jdp@KQNyF)U6242SvKiL&(p+yhf(HF;*qSq36NjZ^#S3%G zk=!CETeBBr!e2ryNAHHUNEgpHX87g@-X_W>H90wXL)Wj~)wcn0!ciyQ`DtUd~(RAJb;rQUO;>xx!A!-YwB-RoH*7NeH@0MYeME!hSW~QQvvDDF@~x z7>@ZIU~HkA{p>lG&1ac{Q)aHGxiAFeJAwUeQrXDlU5oyf_;9|O&LtpjpHAXqZ}8!# zrZ!9eiA?S`rz{!3Ptobq|IL0h63;r>@F|Jh{1yLUXtVP$pm!yTAnlY*?3=}_G1UL4&JhEm7*xmC@-Iphm zBC|}l)_9{aAAxg|0zcvvQ2OJkpb$-=H5zBnFYD_8&I1W{lubYP8+)ZS^`CsX(C~kL zS}=ly&JyBDC;qKm11k$&#fg5HKv>GvuIdnyV>panoRwoYr

}SiAIzbLl@99uUSp^t=xz7()7FHJ}(_0 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