├── converter.parm ├── MinGWStopwatch.h ├── MinGWStopwatch.cpp ├── filler.glsl ├── IStopwatch.h ├── ObjTrianglesLoader.h ├── ITrianglesLoader.h ├── README.md ├── Converter.h ├── kernel.glsl ├── modifier.glsl ├── main.cpp ├── ObjTrianglesLoader.cpp └── Converter.cpp /converter.parm: -------------------------------------------------------------------------------- 1 | Model path:C:\\Users\\Y500\\Documents\\Models\\Buddha_max.obj 2 | Size factor:2.0 3 | SDF path:woman.sdfm 4 | Resolution:0,0,0 5 | Loader type:Obj Loader 6 | Filler value:1000000.0 7 | Delta:1.0 8 | Path to filler shader:filler.glsl 9 | Path to modifier shader:modifier.glsl 10 | Path to kernel shader:kernel.glsl -------------------------------------------------------------------------------- /MinGWStopwatch.h: -------------------------------------------------------------------------------- 1 | #ifndef MINGWSTOPWATCH_H_INCLUDED 2 | #define MINGWSTOPWATCH_H_INCLUDED 3 | 4 | #include 5 | #include "IStopwatch.h" 6 | 7 | namespace conv 8 | { 9 | class MinGWStopwatch : public IStopwatch 10 | { 11 | public: 12 | void start() override; 13 | void lap() override; 14 | private: 15 | timeval timeValue; 16 | }; 17 | } 18 | 19 | #endif // MINGWSTOPWATCH_H_INCLUDED 20 | -------------------------------------------------------------------------------- /MinGWStopwatch.cpp: -------------------------------------------------------------------------------- 1 | #include "MinGWStopwatch.h" 2 | 3 | using namespace conv; 4 | 5 | void MinGWStopwatch::start() 6 | { 7 | gettimeofday(&timeValue, NULL); 8 | previous = (uint64_t)timeValue.tv_sec * 1000 + timeValue.tv_usec / 1000; 9 | } 10 | 11 | void MinGWStopwatch::lap() 12 | { 13 | gettimeofday(&timeValue, NULL); 14 | uint64_t current = (uint64_t)timeValue.tv_sec * 1000 + timeValue.tv_usec / 1000; 15 | laps.push_back(current - previous); 16 | previous = current; 17 | } 18 | -------------------------------------------------------------------------------- /filler.glsl: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 4 | 5 | uniform float filler; 6 | 7 | layout (std430, binding = 2) buffer SDFBuffer 8 | { 9 | vec4 SDF[]; 10 | }; 11 | 12 | void main() 13 | { 14 | uint index = gl_GlobalInvocationID.x * gl_NumWorkGroups.y * gl_NumWorkGroups.z + 15 | gl_GlobalInvocationID.y * gl_NumWorkGroups.z + 16 | gl_GlobalInvocationID.z; 17 | SDF[index] = vec4(filler); 18 | memoryBarrierBuffer(); 19 | } -------------------------------------------------------------------------------- /IStopwatch.h: -------------------------------------------------------------------------------- 1 | #ifndef ISTOPWATCH_H_INCLUDED 2 | #define ISTOPWATCH_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | namespace conv 8 | { 9 | class IStopwatch 10 | { 11 | public: 12 | IStopwatch() {}; 13 | virtual void start() = 0; 14 | virtual void lap() = 0; 15 | std::vector getLaps() {return laps;}; 16 | virtual ~IStopwatch() {}; 17 | protected: 18 | uint64_t previous = 0; 19 | std::vector laps; 20 | }; 21 | } 22 | 23 | #endif // ISTOPWATCH_H_INCLUDED 24 | -------------------------------------------------------------------------------- /ObjTrianglesLoader.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJTRIANGLESLOADER_H_INCLUDED 2 | #define OBJTRIANGLESLOADER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include "ITrianglesLoader.h" 8 | 9 | namespace conv 10 | { 11 | class ObjTrianglesLoader : public ITrianglesLoader 12 | { 13 | public: 14 | ObjTrianglesLoader() : ITrianglesLoader() {}; 15 | ObjTrianglesLoader(const std::string &path); 16 | void load() override; 17 | std::vector getTriangles() override; 18 | private: 19 | bool isVertex(const std::string &line); 20 | bool isNormal(const std::string &line); 21 | bool isTexCoord(const std::string &line); 22 | bool isFace(const std::string &line); 23 | std::tuple getIndices(const std::string &line); 24 | glm::vec3 parse(const std::string &line); 25 | 26 | std::ifstream objFile; 27 | std::vector vertices; 28 | std::vector normals; 29 | std::vector texCoord; 30 | std::vector triangles; 31 | }; 32 | } 33 | 34 | #endif // OBJTRIANGLESLOADER_H_INCLUDED 35 | -------------------------------------------------------------------------------- /ITrianglesLoader.h: -------------------------------------------------------------------------------- 1 | #ifndef ITRIANGLESLOADER_H_INCLUDED 2 | #define ITRIANGLESLOADER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include "../glm/glm.hpp" 7 | 8 | namespace conv 9 | { 10 | struct ModelTriangle 11 | { 12 | glm::vec4 firstVertex{0.0f}; 13 | glm::vec4 secondVertex{0.0f}; 14 | glm::vec4 thirdVertex{0.0f}; 15 | glm::vec4 faceNormal{0.0f}; 16 | }; 17 | 18 | class ITrianglesLoader 19 | { 20 | public: 21 | ITrianglesLoader() {}; 22 | ITrianglesLoader(const std::string &path) {filepath = path;}; 23 | virtual void load() = 0; 24 | virtual std::vector getTriangles() = 0; 25 | virtual ~ITrianglesLoader() {}; 26 | bool isTrianglesRead() {return trianglesRead;}; 27 | void setFilename(const std::string &filename) {filepath = filename;}; 28 | glm::vec3 getAABBMin() {return min;}; 29 | glm::vec3 getAABBMax() {return max;}; 30 | protected: 31 | std::string filepath = ""; 32 | bool trianglesRead = false; 33 | glm::vec3 min{0.0f}; 34 | glm::vec3 max{0.0f}; 35 | }; 36 | } 37 | 38 | #endif // ITRIANGLESLOADER_H_INCLUDED 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MeshToSDF Converter 2 | 3 | This is a simple console application, which allows to convert model with triangular mesh into signed distance field (SDF) representation. Based on algorithm from "Distance Fields for Rapid Collision Detection in Physically Based Modeling" paper, so only two restrictions for supported models: 4 | * Triangular mesh (as mentioned above) 5 | * Must be specified vertex normals 6 | 7 | Application use OpenGL's compute shaders for implementing this algorithm on GPU (and freeGLUT for initializing GL context). 8 | 9 | Parameters for converter can be speicified by converter.parm file (example of which in repo). Some words about it's struct: 10 | * **Model path** Path to the model, which should be converted 11 | * **Size factor** Determining max allowed size for SSBOs, which used by app, by this formula: *MAX_SSBO_SIZE for implementation / Size factor*. Recomended value - 2.0. 12 | * **SDF path** Path to file, in which SDF will be saved. 13 | * **Resolution** Resolution of distance field. 0,0,0 - special value, which means maximum allowed resolution for current system (resolution for the axis depends on model's size). 14 | * **Loader type** Type of the loader, which used for loading model from file. At this moment support only *Obj Loader*. 15 | * **Filler value** Value, which used to fill output buffer before SDF generation. Recomended value - 1000000.0. 16 | * **Delta** Algorithm's parameter, which determine "thickness" of distance envelope (see paper for more information). 17 | * **Path to filler shader** Path to filler.glsl 18 | * **Path to modifier shader** Path to modifier.glsl 19 | * **Path to kernel shader** Path to kernel.glsl 20 | 21 | **Limitations:** 22 | * Max resolution of SDF depends on size of GPU memory and size factor value. 23 | * Delta and time, spent on convertation, has exponential dependency. 24 | * At this moment supported only text version of Wavefront OBJ format as input. 25 | 26 | **Format of output binary file:** 27 | First 4 bytes - amount of elements in file (amount of pairs "point - distance" - amount of elements / 4), then follows float values (4 bytes) in this order: first point x, first point y, first point z, first point distance, second point x, ... 28 | 29 | Source code of this application can be used without any limitations, provided "as is" and without any warranties. 30 | -------------------------------------------------------------------------------- /Converter.h: -------------------------------------------------------------------------------- 1 | #ifndef CONVERTER_H_INCLUDED 2 | #define CONVERTER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../glm/glm.hpp" 8 | #include "classes/ShaderProgram.h" 9 | #include "classes/GLBuffer.h" 10 | #include "ITrianglesLoader.h" 11 | #include "IStopwatch.h" 12 | 13 | namespace conv 14 | { 15 | struct Initializer 16 | { 17 | GLfloat sizeFactor = 2.0f; 18 | std::string SDFfilename = ""; 19 | glm::uvec3 resolution{0}; 20 | std::shared_ptr trianglesLoader; 21 | std::shared_ptr stopwatch; 22 | GLfloat fillerValue = 0.0f; 23 | GLfloat delta = 0.0f; 24 | std::string fillerPath = ""; 25 | std::string modifierPath = ""; 26 | std::string kernelPath = ""; 27 | }; 28 | 29 | class Converter 30 | { 31 | public: 32 | Converter(); 33 | Converter(const std::ostream &inLogStream); 34 | ~Converter(); 35 | void initialize(const Initializer &initializer); 36 | void compute(); 37 | glm::uvec3 getResolution(); 38 | private: 39 | void writeFile(); 40 | glm::uvec3 computeGroups(const uint32_t &inTrianglesAmount); 41 | glm::uvec3 maxResolution(); 42 | 43 | bool unconstructed = true; 44 | bool uninitialized = true; 45 | 46 | GLBuffer triangles{GL_SHADER_STORAGE_BUFFER, std::cout}; 47 | GLBuffer prismAABBs{GL_SHADER_STORAGE_BUFFER, std::cout}; 48 | GLBuffer sdf{GL_SHADER_STORAGE_BUFFER, std::cout}; 49 | 50 | ShaderProgram filler{std::cout}; 51 | ShaderProgram modifier{std::cout}; 52 | ShaderProgram kernel{std::cout}; 53 | 54 | FILE* sdfFile = nullptr; 55 | uint32_t SDFSize = 0; 56 | std::ostream logStream{std::cout.rdbuf()}; 57 | Initializer parameters; 58 | 59 | uint32_t MAX_ALLOWED_SSBO_SIZE = 0; 60 | uint32_t MAX_TRIANGLES_SSBO_SIZE = 0; 61 | uint32_t MAX_PRISM_AABBS_SSBO_SIZE = 0; 62 | uint32_t MAX_SDF_SSBO_SIZE = 0; 63 | uint16_t MAX_WORK_GROUP_COUNT = 0; 64 | uint16_t TRIANGLE_SIZE = sizeof(ModelTriangle); 65 | uint16_t PRISM_AABB_SIZE = sizeof(glm::vec4); 66 | uint16_t SDF_ELEMENT_SIZE = 4 * sizeof(GLfloat); 67 | }; 68 | } 69 | 70 | #endif // CONVERTER_H_INCLUDED 71 | -------------------------------------------------------------------------------- /kernel.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 4 | uniform uint fullyInRange; 5 | uniform vec3 shellMin; 6 | uniform vec3 step; 7 | uniform uvec3 resolution; 8 | uniform uint maxIndex; 9 | uniform uint aabbAndTriangleIndex; 10 | 11 | struct Triangle 12 | { 13 | vec4 v1; 14 | vec4 v2; 15 | vec4 v3; 16 | vec4 n; 17 | }; 18 | 19 | layout (std140, binding = 0) buffer TrianglesBuffer 20 | { 21 | Triangle Triangles[]; 22 | }; 23 | 24 | layout (std140, binding = 1) buffer PrismAABBBuffer 25 | { 26 | vec4 aabbMin[]; 27 | }; 28 | 29 | layout (std430, binding = 2) coherent buffer SDFBuffer 30 | { 31 | vec4 SDF[]; 32 | }; 33 | 34 | float distanceToEdge(in vec3 P0, in vec3 P1, in vec3 P) 35 | { 36 | vec3 v = P1 - P0; 37 | vec3 w = P - P0; 38 | float c1 = dot(w, v); 39 | float c2 = dot(v, v); 40 | float b = c1 / c2; 41 | return length(P - (P0 + b * v)); 42 | } 43 | 44 | void computeDistance(in vec3 inPoint, in uint inIndex) 45 | { 46 | Triangle triangle = Triangles[aabbAndTriangleIndex]; 47 | float distance = dot(inPoint - triangle.v1.xyz, triangle.n.xyz); 48 | float sgn = sign(distance); 49 | sgn = sgn == 0 ? 1.0 : sgn; 50 | float distToV1 = length(inPoint - triangle.v1.xyz); 51 | float distToV2 = length(inPoint - triangle.v2.xyz); 52 | float distToV3 = length(inPoint - triangle.v3.xyz); 53 | float minDistToVertex = min(distToV1, min(distToV2, distToV3)); 54 | float distToE1 = distanceToEdge(triangle.v1.xyz, triangle.v2.xyz, inPoint); 55 | float distToE2 = distanceToEdge(triangle.v2.xyz, triangle.v3.xyz, inPoint); 56 | float distToE3 = distanceToEdge(triangle.v1.xyz, triangle.v3.xyz, inPoint); 57 | float minDistToEdge = min(distToE1, min(distToE2, distToE3)); 58 | distance = min(abs(distance), min(minDistToVertex, minDistToEdge)) * sgn; 59 | float distValue = SDF[inIndex].w; 60 | SDF[inIndex] = vec4(inPoint.x, inPoint.y, inPoint.z, min(abs(distance), abs(distValue))); 61 | memoryBarrierBuffer(); 62 | } 63 | 64 | void main() 65 | { 66 | vec3 point = vec3(aabbMin[aabbAndTriangleIndex]) + vec3(step.x * gl_WorkGroupID.x, 67 | step.y * gl_WorkGroupID.y, 68 | step.z * gl_WorkGroupID.z); 69 | uint index = uint((point.x - shellMin.x) / step.x) * resolution.y * resolution.z + 70 | uint((point.y - shellMin.y) / step.y) * resolution.z + 71 | uint((point.z - shellMin.z) / step.z); 72 | if(fullyInRange == 1) 73 | computeDistance(point, index); 74 | else 75 | { 76 | if(index <= maxIndex) 77 | computeDistance(point, index); 78 | } 79 | } -------------------------------------------------------------------------------- /modifier.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 4 | uniform vec3 shellMin; 5 | uniform vec3 step; 6 | uniform uvec3 resolution; 7 | uniform float epsilon; 8 | 9 | struct Triangle 10 | { 11 | vec4 v1; 12 | vec4 v2; 13 | vec4 v3; 14 | vec4 n; 15 | }; 16 | 17 | layout (std430, binding = 0) buffer TrianglesBuffer 18 | { 19 | Triangle Triangles[]; 20 | }; 21 | 22 | layout (std430, binding = 1) buffer PrismAABBBuffer 23 | { 24 | vec4 aabbMin[]; 25 | }; 26 | 27 | void constructPrismAABB(in Triangle t, out vec3 AABBmin, out vec3 AABBmax) 28 | { 29 | vec3 displacement = epsilon * t.n.xyz; 30 | vec3 minPoint, maxPoint; 31 | minPoint.x = min(t.v1.x, min(t.v2.x, t.v3.x)); 32 | minPoint.y = min(t.v1.y, min(t.v2.y, t.v3.y)); 33 | minPoint.z = min(t.v1.z, min(t.v2.z, t.v3.z)); 34 | maxPoint.x = max(t.v1.x, max(t.v2.x, t.v3.x)); 35 | maxPoint.y = max(t.v1.y, max(t.v2.y, t.v3.y)); 36 | maxPoint.z = max(t.v1.z, max(t.v2.z, t.v3.z)); 37 | 38 | vec3 plusFaceMin = minPoint + displacement; 39 | vec3 plusFaceMax = maxPoint + displacement; 40 | vec3 minusFaceMin = minPoint - displacement; 41 | vec3 minusFaceMax = maxPoint - displacement; 42 | 43 | AABBmin = min(plusFaceMin, minusFaceMin); 44 | AABBmax = max(plusFaceMax, minusFaceMax); 45 | } 46 | 47 | void main() 48 | { 49 | uint index = gl_WorkGroupID.x * gl_NumWorkGroups.y * gl_NumWorkGroups.z + 50 | gl_WorkGroupID.y * gl_NumWorkGroups.z + 51 | gl_WorkGroupID.z; 52 | Triangle triangle = Triangles[index]; 53 | vec3 prismAABBmin = vec3(0, 0, 0); 54 | vec3 prismAABBmax = vec3(0, 0, 0); 55 | constructPrismAABB(triangle, prismAABBmin, prismAABBmax); 56 | prismAABBmin = vec3(ceil((prismAABBmin.x - shellMin.x) / step.x) * step.x + shellMin.x, 57 | ceil((prismAABBmin.y - shellMin.y) / step.y) * step.y + shellMin.y, 58 | ceil((prismAABBmin.z - shellMin.z) / step.z) * step.z + shellMin.z); 59 | prismAABBmax = vec3(floor((prismAABBmax.x - shellMin.x) / step.x) * step.x + shellMin.x, 60 | floor((prismAABBmax.y - shellMin.y) / step.y) * step.y + shellMin.y, 61 | floor((prismAABBmax.z - shellMin.z) / step.z) * step.z + shellMin.z); 62 | triangle.v1.w = abs(floor((prismAABBmax.x - shellMin.x) / step.x) - 63 | ceil((prismAABBmin.x - shellMin.x) / step.x)); 64 | triangle.v2.w = abs(floor((prismAABBmax.y - shellMin.y) / step.y) - 65 | ceil((prismAABBmin.y - shellMin.y) / step.y)); 66 | triangle.v3.w = abs(floor((prismAABBmax.z - shellMin.z) / step.z) - 67 | ceil((prismAABBmin.z - shellMin.z) / step.z)); 68 | uint maxIndex = uint((prismAABBmax.x - shellMin.x) / step.x) * resolution.y * resolution.z + 69 | uint((prismAABBmax.y - shellMin.y) / step.y) * resolution.z + 70 | uint((prismAABBmax.z - shellMin.z) / step.z); 71 | aabbMin[index] = vec4(prismAABBmin, 0.0); 72 | triangle.n.w = maxIndex > (resolution.x * resolution.y * resolution.z) ? 1.0 : 0.0; 73 | Triangles[index] = triangle; 74 | memoryBarrierBuffer(); 75 | } -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Converter.h" 9 | #include "ObjTrianglesLoader.h" 10 | #include "MinGWStopwatch.h" 11 | 12 | conv::Initializer parse(const std::string ¶mFilename) 13 | { 14 | std::ifstream parametersFile(paramFilename); 15 | std::string line; 16 | std::map parameters; 17 | parameters["Model path"] = ""; 18 | parameters["Size factor"] = ""; 19 | parameters["SDF path"] = ""; 20 | parameters["Resolution"] = ""; 21 | parameters["Loader type"] = ""; 22 | parameters["Filler value"] = ""; 23 | parameters["Delta"] = ""; 24 | parameters["Path to filler shader"] = ""; 25 | parameters["Path to modifier shader"] = ""; 26 | parameters["Path to kernel shader"] = ""; 27 | char delimeter = ':'; 28 | size_t pos = 0; 29 | char rubbish[2]; 30 | std::stringstream source; 31 | conv::Initializer initializer; 32 | while(std::getline(parametersFile, line)) 33 | { 34 | pos = line.find(delimeter); 35 | if(parameters.count(line.substr(0, pos)) == 0) 36 | { 37 | std::cout << "Unknown key:" << line.substr(0, pos) << std::endl; 38 | parametersFile.close(); 39 | return initializer; 40 | } 41 | else 42 | parameters[line.substr(0, pos)] = line.substr(pos + 1); 43 | } 44 | parametersFile.close(); 45 | initializer.sizeFactor = std::stof(parameters["Size factor"]); 46 | initializer.SDFfilename = parameters["SDF path"]; 47 | source.str(parameters["Resolution"]); 48 | source >> initializer.resolution.x >> rubbish[0] 49 | >> initializer.resolution.y >> rubbish[1] 50 | >> initializer.resolution.z; 51 | if(parameters["Loader type"] == "Obj Loader") 52 | initializer.trianglesLoader.reset(new conv::ObjTrianglesLoader(parameters["Model path"])); 53 | else 54 | return initializer; 55 | initializer.stopwatch.reset(new conv::MinGWStopwatch); 56 | initializer.fillerValue = std::stof(parameters["Filler value"]); 57 | initializer.delta = std::stof(parameters["Delta"]); 58 | initializer.fillerPath = parameters["Path to filler shader"]; 59 | initializer.modifierPath = parameters["Path to modifier shader"]; 60 | initializer.kernelPath = parameters["Path to kernel shader"]; 61 | return initializer; 62 | } 63 | 64 | int main(int argc, char **argv) { 65 | std::ios::sync_with_stdio(false); 66 | 67 | glutInit(&argc, argv); 68 | glutInitDisplayMode(GLUT_RGBA | GLUT_ALPHA | GLUT_DOUBLE); 69 | glutInitWindowSize(1, 1); 70 | glutCreateWindow("Converter"); 71 | glewInit(); 72 | glewExperimental = true; 73 | 74 | conv::Converter converter; 75 | conv::Initializer initializer = parse("converter.parm"); 76 | if(initializer.trianglesLoader.get() == nullptr) 77 | return 1; 78 | converter.initialize(initializer); 79 | converter.compute(); 80 | auto laps = initializer.stopwatch->getLaps(); 81 | for(uint16_t i = 0; i < laps.size(); ++i) 82 | { 83 | switch(i) 84 | { 85 | case 0: 86 | std::cout << "Loading model: " << laps[0] << " milliseconds\n"; 87 | break; 88 | case 1: 89 | std::cout << "Preparing for computations: " << laps[1] << " milliseconds\n"; 90 | break; 91 | case 2: 92 | std::cout << "Computing SDF: " << laps[2] << " milliseconds\n"; 93 | break; 94 | case 3: 95 | std::cout << "Writing SDF to file: " << laps[3] << " milliseconds\n"; 96 | break; 97 | default: 98 | std::cout << "Unknown lap description\n"; 99 | } 100 | } 101 | glm::uvec3 resolution = converter.getResolution(); 102 | std::cout << "Resolution: " << resolution.x << "," << resolution.y << "," << resolution.z << std::endl; 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /ObjTrianglesLoader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ObjTrianglesLoader.h" 3 | 4 | using namespace conv; 5 | 6 | ObjTrianglesLoader::ObjTrianglesLoader(const std::string &path) : ITrianglesLoader(path) 7 | {} 8 | 9 | void ObjTrianglesLoader::load() 10 | { 11 | objFile.open(filepath); 12 | std::string line = ""; 13 | ModelTriangle triangle; 14 | while(std::getline(objFile, line)) 15 | { 16 | if(isVertex(line)) 17 | { 18 | vertices.push_back(glm::vec4(parse(line), 0.0f)); 19 | min.x = std::min(min.x, vertices.back().x); 20 | min.y = std::min(min.y, vertices.back().y); 21 | min.z = std::min(min.z, vertices.back().z); 22 | max.x = std::max(max.x, vertices.back().x); 23 | max.y = std::max(max.y, vertices.back().y); 24 | max.z = std::max(max.z, vertices.back().z); 25 | } 26 | else if(isNormal(line)) 27 | { 28 | normals.push_back(glm::vec4(parse(line), 0.0f)); 29 | } 30 | else if(isTexCoord(line)) 31 | { 32 | texCoord.push_back(parse(line)); 33 | } 34 | else if(isFace(line)) 35 | { 36 | auto indices = getIndices(line); 37 | triangle.firstVertex = vertices[std::get<0>(indices).x - 1]; 38 | triangle.secondVertex = vertices[std::get<1>(indices).x - 1]; 39 | triangle.thirdVertex = vertices[std::get<2>(indices).x - 1]; 40 | glm::vec4 faceNormal = normals[std::get<0>(indices).z - 1]; 41 | faceNormal += normals[std::get<1>(indices).z - 1]; 42 | faceNormal += normals[std::get<2>(indices).z - 1]; 43 | faceNormal /= 3; 44 | triangle.faceNormal = glm::normalize(faceNormal); 45 | triangles.push_back(triangle); 46 | } 47 | } 48 | objFile.close(); 49 | } 50 | 51 | std::vector ObjTrianglesLoader::getTriangles() 52 | { 53 | return triangles; 54 | } 55 | 56 | bool ObjTrianglesLoader::isVertex(const std::string &line) 57 | { 58 | return line[0] == 'v' && line[1] == ' '; 59 | } 60 | 61 | bool ObjTrianglesLoader::isNormal(const std::string &line) 62 | { 63 | return line[0] == 'v' && line[1] == 'n'; 64 | } 65 | 66 | bool ObjTrianglesLoader::isTexCoord(const std::string &line) 67 | { 68 | return line[0] == 'v' && line[1] == 't'; 69 | } 70 | 71 | bool ObjTrianglesLoader::isFace(const std::string &line) 72 | { 73 | return line[0] == 'f'; 74 | } 75 | 76 | std::tuple ObjTrianglesLoader::getIndices(const std::string &line) 77 | { 78 | glm::uvec3 firstPoint(0); 79 | glm::uvec3 secondPoint(0); 80 | glm::uvec3 thirdPoint(0); 81 | std::string value = ""; 82 | std::stringstream source(line.substr(2)); 83 | 84 | std::getline(source, value, '/'); 85 | firstPoint.x = value.empty() ? -1 : std::stoul(value); 86 | std::getline(source, value, '/'); 87 | firstPoint.y = value.empty() ? -1 : std::stoul(value); 88 | std::getline(source, value, ' '); 89 | firstPoint.z = value.empty() ? -1 : std::stoul(value); 90 | 91 | std::getline(source, value, '/'); 92 | secondPoint.x = value.empty() ? -1 : std::stoul(value); 93 | std::getline(source, value, '/'); 94 | secondPoint.y = value.empty() ? -1 : std::stoul(value); 95 | std::getline(source, value, ' '); 96 | secondPoint.z = value.empty() ? -1 : std::stoul(value); 97 | 98 | std::getline(source, value, '/'); 99 | thirdPoint.x = value.empty() ? -1 : std::stoul(value); 100 | std::getline(source, value, '/'); 101 | thirdPoint.y = value.empty() ? -1 : std::stoul(value); 102 | std::getline(source, value); 103 | thirdPoint.z = value.empty() ? -1 : std::stoul(value); 104 | 105 | return std::make_tuple(firstPoint, secondPoint, thirdPoint); 106 | } 107 | 108 | glm::vec3 ObjTrianglesLoader::parse(const std::string &line) 109 | { 110 | glm::vec3 output(0); 111 | std::string value = ""; 112 | std::stringstream source(line.substr(3)); 113 | std::getline(source, value, ' '); 114 | output.x = std::stof(value); 115 | std::getline(source, value, ' '); 116 | output.y = std::stof(value); 117 | std::getline(source, value, ' '); 118 | output.z = std::stof(value); 119 | return output; 120 | } 121 | -------------------------------------------------------------------------------- /Converter.cpp: -------------------------------------------------------------------------------- 1 | #include "Converter.h" 2 | #include 3 | #include 4 | #include 5 | #include "../glm/gtc/type_ptr.inl" 6 | #include "ObjTrianglesLoader.h" 7 | 8 | using namespace conv; 9 | 10 | Converter::Converter() 11 | { 12 | GLint value = 0; 13 | glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &value); 14 | if(value < 3) 15 | { 16 | logStream << "CRITICAL PROBLEM:You has only " << value 17 | << " SSBO binding points, while converter needs at least 3. Terminating\n"; 18 | return; 19 | } 20 | GLenum error = glGetError(); 21 | if(error != GL_NO_ERROR) 22 | { 23 | logStream << "OpenGL error " << error 24 | << "occurred. Terminating\n"; 25 | return; 26 | } 27 | glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &value); 28 | MAX_WORK_GROUP_COUNT = (uint16_t)value; 29 | glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &value); 30 | MAX_ALLOWED_SSBO_SIZE = (uint32_t)value; 31 | unconstructed = false; 32 | } 33 | 34 | Converter::Converter(const std::ostream &inLogStream) : logStream(inLogStream.rdbuf()) 35 | { 36 | GLint value = 0; 37 | glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &value); 38 | if(value < 3) 39 | { 40 | logStream << "CRITICAL PROBLEM:You has only " << value 41 | << " SSBO binding points, while converter needs at least 3. Terminating\n"; 42 | return; 43 | } 44 | GLenum error = glGetError(); 45 | if(error != GL_NO_ERROR) 46 | { 47 | logStream << "OpenGL error " << error 48 | << "occurred. Terminating\n"; 49 | return; 50 | } 51 | glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &value); 52 | MAX_WORK_GROUP_COUNT = (uint16_t)value; 53 | glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &value); 54 | MAX_ALLOWED_SSBO_SIZE = (uint32_t)value; 55 | 56 | triangles.setErrorStream(logStream); 57 | prismAABBs.setErrorStream(logStream); 58 | sdf.setErrorStream(logStream); 59 | filler.setErrorStream(logStream); 60 | modifier.setErrorStream(logStream); 61 | kernel.setErrorStream(logStream); 62 | unconstructed = false; 63 | } 64 | 65 | Converter::~Converter() 66 | { 67 | triangles.deleteBuffer(); 68 | prismAABBs.deleteBuffer(); 69 | sdf.deleteBuffer(); 70 | 71 | filler.deleteProgram(); 72 | modifier.deleteProgram(); 73 | kernel.deleteProgram(); 74 | } 75 | 76 | void Converter::initialize(const Initializer &initializer) 77 | { 78 | if(unconstructed) 79 | return; 80 | parameters = initializer; 81 | if(parameters.sizeFactor <= 1.0f) 82 | { 83 | logStream << "Size factor " << parameters.sizeFactor 84 | << " less than or equal to 1. Set to default value 2\n"; 85 | parameters.sizeFactor = 2.0f; 86 | } 87 | MAX_TRIANGLES_SSBO_SIZE = MAX_ALLOWED_SSBO_SIZE / (parameters.sizeFactor * TRIANGLE_SIZE); 88 | MAX_PRISM_AABBS_SSBO_SIZE = MAX_ALLOWED_SSBO_SIZE / (parameters.sizeFactor * PRISM_AABB_SIZE); 89 | MAX_SDF_SSBO_SIZE = MAX_ALLOWED_SSBO_SIZE / (parameters.sizeFactor * SDF_ELEMENT_SIZE); 90 | if(!filler.loadShaderFromFile(parameters.fillerPath, GL_COMPUTE_SHADER)) 91 | filler.printError(); 92 | if(!filler.link()) 93 | filler.printError(); 94 | if(!modifier.loadShaderFromFile(parameters.modifierPath, GL_COMPUTE_SHADER)) 95 | modifier.printError(); 96 | if(!modifier.link()) 97 | modifier.printError(); 98 | if(!kernel.loadShaderFromFile(parameters.kernelPath, GL_COMPUTE_SHADER)) 99 | kernel.printError(); 100 | if(!kernel.link()) 101 | kernel.printError(); 102 | uninitialized = false; 103 | } 104 | 105 | void Converter::compute() 106 | { 107 | if(uninitialized) 108 | { 109 | logStream << "You must call initialize() before calling compute\n"; 110 | return; 111 | } 112 | if(parameters.stopwatch != nullptr) 113 | parameters.stopwatch->start(); 114 | if(parameters.trianglesLoader == nullptr) 115 | { 116 | logStream << "Model file loader not specified. Terminating\n"; 117 | return; 118 | } 119 | else 120 | parameters.trianglesLoader->load(); 121 | if(parameters.stopwatch != nullptr) 122 | parameters.stopwatch->lap(); 123 | 124 | auto modelTriangles = parameters.trianglesLoader->getTriangles(); 125 | uint32_t trianglesSSBOSize = modelTriangles.size() < MAX_TRIANGLES_SSBO_SIZE ? modelTriangles.size() : MAX_TRIANGLES_SSBO_SIZE; 126 | auto startPosition = modelTriangles.begin(); 127 | 128 | triangles.bind(); 129 | triangles.data(TRIANGLE_SIZE * trianglesSSBOSize, &(*startPosition), GL_STATIC_DRAW); 130 | triangles.bindBase(0); 131 | 132 | prismAABBs.bind(); 133 | prismAABBs.data(PRISM_AABB_SIZE * trianglesSSBOSize, NULL, GL_DYNAMIC_DRAW); 134 | prismAABBs.bindBase(1); 135 | 136 | if(parameters.resolution.x == 0) 137 | parameters.resolution = maxResolution(); 138 | glm::vec3 shellMin = parameters.trianglesLoader->getAABBMin(); 139 | glm::vec3 shellMax = parameters.trianglesLoader->getAABBMax(); 140 | glm::vec3 step((shellMax.x - shellMin.x) / parameters.resolution.x, 141 | (shellMax.y - shellMin.y) / parameters.resolution.y, 142 | (shellMax.z - shellMin.z) / parameters.resolution.z); 143 | GLfloat epsilon = glm::length(step) * parameters.delta; 144 | GLuint maxIndex = parameters.resolution.x * parameters.resolution.y * parameters.resolution.z; 145 | 146 | modifier.use(); 147 | modifier.bindUniformVector(UTypes::VEC3, "shellMin", glm::value_ptr(shellMin)); 148 | modifier.bindUniformVector(UTypes::VEC3, "step", glm::value_ptr(step)); 149 | modifier.bindUniformVector(UTypes::UVEC3, "resolution", glm::value_ptr(parameters.resolution)); 150 | modifier.bindUniform("epsilon", epsilon); 151 | 152 | glm::uvec3 groups = computeGroups(trianglesSSBOSize); 153 | if(parameters.stopwatch != nullptr) 154 | parameters.stopwatch->lap(); 155 | glDispatchCompute(groups.x, groups.y, groups.z); 156 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 157 | 158 | SDFSize = parameters.resolution.x * parameters.resolution.y * parameters.resolution.z < MAX_SDF_SSBO_SIZE ? 159 | parameters.resolution.x * parameters.resolution.y * parameters.resolution.z : 160 | MAX_SDF_SSBO_SIZE; 161 | sdf.bind(); 162 | sdf.data(SDFSize * SDF_ELEMENT_SIZE, NULL, GL_DYNAMIC_READ); 163 | sdf.bindBase(2); 164 | 165 | std::vector pointsAmount; 166 | triangles.bind(); 167 | ModelTriangle* trianglePointer = (ModelTriangle*)triangles.map(GL_READ_ONLY); 168 | for(uint32_t i = 0; i < trianglesSSBOSize; ++i) 169 | pointsAmount.push_back(glm::uvec4((uint32_t)trianglePointer[i].firstVertex.w, 170 | (uint32_t)trianglePointer[i].secondVertex.w, 171 | (uint32_t)trianglePointer[i].thirdVertex.w, 172 | (uint32_t)trianglePointer[i].faceNormal.w)); 173 | triangles.unmap(); 174 | 175 | filler.use(); 176 | filler.bindUniform("filler", parameters.fillerValue); 177 | glDispatchCompute(parameters.resolution.x, parameters.resolution.y, parameters.resolution.z); 178 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 179 | 180 | kernel.use(); 181 | kernel.bindUniformVector(UTypes::UVEC3, "resolution", glm::value_ptr(parameters.resolution)); 182 | kernel.bindUniformVector(UTypes::VEC3, "shellMin", glm::value_ptr(shellMin)); 183 | kernel.bindUniformVector(UTypes::VEC3, "step", glm::value_ptr(step)); 184 | kernel.bindUniformui("maxIndex", maxIndex); 185 | 186 | for(uint32_t i = 0; i < trianglesSSBOSize; ++i) 187 | { 188 | kernel.bindUniformui("aabbAndTriangleIndex", i); 189 | kernel.bindUniformui("fullyInRange", pointsAmount[i].w); 190 | glDispatchCompute((GLuint)pointsAmount[i].x, (GLuint)pointsAmount[i].y, (GLuint)pointsAmount[i].z); 191 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 192 | } 193 | writeFile(); 194 | } 195 | 196 | glm::uvec3 Converter::getResolution() 197 | { 198 | return parameters.resolution; 199 | } 200 | 201 | void Converter::writeFile() 202 | { 203 | sdfFile = fopen(parameters.SDFfilename.c_str(), "wb"); 204 | sdf.bind(); 205 | GLfloat *sdfPointer = (GLfloat*)sdf.map(GL_READ_ONLY); 206 | if(parameters.stopwatch != nullptr) 207 | parameters.stopwatch->lap(); 208 | uint32_t realSize = 0; 209 | std::vector data(0); 210 | for(uint32_t i = 0; i < SDFSize; ++i) 211 | { 212 | if(sdfPointer[i * 4 + 3] != parameters.fillerValue) 213 | { 214 | data.push_back(sdfPointer[i * 4]); 215 | data.push_back(sdfPointer[i * 4 + 1]); 216 | data.push_back(sdfPointer[i * 4 + 2]); 217 | data.push_back(sdfPointer[i * 4 + 3]); 218 | } 219 | } 220 | realSize = (uint32_t)data.size(); 221 | fwrite(&realSize, sizeof(uint32_t), 1, sdfFile); 222 | uint32_t start = 0; 223 | int count = 0; 224 | while(start != realSize) 225 | { 226 | count = (realSize - start) > 65536 ? 65536 : realSize - start; 227 | fwrite(&data[start], sizeof(GLfloat), count, sdfFile); 228 | start += count; 229 | } 230 | fclose(sdfFile); 231 | data.clear(); 232 | if(parameters.stopwatch != nullptr) 233 | parameters.stopwatch->lap(); 234 | } 235 | 236 | glm::uvec3 Converter::computeGroups(const uint32_t& inTrianglesAmount) 237 | { 238 | glm::uvec3 groups(1, 1, 1); 239 | if(inTrianglesAmount <= MAX_WORK_GROUP_COUNT) 240 | groups.z = inTrianglesAmount; 241 | else 242 | { 243 | uint32_t divisor = MAX_WORK_GROUP_COUNT; 244 | uint32_t divident = inTrianglesAmount; 245 | for(short i = 0; i < 3; ++i) 246 | { 247 | while((divident % divisor != 0) && (divisor > 1) && (divident > MAX_WORK_GROUP_COUNT)) 248 | --divisor; 249 | switch(i) 250 | { 251 | case 0: 252 | groups.z = divisor; 253 | break; 254 | case 1: 255 | groups.y = divisor; 256 | break; 257 | case 2: 258 | groups.x = divisor; 259 | break; 260 | } 261 | divident /= divisor; 262 | if(divident <= MAX_WORK_GROUP_COUNT) 263 | divisor = divident; 264 | else 265 | divisor = MAX_WORK_GROUP_COUNT; 266 | } 267 | } 268 | return groups; 269 | } 270 | 271 | glm::uvec3 Converter::maxResolution() 272 | { 273 | glm::vec3 shellMin = parameters.trianglesLoader->getAABBMin(); 274 | glm::vec3 shellMax = parameters.trianglesLoader->getAABBMax(); 275 | glm::vec3 lengthes(shellMax.x - shellMin.x, shellMax.y - shellMin.y, shellMax.z - shellMin.z); 276 | GLfloat minLength = glm::min(lengthes.x, glm::min(lengthes.y, lengthes.z)); 277 | lengthes /= minLength; 278 | uint16_t totalParts = glm::round(lengthes.x) * glm::round(lengthes.y) * glm::round(lengthes.z); 279 | if(totalParts == 0) 280 | logStream << "Invalid AABB min and max (check that loader load file and properly compute AABB)\n"; 281 | uint16_t partSize = (uint16_t)glm::floor(glm::pow((double)MAX_SDF_SSBO_SIZE / (1 * totalParts), 0.333333/*power 1/3*/)); 282 | return (glm::uvec3(glm::round(lengthes.x), glm::round(lengthes.y), glm::round(lengthes.z)) *= partSize); 283 | } 284 | --------------------------------------------------------------------------------