├── assets ├── blob.png ├── craft.png ├── shapes.png └── aileron-regular.otf ├── images ├── method.png ├── method2.png └── extruded.png ├── .gitignore ├── src ├── 3rd_party │ ├── stb │ │ └── stb_image.c │ ├── mesh_optimizer │ │ ├── allocator.cpp │ │ ├── vfetchanalyzer.cpp │ │ ├── vfetchoptimizer.cpp │ │ ├── vcacheanalyzer.cpp │ │ ├── spatialorder.cpp │ │ ├── overdrawanalyzer.cpp │ │ └── stripifier.cpp │ ├── polyline_simplification.h │ └── polyline_simplification.cpp ├── header_builds_src.c ├── types │ ├── point.h │ ├── rectf.h │ ├── pointf.h │ ├── rect.h │ ├── polygonf.h │ ├── point.cpp │ ├── image.h │ ├── rectf.cpp │ ├── bitmap.h │ ├── rect.cpp │ ├── vec2.h │ ├── pointf.cpp │ ├── color.h │ ├── vec3.h │ ├── polygonf.cpp │ ├── vec2.cpp │ ├── image.cpp │ ├── vec3.cpp │ ├── bitmap.cpp │ └── color.cpp ├── imaging.h ├── imaging_filter.cpp ├── mesh.h ├── compare.h ├── compare.cpp └── mesh.cpp ├── .vscode ├── c_cpp_properties.json └── settings.json ├── LICENSE ├── example ├── header_builds_demo.c ├── 3rd_party │ ├── wai │ │ └── whereami.h │ └── sokol │ │ ├── sokol_glue.h │ │ └── sokol_time.h └── input.h ├── README.md ├── shader.glsl ├── shell.html └── CMakeLists.txt /assets/blob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/assets/blob.png -------------------------------------------------------------------------------- /assets/craft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/assets/craft.png -------------------------------------------------------------------------------- /assets/shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/assets/shapes.png -------------------------------------------------------------------------------- /images/method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/images/method.png -------------------------------------------------------------------------------- /images/method2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/images/method2.png -------------------------------------------------------------------------------- /images/extruded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/images/extruded.png -------------------------------------------------------------------------------- /assets/aileron-regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevinz/extrude/HEAD/assets/aileron-regular.otf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /art 2 | /build 3 | /executable 4 | /shader_instructions.txt 5 | /sokol-shdc 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /src/3rd_party/stb/stb_image.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #if defined(__clang__) 3 | #pragma clang diagnostic push 4 | #pragma clang diagnostic ignored "-Wunused-function" 5 | #endif 6 | #include "stb_image.h" 7 | #if defined(__clang__) 8 | #pragma clang diagnostic pop 9 | #endif 10 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/allocator.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | void meshopt_setAllocator(void* (*allocate)(size_t), void (*deallocate)(void*)) 5 | { 6 | meshopt_Allocator::Storage::allocate = allocate; 7 | meshopt_Allocator::Storage::deallocate = deallocate; 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks", 8 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks", 9 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers" 10 | ], 11 | "defines": [], 12 | "macFrameworkPath": [ 13 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks", 14 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks" 15 | ], 16 | "intelliSenseMode": "macos-clang-x64", 17 | "compileCommands": "${workspaceFolder}/build/compile_commands.json", 18 | "configurationProvider": "ms-vscode.cmake-tools" 19 | } 20 | ], 21 | "version": 4 22 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Stephens Nunnally 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/header_builds_src.c: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | //############################################################ 11 | //## 12 | //## Single Header Library Initialization 13 | //## 14 | //############################################################ 15 | 16 | //############################################################ 17 | //## Handmade Math Implementation 18 | //############################################################ 19 | #define HANDMADE_MATH_IMPLEMENTATION 20 | #define HANDMADE_MATH_NO_SSE 21 | #include "3rd_party/handmade_math.h" 22 | 23 | //#################################################################################### 24 | //## STB Libraries 25 | //#################################################################################### 26 | #define STB_IMAGE_IMPLEMENTATION 27 | #include "3rd_party/stb/stb_image.h" 28 | 29 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 30 | #include "3rd_party/stb/stb_image_resize.h" 31 | 32 | #define STB_IMAGE_WRITE_IMPLEMENTATION 33 | #include "3rd_party/stb/stb_image_write.h" 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/3rd_party/polyline_simplification.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: Polyline Simplification, 2D implementation of the Ramer-Douglas-Peucker algorithm 3 | // Author: Tim Sheerman-Chase 4 | // License: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication 5 | // Source(s): https://gist.github.com/TimSC/0813573d77734bcb6f2cd2cf6cc7aa51 6 | // https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm 7 | // 8 | // Copyright (C) 2016 by Tim Sheerman-Chase 9 | // 10 | // 11 | // #KEYWORD: "low poly" 12 | // 13 | // 14 | #ifndef POLYLINE_SIMPLIFICATION_H 15 | #define POLYLINE_SIMPLIFICATION_H 16 | 17 | #include 18 | 19 | // Forward Declarations 20 | class DrPointF; 21 | 22 | //#################################################################################### 23 | //## PolylineSimplification 24 | //## Decimates a curve composed of line segments to a similar curve with fewer points 25 | //## (that is to say, reduces number of points in a polygon)... 26 | //############################ 27 | class PolylineSimplification 28 | { 29 | 30 | public: 31 | PolylineSimplification(); 32 | 33 | static std::vector RamerDouglasPeucker(const std::vector &point_list, double epsilon); 34 | }; 35 | 36 | 37 | #endif // POLYLINE_SIMPLIFICATION_H 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/types/point.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_POINT_H 11 | #define DR_POINT_H 12 | 13 | #include 14 | 15 | // Forward Declarations 16 | class DrPointF; 17 | 18 | //#################################################################################### 19 | //## DrPoint 20 | //## Useful 2D int Point Class, adapted from HullFinder 21 | //############################ 22 | class DrPoint 23 | { 24 | public: 25 | int x; 26 | int y; 27 | 28 | public: 29 | // Constructors 30 | DrPoint(); 31 | DrPoint(int x, int y_); 32 | DrPoint(long x, long y_); 33 | DrPoint(float x_, float y_); 34 | DrPoint(double x_, double y_); 35 | DrPoint(const DrPointF pointf); 36 | 37 | // Operator Overloads 38 | DrPoint& operator= (const DrPoint &other); 39 | DrPoint operator+ (const DrPoint &other) const; 40 | DrPoint operator- (const DrPoint &other) const; 41 | DrPoint operator* (int k) const; 42 | DrPoint operator/ (int k) const; 43 | bool operator== (const DrPoint &other) const; 44 | 45 | // Conversions 46 | DrPointF toPointF(); 47 | }; 48 | 49 | 50 | #endif // DR_POINT_H 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/types/rectf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_RECTF_H 11 | #define DR_RECTF_H 12 | 13 | // Forward Declarations 14 | class DrPoint; 15 | class DrPointF; 16 | class DrRect; 17 | 18 | 19 | //#################################################################################### 20 | //## DrRectF 21 | //## A double Rectangle Class, Y axis starts at 0 at top and increases as it goes downward 22 | //############################ 23 | class DrRectF 24 | { 25 | 26 | public: 27 | double x; // As vec4, x 28 | double y; // As vec4, y 29 | double width; // As vec4, z 30 | double height; // As vec4, w 31 | 32 | // Constructors 33 | DrRectF(); 34 | DrRectF(double x_, double y_, double width_, double height_); 35 | DrRectF(const DrPointF &top_left, const DrPointF &bottom_right); 36 | DrRectF(const DrRect &r); 37 | DrRectF(const DrRectF &r); 38 | 39 | // Conversion 40 | DrRect toRect(); 41 | 42 | // Helper Functions 43 | bool contains(const DrPoint); 44 | bool contains(const DrPointF); 45 | 46 | // Getters 47 | double left(); 48 | double right(); 49 | double top(); 50 | double bottom(); 51 | 52 | DrPointF topLeft(); 53 | DrPointF topRight(); 54 | DrPointF bottomLeft(); 55 | DrPointF bottomRight(); 56 | 57 | }; 58 | 59 | 60 | #endif // DR_RECTF_H 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/types/pointf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_POINT_F_H 11 | #define DR_POINT_F_H 12 | 13 | #include 14 | 15 | // Forward Declarations 16 | class DrPoint; 17 | 18 | //#################################################################################### 19 | //## DrPointF 20 | //## Useful 2D double Point Class, adapted from HullFinder 21 | //############################ 22 | class DrPointF 23 | { 24 | public: 25 | double x; 26 | double y; 27 | 28 | public: 29 | // Constructor 30 | DrPointF(); 31 | DrPointF(double x_, double y_); 32 | DrPointF(const DrPoint point); 33 | 34 | // Operator Overloads 35 | DrPointF& operator= (const DrPointF &other); 36 | DrPointF operator+ (const DrPointF &other) const; 37 | DrPointF operator- (const DrPointF &other) const; 38 | DrPointF operator* (double k) const; 39 | DrPointF operator/ (double k) const; 40 | DrPointF& operator*= (double k); 41 | bool operator== (const DrPointF &other) const; 42 | 43 | // Functions 44 | double dotProduct(const DrPointF &other) const; 45 | double distanceSquared(const DrPointF &to) const; 46 | double distance(const DrPointF &to) const; 47 | double distance(const DrPointF &segment_start, const DrPointF &segment_end) const; 48 | double decisionDistance(const std::vector &points) const; 49 | 50 | // Conversions 51 | DrPoint toPoint(); 52 | }; 53 | 54 | 55 | 56 | 57 | #endif // DR_POINT_F_H 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/types/rect.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_RECT_H 11 | #define DR_RECT_H 12 | 13 | // Forward Declarations 14 | class DrPoint; 15 | class DrPointF; 16 | class DrRectF; 17 | 18 | 19 | //#################################################################################### 20 | //## DrRect 21 | //## An int Rectangle Class, Y axis starts at 0 at top and increases as it goes downward 22 | //############################ 23 | class DrRect 24 | { 25 | 26 | public: 27 | int x; // As vec4, x 28 | int y; // As vec4, y 29 | int width; // As vec4, z 30 | int height; // As vec4, w 31 | 32 | // Constructors 33 | DrRect(); 34 | DrRect(int x_, int y_, int width_, int height_); 35 | DrRect(const DrPoint &top_left, const DrPoint &bottom_right); 36 | DrRect(const DrRect &r); 37 | DrRect(const DrRectF &r); 38 | 39 | // Conversion 40 | DrRectF toRectF(); 41 | 42 | // Operator Overloads 43 | DrRectF& operator= (const DrRectF &other); 44 | 45 | // Helper Functions 46 | bool contains(const DrPoint); 47 | bool contains(const DrPointF); 48 | void adjust(int adjust_left, int adjust_top, int adjust_right, int adjust_bottom); 49 | 50 | // Getters 51 | int left(); 52 | int right(); 53 | int top(); 54 | int bottom(); 55 | 56 | DrPoint topLeft(); 57 | DrPoint topRight(); 58 | DrPoint bottomLeft(); 59 | DrPoint bottomRight(); 60 | 61 | }; 62 | 63 | 64 | #endif // DR_RECT_H 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/vfetchanalyzer.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | #include 5 | #include 6 | 7 | meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size) 8 | { 9 | assert(index_count % 3 == 0); 10 | assert(vertex_size > 0 && vertex_size <= 256); 11 | 12 | meshopt_Allocator allocator; 13 | 14 | meshopt_VertexFetchStatistics result = {}; 15 | 16 | unsigned char* vertex_visited = allocator.allocate(vertex_count); 17 | memset(vertex_visited, 0, vertex_count); 18 | 19 | const size_t kCacheLine = 64; 20 | const size_t kCacheSize = 128 * 1024; 21 | 22 | // simple direct mapped cache; on typical mesh data this is close to 4-way cache, and this model is a gross approximation anyway 23 | size_t cache[kCacheSize / kCacheLine] = {}; 24 | 25 | for (size_t i = 0; i < index_count; ++i) 26 | { 27 | unsigned int index = indices[i]; 28 | assert(index < vertex_count); 29 | 30 | vertex_visited[index] = 1; 31 | 32 | size_t start_address = index * vertex_size; 33 | size_t end_address = start_address + vertex_size; 34 | 35 | size_t start_tag = start_address / kCacheLine; 36 | size_t end_tag = (end_address + kCacheLine - 1) / kCacheLine; 37 | 38 | assert(start_tag < end_tag); 39 | 40 | for (size_t tag = start_tag; tag < end_tag; ++tag) 41 | { 42 | size_t line = tag % (sizeof(cache) / sizeof(cache[0])); 43 | 44 | // we store +1 since cache is filled with 0 by default 45 | result.bytes_fetched += (cache[line] != tag + 1) * kCacheLine; 46 | cache[line] = tag + 1; 47 | } 48 | } 49 | 50 | size_t unique_vertex_count = 0; 51 | 52 | for (size_t i = 0; i < vertex_count; ++i) 53 | unique_vertex_count += vertex_visited[i]; 54 | 55 | result.overfetch = unique_vertex_count == 0 ? 0 : float(result.bytes_fetched) / float(unique_vertex_count * vertex_size); 56 | 57 | return result; 58 | } 59 | -------------------------------------------------------------------------------- /src/types/polygonf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_POLYGONF_H 11 | #define DR_POLYGONF_H 12 | 13 | #include 14 | 15 | // Forward Declarations 16 | class DrPointF; 17 | enum class Winding_Orientation; 18 | 19 | 20 | //#################################################################################### 21 | //## DrPolygonF 22 | //## A polygon class, useful for intersections 23 | //############################ 24 | class DrPolygonF 25 | { 26 | private: 27 | std::vector m_points; 28 | 29 | public: 30 | // Constructor 31 | DrPolygonF(); 32 | DrPolygonF(const DrPolygonF &polygon); 33 | 34 | // Access 35 | const std::vector& points() { return m_points; } 36 | 37 | // Info 38 | bool isEmpty() const { return (m_points.size() == 0); } 39 | long numberOfPoints() const { return static_cast(m_points.size()); } 40 | 41 | // Manangement 42 | void addPoint(DrPointF point); 43 | void clear() { m_points.clear(); } 44 | 45 | // Polygon / Line Functions 46 | static bool onSegment(DrPointF line_a, DrPointF point, DrPointF line_b); 47 | static Winding_Orientation orientation(DrPointF p, DrPointF q, DrPointF r); 48 | static void ensureWindingOrientation(std::vector &points, Winding_Orientation direction_desired); 49 | static Winding_Orientation findWindingOrientation(const std::vector &points); 50 | static bool doIntersect(DrPointF p1, DrPointF q1, DrPointF p2, DrPointF q2); 51 | bool isInside(DrPointF point); 52 | 53 | 54 | 55 | }; 56 | 57 | #endif // DR_POLYGONF_H 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /example/header_builds_demo.c: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | //############################################################ 11 | //## 12 | //## Single Header Library Initialization 13 | //## 14 | //############################################################ 15 | #if defined(_WIN32) 16 | #include 17 | #endif 18 | 19 | 20 | //############################################################ 21 | //## Sokol Libraries... ADJUST GRAPHICS PLATFORM HERE!! 22 | //############################################################ 23 | #define SOKOL_IMPL 24 | #if defined(__EMSCRIPTEN__) 25 | #define SOKOL_GLES2 // Android, WebAssembly 26 | //#define SOKOL_GLES3 // Android, WebAssembly 27 | //#define SOKOL_WGPU // Next Gen WebAssembly 28 | #elif defined(__APPLE__) 29 | #define SOKOL_GLCORE33 // MacOS, Windows, Linux, Switch, Playstation 30 | //#define SOKOL_METAL // MacOS, iOS, tvOS 31 | #elif defined(_WIN32) 32 | #define SOKOL_GLCORE33 33 | //#define SOKOL_D3D11 // Windows, XBox 34 | #else 35 | #define SOKOL_GLCORE33 36 | #endif 37 | 38 | #include "3rd_party/sokol/sokol_app.h" 39 | #include "3rd_party/sokol/sokol_gfx.h" 40 | #define SOKOL_GL_IMPL 41 | #include "3rd_party/sokol/sokol_gl.h" 42 | #include "3rd_party/sokol/sokol_fetch.h" 43 | #include "3rd_party/sokol/sokol_glue.h" 44 | #include "3rd_party/sokol/sokol_time.h" 45 | #include "3rd_party/sokol/sokol_audio.h" 46 | 47 | 48 | //#################################################################################### 49 | //## Font Stash 50 | //#################################################################################### 51 | #include // malloc, free, fopen, fclose, ftell, fseek, fread 52 | #include // memset 53 | #define FONTSTASH_IMPLEMENTATION // Expands implementation 54 | #include "3rd_party/fontstash.h" 55 | #include "3rd_party/sokol/sokol_fontstash.h" 56 | 57 | 58 | -------------------------------------------------------------------------------- /example/3rd_party/wai/whereami.h: -------------------------------------------------------------------------------- 1 | // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses 2 | // without any warranty. 3 | // by Gregory Pakosz (@gpakosz) 4 | // https://github.com/gpakosz/whereami 5 | 6 | #ifndef WHEREAMI_H 7 | #define WHEREAMI_H 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #ifndef WAI_FUNCSPEC 14 | #define WAI_FUNCSPEC 15 | #endif 16 | #ifndef WAI_PREFIX 17 | #define WAI_PREFIX(function) wai_##function 18 | #endif 19 | 20 | /** 21 | * Returns the path to the current executable. 22 | * 23 | * Usage: 24 | * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to 25 | * retrieve the length of the path 26 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);` 27 | * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the 28 | * path 29 | * - add a terminal NUL character with `path[length] = '\0';` 30 | * 31 | * @param out destination buffer, optional 32 | * @param capacity destination buffer capacity 33 | * @param dirname_length optional recipient for the length of the dirname part 34 | * of the path. 35 | * 36 | * @return the length of the executable path on success (without a terminal NUL 37 | * character), otherwise `-1` 38 | */ 39 | WAI_FUNCSPEC 40 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); 41 | 42 | /** 43 | * Returns the path to the current module 44 | * 45 | * Usage: 46 | * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve 47 | * the length of the path 48 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);` 49 | * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path 50 | * - add a terminal NUL character with `path[length] = '\0';` 51 | * 52 | * @param out destination buffer, optional 53 | * @param capacity destination buffer capacity 54 | * @param dirname_length optional recipient for the length of the dirname part 55 | * of the path. 56 | * 57 | * @return the length of the module path on success (without a terminal NUL 58 | * character), otherwise `-1` 59 | */ 60 | WAI_FUNCSPEC 61 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif // #ifndef WHEREAMI_H 68 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/vfetchoptimizer.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | #include 5 | #include 6 | 7 | size_t meshopt_optimizeVertexFetchRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count) 8 | { 9 | assert(index_count % 3 == 0); 10 | 11 | memset(destination, -1, vertex_count * sizeof(unsigned int)); 12 | 13 | unsigned int next_vertex = 0; 14 | 15 | for (size_t i = 0; i < index_count; ++i) 16 | { 17 | unsigned int index = indices[i]; 18 | assert(index < vertex_count); 19 | 20 | if (destination[index] == ~0u) 21 | { 22 | destination[index] = next_vertex++; 23 | } 24 | } 25 | 26 | assert(next_vertex <= vertex_count); 27 | 28 | return next_vertex; 29 | } 30 | 31 | size_t meshopt_optimizeVertexFetch(void* destination, unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size) 32 | { 33 | assert(index_count % 3 == 0); 34 | assert(vertex_size > 0 && vertex_size <= 256); 35 | 36 | meshopt_Allocator allocator; 37 | 38 | // support in-place optimization 39 | if (destination == vertices) 40 | { 41 | unsigned char* vertices_copy = allocator.allocate(vertex_count * vertex_size); 42 | memcpy(vertices_copy, vertices, vertex_count * vertex_size); 43 | vertices = vertices_copy; 44 | } 45 | 46 | // build vertex remap table 47 | unsigned int* vertex_remap = allocator.allocate(vertex_count); 48 | memset(vertex_remap, -1, vertex_count * sizeof(unsigned int)); 49 | 50 | unsigned int next_vertex = 0; 51 | 52 | for (size_t i = 0; i < index_count; ++i) 53 | { 54 | unsigned int index = indices[i]; 55 | assert(index < vertex_count); 56 | 57 | unsigned int& remap = vertex_remap[index]; 58 | 59 | if (remap == ~0u) // vertex was not added to destination VB 60 | { 61 | // add vertex 62 | memcpy(static_cast(destination) + next_vertex * vertex_size, static_cast(vertices) + index * vertex_size, vertex_size); 63 | 64 | remap = next_vertex++; 65 | } 66 | 67 | // modify indices in place 68 | indices[i] = remap; 69 | } 70 | 71 | assert(next_vertex <= vertex_count); 72 | 73 | return next_vertex; 74 | } 75 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "sokol_time.h": "c", 4 | "chrono": "cpp", 5 | "iosfwd": "cpp", 6 | "vector": "cpp", 7 | "sokol_app.h": "c", 8 | "sokol_glue.h": "c", 9 | "sokol_audio.h": "c", 10 | "handmade_math.h": "c", 11 | "__bit_reference": "cpp", 12 | "__config": "cpp", 13 | "__debug": "cpp", 14 | "__errc": "cpp", 15 | "__functional_base": "cpp", 16 | "__hash_table": "cpp", 17 | "__locale": "cpp", 18 | "__mutex_base": "cpp", 19 | "__node_handle": "cpp", 20 | "__nullptr": "cpp", 21 | "__split_buffer": "cpp", 22 | "__string": "cpp", 23 | "__threading_support": "cpp", 24 | "__tree": "cpp", 25 | "__tuple": "cpp", 26 | "algorithm": "cpp", 27 | "array": "cpp", 28 | "atomic": "cpp", 29 | "bit": "cpp", 30 | "bitset": "cpp", 31 | "cctype": "cpp", 32 | "cmath": "cpp", 33 | "complex": "cpp", 34 | "cstdarg": "cpp", 35 | "cstddef": "cpp", 36 | "cstdint": "cpp", 37 | "cstdio": "cpp", 38 | "cstdlib": "cpp", 39 | "cstring": "cpp", 40 | "ctime": "cpp", 41 | "cwchar": "cpp", 42 | "cwctype": "cpp", 43 | "deque": "cpp", 44 | "exception": "cpp", 45 | "functional": "cpp", 46 | "initializer_list": "cpp", 47 | "iomanip": "cpp", 48 | "ios": "cpp", 49 | "iostream": "cpp", 50 | "istream": "cpp", 51 | "iterator": "cpp", 52 | "limits": "cpp", 53 | "list": "cpp", 54 | "locale": "cpp", 55 | "map": "cpp", 56 | "memory": "cpp", 57 | "mutex": "cpp", 58 | "new": "cpp", 59 | "numeric": "cpp", 60 | "optional": "cpp", 61 | "ostream": "cpp", 62 | "random": "cpp", 63 | "ratio": "cpp", 64 | "set": "cpp", 65 | "sstream": "cpp", 66 | "stdexcept": "cpp", 67 | "streambuf": "cpp", 68 | "string": "cpp", 69 | "string_view": "cpp", 70 | "system_error": "cpp", 71 | "tuple": "cpp", 72 | "type_traits": "cpp", 73 | "typeinfo": "cpp", 74 | "unordered_map": "cpp", 75 | "utility": "cpp", 76 | "__functional_03": "cpp", 77 | "__cxx_version": "cpp", 78 | "memory_resource": "cpp", 79 | "version": "cpp" 80 | } 81 | } -------------------------------------------------------------------------------- /src/types/point.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include 11 | 12 | #include "point.h" 13 | #include "pointf.h" 14 | 15 | 16 | //#################################################################################### 17 | //## Constructors 18 | //#################################################################################### 19 | DrPoint::DrPoint() { 20 | x = 0; 21 | y = 0; 22 | } 23 | 24 | DrPoint::DrPoint(int x_, int y_) { 25 | x = x_; 26 | y = y_; 27 | } 28 | 29 | DrPoint::DrPoint(long x_, long y_) { 30 | x = static_cast(x_); 31 | y = static_cast(y_); 32 | } 33 | 34 | DrPoint::DrPoint(float x_, float y_) { 35 | x = static_cast(x_); 36 | y = static_cast(y_); 37 | } 38 | 39 | DrPoint::DrPoint(double x_, double y_) { 40 | x = static_cast(x_); 41 | y = static_cast(y_); 42 | } 43 | 44 | DrPoint::DrPoint(const DrPointF pointf) { 45 | x = static_cast(pointf.x); 46 | y = static_cast(pointf.y); 47 | } 48 | 49 | //#################################################################################### 50 | //## Overload Operators 51 | //#################################################################################### 52 | DrPoint& DrPoint::operator=(const DrPoint &other) { 53 | x = other.x; 54 | y = other.y; 55 | return *this; 56 | } 57 | 58 | DrPoint DrPoint::operator+(const DrPoint &other) const { 59 | return DrPoint(x + other.x, y + other.y); 60 | } 61 | 62 | DrPoint DrPoint::operator-(const DrPoint &other) const { 63 | return DrPoint(x - other.x, y - other.y); 64 | } 65 | 66 | DrPoint DrPoint::operator*(int k) const { 67 | return DrPoint(x * k, y * k); 68 | } 69 | 70 | DrPoint DrPoint::operator/(int k) const { 71 | return DrPoint(x / k, y / k); 72 | } 73 | 74 | bool DrPoint::operator==(const DrPoint &other) const { 75 | return (x == other.x) && (y == other.y); 76 | } 77 | 78 | 79 | //#################################################################################### 80 | //## Conversions 81 | //#################################################################################### 82 | DrPointF DrPoint::toPointF() { 83 | return DrPointF( x, y ); 84 | } 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/vcacheanalyzer.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | #include 5 | #include 6 | 7 | meshopt_VertexCacheStatistics meshopt_analyzeVertexCache(const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size, unsigned int warp_size, unsigned int primgroup_size) 8 | { 9 | assert(index_count % 3 == 0); 10 | assert(cache_size >= 3); 11 | assert(warp_size == 0 || warp_size >= 3); 12 | 13 | meshopt_Allocator allocator; 14 | 15 | meshopt_VertexCacheStatistics result = {}; 16 | 17 | unsigned int warp_offset = 0; 18 | unsigned int primgroup_offset = 0; 19 | 20 | unsigned int* cache_timestamps = allocator.allocate(vertex_count); 21 | memset(cache_timestamps, 0, vertex_count * sizeof(unsigned int)); 22 | 23 | unsigned int timestamp = cache_size + 1; 24 | 25 | for (size_t i = 0; i < index_count; i += 3) 26 | { 27 | unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2]; 28 | assert(a < vertex_count && b < vertex_count && c < vertex_count); 29 | 30 | bool ac = (timestamp - cache_timestamps[a]) > cache_size; 31 | bool bc = (timestamp - cache_timestamps[b]) > cache_size; 32 | bool cc = (timestamp - cache_timestamps[c]) > cache_size; 33 | 34 | // flush cache if triangle doesn't fit into warp or into the primitive buffer 35 | if ((primgroup_size && primgroup_offset == primgroup_size) || (warp_size && warp_offset + ac + bc + cc > warp_size)) 36 | { 37 | result.warps_executed += warp_offset > 0; 38 | 39 | warp_offset = 0; 40 | primgroup_offset = 0; 41 | 42 | // reset cache 43 | timestamp += cache_size + 1; 44 | } 45 | 46 | // update cache and add vertices to warp 47 | for (int j = 0; j < 3; ++j) 48 | { 49 | unsigned int index = indices[i + j]; 50 | 51 | if (timestamp - cache_timestamps[index] > cache_size) 52 | { 53 | cache_timestamps[index] = timestamp++; 54 | result.vertices_transformed++; 55 | warp_offset++; 56 | } 57 | } 58 | 59 | primgroup_offset++; 60 | } 61 | 62 | size_t unique_vertex_count = 0; 63 | 64 | for (size_t i = 0; i < vertex_count; ++i) 65 | unique_vertex_count += cache_timestamps[i] > 0; 66 | 67 | result.warps_executed += warp_offset > 0; 68 | 69 | result.acmr = index_count == 0 ? 0 : float(result.vertices_transformed) / float(index_count / 3); 70 | result.atvr = unique_vertex_count == 0 ? 0 : float(result.vertices_transformed) / float(unique_vertex_count); 71 | 72 | return result; 73 | } 74 | -------------------------------------------------------------------------------- /src/imaging.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef IMAGE_FILTER_H 11 | #define IMAGE_FILTER_H 12 | 13 | #include "types/bitmap.h" 14 | #include "types/pointf.h" 15 | 16 | 17 | // Filters types 18 | enum class Image_Filter_Type { // ApplySinglePixelFilter 'value' argument 19 | Brightness, // -255 to 255 20 | Contrast, // -255 to 255 21 | Saturation, // -255 to 255 22 | Hue, // -360 to 360 23 | Grayscale, // N/A 24 | Negative, // N/A 25 | Opacity, // -255 to 255 26 | }; 27 | 28 | // Imaging Enumerations 29 | enum class Flood_Fill_Type { 30 | Compare_4, 31 | Compare_8, 32 | }; 33 | 34 | 35 | //#################################################################################### 36 | //## Image editing / object finding 37 | //############################ 38 | namespace Dr { 39 | 40 | // ***** Filters 41 | DrBitmap ApplySinglePixelFilter(Image_Filter_Type filter, const DrBitmap &from_bitmap, int value); 42 | 43 | // ***** Comparison 44 | bool CompareBitmaps(const DrBitmap &bitmap1, const DrBitmap &bitmap2); 45 | 46 | // ***** Object Counting / Fill (a la Ravens Project) 47 | DrBitmap BlackAndWhiteFromAlpha(const DrBitmap &bitmap, double alpha_tolerance, bool inverse, 48 | Bitmap_Format desired_format = Bitmap_Format::ARGB); 49 | void FillBorder(DrBitmap &bitmap, DrColor fill_color, DrRect rect); 50 | bool FindObjectsInBitmap(const DrBitmap &bitmap, std::vector &bitmaps, std::vector &rects, 51 | double alpha_tolerance, bool convert = true); 52 | DrBitmap FloodFill(DrBitmap &bitmap, int at_x, int at_y, DrColor fill_color, double tolerance, Flood_Fill_Type type, 53 | int &flood_pixel_count, DrRect &flood_rect); 54 | 55 | // ***** Outlining 56 | std::vector OutlinePointList(const DrBitmap &bitmap); 57 | std::vector TraceImageOutline(const DrBitmap &bitmap); 58 | 59 | } 60 | 61 | #endif // IMAGE_FILTER_H 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extrude 2 | 3 | Extrudes an image into a 3D mesh in C++11 with no external dependencies. Sample VS Code project uses the sokol graphics library for rendering the extruded model. 4 | 5 | web assembly demo: https://stevinz.github.io/extrude-html5/ 6 | 7 | ## Screenshots: 8 | 9 |

10 | 11 | ## Methodology: 12 | 13 |

14 | 15 | 1. Image is loaded into an array and binarized based on the alpha channel (with an adjustable tolerance). 16 | 2. Objects are found using flood fill and removed from original image. This is repeated until the entire image has been scanned. 17 | 3. Holes within each object are found using additional flood fills. 18 | 4. Polygons are formed by outlining the resulting objects. The outlining algorithm starts at the top left of an image and scans down and to the right until a pixel is hit. It then proceeds, in a clockwise rotation, to the next adjacent pixel found with the smallest angle between that pixel and the current position. 19 | 20 |
21 | 22 |

23 | 24 | 5. The resulting list of points, stored as a polygon, is then simplified using the Ramer-Douglas-Peucker algorithm. This polyline simplification algorithm reduces points that fall along the same lines. 25 | 6. The simplified outline polygon, along with a list of any hole polygons, is fed into an optimal polygon triangulation algorithm to form the front and back faces. 26 | 7. Triangles are added to connecct the front and back faces, forming the sides. 27 | 8. 3D! 28 | 29 |
30 | 31 | ## To Run 32 | 33 | - Download [VSCode](https://code.visualstudio.com/) and install the [CMake extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools). 34 | - Download or clone the repository, open the project folder in VSCode. 35 | - Under the CMake extension, select 'Build All Projects'. 36 | - A standalone binary will be created in the 'build' directory. 37 | 38 | ## Thanks to these libraries used during extrusion: 39 | 40 | - Handmade-Math (CC0): https://github.com/StrangeZak/Handmade-Math 41 | - Mesh Optimizer (MIT): https://github.com/zeux/meshoptimizer 42 | - Poly Partition (MIT): https://github.com/ivanfratric/polypartition 43 | - Ramer-Douglas-Peucker (CC0): https://gist.github.com/TimSC/0813573d77734bcb6f2cd2cf6cc7aa51 44 | - Stb_image (MIT): https://github.com/nothings/stb 45 | 46 | ## Also thanks to these libraries used for the demo: 47 | 48 | - Fontstash (Zlib): https://github.com/memononen/fontstash 49 | - Sokol (Zlib): https://github.com/floooh/sokol 50 | - Whereami (MIT): https://github.com/gpakosz/whereami -------------------------------------------------------------------------------- /shader.glsl: -------------------------------------------------------------------------------- 1 | //################################################################################ 2 | //## Shader code 3 | //################################################################################ 4 | @ctype mat4 hmm_mat4 5 | 6 | 7 | //########## Vertex Shader ########## 8 | @vs vs 9 | uniform vs_params { 10 | mat4 mvp; 11 | mat4 m; 12 | }; 13 | 14 | in vec4 pos; 15 | in vec3 norm; 16 | in vec2 texcoord0; 17 | in vec3 bary; 18 | 19 | out vec2 uv; 20 | out vec3 vert; 21 | out vec3 vert_normal; 22 | out vec3 vert_bary; 23 | 24 | void main() { 25 | gl_Position = mvp * pos; 26 | uv = texcoord0; 27 | vert = (m * vec4(pos.xyz, 1.0)).xyz; 28 | vert_normal = (m * vec4(norm, 0.0)).xyz; 29 | vert_bary = bary; 30 | } 31 | @end 32 | 33 | 34 | //########## Fragment Shader ########## 35 | @fs fs 36 | uniform sampler2D tex; 37 | 38 | uniform fs_params { 39 | float u_wireframe; 40 | }; 41 | 42 | in vec2 uv; 43 | in vec3 vert; 44 | in vec3 vert_normal; 45 | in vec3 vert_bary; 46 | 47 | out vec4 frag_color; 48 | 49 | void main() { 50 | vec4 norm = vec4(0.0, vert_normal * 0.0); 51 | vec4 bary = vec4(0.0, vert_bary * 0.0); 52 | 53 | 54 | // ***** Color from texture 55 | vec4 color_in = texture(tex, uv); 56 | vec3 rgb_in = color_in.xyz; 57 | float alpha_in = 1.0;//color_in.a; 58 | vec3 rgb_out = rgb_in; 59 | float alpha_out = 1.0; 60 | 61 | 62 | // ***** Wireframe 63 | if (u_wireframe == 1.0) { 64 | float width = 1.0; 65 | 66 | vec3 d = fwidth(vert_bary); 67 | vec3 a3 = smoothstep(vec3(0.0), d * width, vert_bary); 68 | float wire = min(min(a3.x, a3.y), a3.z); 69 | rgb_out = rgb_in * (1.0 - wire); 70 | 71 | // If not on edge, draw texture faded 72 | if (rgb_out.x < 0.02 && rgb_out.y < 0.02 && rgb_out.z < 0.02) { 73 | // Texture is slightly there 74 | rgb_out = rgb_in * 0.8; 75 | alpha_out = alpha_in * 0.8; 76 | } 77 | } 78 | 79 | 80 | // ***** Shade Away 81 | // Calculate angle between camera vector and vertex normal for triangle shading 82 | float shade_away = 1.0; 83 | if (shade_away == 1.0) { 84 | vec3 eye = vec3(0.0, 1.5, 500.0); 85 | float dp = dot(normalize(vert_normal), normalize(vert - eye)) + 0.15; 86 | dp = clamp(dp, 0.0, 1.0); 87 | rgb_out = mix(vec3(0.0), rgb_out, dp); 88 | } 89 | 90 | 91 | // ***** Set Final Color 92 | frag_color = vec4(rgb_out, alpha_out); 93 | } 94 | @end 95 | 96 | 97 | //########## Shader Name ########## 98 | @program extrude3D vs fs 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/types/image.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | // File: 11 | // A container class for one image to be loaded 12 | // 13 | #ifndef DRIMAGE_H 14 | #define DRIMAGE_H 15 | 16 | #include 17 | 18 | #include "bitmap.h" 19 | #include "pointf.h" 20 | 21 | 22 | // Local Constants 23 | #define vtr std::vector 24 | const double c_alpha_tolerance = 0.875; 25 | 26 | 27 | //#################################################################################### 28 | //## DrImage 29 | //## Class to hold an Image 30 | //############################ 31 | class DrImage 32 | { 33 | private: 34 | // Local Variables 35 | std::string m_simple_name; // Simple name, i.e. "pretty tree 1" 36 | DrBitmap m_bitmap; // Stored image as DrBitmap 37 | 38 | public: 39 | vtr> m_poly_list; // Stores list of image outline points 40 | vtr>> m_hole_list; // Stores list of hole outline points 41 | bool m_outline_canceled { false }; // True when Image Outline has been canceled, when true extrudes in 3D as simple square 42 | bool m_outline_processed { false }; // Turns true when autoOutlinePoints() has completed successfully 43 | 44 | private: 45 | // Internal Variables 46 | std::string m_folder_name { "" }; // Used for External Images to belong to a category 47 | 48 | 49 | public: 50 | // Constructors 51 | DrImage(std::string image_name, DrBitmap &bitmap, float lod = 0.25, bool outline = true); 52 | 53 | // Settings 54 | std::string getName() { return m_simple_name; } 55 | 56 | // Image Helper Functions 57 | void outlinePoints(float lod); 58 | bool outlineCanceled() { return m_outline_canceled; } 59 | bool outlineProcessed() { return m_outline_processed; } 60 | void setSimpleBox(); 61 | 62 | // Getters / Setters 63 | std::string getSimplifiedName() { return m_simple_name; } 64 | const DrBitmap& getBitmap() const { return m_bitmap; } 65 | 66 | 67 | // Internal Variable Functions 68 | std::string getFolderName() { return m_folder_name; } 69 | void setFolderName(std::string folder) { m_folder_name = folder; } 70 | 71 | }; 72 | 73 | #endif // DRIMAGE_H 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/types/rectf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "../compare.h" 11 | #include "point.h" 12 | #include "pointf.h" 13 | #include "rect.h" 14 | #include "rectf.h" 15 | 16 | 17 | //#################################################################################### 18 | //## Constructors 19 | //#################################################################################### 20 | DrRectF::DrRectF() { 21 | x = 0.0; 22 | y = 0.0; 23 | width = 0.0; 24 | height = 0.0; 25 | } 26 | DrRectF::DrRectF(double x_, double y_, double width_, double height_) { 27 | x = x_; 28 | y = y_; 29 | width = width_; 30 | height = height_; 31 | } 32 | DrRectF::DrRectF(const DrPointF &top_left, const DrPointF &bottom_right) { 33 | x = top_left.x; 34 | y = top_left.y; 35 | width = (bottom_right.x - top_left.x); 36 | height = (bottom_right.y - top_left.y); 37 | } 38 | DrRectF::DrRectF(const DrRect &r) { 39 | x = r.x; 40 | y = r.y; 41 | width = r.width; 42 | height = r.height; 43 | } 44 | DrRectF::DrRectF(const DrRectF &r) { 45 | x = r.x; 46 | y = r.y; 47 | width = r.width; 48 | height = r.height; 49 | } 50 | 51 | 52 | //#################################################################################### 53 | //## Conversion 54 | //#################################################################################### 55 | DrRect DrRectF::toRect() { return DrRect(*this); } 56 | 57 | 58 | //#################################################################################### 59 | //## Helper Functions 60 | //#################################################################################### 61 | bool DrRectF::contains(const DrPoint point) { return contains(DrPointF(point)); } 62 | bool DrRectF::contains(const DrPointF pointf) { 63 | if (pointf.x > left() && pointf.x < right()) { 64 | if (pointf.y > top() && pointf.y < bottom()) 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | 71 | //#################################################################################### 72 | //## Getters 73 | //#################################################################################### 74 | double DrRectF::left() { return (width > 0) ? (x) : (x + width + 1); } 75 | double DrRectF::right() { return (width > 0) ? (x + width - 1) : (x); } 76 | 77 | double DrRectF::top() { return (height > 0) ? (y) : (y + height + 1); } 78 | double DrRectF::bottom() { return (height > 0) ? (y + height - 1) : (y); } 79 | 80 | DrPointF DrRectF::topLeft() { return DrPointF(left(), top()); } 81 | DrPointF DrRectF::topRight() { return DrPointF(right(), top()); } 82 | DrPointF DrRectF::bottomLeft() { return DrPointF(left(), bottom()); } 83 | DrPointF DrRectF::bottomRight() { return DrPointF(right(), bottom()); } 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/types/bitmap.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_BITMAP_H 11 | #define DR_BITMAP_H 12 | 13 | #include 14 | #include 15 | 16 | 17 | // Forward Declarations 18 | class DrColor; 19 | class DrPolygonF; 20 | class DrRect; 21 | 22 | // Local constants 23 | enum Bitmap_Format { 24 | Grayscale = 1, 25 | ARGB = 4, 26 | }; 27 | 28 | //#################################################################################### 29 | //## DrBitmap 30 | //## Holds an image, compatible / loads with stb_image 31 | //############################ 32 | class DrBitmap 33 | { 34 | public: 35 | Bitmap_Format format = Bitmap_Format::ARGB; // Bitmap format 36 | 37 | int channels = 4; // Number of 8-bit components per pixel (default is 4: R, G, B, A 38 | int width = 0; // Image width 39 | int height = 0; // Image height 40 | 41 | std::vector data; // Pixel data 42 | 43 | 44 | public: 45 | // Constructors 46 | DrBitmap(Bitmap_Format desired_format = Bitmap_Format::ARGB); 47 | ~DrBitmap(); 48 | DrBitmap(const DrBitmap &bitmap, Bitmap_Format desired_format = Bitmap_Format::ARGB); 49 | DrBitmap(int width_, int height_, Bitmap_Format desired_format = Bitmap_Format::ARGB); 50 | DrBitmap(std::string filename, Bitmap_Format desired_format = Bitmap_Format::ARGB); 51 | DrBitmap(const unsigned char *from_data, const int &number_of_bytes, 52 | bool compressed = true, int width_ = 0, int height_ = 0); 53 | 54 | // Info 55 | int size() const { return (width * height * channels); } 56 | 57 | // Manipulation 58 | DrBitmap copy(); 59 | DrBitmap copy(DrRect ©_rect); 60 | DrPolygonF polygon() const; 61 | DrRect rect() const; 62 | DrColor getPixel(int x, int y) const; 63 | void setPixel(int x, int y, DrColor color); 64 | 65 | // Alpha Testing 66 | void fuzzyAlpha(); 67 | 68 | // Image Loaders 69 | void loadFromFile(std::string filename, Bitmap_Format desired_format = Bitmap_Format::ARGB); 70 | void loadFromMemory(const unsigned char *compressed_data, const int &number_of_bytes, 71 | bool compressed = true, int width_ = 0, int height_ = 0); 72 | 73 | 74 | void saveFormat(std::vector &formatted); // Realigns pixels with stb image format 75 | int saveAsBmp(std::string filename); // Returns 0 on failure, non-zero on success 76 | int saveAsJpg(std::string filename, int quality = 100); // Returns 0 on failure, non-zero on success, Quality 0-100 77 | int saveAsPng(std::string filename); // Returns 0 on failure, non-zero on success 78 | 79 | 80 | // Functions 81 | bool isValid() const { return (width > 0 && height > 0); } 82 | 83 | }; 84 | 85 | 86 | #endif // DR_BITMAP_H 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/3rd_party/polyline_simplification.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: Polyline Simplification, 2D implementation of the Ramer-Douglas-Peucker algorithm 3 | // Author: Tim Sheerman-Chase 4 | // License: Released under CC0 5 | // Source(s): https://gist.github.com/TimSC/0813573d77734bcb6f2cd2cf6cc7aa51 6 | // https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm 7 | // 8 | // Copyright (C) 2016 by Tim Sheerman-Chase 9 | // 10 | // 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../types/pointf.h" 16 | #include "polyline_simplification.h" 17 | 18 | 19 | double PerpendicularDistance(const DrPointF &pt, const DrPointF &line_start, const DrPointF &line_end) { 20 | double dx = line_end.x - line_start.x; 21 | double dy = line_end.y - line_start.y; 22 | 23 | // Normalise 24 | double mag = pow(pow(dx,2.0) + pow(dy,2.0), 0.5); 25 | if (mag > 0.0) { 26 | dx /= mag; 27 | dy /= mag; 28 | } 29 | 30 | double pvx = pt.x - line_start.x; 31 | double pvy = pt.y - line_start.y; 32 | 33 | // Get dot product (project pv onto normalized direction) 34 | double pvdot = dx * pvx + dy * pvy; 35 | 36 | // Scale line direction vector 37 | double dsx = pvdot * dx; 38 | double dsy = pvdot * dy; 39 | 40 | // Subtract this from pv 41 | double ax = pvx - dsx; 42 | double ay = pvy - dsy; 43 | 44 | return pow(pow(ax,2.0) + pow(ay,2.0), 0.5); 45 | } 46 | 47 | 48 | std::vector PolylineSimplification::RamerDouglasPeucker(const std::vector &point_list, double epsilon) { 49 | std::vector simplified; 50 | 51 | if (point_list.size() < 2) { 52 | //throw std::invalid_argument("Not enough points to simplify"); 53 | return point_list; 54 | } 55 | 56 | // Find the point with the maximum distance from line between start and end 57 | double dmax = 0.0; 58 | size_t index = 0; 59 | size_t end = point_list.size() - 1; 60 | for (size_t i = 1; i < end; i++) { 61 | double d = PerpendicularDistance(point_list[i], point_list[0], point_list[end]); 62 | if (d > dmax) { 63 | index = i; 64 | dmax = d; 65 | } 66 | } 67 | 68 | // If max distance is greater than epsilon, recursively simplify 69 | if (dmax > epsilon) { 70 | // Recursive call 71 | std::vector first_line { point_list.begin(), point_list.begin() + static_cast(index) + 1 }; 72 | std::vector last_line { point_list.begin() + static_cast(index), point_list.end() }; 73 | std::vector recursive_results1 = RamerDouglasPeucker(first_line, epsilon); 74 | std::vector recursive_results2 = RamerDouglasPeucker(last_line, epsilon); 75 | 76 | // Build the result list 77 | simplified.assign(recursive_results1.begin(), recursive_results1.end() - 1); 78 | simplified.insert(simplified.end(), recursive_results2.begin(), recursive_results2.end()); 79 | if (simplified.size() < 2) { 80 | throw std::runtime_error("Problem assembling output for Polyline Simplification..."); 81 | return point_list; 82 | } 83 | 84 | } else { 85 | //Just return start and end points 86 | simplified.clear(); 87 | simplified.push_back(point_list[0]); 88 | simplified.push_back(point_list[end]); 89 | } 90 | return simplified; 91 | } 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/types/rect.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "../compare.h" 11 | #include "point.h" 12 | #include "pointf.h" 13 | #include "rect.h" 14 | #include "rectf.h" 15 | 16 | 17 | //#################################################################################### 18 | //## Constructors 19 | //#################################################################################### 20 | DrRect::DrRect() { 21 | x = 0; 22 | y = 0; 23 | width = 0; 24 | height = 0; 25 | } 26 | DrRect::DrRect(int x_, int y_, int width_, int height_) { 27 | x = x_; 28 | y = y_; 29 | width = width_; 30 | height = height_; 31 | } 32 | DrRect::DrRect(const DrPoint &top_left, const DrPoint &bottom_right) { 33 | x = top_left.x; 34 | y = top_left.y; 35 | width = (bottom_right.x - top_left.x); 36 | height = (bottom_right.y - top_left.y); 37 | } 38 | DrRect::DrRect(const DrRect &r) { 39 | x = r.x; 40 | y = r.y; 41 | width = r.width; 42 | height = r.height; 43 | } 44 | DrRect::DrRect(const DrRectF &r) { 45 | x = static_cast(r.x); 46 | y = static_cast(r.y); 47 | width = static_cast(r.width); 48 | height = static_cast(r.height); 49 | } 50 | 51 | 52 | //#################################################################################### 53 | //## Conversion 54 | //#################################################################################### 55 | DrRectF DrRect::toRectF() { return DrRectF(*this); } 56 | 57 | 58 | //#################################################################################### 59 | //## Helper Functions 60 | //#################################################################################### 61 | bool DrRect::contains(const DrPointF pointf) { return contains(DrPoint(pointf)); } 62 | bool DrRect::contains(const DrPoint point) { 63 | if (point.x > left() && point.x < right()) { 64 | if (point.y > top() && point.y < bottom()) 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | // Adjusts bounds of Rect 71 | void DrRect::adjust(int adjust_left, int adjust_top, int adjust_right, int adjust_bottom) { 72 | x += adjust_left; width += (adjust_left * -1); 73 | y += adjust_top; height += (adjust_top * -1); 74 | width += adjust_right; 75 | height += adjust_bottom; 76 | } 77 | 78 | 79 | //#################################################################################### 80 | //## Getters 81 | //#################################################################################### 82 | int DrRect::left() { return (width > 0) ? (x) : (x + width + 1); } 83 | int DrRect::right() { return (width > 0) ? (x + width - 1) : (x); } 84 | 85 | int DrRect::top() { return (height > 0) ? (y) : (y + height + 1); } 86 | int DrRect::bottom() { return (height > 0) ? (y + height - 1) : (y); } 87 | 88 | DrPoint DrRect::topLeft() { return DrPoint(left(), top()); } 89 | DrPoint DrRect::topRight() { return DrPoint(right(), top()); } 90 | DrPoint DrRect::bottomLeft() { return DrPoint(left(), bottom()); } 91 | DrPoint DrRect::bottomRight() { return DrPoint(right(), bottom()); } 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/imaging_filter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "compare.h" 11 | #include "imaging.h" 12 | #include "types/color.h" 13 | #include "types/point.h" 14 | #include "types/pointf.h" 15 | #include "types/rect.h" 16 | 17 | namespace Dr 18 | { 19 | 20 | 21 | //#################################################################################### 22 | //## Loops through image and changes one pixel at a time based on a 23 | //## premultiplied table 24 | //#################################################################################### 25 | DrBitmap ApplySinglePixelFilter(Image_Filter_Type filter, const DrBitmap &from_bitmap, int value) { 26 | DrBitmap image = from_bitmap; 27 | 28 | int table[256]; 29 | for ( int i = 0; i < 256; ++i ) { 30 | switch (filter) { 31 | case Image_Filter_Type::Brightness: table[i] = Dr::Clamp( i + value, 0, 255 ); break; 32 | case Image_Filter_Type::Contrast: table[i] = Dr::Clamp( (( i - 127 ) * (value + 128) / 128 ) + 127, 0, 255 ); break; 33 | default: ; 34 | } 35 | } 36 | 37 | for (size_t y = 0; y < static_cast(image.height); ++y) { 38 | for (size_t x = 0; x < static_cast(image.width); ++x) { 39 | 40 | // Grab the current pixel color 41 | DrColor color = image.getPixel(x, y); 42 | DrHsv hsv; 43 | 44 | switch (filter) { 45 | case Image_Filter_Type::Brightness: 46 | case Image_Filter_Type::Contrast: 47 | color.setRed( table[color.red()] ); 48 | color.setGreen( table[color.green()] ); 49 | color.setBlue( table[color.blue()] ); 50 | break; 51 | case Image_Filter_Type::Saturation: { 52 | // !!!!! #NOTE: QColor returns -1 if image is grayscale 53 | // If thats the case give it a default hue of 0 (red) to match shader 54 | // int hue = (color.hue() == -1) ? 0 : color.hue(); 55 | // color.setHsv(hue, Dr::Clamp(color.saturation() + value, 0, 255), color.value(), color.alpha()); 56 | break; 57 | } 58 | case Image_Filter_Type::Hue: 59 | hsv = color.getHsv(); 60 | hsv.hue = Dr::Clamp(hsv.hue + value, -360.0, 360.0); 61 | color.setFromHsv(hsv); 62 | break; 63 | case Image_Filter_Type::Grayscale: { 64 | double temp = (color.redF() * 0.2126) + (color.greenF() * 0.7152) + (color.blueF() * 0.0722); 65 | color.setRgbF(temp, temp, temp, color.alphaF()); 66 | break; 67 | } 68 | case Image_Filter_Type::Negative: 69 | color.setRgbF(1.0 - color.redF(), 1.0 - color.greenF(), 1.0 - color.blueF(), color.alphaF()); 70 | break; 71 | case Image_Filter_Type::Opacity: 72 | color.setAlpha( Dr::Clamp(color.alpha() + value, 0, 255) ); 73 | break; 74 | } 75 | 76 | // Sets the new pixel color 77 | image.setPixel(x, y, color); 78 | } 79 | } 80 | return image; 81 | } 82 | 83 | 84 | 85 | 86 | } // End namespace Dr 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/types/vec2.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_VEC2_H 11 | #define DR_VEC2_H 12 | 13 | #include 14 | 15 | 16 | 17 | //#################################################################################### 18 | //## DrVec2 19 | //## 2D Vector with the usual operations + - * / overloaded 20 | //############################ 21 | class DrVec2 22 | { 23 | 24 | public: 25 | float x; 26 | float y; 27 | 28 | // Constructors 29 | DrVec2(); 30 | DrVec2(float f); 31 | DrVec2(float x_, float y_); 32 | DrVec2(const DrVec2 &v); 33 | 34 | // Common Vector 2 Types 35 | static DrVec2 unitX() { return DrVec2(1.f, 0.f); } 36 | static DrVec2 unitY() { return DrVec2(0.f, 1.f); } 37 | static DrVec2 zero() { return DrVec2(0.f, 0.f); } 38 | static DrVec2 one() { return DrVec2(1.f, 1.f); } 39 | 40 | // Setters 41 | void set(float x_, float y_) { x = x_; y = y_; } 42 | void setX(float x_) { x = x_; } 43 | void setY(float y_) { y = y_; } 44 | 45 | // Overload Operators - Additions 46 | DrVec2& operator+= (const DrVec2 &v_); 47 | DrVec2& operator+= (float f_); 48 | DrVec2 operator+ (const DrVec2 &v_) const; 49 | DrVec2 operator+ (float f_) const; 50 | friend DrVec2 operator+ (const float d_, const DrVec2 &vec); // Left hand side scalar clockwise addition 51 | 52 | // Overload Operators - Subtractions 53 | DrVec2& operator-= (const DrVec2 &v_); // Opposite vector 54 | DrVec2& operator-= (float f_); 55 | DrVec2 operator- (const DrVec2 &v_) const; 56 | DrVec2 operator- (float f_) const; 57 | friend DrVec2 operator- (const float d_, const DrVec2 &vec); // Left hand side scalar clockwise substraction 58 | 59 | // Overload Operators - Negative 60 | DrVec2 operator- () const; 61 | 62 | // Overload Operators - Comparisons 63 | bool operator!= (const DrVec2 &v_) const; 64 | bool operator== (const DrVec2 &d_) const; 65 | bool operator< (const DrVec2 &v_) const; // No mathematical meaning, but nice for std::map ordering 66 | 67 | // Overload Operators - Divisions 68 | DrVec2& operator/= (const float d_); 69 | DrVec2 operator/ (const float d_) const; 70 | DrVec2 operator/ (const DrVec2 &v_) const; 71 | 72 | // Overload Operators - Multiplication 73 | DrVec2& operator*= (const DrVec2 &d_); 74 | DrVec2& operator*= (const float d_); 75 | DrVec2 operator* (const DrVec2 &v_) const; 76 | DrVec2 operator* (const float d_) const; // Right hand side scalar multiplication 77 | friend DrVec2 operator* (const float d_, const DrVec2 &vec); // Left hand side scalar multiplication 78 | 79 | // Operators on Vector3 80 | float dot(const DrVec2 &v_) const; // Dot product 81 | float normSquared() const; 82 | DrVec2 normalized() const; 83 | float normalize(); 84 | float norm() const; 85 | 86 | // Accessors 87 | const float& operator[] (int i) const; 88 | float& operator[] (int i); 89 | operator const float* () const; // Const Pointer 90 | operator float* (); // Pointer 91 | 92 | }; 93 | 94 | 95 | #endif // DR_VEC2_H 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /shell.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2D to 3D Extrusion 5 | 6 | 7 | 36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 |
44 |
45 | Instructions
46 | Drag and drop image from below (or from desktop, *.png / *.bmp) onto canvas to generate 3D model. Images should be 32bit, 47 | alpha channel is used for determining shape. Image size for this demo limited to a maximum height and width of 2048 pixels. 48 |

49 | Controls
50 | Press keys '1' (lowest) thru '9' (highest) to adjust number of triangles in model.
51 | Press 'w' key to toggle wireframe.
52 | Use mouse to rotate model, mouse wheel to zoom.
53 |
54 |
55 | 56 |
57 | 58 |
59 | 60 | 61 | 62 |
63 | 64 | 102 | 103 | {{{ SCRIPT }}} 104 | 105 | 106 | -------------------------------------------------------------------------------- /src/types/pointf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include 11 | 12 | #include "../compare.h" 13 | #include "point.h" 14 | #include "pointf.h" 15 | 16 | 17 | //#################################################################################### 18 | //## Constructors 19 | //#################################################################################### 20 | DrPointF::DrPointF() { 21 | x = 0; 22 | y = 0; 23 | } 24 | 25 | DrPointF::DrPointF(double x_, double y_) { 26 | x = x_; 27 | y = y_; 28 | } 29 | 30 | DrPointF::DrPointF(const DrPoint point) { 31 | x = static_cast(point.x); 32 | y = static_cast(point.y); 33 | } 34 | 35 | //#################################################################################### 36 | //## Overload Operators 37 | //#################################################################################### 38 | DrPointF& DrPointF::operator=(const DrPointF &other) { 39 | x = other.x; 40 | y = other.y; 41 | return *this; 42 | } 43 | 44 | DrPointF DrPointF::operator+(const DrPointF &other) const { 45 | return DrPointF(x + other.x, y + other.y); 46 | } 47 | 48 | DrPointF DrPointF::operator-(const DrPointF &other) const { 49 | return DrPointF(x - other.x, y - other.y); 50 | } 51 | 52 | DrPointF DrPointF::operator*(double k) const { 53 | return DrPointF(x * k, y * k); 54 | } 55 | 56 | DrPointF DrPointF::operator/(double k) const { 57 | return DrPointF(x / k, y / k); 58 | } 59 | 60 | DrPointF& DrPointF::operator*=(double k) { 61 | x *= k; 62 | y *= k; 63 | return *this; 64 | } 65 | 66 | bool DrPointF::operator==(const DrPointF &other) const { 67 | return Dr::FuzzyCompare(x, other.x) && Dr::FuzzyCompare(y, other.y); 68 | } 69 | 70 | //#################################################################################### 71 | //## Point Functions 72 | //#################################################################################### 73 | double DrPointF::dotProduct(const DrPointF &other) const { 74 | return x * other.x + y * other.y; 75 | } 76 | 77 | double DrPointF::distanceSquared(const DrPointF &to) const { 78 | return static_cast( ((to.x - x) * (to.x - x) + (to.y - y) * (to.y - y)) ); 79 | } 80 | 81 | double DrPointF::distance(const DrPointF &to) const { 82 | return sqrt(distanceSquared(to)); 83 | } 84 | 85 | double DrPointF::distance(const DrPointF &segment_start, const DrPointF &segment_end) const { 86 | const double l2 = segment_start.distanceSquared(segment_end); 87 | if (l2 == 0.0) { 88 | return distance(segment_start); // v == w case 89 | } 90 | 91 | // Consider the line extending the segment, parameterized as v + t (w - v) 92 | // We find projection of DrPointF p onto the line. 93 | // It falls where t = [(p-v) . (w-v)] / |w-v|^2 94 | const double t = ((*this - segment_start).dotProduct(segment_end - segment_end)) / l2; 95 | if (t < 0.0) { 96 | return distance(segment_start); // Beyond the 'v' end of the segment 97 | } else if (t > 1.0) { 98 | return distance(segment_end); // Beyond the 'w' end of the segment 99 | } 100 | 101 | // Projection falls on the segment 102 | DrPointF projection = segment_start + (segment_end - segment_start) * t; 103 | return distance(projection); 104 | } 105 | 106 | double DrPointF::decisionDistance(const std::vector &DrPointFs) const { 107 | DrPointF result = DrPointFs[0]; 108 | double dst = distance(DrPointFs[0]); 109 | for (std::size_t i = 1; i < DrPointFs.size(); i++) { 110 | DrPointF cur = DrPointFs[i]; 111 | double curDistance = distance(cur); 112 | if (curDistance < dst) { 113 | result = cur; 114 | dst = curDistance; 115 | } 116 | } 117 | return dst; 118 | } 119 | 120 | 121 | //#################################################################################### 122 | //## Conversions 123 | //#################################################################################### 124 | DrPoint DrPointF::toPoint() { 125 | return DrPoint( x, y ); 126 | } 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/mesh.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef ENGINE_MESH_H 11 | #define ENGINE_MESH_H 12 | 13 | #include 14 | #include 15 | #include "types/vec3.h" 16 | 17 | // Forward Declarations 18 | class DrBitmap; 19 | class DrImage; 20 | class DrPointF; 21 | class DrVec2; 22 | class DrVec3; 23 | class Vertex; 24 | 25 | // Type Definitions 26 | typedef std::map> NeighborMap; 27 | 28 | // Defines 29 | #define PAR_RGB 3 30 | #define PAR_RGBA 4 31 | 32 | // Constants 33 | const int c_vertex_length = 11; 34 | const float c_extrude_depth = 0.1f; 35 | const float c_cube_depth = 0.5f; 36 | 37 | // Local Enums 38 | enum class Trianglulation { 39 | Ear_Clipping, 40 | Triangulate_Opt, 41 | Monotone, 42 | }; 43 | 44 | enum class Triangle_Point { 45 | Point1, 46 | Point2, 47 | Point3, 48 | }; 49 | 50 | 51 | //#################################################################################### 52 | //## Vertex 53 | //############################ 54 | struct Vertex { 55 | float px, py, pz; // position 56 | float nx, ny, nz; // normal 57 | float tx, ty; // texture_coords 58 | float bx, by, bz; // barycentric 59 | 60 | static Vertex createVertex(DrVec3 pos, DrVec3 norm, DrVec3 uv, DrVec3 bary); 61 | }; 62 | 63 | union Triangle { 64 | Vertex v[3]; 65 | char data[sizeof(Vertex) * 3]; 66 | }; 67 | 68 | 69 | //#################################################################################### 70 | //## DrMesh 71 | //## Stores a list of triangles for rendering 72 | //############################ 73 | class DrMesh 74 | { 75 | public: 76 | std::vector indices { }; 77 | std::vector vertices { }; 78 | 79 | public: 80 | // Constructor 81 | DrMesh(); 82 | 83 | // Properties 84 | int indexCount() const { return indices.size(); } 85 | int vertexCount() const { return vertices.size(); } 86 | 87 | // Creation Functions 88 | void extrudeObjectFromPolygon(DrImage *image, int poly_number, int quality, float depth_multiplier); 89 | void initializeTextureCube(float size); 90 | void initializeTextureQuad(float size); 91 | 92 | // Optimize Mesh 93 | void optimizeMesh(); 94 | void smoothMesh(); 95 | 96 | // Helper Functions 97 | static std::vector insertPoints( const std::vector &outline_points); 98 | static std::vector smoothPoints( const std::vector &outline_points, int neighbors, double neighbor_distance, double weight); 99 | 100 | 101 | // Extrusion Functions 102 | void extrudeFacePolygon(const std::vector &outline_points, int width, int height, int steps, bool reverse = false, float depth_multiplier = 1.f); 103 | void triangulateFace(const std::vector &outline_points, const std::vector> &hole_list, 104 | const DrBitmap &image, Trianglulation type, double alpha_tolerance, float depth_multiplier); 105 | 106 | // Assignment 107 | static void set(Vertex &from_vertex, Vertex &to_vertex); 108 | 109 | // Building Functions 110 | void add(const DrVec3 &vertex, const DrVec3 &normal, const DrVec2 &text_coord, Triangle_Point point_number); 111 | void extrude(float x1, float y1, float tx1, float ty1, 112 | float x2, float y2, float tx2, float ty2, int steps = 1, float depth_multiplier = 1.f); 113 | void cube(float x1, float y1, float tx1, float ty1, 114 | float x2, float y2, float tx2, float ty2, 115 | float x3, float y3, float tx3, float ty3, 116 | float x4, float y4, float tx4, float ty4, float depth); 117 | void quad(float x1, float y1, float tx1, float ty1, 118 | float x2, float y2, float tx2, float ty2, 119 | float x3, float y3, float tx3, float ty3, 120 | float x4, float y4, float tx4, float ty4); 121 | void triangle(float x1, float y1, float tx1, float ty1, 122 | float x2, float y2, float tx2, float ty2, 123 | float x3, float y3, float tx3, float ty3, float depth_multiplier); 124 | }; 125 | 126 | 127 | #endif // ENGINE_MESH_H 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/types/color.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_COLOR_H 11 | #define DR_COLOR_H 12 | 13 | #include 14 | 15 | 16 | //#################################################################################### 17 | //## Easy Colors 18 | //############################ 19 | namespace Dr { 20 | enum Colors { 21 | transparent = 0x00000000, 22 | 23 | white = 0xFFFFFFFF, 24 | black = 0xFF000000, 25 | gray = 0xFF808080, 26 | 27 | red = 0xFFFF0000, 28 | green = 0xFF00FF00, 29 | blue = 0xFF0000FF, 30 | 31 | magenta = 0xFFFF00FF, 32 | cyan = 0xFF00FFFF, 33 | yellow = 0xFFFFFF00, 34 | 35 | orange = 0xFFFFA500, 36 | purple = 0xFF800080, 37 | brown = 0xFF5D4037, 38 | }; 39 | } 40 | 41 | 42 | //#################################################################################### 43 | //## Struct for Hue, Saturation, Value 44 | //############################ 45 | struct DrHsv { 46 | double hue = 0; // 0.0 to 360.0 47 | double saturation = 0; // 0.0 to 1.0 48 | double value = 0; // 0.0 to 1.0 49 | }; 50 | 51 | struct DrRgb { 52 | int red = 0; // 0 to 255 53 | int green = 0; // 0 to 255 54 | int blue = 0; // 0 to 255 55 | }; 56 | 57 | 58 | //#################################################################################### 59 | //## DrColor 60 | //## Useful rgba Color Class 61 | //############################ 62 | class DrColor 63 | { 64 | private: 65 | unsigned char r = 0; // Red Range: 0 to 255 66 | unsigned char g = 0; // Green Range: 0 to 255 67 | unsigned char b = 0; // Blue Range: 0 to 255 68 | unsigned char a = 255; // Alpha Range: 0 to 255 69 | 70 | public: 71 | // Constructors 72 | DrColor(); 73 | DrColor(const unsigned char &r_, const unsigned char &g_, const unsigned char &b_, const unsigned char &a_ = static_cast(255)); 74 | DrColor(int r_, int g_, int b_, int a_ = 255); 75 | DrColor(float r_, float g_, float b_, float a_ = 1.0f); 76 | DrColor(double r_, double g_, double b_, double a_ = 1.0); 77 | DrColor(unsigned int ui); 78 | 79 | // Conversions 80 | unsigned int rgb(); 81 | unsigned int rgba(); 82 | 83 | // Color Editing 84 | DrColor redistributeRgb(double r, double g, double b); 85 | DrColor darker(int percent = 200); 86 | DrColor lighter(int percent = 150); 87 | 88 | // Getters / Setters 89 | const unsigned char& red() const { return r; } 90 | const unsigned char& green() const { return g; } 91 | const unsigned char& blue() const { return b; } 92 | const unsigned char& alpha() const { return a; } 93 | double redF() const { return static_cast(r) / 255.0; } 94 | double greenF() const { return static_cast(g) / 255.0; } 95 | double blueF() const { return static_cast(b) / 255.0; } 96 | double alphaF() const { return static_cast(a) / 255.0; } 97 | 98 | void setRed(int red); 99 | void setRedF(double red); 100 | void setGreen(int green); 101 | void setGreenF(double green); 102 | void setBlue(int blue); 103 | void setBlueF(double blue); 104 | void setAlpha(int alpha); 105 | void setAlphaF(double alpha); 106 | 107 | void setRgbF(double r, double g, double b, double a); 108 | 109 | 110 | // Operator Overloads 111 | DrColor& operator= (const DrColor &other); 112 | DrColor operator+ (const DrColor &other) const; 113 | DrColor operator- (const DrColor &other) const; 114 | DrColor operator* (int k) const; 115 | DrColor operator/ (int k) const; 116 | bool operator== (const DrColor &other) const; 117 | bool operator!= (const DrColor &other) const; 118 | 119 | 120 | // Hsv 121 | DrHsv getHsv(); 122 | void setFromHsv(DrHsv hsv); 123 | 124 | }; 125 | 126 | 127 | #endif // DR_COLOR_H 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/types/vec3.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_VEC3_H 11 | #define DR_VEC3_H 12 | 13 | #include 14 | 15 | #include "../3rd_party/handmade_math.h" 16 | 17 | 18 | //#################################################################################### 19 | //## DrVec3 20 | //## 3D Vector with the usual operations + - * / overloaded 21 | //############################ 22 | class DrVec3 23 | { 24 | 25 | public: 26 | float x; 27 | float y; 28 | float z; 29 | 30 | // Constructors 31 | DrVec3(); 32 | DrVec3(float f); 33 | DrVec3(float x_, float y_, float z_); 34 | DrVec3(double x_, double y_, double z_); 35 | DrVec3(int x_, int y_, int z_); 36 | DrVec3(const DrVec3 &v); 37 | 38 | // Vector 3 Functions 39 | /// @brief Calculates triangle normal from three points of triangle 40 | static DrVec3 triangleNormal(const DrVec3 &point_1, const DrVec3 &point_2, const DrVec3 &point_3); 41 | 42 | // Common Vector 3 Types 43 | static DrVec3 unitX() { return DrVec3(1.f, 0.f, 0.f); } 44 | static DrVec3 unitY() { return DrVec3(0.f, 1.f, 0.f); } 45 | static DrVec3 unitZ() { return DrVec3(0.f, 0.f, 1.f); } 46 | static DrVec3 zero() { return DrVec3(0.f, 0.f, 0.f); } 47 | static DrVec3 one() { return DrVec3(1.f, 1.f, 1.f); } 48 | 49 | // Setters 50 | void set(float x_, float y_, float z_) { x = x_; y = y_; z = z_; } 51 | void setX(float x_) { x = x_; } 52 | void setY(float y_) { y = y_; } 53 | void setZ(float z_) { z = z_; } 54 | 55 | // Overload Operators - Additions 56 | DrVec3& operator+= (const DrVec3 &v_); 57 | DrVec3& operator+= (float f_); 58 | DrVec3 operator+ (const DrVec3 &v_) const; 59 | DrVec3 operator+ (float f_) const; 60 | friend DrVec3 operator+ (const float d_, const DrVec3 &vec); // Left hand side (lhs) scalar cwise addition 61 | 62 | // Overload Operators - Subtractions 63 | DrVec3& operator-= (const DrVec3 &v_); // Opposite vector 64 | DrVec3& operator-= (float f_); 65 | DrVec3 operator- (const DrVec3 &v_) const; 66 | DrVec3 operator- (float f_) const; 67 | friend DrVec3 operator- (const float d_, const DrVec3 &vec); // Left hand side (lhs) scalar cwise substraction 68 | 69 | // Overload Operators - Negative 70 | DrVec3 operator- () const; 71 | 72 | // Overload Operators - Comparisons 73 | bool operator!= (const DrVec3 &v_) const; 74 | bool operator== (const DrVec3 &d_) const; 75 | bool operator< (const DrVec3 &v_) const; // No mathematical meaning, but nice for std::map ordering 76 | 77 | // Overload Operators - Divisions 78 | DrVec3& operator/= (const float d_); 79 | DrVec3 operator/ (const float d_) const; 80 | DrVec3 operator/ (const DrVec3 &v_) const; 81 | 82 | // Overload Operators - Multiplication 83 | DrVec3& operator*= (const DrVec3 &d_); 84 | DrVec3& operator*= (const float d_); 85 | DrVec3 operator* (const DrVec3 &v_) const; 86 | DrVec3 operator* (const float d_) const; // Right hand side (rhs) scalar multiplication 87 | friend DrVec3 operator* (const float d_, const DrVec3 &vec); // Left hand side (lhs) scalar multiplication 88 | friend DrVec3 operator* (const hmm_mat4 &matrix, const DrVec3 &vec); // Left hand side (lhs) matrix multiplication 89 | 90 | // Operators on Vector3 91 | DrVec3 cross (const DrVec3 &v_) const; // Cross product 92 | DrVec3 operator% (const DrVec3 &rhs) const; // Cross product 93 | float dot (const DrVec3 &v_) const; // Dot product 94 | float cotan (const DrVec3 &v_) const; // Cotangent (i.e. 1/tan) between 'this' and v_ 95 | float normSquared() const; 96 | DrVec3 normalized() const; 97 | float normalize(); 98 | float norm() const; 99 | float distance (const DrVec3 &v_) const; 100 | 101 | // Accessors 102 | const float& operator[] (int i) const; 103 | float& operator[] (int i); 104 | operator const float* () const; // Const Pointer 105 | operator float* (); // Pointer 106 | }; 107 | 108 | #endif // DR_VEC3_H 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /example/3rd_party/sokol/sokol_glue.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #ifndef SOKOL_GLUE_INCLUDED 5 | /* 6 | sokol_glue.h -- glue helper functions for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_GLUE_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | ...optionally provide the following macros to override defaults: 17 | 18 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) 20 | SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL 21 | SOKOL_API_IMPL - public function implementation prefix (default: -) 22 | 23 | If sokol_glue.h is compiled as a DLL, define the following before 24 | including the declaration or implementation: 25 | 26 | SOKOL_DLL 27 | 28 | On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) 29 | or __declspec(dllimport) as needed. 30 | 31 | OVERVIEW 32 | ======== 33 | The sokol core headers should not depend on each other, but sometimes 34 | it's useful to have a set of helper functions as "glue" between 35 | two or more sokol headers. 36 | 37 | This is what sokol_glue.h is for. Simply include the header after other 38 | sokol headers (both for the implementation and declaration), and 39 | depending on what headers have been included before, sokol_glue.h 40 | will make available "glue functions". 41 | 42 | PROVIDED FUNCTIONS 43 | ================== 44 | 45 | - if sokol_app.h and sokol_gfx.h is included: 46 | 47 | sg_context_desc sapp_sgcontext(void): 48 | 49 | Returns an initialized sg_context_desc function initialized 50 | by calling sokol_app.h functions. 51 | 52 | LICENSE 53 | ======= 54 | zlib/libpng license 55 | 56 | Copyright (c) 2018 Andre Weissflog 57 | 58 | This software is provided 'as-is', without any express or implied warranty. 59 | In no event will the authors be held liable for any damages arising from the 60 | use of this software. 61 | 62 | Permission is granted to anyone to use this software for any purpose, 63 | including commercial applications, and to alter it and redistribute it 64 | freely, subject to the following restrictions: 65 | 66 | 1. The origin of this software must not be misrepresented; you must not 67 | claim that you wrote the original software. If you use this software in a 68 | product, an acknowledgment in the product documentation would be 69 | appreciated but is not required. 70 | 71 | 2. Altered source versions must be plainly marked as such, and must not 72 | be misrepresented as being the original software. 73 | 74 | 3. This notice may not be removed or altered from any source 75 | distribution. 76 | */ 77 | #define SOKOL_GLUE_INCLUDED 78 | 79 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) 80 | #define SOKOL_GLUE_API_DECL SOKOL_API_DECL 81 | #endif 82 | #ifndef SOKOL_GLUE_API_DECL 83 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) 84 | #define SOKOL_GLUE_API_DECL __declspec(dllexport) 85 | #elif defined(_WIN32) && defined(SOKOL_DLL) 86 | #define SOKOL_GLUE_API_DECL __declspec(dllimport) 87 | #else 88 | #define SOKOL_GLUE_API_DECL extern 89 | #endif 90 | #endif 91 | 92 | #ifdef __cplusplus 93 | extern "C" { 94 | #endif 95 | 96 | #if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) 97 | SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void); 98 | #endif 99 | 100 | #ifdef __cplusplus 101 | } /* extern "C" */ 102 | #endif 103 | #endif /* SOKOL_GLUE_INCLUDED */ 104 | 105 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 106 | #ifdef SOKOL_GLUE_IMPL 107 | #define SOKOL_GLUE_IMPL_INCLUDED (1) 108 | #include /* memset */ 109 | 110 | #ifndef SOKOL_API_IMPL 111 | #define SOKOL_API_IMPL 112 | #endif 113 | 114 | #if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) 115 | SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) { 116 | sg_context_desc desc; 117 | memset(&desc, 0, sizeof(desc)); 118 | desc.color_format = (sg_pixel_format) sapp_color_format(); 119 | desc.depth_format = (sg_pixel_format) sapp_depth_format(); 120 | desc.sample_count = sapp_sample_count(); 121 | desc.gl.force_gles2 = sapp_gles2(); 122 | desc.metal.device = sapp_metal_get_device(); 123 | desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor; 124 | desc.metal.drawable_cb = sapp_metal_get_drawable; 125 | desc.d3d11.device = sapp_d3d11_get_device(); 126 | desc.d3d11.device_context = sapp_d3d11_get_device_context(); 127 | desc.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view; 128 | desc.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view; 129 | desc.wgpu.device = sapp_wgpu_get_device(); 130 | desc.wgpu.render_view_cb = sapp_wgpu_get_render_view; 131 | desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view; 132 | desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view; 133 | return desc; 134 | } 135 | #endif 136 | 137 | #endif /* SOKOL_GLUE_IMPL */ 138 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################## 2 | # 3 | # To compile, set target manually below: 4 | # 5 | # "auto" Attempt to autodetect 6 | # "osx" MacOS 7 | # "web" Emscripten 8 | # "windows" Windows 9 | # "linux" Linux 10 | # 11 | ################################################################## 12 | ################################################################## 13 | 14 | 15 | set(EXPORT_TARGET "auto") 16 | 17 | 18 | ################################################################## 19 | ################################################################## 20 | # set cmake version 21 | cmake_minimum_required(VERSION 3.10) 22 | 23 | # set the project name 24 | project(extrude) 25 | 26 | # glob the files, add the executable 27 | file(GLOB SOURCE_CODE_FILES 28 | "example/*.c**" 29 | "example/3rd_party/*.c**" 30 | "example/3rd_party/sokol/*.c**" 31 | "example/3rd_party/whereami/*.c**" 32 | "src/*.c**" 33 | "src/3rd_party/*.c**" 34 | "src/3rd_party/mesh_optimizer/*.c**" 35 | "src/types/*.c**" 36 | ) 37 | add_executable(${PROJECT_NAME} ${SOURCE_CODE_FILES}) 38 | 39 | # determine target 40 | if (EXPORT_TARGET MATCHES "auto") 41 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 42 | set(EXPORT_TARGET "osx") 43 | elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") 44 | set(EXPORT_TARGET "windows") 45 | elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 46 | set(EXPORT_TARGET "linux") 47 | endif() 48 | endif() 49 | if (EXPORT_TARGET MATCHES "osx") 50 | add_compile_definitions(TARGET_MAC) 51 | elseif (EXPORT_TARGET MATCHES "web") 52 | add_compile_definitions(TARGET_WEB) 53 | elseif (EXPORT_TARGET MATCHES "windows") 54 | add_compile_definitions(TARGET_WIN) 55 | elseif (EXPORT_TARGET MATCHES "linux") 56 | add_compile_definitions(TARGET_LIN) 57 | endif() 58 | 59 | # compile Shaders 60 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 61 | message("Compiling shader") 62 | execute_process ( 63 | COMMAND ./sokol-shdc -i shader.glsl -o example/shader.glsl.h -l glsl330:glsl100:metal_macos:hlsl4 64 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 65 | ) 66 | endif() 67 | 68 | 69 | # copy 'assets' directory to 'build' directory 70 | add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/ $/assets/) 71 | 72 | 73 | ################################################################## 74 | # Sokol Gfx Requires Libraries: 75 | # - on macOS with Metal: Cocoa, IOKit, QuartzCore, Metal, MetalKit, Foundation 76 | # - on macOS with GL: Cocoa, IOKit, QuartzCore, OpenGL, Foundation 77 | # - on iOS with Metal: Foundation, UIKit, Metal, MetalKit 78 | # - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit 79 | # - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?) 80 | # - on Android: GLESv3, EGL, log, android 81 | # - on Windows: no action needed, libs are defined in-source via pragma-comment-lib 82 | # 83 | # Sokol Audio Requires 84 | # - Windows: WASAPI 85 | # - Linux: ALSA (link with asound) 86 | # - macOS/iOS: CoreAudio (link with AudioToolbox) 87 | # - emscripten: WebAudio with ScriptProcessorNode 88 | # - Android: OpenSLES (link with OpenSLES) 89 | ################################################################## 90 | if (EXPORT_TARGET MATCHES "osx") 91 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -xobjective-c -fobjc-arc") 92 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fobjc-arc") 93 | set(CMAKE_C_COMPILER "gcc") 94 | set(CMAKE_CXX_COMPILER "g++") 95 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Cocoa -framework OpenGL -framework IOKit -framework Foundation") 96 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Metal -framework MetalKit -framework AudioToolbox") 97 | find_library(COCOA_LIBRARY Cocoa REQUIRED) 98 | find_library(IOKIT_LIBRARY IOKit REQUIRED) 99 | find_library(OPENGL_LIBRARY OpenGL REQUIRED) 100 | find_library(FOUNDATION_LIBRARY Foundation REQUIRED) 101 | find_library(METAL_LIBRARY Metal REQUIRED) 102 | find_library(METALKIT_LIBRARY MetalKit REQUIRED) 103 | find_library(AUDIOKIT_LIBRARY AudioToolbox REQUIRED) 104 | message(${COCOA_LIBRARY}) 105 | message(${IOKIT_LIBRARY}) 106 | message(${OPENGL_LIBRARY}) 107 | message(${FOUNDATION_LIBRARY}) 108 | message(${METAL_LIBRARY}) 109 | message(${METALKIT_LIBRARY}) 110 | message(${AUDIOKIT_LIBRARY}) 111 | target_link_libraries(${PROJECT_NAME} ${COCOA_LIBRARY}) 112 | target_link_libraries(${PROJECT_NAME} ${IOKIT_LIBRARY}) 113 | target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARY}) 114 | target_link_libraries(${PROJECT_NAME} ${FOUNDATION_LIBRARY}) 115 | target_link_libraries(${PROJECT_NAME} ${METAL_LIBRARY}) 116 | target_link_libraries(${PROJECT_NAME} ${METALKIT_LIBRARY}) 117 | target_link_libraries(${PROJECT_NAME} ${AUDIOKIT_LIBRARY}) 118 | 119 | elseif (EXPORT_TARGET MATCHES "web") 120 | # To optimze file size in order: -O1, -O2, -O3, -Os, -Oz 121 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Oz -std=c++11 --shell-file ../shell.html") 122 | set(CMAKE_C_COMPILER "emcc") 123 | set(CMAKE_CXX_COMPILER "emcc") 124 | SET(CMAKE_EXECUTABLE_SUFFIX ".html") 125 | 126 | # --preload-file assets/shapes.png" # To allow access to local filesystem 127 | # --embed-file assets/shapes.png" # To embed in js file, not compatible yet with sokol_app 128 | # -s ALLOW_MEMORY_GROWTH=1 # To allow for dynamic memory access 129 | set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-s TOTAL_MEMORY=33554432 -s DEMANGLE_SUPPORT=1 -s WASM=0 -std=c++11 --bind ") 130 | 131 | # -s TOTAL_MEMORY=X with X higher than the current value 16777216 132 | 133 | # -s WASM=0 force javascript or 134 | # -s WASM=1 use webassembly 135 | # --shell-file create a html file without emscripten logo and debug shell 136 | 137 | elseif (EXPORT_TARGET MATCHES "windows") 138 | 139 | ### Don't need to do anything for MSVC... ### 140 | 141 | elseif (EXPORT_TARGET MATCHES "linux") 142 | 143 | ### TODO ### 144 | 145 | endif() 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/types/polygonf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | // DrPolygonF::isInside() adapted from: 11 | // https://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/ 12 | // 13 | // 14 | #include 15 | 16 | #include "../compare.h" 17 | #include "pointf.h" 18 | #include "polygonf.h" 19 | 20 | 21 | //#################################################################################### 22 | //## Constructors 23 | //#################################################################################### 24 | DrPolygonF::DrPolygonF() { 25 | m_points.clear(); 26 | } 27 | 28 | DrPolygonF::DrPolygonF(const DrPolygonF &polygon) { 29 | m_points.clear(); 30 | for (auto point : polygon.m_points) { 31 | m_points.push_back(point); 32 | } 33 | } 34 | 35 | 36 | //#################################################################################### 37 | //## Manangement 38 | //#################################################################################### 39 | void DrPolygonF::addPoint(DrPointF point) { 40 | m_points.push_back(point); 41 | } 42 | 43 | 44 | //#################################################################################### 45 | //## Polygon Functions 46 | //#################################################################################### 47 | // Checks if point lies on line segment made from line_a and line_b 48 | bool DrPolygonF::onSegment(DrPointF line_a, DrPointF point, DrPointF line_b) { 49 | return (point.x <= Dr::Max(line_a.x, line_b.x) && point.x >= Dr::Min(line_a.x, line_b.x) && 50 | point.y <= Dr::Max(line_a.y, line_b.y) && point.y >= Dr::Min(line_a.y, line_b.y)); 51 | } 52 | 53 | // Finds orientation of three points (p, q, r). 54 | // RETURNS: 55 | // 0 - Fall along same segment 56 | // 1 - Clock-wise 57 | // 2 - Counterclock-wise 58 | Winding_Orientation DrPolygonF::orientation(DrPointF p, DrPointF q, DrPointF r) { 59 | double value = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); 60 | 61 | if (Dr::FuzzyCompare(value, 0.0)) return Winding_Orientation::LineSegment; // On a line 62 | return ((value > 0) ? Winding_Orientation::Clockwise: Winding_Orientation::CounterClockwise); // Clock-wise or Counterclock-wise 63 | } 64 | 65 | // Makes sure points are in the desired Winding Orientation 66 | void DrPolygonF::ensureWindingOrientation(std::vector &points, Winding_Orientation direction_desired) { 67 | Winding_Orientation winding = findWindingOrientation(points); 68 | if ((winding == Winding_Orientation::Clockwise && direction_desired == Winding_Orientation::CounterClockwise) || 69 | (winding == Winding_Orientation::CounterClockwise && direction_desired == Winding_Orientation::Clockwise)) 70 | { 71 | std::reverse(points.begin(), points.end()); 72 | } 73 | } 74 | 75 | // Returns winding direction of points 76 | Winding_Orientation DrPolygonF::findWindingOrientation(const std::vector &points) { 77 | size_t i1, i2; 78 | double area = 0; 79 | for (i1 = 0; i1 < points.size(); i1++) { 80 | i2 = i1 + 1; 81 | if (i2 == points.size()) i2 = 0; 82 | area += points[i1].x * points[i2].y - points[i1].y * points[i2].x; 83 | } 84 | if (area > 0) return Winding_Orientation::CounterClockwise; 85 | if (area < 0) return Winding_Orientation::Clockwise; 86 | return Winding_Orientation::LineSegment; 87 | } 88 | 89 | 90 | 91 | // RETURNS true if line segment 'p1q1' and 'p2q2' intersect 92 | bool DrPolygonF::doIntersect(DrPointF p1, DrPointF q1, DrPointF p2, DrPointF q2) { 93 | // Find the four orientations needed for general and special cases 94 | Winding_Orientation o1 = orientation(p1, q1, p2); 95 | Winding_Orientation o2 = orientation(p1, q1, q2); 96 | Winding_Orientation o3 = orientation(p2, q2, p1); 97 | Winding_Orientation o4 = orientation(p2, q2, q1); 98 | 99 | // General case 100 | if (o1 != o2 && o3 != o4) return true; 101 | 102 | // Special Cases 103 | if (o1 == Winding_Orientation::LineSegment && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are colinear and p2 lies on segment p1q1 104 | if (o2 == Winding_Orientation::LineSegment && onSegment(p1, q2, q1)) return true; // p1, q1 and p2 are colinear and q2 lies on segment p1q1 105 | if (o3 == Winding_Orientation::LineSegment && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are colinear and p1 lies on segment p2q2 106 | if (o4 == Winding_Orientation::LineSegment && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are colinear and q1 lies on segment p2q2 107 | return false; // Doesn't fall in any of the above cases 108 | } 109 | 110 | // RETURNS true if the point p lies inside the polygon 111 | bool DrPolygonF::isInside(DrPointF point) { 112 | if (numberOfPoints() < 3) return false; // There must be at least 3 vertices 113 | 114 | DrPointF extreme { DR_INFINITY, point.y }; // Create a point for line segment from p to infinite 115 | point.x += 0.00005; // Adjust for edge cases 116 | point.y += 0.00005; // Adjust for edge cases 117 | 118 | // Count intersections of the above line with sides of polygon 119 | int count = 0, i = 0; 120 | do { 121 | int next = (i+1)%numberOfPoints(); 122 | 123 | // Check if the line segment from 'p' to 'extreme' intersects with the line segment from 'polygon[i]' to 'polygon[next]' 124 | if (doIntersect(points()[i], points()[next], point, extreme)) { 125 | // If the point 'p' is colinear with line segment 'i-next', then check if it lies on segment, if it does, return true 126 | if (orientation(points()[i], point, points()[next]) == Winding_Orientation::LineSegment) 127 | return onSegment(points()[i], point, points()[next]); 128 | count++; 129 | } 130 | i = next; 131 | } while (i != 0); 132 | 133 | // Return true if count is odd, false otherwise 134 | return count&1; // Same as (count%2 == 1) 135 | } 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/types/vec2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "../compare.h" 11 | #include "vec2.h" 12 | 13 | 14 | //#################################################################################### 15 | //## Constructors 16 | //#################################################################################### 17 | DrVec2::DrVec2() { x = 0.f; y = 0.f; } 18 | DrVec2::DrVec2(float f) { x = f; y = f; } 19 | DrVec2::DrVec2(float x_, float y_) { x = x_; y = y_; } 20 | DrVec2::DrVec2(const DrVec2 &v) { x = static_cast(v.x); y = static_cast(v.y); } 21 | 22 | 23 | //#################################################################################### 24 | //## Overload Operators - Additions 25 | //#################################################################################### 26 | DrVec2& DrVec2::operator+=(const DrVec2 &v_) { 27 | x += v_.x; 28 | y += v_.y; 29 | return *this; 30 | } 31 | 32 | DrVec2& DrVec2::operator+=(float f_) { 33 | x += f_; 34 | y += f_; 35 | return *this; 36 | } 37 | 38 | DrVec2 DrVec2::operator+(const DrVec2 &v_) const { 39 | return DrVec2(x+v_.x, y+v_.y); 40 | } 41 | 42 | DrVec2 DrVec2::operator+(float f_) const { 43 | return DrVec2(x+f_, y+f_); 44 | } 45 | 46 | // Friend function, left hand side addition 47 | DrVec2 operator+(const float d_, const DrVec2 &vec) { 48 | return DrVec2(d_+vec.x, d_+vec.y); 49 | } 50 | 51 | 52 | //#################################################################################### 53 | //## Overload Operators - Subtractions 54 | //#################################################################################### 55 | DrVec2& DrVec2::operator-=(const DrVec2 &v_) { 56 | x -= v_.x; 57 | y -= v_.y; 58 | return *this; 59 | } 60 | 61 | DrVec2& DrVec2::operator-=(float f_) { 62 | x -= f_; 63 | y -= f_; 64 | return *this; 65 | } 66 | 67 | // Negative 68 | DrVec2 DrVec2::operator-() const { 69 | return DrVec2(-x, -y); 70 | } 71 | 72 | DrVec2 DrVec2::operator-(const DrVec2 &v_) const { 73 | return DrVec2(x-v_.x, y-v_.y); 74 | } 75 | 76 | DrVec2 DrVec2::operator-(float f_) const { return DrVec2(x-f_, y-f_); } 77 | 78 | // Friend function, left hand side subtraction 79 | DrVec2 operator-(const float d_, const DrVec2 &vec) { 80 | return DrVec2(d_-vec.x, d_-vec.y); 81 | } 82 | 83 | 84 | //#################################################################################### 85 | //## Overload Operators - Comparisons 86 | //#################################################################################### 87 | bool DrVec2::operator!=(const DrVec2 &v_) const { 88 | return (Dr::IsCloseTo(x, v_.x, 0.001f) == false) || (Dr::IsCloseTo(y, v_.y, 0.001f) == false); 89 | } 90 | 91 | bool DrVec2::operator==(const DrVec2 &d_) const { 92 | return Dr::IsCloseTo(x, d_.x, 0.001f) && Dr::IsCloseTo(y, d_.y, 0.001f); 93 | } 94 | 95 | bool DrVec2::operator<(const DrVec2 &v_) const { 96 | if (Dr::IsCloseTo(x, v_.x, 0.001f) == false) 97 | return x < v_.x; 98 | else 99 | return y < v_.y; 100 | } 101 | 102 | 103 | //#################################################################################### 104 | //## Overload Operators - Divisions 105 | //#################################################################################### 106 | DrVec2& DrVec2::operator/=(const float d_) { 107 | x /= d_; 108 | y /= d_; 109 | return *this; 110 | } 111 | 112 | DrVec2 DrVec2::operator/(const float d_) const { 113 | return DrVec2(x/d_, y/d_); 114 | } 115 | 116 | DrVec2 DrVec2::operator/(const DrVec2 &v_) const { 117 | return DrVec2(x/v_.x, y/v_.y); 118 | } 119 | 120 | 121 | //#################################################################################### 122 | //## Overload Operators - Divisions 123 | //#################################################################################### 124 | DrVec2& DrVec2::operator*=(const DrVec2 &d_) { 125 | x *= d_.x; 126 | y *= d_.y; 127 | return *this; 128 | } 129 | 130 | DrVec2& DrVec2::operator*=(const float d_) { 131 | x *= d_; 132 | y *= d_; 133 | return *this; 134 | } 135 | 136 | DrVec2 DrVec2::operator*(const DrVec2 &v_) const { 137 | return DrVec2(x*v_.x, y*v_.y); 138 | } 139 | 140 | // Right hand side scalar multiplication 141 | DrVec2 DrVec2::operator*(const float d_) const { 142 | return DrVec2(x*d_, y*d_); 143 | } 144 | 145 | // Left hand side scalar multiplication 146 | DrVec2 operator*(const float d_, const DrVec2 &vec) { 147 | return DrVec2(d_*vec.x, d_*vec.y); 148 | } 149 | 150 | //#################################################################################### 151 | //## Operators on DrVec2 152 | //#################################################################################### 153 | // Dot product 154 | float DrVec2::dot(const DrVec2 &v_) const { 155 | return x*v_.x + y*v_.y; 156 | } 157 | 158 | float DrVec2::normSquared() const { 159 | return dot(*this); 160 | } 161 | 162 | DrVec2 DrVec2::normalized() const { 163 | return (*this) * (1.f / std::sqrt(normSquared())); 164 | } 165 | 166 | float DrVec2::normalize() { 167 | float l = std::sqrt(normSquared()); 168 | float f = 1.f / l; 169 | x *= f; 170 | y *= f; 171 | return l; 172 | } 173 | 174 | float DrVec2::norm() const { 175 | return std::sqrt(normSquared()); 176 | } 177 | 178 | 179 | //#################################################################################### 180 | //## Accessors 181 | //#################################################################################### 182 | // Conversion returns the memory address at the index i 183 | const float& DrVec2::operator[](int i) const { 184 | return (reinterpret_cast(this))[i]; 185 | } 186 | 187 | // Conversion returns the memory address at the index i 188 | float& DrVec2::operator[](int i) { 189 | return (reinterpret_cast(this))[i]; 190 | } 191 | 192 | // Conversion returns the memory address of the vector 193 | // Very convenient to pass a DrVec2 pointer as a parameter to OpenGL: 194 | // EX: 195 | // DrVec2 pos, normal; 196 | // glNormal3fv(normal); 197 | // glVertex3fv(pos); 198 | DrVec2::operator const float*() const { 199 | return &x; 200 | } 201 | 202 | // Conversion returns the memory address of the vector (non const version) 203 | DrVec2::operator float*() { 204 | return &x; 205 | } 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/spatialorder.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // This work is based on: 9 | // Fabian Giesen. Decoding Morton codes. 2009 10 | namespace meshopt 11 | { 12 | 13 | // "Insert" two 0 bits after each of the 10 low bits of x 14 | inline unsigned int part1By2(unsigned int x) 15 | { 16 | x &= 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210 17 | x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210 18 | x = (x ^ (x << 8)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210 19 | x = (x ^ (x << 4)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10 20 | x = (x ^ (x << 2)) & 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 21 | return x; 22 | } 23 | 24 | static void computeOrder(unsigned int* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride) 25 | { 26 | size_t vertex_stride_float = vertex_positions_stride / sizeof(float); 27 | 28 | float minv[3] = {FLT_MAX, FLT_MAX, FLT_MAX}; 29 | float maxv[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; 30 | 31 | for (size_t i = 0; i < vertex_count; ++i) 32 | { 33 | const float* v = vertex_positions_data + i * vertex_stride_float; 34 | 35 | for (int j = 0; j < 3; ++j) 36 | { 37 | float vj = v[j]; 38 | 39 | minv[j] = minv[j] > vj ? vj : minv[j]; 40 | maxv[j] = maxv[j] < vj ? vj : maxv[j]; 41 | } 42 | } 43 | 44 | float extent = 0.f; 45 | 46 | extent = (maxv[0] - minv[0]) < extent ? extent : (maxv[0] - minv[0]); 47 | extent = (maxv[1] - minv[1]) < extent ? extent : (maxv[1] - minv[1]); 48 | extent = (maxv[2] - minv[2]) < extent ? extent : (maxv[2] - minv[2]); 49 | 50 | float scale = extent == 0 ? 0.f : 1.f / extent; 51 | 52 | // generate Morton order based on the position inside a unit cube 53 | for (size_t i = 0; i < vertex_count; ++i) 54 | { 55 | const float* v = vertex_positions_data + i * vertex_stride_float; 56 | 57 | int x = int((v[0] - minv[0]) * scale * 1023.f + 0.5f); 58 | int y = int((v[1] - minv[1]) * scale * 1023.f + 0.5f); 59 | int z = int((v[2] - minv[2]) * scale * 1023.f + 0.5f); 60 | 61 | result[i] = part1By2(x) | (part1By2(y) << 1) | (part1By2(z) << 2); 62 | } 63 | } 64 | 65 | static void computeHistogram(unsigned int (&hist)[1024][3], const unsigned int* data, size_t count) 66 | { 67 | memset(hist, 0, sizeof(hist)); 68 | 69 | // compute 3 10-bit histograms in parallel 70 | for (size_t i = 0; i < count; ++i) 71 | { 72 | unsigned int id = data[i]; 73 | 74 | hist[(id >> 0) & 1023][0]++; 75 | hist[(id >> 10) & 1023][1]++; 76 | hist[(id >> 20) & 1023][2]++; 77 | } 78 | 79 | unsigned int sumx = 0, sumy = 0, sumz = 0; 80 | 81 | // replace histogram data with prefix histogram sums in-place 82 | for (int i = 0; i < 1024; ++i) 83 | { 84 | unsigned int hx = hist[i][0], hy = hist[i][1], hz = hist[i][2]; 85 | 86 | hist[i][0] = sumx; 87 | hist[i][1] = sumy; 88 | hist[i][2] = sumz; 89 | 90 | sumx += hx; 91 | sumy += hy; 92 | sumz += hz; 93 | } 94 | 95 | assert(sumx == count && sumy == count && sumz == count); 96 | } 97 | 98 | static void radixPass(unsigned int* destination, const unsigned int* source, const unsigned int* keys, size_t count, unsigned int (&hist)[1024][3], int pass) 99 | { 100 | int bitoff = pass * 10; 101 | 102 | for (size_t i = 0; i < count; ++i) 103 | { 104 | unsigned int id = (keys[source[i]] >> bitoff) & 1023; 105 | 106 | destination[hist[id][pass]++] = source[i]; 107 | } 108 | } 109 | 110 | } // namespace meshopt 111 | 112 | void meshopt_spatialSortRemap(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) 113 | { 114 | using namespace meshopt; 115 | 116 | assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); 117 | assert(vertex_positions_stride % sizeof(float) == 0); 118 | 119 | meshopt_Allocator allocator; 120 | 121 | unsigned int* keys = allocator.allocate(vertex_count); 122 | computeOrder(keys, vertex_positions, vertex_count, vertex_positions_stride); 123 | 124 | unsigned int hist[1024][3]; 125 | computeHistogram(hist, keys, vertex_count); 126 | 127 | unsigned int* scratch = allocator.allocate(vertex_count); 128 | 129 | for (size_t i = 0; i < vertex_count; ++i) 130 | destination[i] = unsigned(i); 131 | 132 | // 3-pass radix sort computes the resulting order into scratch 133 | radixPass(scratch, destination, keys, vertex_count, hist, 0); 134 | radixPass(destination, scratch, keys, vertex_count, hist, 1); 135 | radixPass(scratch, destination, keys, vertex_count, hist, 2); 136 | 137 | // since our remap table is mapping old=>new, we need to reverse it 138 | for (size_t i = 0; i < vertex_count; ++i) 139 | destination[scratch[i]] = unsigned(i); 140 | } 141 | 142 | void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) 143 | { 144 | using namespace meshopt; 145 | 146 | assert(index_count % 3 == 0); 147 | assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); 148 | assert(vertex_positions_stride % sizeof(float) == 0); 149 | 150 | (void)vertex_count; 151 | 152 | size_t face_count = index_count / 3; 153 | size_t vertex_stride_float = vertex_positions_stride / sizeof(float); 154 | 155 | meshopt_Allocator allocator; 156 | 157 | float* centroids = allocator.allocate(face_count * 3); 158 | 159 | for (size_t i = 0; i < face_count; ++i) 160 | { 161 | unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2]; 162 | assert(a < vertex_count && b < vertex_count && c < vertex_count); 163 | 164 | const float* va = vertex_positions + a * vertex_stride_float; 165 | const float* vb = vertex_positions + b * vertex_stride_float; 166 | const float* vc = vertex_positions + c * vertex_stride_float; 167 | 168 | centroids[i * 3 + 0] = (va[0] + vb[0] + vc[0]) / 3.f; 169 | centroids[i * 3 + 1] = (va[1] + vb[1] + vc[1]) / 3.f; 170 | centroids[i * 3 + 2] = (va[2] + vb[2] + vc[2]) / 3.f; 171 | } 172 | 173 | unsigned int* remap = allocator.allocate(face_count); 174 | 175 | meshopt_spatialSortRemap(remap, centroids, face_count, sizeof(float) * 3); 176 | 177 | // support in-order remap 178 | if (destination == indices) 179 | { 180 | unsigned int* indices_copy = allocator.allocate(index_count); 181 | memcpy(indices_copy, indices, index_count * sizeof(unsigned int)); 182 | indices = indices_copy; 183 | } 184 | 185 | for (size_t i = 0; i < face_count; ++i) 186 | { 187 | unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2]; 188 | unsigned int r = remap[i]; 189 | 190 | destination[r * 3 + 0] = a; 191 | destination[r * 3 + 1] = b; 192 | destination[r * 3 + 2] = c; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/compare.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #ifndef DR_MATH_H 11 | #define DR_MATH_H 12 | 13 | #include 14 | 15 | #include "3rd_party/handmade_math.h" 16 | 17 | 18 | // Forward Declarations 19 | class DrColor; 20 | class DrPoint; 21 | class DrPointF; 22 | 23 | // Local Defines 24 | #define EPSILON 0.00001 25 | #define DR_INFINITY 1e300 26 | #define DR_PI 3.141592653589793238463 27 | 28 | // Math Enums 29 | enum class Winding_Orientation { 30 | Clockwise = 0, 31 | CounterClockwise = 1, 32 | LineSegment = 2, 33 | }; 34 | 35 | namespace Dr { 36 | 37 | 38 | //#################################################################################### 39 | //## FuzzyCompare 40 | //## Returns true if 'number_a' is within +-'EPSILON' of 'number_b' 41 | //## IsCloseTo 42 | //## Returns true if 'number_desired' is within +-'tolerance' of 'number_to_check' 43 | //############################ 44 | template bool FuzzyCompare(const T& number_a, const T& number_b) { 45 | return ( (number_a <= (number_b + EPSILON)) && (number_a >= (number_b - EPSILON)) ); } 46 | 47 | template bool IsCloseTo(const T& number_desired, const T& number_to_check, const T& tolerance) { 48 | return ( (number_to_check <= (number_desired + tolerance)) && (number_to_check >= (number_desired - tolerance)) ); } 49 | 50 | 51 | //#################################################################################### 52 | //## Comparison Functions 53 | //############################ 54 | // Returns number_to_check fit to within the bounds of min / max 55 | template T Clamp(const T& number_to_check, const T& min, const T& max) { 56 | if (number_to_check < min) return min; 57 | if (number_to_check > max) return max; 58 | return number_to_check; 59 | } 60 | 61 | // Return the Max of two values 62 | template T Max(const T& a, const T& b) { return (a > b) ? a : b; } 63 | 64 | // Return the Min of two values 65 | template T Min(const T& a, const T& b) { return (a < b) ? a : b; } 66 | 67 | // Linear Interpolation between two values 68 | template T Lerp(const T& f1, const T& f2, const T& t) { return (f1 * (static_cast(1.0) - t)) + (f2 * t); } 69 | 70 | // Linear Interpolation between two values by no more than d 71 | template T LerpConst(const T& f1, const T& f2, const T& d) { return f1 + Clamp(f2 - f1, -d, d); } 72 | 73 | // Swaps two values 74 | template void Swap(T& number1, T& number2) { T temp = number1; number1 = number2; number2 = temp; } 75 | 76 | 77 | //#################################################################################### 78 | //## Range Functions 79 | //############################ 80 | // Converts value from one range to another 81 | template T RangeConvert(T value, T old_range_min, T old_range_max, T new_range_min, T new_range_max) { 82 | if (value < old_range_min) value = old_range_min; 83 | if (value > old_range_max) value = old_range_max; 84 | 85 | T old_range = (old_range_max - old_range_min); 86 | T new_range = old_range; 87 | T new_value = value; 88 | if (Dr::FuzzyCompare(old_range, static_cast(0))) { 89 | new_value = new_range_min; 90 | } else { 91 | new_range = (new_range_max - new_range_min); 92 | new_value = (((value - old_range_min) * new_range) / old_range) + new_range_min; 93 | } 94 | return new_value; 95 | } 96 | 97 | /// @brief: Rounds to nearest multiple of m, so m == 5 rounds to nearest multiple of 5, m == 0.1 rounds to nearst first decimal place 98 | template T RoundToMultiple(T value, double m) { 99 | double rounded = std::round(static_cast(value) / m) * m; 100 | return static_cast(rounded); 101 | } 102 | 103 | // Round to next power of 2 (see bit-twiddling-hacks) 104 | template int RoundPowerOf2(T number) { 105 | int pow2 = 1; 106 | int new_number = 2; 107 | while (new_number < static_cast(number)) { 108 | new_number = std::pow(2.0, pow2); 109 | pow2++; 110 | } 111 | return new_number; 112 | } 113 | 114 | 115 | //#################################################################################### 116 | //## Angle Functions 117 | //############################ 118 | template T RadiansToDegrees(const T& rad) { return (rad * 57.295779513082321); } // == (180.0 / 3.141592653589793238463); 119 | template T DegreesToRadians(const T& degrees) { return degrees * (DR_PI / 180.0); } 120 | 121 | // Equalizes x, y, and z angles to within 0 to 360 122 | template T EqualizeAngle0to360(const T& angle) { 123 | T equalized = angle; 124 | while (equalized < 0) { equalized += 360; } 125 | while (equalized > 360) { equalized -= 360; } 126 | return equalized; 127 | } 128 | 129 | // Finds closest angle within 180 degrees of angle (both angles must be between 0 to 360) 130 | template T FindClosestAngle180(const T& start, const T& angle) { 131 | T closest = angle; 132 | if (closest - start > 180) 133 | closest -= 360; 134 | else if (start - closest > 180) 135 | closest += 360; 136 | return closest; 137 | } 138 | 139 | double CalcRotationAngleInDegrees(DrPointF center_point, DrPointF target_point); 140 | double Closest90DegreeAngle(double angle, double angle_to_find); 141 | double DifferenceBetween2Angles(double angle1, double angle2); 142 | bool IsSimilarAngle(double angle1, double angle2, double tolerance = 0.001); 143 | bool IsSquare(double check_angle); 144 | bool IsRectangle(DrPointF p1, DrPointF p2, DrPointF p3, DrPointF p4); 145 | DrPointF RotatePointAroundOrigin(DrPointF point, DrPointF origin, double angle, bool angle_is_in_radians = false); 146 | 147 | 148 | //#################################################################################### 149 | //## Color Helper Functions 150 | //############################ 151 | bool IsSameColor(const DrColor &color1, const DrColor &color2, double tolerance); 152 | 153 | 154 | //#################################################################################### 155 | //## Matrix Functions 156 | //############################ 157 | hmm_m4 IdentityMatrix(); 158 | 159 | 160 | } // End namespace Dr 161 | 162 | #endif // DR_MATH_H 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/compare.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "compare.h" 11 | #include "types/color.h" 12 | #include "types/pointf.h" 13 | 14 | namespace Dr { 15 | 16 | 17 | //#################################################################################### 18 | //## 19 | //## Angle Comparision Functions 20 | //## 21 | //#################################################################################### 22 | // Calculates angle from a center point to any target point, 0 = Up 23 | // #KEYWORD: "angleTo", "angleFrom", "angle between", "angleBetween" 24 | // 0 25 | // | 26 | // 270 --+-- 90 27 | // | 28 | // 180 29 | double CalcRotationAngleInDegrees(DrPointF center_point, DrPointF target_point) { 30 | // Calculate the angle in RADIANS from the deltaY and deltaX values (atan2 returns radians values from [-PI, PI]), 0 currently points EAST 31 | // #NOTE: By preserving Y and X param order to atan2, we are expecting a CLOCKWISE angle direction 32 | ///double theta = qAtan2(target_point.y() - center_point.y(), target_point.x() - center_point.x()); 33 | double angle = std::atan2(target_point.y - center_point.y, target_point.x - center_point.x); 34 | angle = RadiansToDegrees( angle ); 35 | 36 | // Rotate the returned angle clockwise by 90 degrees (this rotates the possible results counter-clockwise and makes 0 point NORTH) 37 | // #NOTE: adding to an angle rotates it clockwise, subtracting would rotate it counter-clockwise 38 | angle += 90.0; // in radians 3.141592653589793238463 / 2.0; 39 | 40 | // Convert to positive range (0-360) since we want to prevent negative angles, adjust them now 41 | // We can assume that atan2 will not return a negative value greater than one partial rotation 42 | if (angle < 0) { angle += 360; } 43 | 44 | return angle; 45 | } 46 | 47 | // Returns 'angle' rounded to the nearest 90 multiple of 'angle_to_find' 48 | double Closest90DegreeAngle(double angle, double angle_to_find) { 49 | double closest_angle = 0.0; 50 | double distance_apart = 180.0; 51 | for (double d = angle_to_find - 1080; d < angle_to_find + 1080; d += 90.0) { 52 | if (abs(d - angle) < distance_apart) { 53 | closest_angle = d; 54 | distance_apart = abs(d - angle); 55 | } 56 | } 57 | return closest_angle; 58 | } 59 | 60 | // Returns difference between two angles between 0 and 180 61 | double DifferenceBetween2Angles(double angle1, double angle2) { 62 | while (angle1 < 0) { angle1 += 360; } 63 | while (angle2 < 0) { angle2 += 360; } 64 | while (angle1 >= 360) { angle1 -= 360; } 65 | while (angle2 >= 360) { angle2 -= 360; } 66 | 67 | double diff; 68 | if (angle1 > angle2) { 69 | diff = angle1 - angle2; 70 | if (diff > 180) diff = angle2 - (angle1 - 360); 71 | } else { 72 | diff = angle2 - angle1; 73 | if (diff > 180) diff = angle1 - (angle2 - 360); 74 | } 75 | return diff; 76 | } 77 | 78 | // Returns true of the two angles are parrallel or perpendicular 79 | bool IsSimilarAngle(double angle1, double angle2, double tolerance) { 80 | while (angle1 >= 90) { angle1 -= 90; } 81 | while (angle1 < 0) { angle1 += 90; } 82 | while (angle2 >= 90) { angle2 -= 90; } 83 | while (angle2 < 0) { angle2 += 90; } 84 | return IsCloseTo(angle1, angle2, tolerance); 85 | } 86 | 87 | // Returns true is 'check_angle' in equal to 0, 90, 180, or 270, i.e. "square" angle 88 | bool IsSquare(double check_angle) { 89 | check_angle = abs(check_angle); 90 | while (check_angle >= 360) check_angle -= 360; 91 | if (FuzzyCompare(check_angle, 0.0)) return true; 92 | if (FuzzyCompare(check_angle, 90.0)) return true; 93 | if (FuzzyCompare(check_angle, 180.0)) return true; 94 | if (FuzzyCompare(check_angle, 270.0)) return true; 95 | return false; 96 | } 97 | 98 | // Returns true if 4 points make up a rectangle 99 | bool IsRectangle(DrPointF p1, DrPointF p2, DrPointF p3, DrPointF p4) { 100 | double cx, cy; 101 | double dd1, dd2, dd3, dd4; 102 | 103 | cx = (p1.x + p2.x + p3.x + p4.x) / 4; 104 | cy = (p1.y + p2.y + p3.y + p4.y) / 4; 105 | 106 | dd1 = pow(cx - p1.x, 2.0) + pow(cy - p1.y, 2.0); 107 | dd2 = pow(cx - p2.x, 2.0) + pow(cy - p2.y, 2.0); 108 | dd3 = pow(cx - p3.x, 2.0) + pow(cy - p3.y, 2.0); 109 | dd4 = pow(cx - p4.x, 2.0) + pow(cy - p4.y, 2.0); 110 | return (IsCloseTo(dd1, dd2, 0.001) && IsCloseTo(dd1, dd3, 0.001) && IsCloseTo(dd1, dd4, 0.001)); 111 | } 112 | 113 | DrPointF RotatePointAroundOrigin(DrPointF point, DrPointF origin, double angle, bool angle_is_in_radians) { 114 | if (angle_is_in_radians == false) angle = DegreesToRadians(angle); 115 | 116 | double s = sin(angle); 117 | double c = cos(angle); 118 | 119 | // Translate point to origin 120 | point.x -= origin.x; 121 | point.y -= origin.y; 122 | 123 | // Rotate point 124 | double x = point.x * c - point.y * s; 125 | double y = point.x * s + point.y * c; 126 | 127 | // Translate point back 128 | point.x = x + origin.x; 129 | point.y = y + origin.y; 130 | return point; 131 | } 132 | 133 | 134 | //#################################################################################### 135 | //## Color Helper Functions 136 | //#################################################################################### 137 | // Compares 2 colors, returns true if they are the same 138 | bool IsSameColor(const DrColor &color1, const DrColor &color2, double tolerance) { 139 | return ( Dr::IsCloseTo(color1.redF(), color2.redF(), tolerance) && 140 | Dr::IsCloseTo(color1.greenF(), color2.greenF(), tolerance) && 141 | Dr::IsCloseTo(color1.blueF(), color2.blueF(), tolerance) && 142 | Dr::IsCloseTo(color1.alphaF(), color2.alphaF(), tolerance) ); 143 | } 144 | 145 | 146 | //#################################################################################### 147 | //## 148 | //## Matrix Functions 149 | //## 150 | //############################ 151 | hmm_m4 IdentityMatrix() { 152 | hmm_m4 m; 153 | m.Elements[0][0] = 1.0; 154 | m.Elements[0][1] = 0.0; 155 | m.Elements[0][2] = 0.0; 156 | m.Elements[0][3] = 0.0; 157 | 158 | m.Elements[1][0] = 0.0; 159 | m.Elements[1][1] = 1.0; 160 | m.Elements[1][2] = 0.0; 161 | m.Elements[1][3] = 0.0; 162 | 163 | m.Elements[2][0] = 0.0; 164 | m.Elements[2][1] = 0.0; 165 | m.Elements[2][2] = 1.0; 166 | m.Elements[2][3] = 0.0; 167 | 168 | m.Elements[3][0] = 0.0; 169 | m.Elements[3][1] = 0.0; 170 | m.Elements[3][2] = 0.0; 171 | m.Elements[3][3] = 1.0; 172 | return m; 173 | } 174 | 175 | 176 | 177 | } // End namespace Dr 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/overdrawanalyzer.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // This work is based on: 9 | // Nicolas Capens. Advanced Rasterization. 2004 10 | namespace meshopt 11 | { 12 | 13 | const int kViewport = 256; 14 | 15 | struct OverdrawBuffer 16 | { 17 | float z[kViewport][kViewport][2]; 18 | unsigned int overdraw[kViewport][kViewport][2]; 19 | }; 20 | 21 | #ifndef min 22 | #define min(a, b) ((a) < (b) ? (a) : (b)) 23 | #endif 24 | 25 | #ifndef max 26 | #define max(a, b) ((a) > (b) ? (a) : (b)) 27 | #endif 28 | 29 | static float computeDepthGradients(float& dzdx, float& dzdy, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) 30 | { 31 | // z2 = z1 + dzdx * (x2 - x1) + dzdy * (y2 - y1) 32 | // z3 = z1 + dzdx * (x3 - x1) + dzdy * (y3 - y1) 33 | // (x2-x1 y2-y1)(dzdx) = (z2-z1) 34 | // (x3-x1 y3-y1)(dzdy) (z3-z1) 35 | // we'll solve it with Cramer's rule 36 | float det = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1); 37 | float invdet = (det == 0) ? 0 : 1 / det; 38 | 39 | dzdx = (z2 - z1) * (y3 - y1) - (y2 - y1) * (z3 - z1) * invdet; 40 | dzdy = (x2 - x1) * (z3 - z1) - (z2 - z1) * (x3 - x1) * invdet; 41 | 42 | return det; 43 | } 44 | 45 | // half-space fixed point triangle rasterizer 46 | static void rasterize(OverdrawBuffer* buffer, float v1x, float v1y, float v1z, float v2x, float v2y, float v2z, float v3x, float v3y, float v3z) 47 | { 48 | // compute depth gradients 49 | float DZx, DZy; 50 | float det = computeDepthGradients(DZx, DZy, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z); 51 | int sign = det > 0; 52 | 53 | // flip backfacing triangles to simplify rasterization logic 54 | if (sign) 55 | { 56 | // flipping v2 & v3 preserves depth gradients since they're based on v1 57 | float t; 58 | t = v2x, v2x = v3x, v3x = t; 59 | t = v2y, v2y = v3y, v3y = t; 60 | t = v2z, v2z = v3z, v3z = t; 61 | 62 | // flip depth since we rasterize backfacing triangles to second buffer with reverse Z; only v1z is used below 63 | v1z = kViewport - v1z; 64 | DZx = -DZx; 65 | DZy = -DZy; 66 | } 67 | 68 | // coordinates, 28.4 fixed point 69 | int X1 = int(16.0f * v1x + 0.5f); 70 | int X2 = int(16.0f * v2x + 0.5f); 71 | int X3 = int(16.0f * v3x + 0.5f); 72 | 73 | int Y1 = int(16.0f * v1y + 0.5f); 74 | int Y2 = int(16.0f * v2y + 0.5f); 75 | int Y3 = int(16.0f * v3y + 0.5f); 76 | 77 | // bounding rectangle, clipped against viewport 78 | // since we rasterize pixels with covered centers, min >0.5 should round up 79 | // as for max, due to top-left filling convention we will never rasterize right/bottom edges 80 | // so max >= 0.5 should round down 81 | int minx = max((min(X1, min(X2, X3)) + 7) >> 4, 0); 82 | int maxx = min((max(X1, max(X2, X3)) + 7) >> 4, kViewport); 83 | int miny = max((min(Y1, min(Y2, Y3)) + 7) >> 4, 0); 84 | int maxy = min((max(Y1, max(Y2, Y3)) + 7) >> 4, kViewport); 85 | 86 | // deltas, 28.4 fixed point 87 | int DX12 = X1 - X2; 88 | int DX23 = X2 - X3; 89 | int DX31 = X3 - X1; 90 | 91 | int DY12 = Y1 - Y2; 92 | int DY23 = Y2 - Y3; 93 | int DY31 = Y3 - Y1; 94 | 95 | // fill convention correction 96 | int TL1 = DY12 < 0 || (DY12 == 0 && DX12 > 0); 97 | int TL2 = DY23 < 0 || (DY23 == 0 && DX23 > 0); 98 | int TL3 = DY31 < 0 || (DY31 == 0 && DX31 > 0); 99 | 100 | // half edge equations, 24.8 fixed point 101 | // note that we offset minx/miny by half pixel since we want to rasterize pixels with covered centers 102 | int FX = (minx << 4) + 8; 103 | int FY = (miny << 4) + 8; 104 | int CY1 = DX12 * (FY - Y1) - DY12 * (FX - X1) + TL1 - 1; 105 | int CY2 = DX23 * (FY - Y2) - DY23 * (FX - X2) + TL2 - 1; 106 | int CY3 = DX31 * (FY - Y3) - DY31 * (FX - X3) + TL3 - 1; 107 | float ZY = v1z + (DZx * float(FX - X1) + DZy * float(FY - Y1)) * (1 / 16.f); 108 | 109 | for (int y = miny; y < maxy; y++) 110 | { 111 | int CX1 = CY1; 112 | int CX2 = CY2; 113 | int CX3 = CY3; 114 | float ZX = ZY; 115 | 116 | for (int x = minx; x < maxx; x++) 117 | { 118 | // check if all CXn are non-negative 119 | if ((CX1 | CX2 | CX3) >= 0) 120 | { 121 | if (ZX >= buffer->z[y][x][sign]) 122 | { 123 | buffer->z[y][x][sign] = ZX; 124 | buffer->overdraw[y][x][sign]++; 125 | } 126 | } 127 | 128 | // signed left shift is UB for negative numbers so use unsigned-signed casts 129 | CX1 -= int(unsigned(DY12) << 4); 130 | CX2 -= int(unsigned(DY23) << 4); 131 | CX3 -= int(unsigned(DY31) << 4); 132 | ZX += DZx; 133 | } 134 | 135 | // signed left shift is UB for negative numbers so use unsigned-signed casts 136 | CY1 += int(unsigned(DX12) << 4); 137 | CY2 += int(unsigned(DX23) << 4); 138 | CY3 += int(unsigned(DX31) << 4); 139 | ZY += DZy; 140 | } 141 | } 142 | 143 | } // namespace meshopt 144 | 145 | meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) 146 | { 147 | using namespace meshopt; 148 | 149 | assert(index_count % 3 == 0); 150 | assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); 151 | assert(vertex_positions_stride % sizeof(float) == 0); 152 | 153 | meshopt_Allocator allocator; 154 | 155 | size_t vertex_stride_float = vertex_positions_stride / sizeof(float); 156 | 157 | meshopt_OverdrawStatistics result = {}; 158 | 159 | float minv[3] = {FLT_MAX, FLT_MAX, FLT_MAX}; 160 | float maxv[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; 161 | 162 | for (size_t i = 0; i < vertex_count; ++i) 163 | { 164 | const float* v = vertex_positions + i * vertex_stride_float; 165 | 166 | for (int j = 0; j < 3; ++j) 167 | { 168 | minv[j] = min(minv[j], v[j]); 169 | maxv[j] = max(maxv[j], v[j]); 170 | } 171 | } 172 | 173 | float extent = max(maxv[0] - minv[0], max(maxv[1] - minv[1], maxv[2] - minv[2])); 174 | float scale = kViewport / extent; 175 | 176 | float* triangles = allocator.allocate(index_count * 3); 177 | 178 | for (size_t i = 0; i < index_count; ++i) 179 | { 180 | unsigned int index = indices[i]; 181 | assert(index < vertex_count); 182 | 183 | const float* v = vertex_positions + index * vertex_stride_float; 184 | 185 | triangles[i * 3 + 0] = (v[0] - minv[0]) * scale; 186 | triangles[i * 3 + 1] = (v[1] - minv[1]) * scale; 187 | triangles[i * 3 + 2] = (v[2] - minv[2]) * scale; 188 | } 189 | 190 | OverdrawBuffer* buffer = allocator.allocate(1); 191 | 192 | for (int axis = 0; axis < 3; ++axis) 193 | { 194 | memset(buffer, 0, sizeof(OverdrawBuffer)); 195 | 196 | for (size_t i = 0; i < index_count; i += 3) 197 | { 198 | const float* vn0 = &triangles[3 * (i + 0)]; 199 | const float* vn1 = &triangles[3 * (i + 1)]; 200 | const float* vn2 = &triangles[3 * (i + 2)]; 201 | 202 | switch (axis) 203 | { 204 | case 0: 205 | rasterize(buffer, vn0[2], vn0[1], vn0[0], vn1[2], vn1[1], vn1[0], vn2[2], vn2[1], vn2[0]); 206 | break; 207 | case 1: 208 | rasterize(buffer, vn0[0], vn0[2], vn0[1], vn1[0], vn1[2], vn1[1], vn2[0], vn2[2], vn2[1]); 209 | break; 210 | case 2: 211 | rasterize(buffer, vn0[1], vn0[0], vn0[2], vn1[1], vn1[0], vn1[2], vn2[1], vn2[0], vn2[2]); 212 | break; 213 | } 214 | } 215 | 216 | for (int y = 0; y < kViewport; ++y) 217 | for (int x = 0; x < kViewport; ++x) 218 | for (int s = 0; s < 2; ++s) 219 | { 220 | unsigned int overdraw = buffer->overdraw[y][x][s]; 221 | 222 | result.pixels_covered += overdraw > 0; 223 | result.pixels_shaded += overdraw; 224 | } 225 | } 226 | 227 | result.overdraw = result.pixels_covered ? float(result.pixels_shaded) / float(result.pixels_covered) : 0.f; 228 | 229 | return result; 230 | } 231 | -------------------------------------------------------------------------------- /src/types/image.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "../3rd_party/polyline_simplification.h" 11 | #include "../compare.h" 12 | #include "../imaging.h" 13 | #include "../mesh.h" 14 | #include "color.h" 15 | #include "image.h" 16 | #include "point.h" 17 | #include "pointf.h" 18 | #include "polygonf.h" 19 | #include "rect.h" 20 | #include "rectf.h" 21 | 22 | // Local Constants 23 | const int c_neighbors = 5; // Number of neighbors to smooth points with 24 | 25 | 26 | //#################################################################################### 27 | //## Constructors 28 | //#################################################################################### 29 | DrImage::DrImage(std::string image_name, DrBitmap &bitmap, float lod, bool outline) { 30 | this->m_simple_name = image_name; 31 | this->m_bitmap = bitmap; 32 | 33 | if (outline) { 34 | outlinePoints(lod); 35 | } else { 36 | m_poly_list.push_back(bitmap.polygon().points()); 37 | m_hole_list.push_back({}); 38 | } 39 | } 40 | 41 | 42 | 43 | //#################################################################################### 44 | //## Sets Image Shape as simple box 45 | //#################################################################################### 46 | void DrImage::setSimpleBox() { 47 | std::vector one_poly = m_bitmap.polygon().points(); 48 | std::vector> hole_list {{ }}; 49 | DrPolygonF::ensureWindingOrientation(one_poly, Winding_Orientation::CounterClockwise); 50 | m_poly_list.clear(); 51 | m_hole_list.clear(); 52 | m_poly_list.push_back(one_poly); 53 | m_hole_list.push_back(hole_list); 54 | m_outline_canceled = true; 55 | m_outline_processed = false; 56 | } 57 | 58 | 59 | //#################################################################################### 60 | //## Loads list of points for Image and Image Holes 61 | //## 62 | //## Level of Detail: 63 | //## 0.075 = Detailed 64 | //## 0.250 = Nice 65 | //## 1.000 = Low poly 66 | //## 10.000 = Really low poly 67 | //## 68 | //#################################################################################### 69 | void DrImage::outlinePoints(float lod) { 70 | m_poly_list.clear(); 71 | m_hole_list.clear(); 72 | 73 | // ***** Break pixmap into seperate images for each object in image 74 | std::vector bitmaps; 75 | std::vector rects; 76 | bool cancel = Dr::FindObjectsInBitmap(m_bitmap, bitmaps, rects, c_alpha_tolerance, true); 77 | int number_of_objects = static_cast(bitmaps.size()); 78 | 79 | //std::cout << "Number of objects in image: " << number_of_objects << std::endl; 80 | 81 | // ***** If Find Objects In Bitmap never finished, just add simple box shape 82 | if (cancel) { setSimpleBox(); return; } 83 | 84 | // ******************** Go through each image (object) and Polygon for it 85 | for (int image_number = 0; image_number < number_of_objects; image_number++) { 86 | // Grab next image, check if its valid 87 | DrBitmap &image = bitmaps[image_number]; 88 | if (image.width < 1 || image.height < 1) continue; 89 | 90 | // Trace edge of image 91 | std::vector one_poly = Dr::TraceImageOutline(image); 92 | 93 | // Add rect offset, and add 1.00 pixels buffer around image 94 | double plus_one_pixel_percent_x = 1.0 + (1.00 / m_bitmap.width); 95 | double plus_one_pixel_percent_y = 1.0 + (1.00 / m_bitmap.height); 96 | for (auto &point : one_poly) { 97 | point.x += rects[image_number].left(); 98 | point.y += rects[image_number].top(); 99 | point.x = point.x * plus_one_pixel_percent_x; 100 | point.y = point.y * plus_one_pixel_percent_y; 101 | } 102 | 103 | // Remove duplicate first point 104 | if (one_poly.size() > 3) one_poly.pop_back(); 105 | 106 | // Optimize point list 107 | if (one_poly.size() > (c_neighbors * 2)) { 108 | one_poly = DrMesh::smoothPoints(one_poly, c_neighbors, 20.0, 1.0); 109 | one_poly = PolylineSimplification::RamerDouglasPeucker(one_poly, lod); 110 | //one_poly = DrMesh::insertPoints(one_poly); 111 | } 112 | 113 | // If we only have a couple points left, add shape as a box of the original image, otherwise use PolylineSimplification points 114 | if (one_poly.size() < 4) { 115 | ///points = HullFinder::FindConcaveHull(points, 5.0); 116 | one_poly.clear(); 117 | one_poly.push_back( DrPointF(rects[image_number].topLeft().x, rects[image_number].topLeft().y) ); 118 | one_poly.push_back( DrPointF(rects[image_number].topRight().x, rects[image_number].topRight().y) ); 119 | one_poly.push_back( DrPointF(rects[image_number].bottomRight().x, rects[image_number].bottomRight().y) ); 120 | one_poly.push_back( DrPointF(rects[image_number].bottomLeft().x, rects[image_number].bottomLeft().y) ); 121 | } 122 | 123 | // Check winding 124 | DrPolygonF::ensureWindingOrientation(one_poly, Winding_Orientation::CounterClockwise); 125 | 126 | // Add polygon to list of polygons in shape 127 | m_poly_list.push_back(one_poly); 128 | 129 | 130 | // ******************** Copy image and finds holes as seperate outlines 131 | DrBitmap holes = image.copy(); 132 | Dr::FillBorder(holes, Dr::white, holes.rect()); // Ensures only holes are left as black spots 133 | 134 | // Breaks holes into seperate images for each Hole 135 | std::vector hole_images; 136 | std::vector hole_rects; 137 | Dr::FindObjectsInBitmap(holes, hole_images, hole_rects, c_alpha_tolerance, false); 138 | 139 | // Go through each image (Hole) create list for it 140 | std::vector> hole_list; 141 | for (int hole_number = 0; hole_number < static_cast(hole_images.size()); hole_number++) { 142 | DrBitmap &hole = hole_images[hole_number]; 143 | if (hole.width < 1 || hole.height < 1) continue; 144 | 145 | // Trace edge of hole 146 | std::vector one_hole = Dr::TraceImageOutline(hole); 147 | 148 | // Add in sub image offset to points and hole rects 149 | for (auto &point : one_hole) { 150 | point.x += rects[image_number].left() + hole_rects[hole_number].left(); 151 | point.y += rects[image_number].top() + hole_rects[hole_number].top(); 152 | } 153 | 154 | // Remove duplicate first point 155 | if (one_hole.size() > 3) one_hole.pop_back(); 156 | 157 | // Optimize point list 158 | if (one_hole.size() > (c_neighbors * 2)) { 159 | one_hole = DrMesh::smoothPoints(one_hole, c_neighbors, 30.0, 1.0); 160 | one_hole = PolylineSimplification::RamerDouglasPeucker(one_hole, lod); 161 | //one_hole = DrMesh::insertPoints(one_hole); 162 | } 163 | 164 | if (one_hole.size() > 3) { 165 | DrPolygonF::ensureWindingOrientation(one_hole, Winding_Orientation::Clockwise); 166 | hole_list.push_back(one_hole); 167 | } 168 | } 169 | m_hole_list.push_back(hole_list); 170 | 171 | } // End for each bitmap 172 | 173 | 174 | // ***** Mark this DrImage as having traced the image outline 175 | m_outline_canceled = false; 176 | m_outline_processed = true; 177 | 178 | 179 | } // End outlinePoints() 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/3rd_party/mesh_optimizer/stripifier.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details 2 | #include "meshoptimizer.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // This work is based on: 9 | // Francine Evans, Steven Skiena and Amitabh Varshney. Optimizing Triangle Strips for Fast Rendering. 1996 10 | namespace meshopt 11 | { 12 | 13 | static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence) 14 | { 15 | unsigned int index = 0; 16 | unsigned int iv = ~0u; 17 | 18 | for (size_t i = 0; i < buffer_size; ++i) 19 | { 20 | unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]]; 21 | unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc; 22 | 23 | if (v < iv) 24 | { 25 | index = unsigned(i); 26 | iv = v; 27 | } 28 | } 29 | 30 | return index; 31 | } 32 | 33 | static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1) 34 | { 35 | for (size_t i = 0; i < buffer_size; ++i) 36 | { 37 | unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2]; 38 | 39 | if (e0 == a && e1 == b) 40 | return (int(i) << 2) | 2; 41 | else if (e0 == b && e1 == c) 42 | return (int(i) << 2) | 0; 43 | else if (e0 == c && e1 == a) 44 | return (int(i) << 2) | 1; 45 | } 46 | 47 | return -1; 48 | } 49 | 50 | } // namespace meshopt 51 | 52 | size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index) 53 | { 54 | assert(destination != indices); 55 | assert(index_count % 3 == 0); 56 | 57 | using namespace meshopt; 58 | 59 | meshopt_Allocator allocator; 60 | 61 | const size_t buffer_capacity = 8; 62 | 63 | unsigned int buffer[buffer_capacity][3] = {}; 64 | unsigned int buffer_size = 0; 65 | 66 | size_t index_offset = 0; 67 | 68 | unsigned int strip[2] = {}; 69 | unsigned int parity = 0; 70 | 71 | size_t strip_size = 0; 72 | 73 | // compute vertex valence; this is used to prioritize starting triangle for strips 74 | unsigned int* valence = allocator.allocate(vertex_count); 75 | memset(valence, 0, vertex_count * sizeof(unsigned int)); 76 | 77 | for (size_t i = 0; i < index_count; ++i) 78 | { 79 | unsigned int index = indices[i]; 80 | assert(index < vertex_count); 81 | 82 | valence[index]++; 83 | } 84 | 85 | int next = -1; 86 | 87 | while (buffer_size > 0 || index_offset < index_count) 88 | { 89 | assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3)); 90 | 91 | // fill triangle buffer 92 | while (buffer_size < buffer_capacity && index_offset < index_count) 93 | { 94 | buffer[buffer_size][0] = indices[index_offset + 0]; 95 | buffer[buffer_size][1] = indices[index_offset + 1]; 96 | buffer[buffer_size][2] = indices[index_offset + 2]; 97 | 98 | buffer_size++; 99 | index_offset += 3; 100 | } 101 | 102 | assert(buffer_size > 0); 103 | 104 | if (next >= 0) 105 | { 106 | unsigned int i = next >> 2; 107 | unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2]; 108 | unsigned int v = buffer[i][next & 3]; 109 | 110 | // ordered removal from the buffer 111 | memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0])); 112 | buffer_size--; 113 | 114 | // update vertex valences for strip start heuristic 115 | valence[a]--; 116 | valence[b]--; 117 | valence[c]--; 118 | 119 | // find next triangle (note that edge order flips on every iteration) 120 | // in some cases we need to perform a swap to pick a different outgoing triangle edge 121 | // for [a b c], the default strip edge is [b c], but we might want to use [a c] 122 | int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]); 123 | int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1; 124 | 125 | if (cont < 0 && swap >= 0) 126 | { 127 | // [a b c] => [a b a c] 128 | destination[strip_size++] = strip[0]; 129 | destination[strip_size++] = v; 130 | 131 | // next strip has same winding 132 | // ? a b => b a v 133 | strip[1] = v; 134 | 135 | next = swap; 136 | } 137 | else 138 | { 139 | // emit the next vertex in the strip 140 | destination[strip_size++] = v; 141 | 142 | // next strip has flipped winding 143 | strip[0] = strip[1]; 144 | strip[1] = v; 145 | parity ^= 1; 146 | 147 | next = cont; 148 | } 149 | } 150 | else 151 | { 152 | // if we didn't find anything, we need to find the next new triangle 153 | // we use a heuristic to maximize the strip length 154 | unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]); 155 | unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2]; 156 | 157 | // ordered removal from the buffer 158 | memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0])); 159 | buffer_size--; 160 | 161 | // update vertex valences for strip start heuristic 162 | valence[a]--; 163 | valence[b]--; 164 | valence[c]--; 165 | 166 | // we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration 167 | int ea = findStripNext(buffer, buffer_size, c, b); 168 | int eb = findStripNext(buffer, buffer_size, a, c); 169 | int ec = findStripNext(buffer, buffer_size, b, a); 170 | 171 | // in some cases we can have several matching edges; since we can pick any edge, we pick the one with the smallest 172 | // triangle index in the buffer. this reduces the effect of stripification on ACMR and additionally - for unclear 173 | // reasons - slightly improves the stripification efficiency 174 | int mine = INT_MAX; 175 | mine = (ea >= 0 && mine > ea) ? ea : mine; 176 | mine = (eb >= 0 && mine > eb) ? eb : mine; 177 | mine = (ec >= 0 && mine > ec) ? ec : mine; 178 | 179 | if (ea == mine) 180 | { 181 | // keep abc 182 | next = ea; 183 | } 184 | else if (eb == mine) 185 | { 186 | // abc -> bca 187 | unsigned int t = a; 188 | a = b, b = c, c = t; 189 | 190 | next = eb; 191 | } 192 | else if (ec == mine) 193 | { 194 | // abc -> cab 195 | unsigned int t = c; 196 | c = b, b = a, a = t; 197 | 198 | next = ec; 199 | } 200 | 201 | if (restart_index) 202 | { 203 | if (strip_size) 204 | destination[strip_size++] = restart_index; 205 | 206 | destination[strip_size++] = a; 207 | destination[strip_size++] = b; 208 | destination[strip_size++] = c; 209 | 210 | // new strip always starts with the same edge winding 211 | strip[0] = b; 212 | strip[1] = c; 213 | parity = 1; 214 | } 215 | else 216 | { 217 | if (strip_size) 218 | { 219 | // connect last strip using degenerate triangles 220 | destination[strip_size++] = strip[1]; 221 | destination[strip_size++] = a; 222 | } 223 | 224 | // note that we may need to flip the emitted triangle based on parity 225 | // we always end up with outgoing edge "cb" in the end 226 | unsigned int e0 = parity ? c : b; 227 | unsigned int e1 = parity ? b : c; 228 | 229 | destination[strip_size++] = a; 230 | destination[strip_size++] = e0; 231 | destination[strip_size++] = e1; 232 | 233 | strip[0] = e0; 234 | strip[1] = e1; 235 | parity ^= 1; 236 | } 237 | } 238 | } 239 | 240 | return strip_size; 241 | } 242 | 243 | size_t meshopt_stripifyBound(size_t index_count) 244 | { 245 | assert(index_count % 3 == 0); 246 | 247 | // worst case without restarts is 2 degenerate indices and 3 indices per triangle 248 | // worst case with restarts is 1 restart index and 3 indices per triangle 249 | return (index_count / 3) * 5; 250 | } 251 | 252 | size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index) 253 | { 254 | assert(destination != indices); 255 | 256 | size_t offset = 0; 257 | size_t start = 0; 258 | 259 | for (size_t i = 0; i < index_count; ++i) 260 | { 261 | if (restart_index && indices[i] == restart_index) 262 | { 263 | start = i + 1; 264 | } 265 | else if (i - start >= 2) 266 | { 267 | unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i]; 268 | 269 | // flip winding for odd triangles 270 | if ((i - start) & 1) 271 | { 272 | unsigned int t = a; 273 | a = b, b = t; 274 | } 275 | 276 | // although we use restart indices, strip swaps still produce degenerate triangles, so skip them 277 | if (a != b && a != c && b != c) 278 | { 279 | destination[offset + 0] = a; 280 | destination[offset + 1] = b; 281 | destination[offset + 2] = c; 282 | offset += 3; 283 | } 284 | } 285 | } 286 | 287 | return offset; 288 | } 289 | 290 | size_t meshopt_unstripifyBound(size_t index_count) 291 | { 292 | assert(index_count == 0 || index_count >= 3); 293 | 294 | return (index_count == 0) ? 0 : (index_count - 2) * 3; 295 | } 296 | -------------------------------------------------------------------------------- /src/types/vec3.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "../compare.h" 11 | #include "vec3.h" 12 | 13 | 14 | //#################################################################################### 15 | //## Constructors 16 | //#################################################################################### 17 | DrVec3::DrVec3() { x = 0.f; y = 0.f; z = 0.f; } 18 | DrVec3::DrVec3(float f) { x = f; y = f; z = f; } 19 | DrVec3::DrVec3(float x_, float y_, float z_) { x = x_; y = y_; z = z_; } 20 | DrVec3::DrVec3(double x_, double y_, double z_) { x = static_cast(x_); y = static_cast(y_); z = static_cast(z_); } 21 | DrVec3::DrVec3(int x_, int y_, int z_) { x = static_cast(x_); y = static_cast(y_); z = static_cast(z_); } 22 | DrVec3::DrVec3(const DrVec3 &v) { x = static_cast(v.x); y = static_cast(v.y); z = static_cast(v.z); } 23 | 24 | 25 | //#################################################################################### 26 | //## Vector 3 Functions 27 | //#################################################################################### 28 | // Calculates triangle normal from three points of triangle 29 | DrVec3 DrVec3::triangleNormal(const DrVec3 &point_1, const DrVec3 &point_2, const DrVec3 &point_3) { 30 | // glm::vec3 n = glm::triangleNormal(glm::vec3(point_1.x, point_1.y, point_1.z), 31 | // glm::vec3(point_2.x, point_2.y, point_2.z), 32 | // glm::vec3(point_3.x, point_3.y, point_3.z)); 33 | 34 | // Cross product of two lines on plane 35 | DrVec3 n = (point_1 - point_2) % (point_2 - point_3); 36 | n.normalize(); 37 | 38 | return DrVec3(n.x, n.y, n.z); 39 | } 40 | 41 | 42 | 43 | //#################################################################################### 44 | //## Overload Operators - Additions 45 | //#################################################################################### 46 | DrVec3& DrVec3::operator+=(const DrVec3 &v_) { 47 | x += v_.x; 48 | y += v_.y; 49 | z += v_.z; 50 | return *this; 51 | } 52 | 53 | DrVec3& DrVec3::operator+=(float f_) { 54 | x += f_; 55 | y += f_; 56 | z += f_; 57 | return *this; 58 | } 59 | 60 | DrVec3 DrVec3::operator+(const DrVec3 &v_) const { 61 | return DrVec3(x+v_.x, y+v_.y, z+v_.z); 62 | } 63 | 64 | DrVec3 DrVec3::operator+(float f_) const { 65 | return DrVec3(x+f_, y+f_, z+f_); 66 | } 67 | 68 | // Friend function, left hand side addition 69 | DrVec3 operator+(const float d_, const DrVec3 &vec) { 70 | return DrVec3(d_+vec.x, d_+vec.y, d_+vec.z); 71 | } 72 | 73 | 74 | //#################################################################################### 75 | //## Overload Operators - Subtractions 76 | //#################################################################################### 77 | DrVec3& DrVec3::operator-=(const DrVec3 &v_) { 78 | x -= v_.x; 79 | y -= v_.y; 80 | z -= v_.z; 81 | return *this; 82 | } 83 | 84 | DrVec3& DrVec3::operator-=(float f_) { 85 | x -= f_; 86 | y -= f_; 87 | z -= f_; 88 | return *this; 89 | } 90 | 91 | // Negative 92 | DrVec3 DrVec3::operator-() const { 93 | return DrVec3(-x, -y, -z); 94 | } 95 | 96 | DrVec3 DrVec3::operator-(const DrVec3 &v_) const { 97 | return DrVec3(x-v_.x, y-v_.y, z-v_.z); 98 | } 99 | 100 | DrVec3 DrVec3::operator-(float f_) const { return DrVec3(x-f_, y-f_, z-f_); } 101 | 102 | // Friend function, left hand side subtraction 103 | DrVec3 operator-(const float d_, const DrVec3 &vec) { 104 | return DrVec3(d_-vec.x, d_-vec.y, d_-vec.z); 105 | } 106 | 107 | 108 | //#################################################################################### 109 | //## Overload Operators - Comparisons 110 | //#################################################################################### 111 | bool DrVec3::operator!=(const DrVec3 &v_) const { 112 | return (Dr::IsCloseTo(x, v_.x, 0.001f) == false) || (Dr::IsCloseTo(y, v_.y, 0.001f) == false) || (Dr::IsCloseTo(z, v_.z, 0.001f) == false); 113 | } 114 | 115 | bool DrVec3::operator==(const DrVec3 &d_) const { 116 | return Dr::IsCloseTo(x, d_.x, 0.001f) && Dr::IsCloseTo(y, d_.y, 0.001f) && Dr::IsCloseTo(z, d_.z, 0.001f); 117 | } 118 | 119 | bool DrVec3::operator<(const DrVec3 &v_) const { 120 | if (Dr::IsCloseTo(x, v_.x, 0.001f) == false) 121 | return x < v_.x; 122 | else if (Dr::IsCloseTo(y, v_.y, 0.001f) == false) 123 | return y < v_.y; 124 | else 125 | return z < v_.z; 126 | } 127 | 128 | 129 | //#################################################################################### 130 | //## Overload Operators - Divisions 131 | //#################################################################################### 132 | DrVec3& DrVec3::operator/=(const float d_) { 133 | x /= d_; 134 | y /= d_; 135 | z /= d_; 136 | return *this; 137 | } 138 | 139 | DrVec3 DrVec3::operator/(const float d_) const { 140 | return DrVec3(x/d_, y/d_, z/d_); 141 | } 142 | 143 | DrVec3 DrVec3::operator/(const DrVec3 &v_) const { 144 | return DrVec3(x/v_.x, y/v_.y, z/v_.z); 145 | } 146 | 147 | 148 | //#################################################################################### 149 | //## Overload Operators - Divisions 150 | //#################################################################################### 151 | DrVec3& DrVec3::operator*=(const DrVec3 &d_) { 152 | x *= d_.x; 153 | y *= d_.y; 154 | z *= d_.z; 155 | return *this; 156 | } 157 | 158 | DrVec3& DrVec3::operator*=(const float d_) { 159 | x *= d_; 160 | y *= d_; 161 | z *= d_; 162 | return *this; 163 | } 164 | 165 | DrVec3 DrVec3::operator*(const DrVec3 &v_) const { 166 | return DrVec3(x*v_.x, y*v_.y, z*v_.z); 167 | } 168 | 169 | // Right hand side scalar multiplication 170 | DrVec3 DrVec3::operator*(const float d_) const { 171 | return DrVec3(x*d_, y*d_, z*d_); 172 | } 173 | 174 | // Left hand side scalar multiplication 175 | DrVec3 operator*(const float d_, const DrVec3 &vec) { 176 | return DrVec3(d_*vec.x, d_*vec.y, d_*vec.z); 177 | } 178 | 179 | // Left hand side (lhs) matrix multiplication 180 | DrVec3 operator* (const hmm_mat4 &matrix, const DrVec3 &vec) { 181 | hmm_vec4 multiplied = matrix * hmm_vec4 { vec.x, vec.y, vec.z, 1.f }; 182 | return DrVec3(multiplied.X, multiplied.Y, multiplied.Z); 183 | } 184 | 185 | 186 | //#################################################################################### 187 | //## Operators on DrVec3 188 | //#################################################################################### 189 | // Cross product 190 | DrVec3 DrVec3::cross(const DrVec3 &v_) const { 191 | return DrVec3(y*v_.z - z*v_.y, 192 | z*v_.x - x*v_.z, 193 | x*v_.y - y*v_.x); 194 | } 195 | // Cross product 196 | DrVec3 DrVec3::operator%(const DrVec3 &rhs) const { 197 | return DrVec3(y*rhs.z - z*rhs.y, 198 | z*rhs.x - x*rhs.z, 199 | x*rhs.y - y*rhs.x); 200 | } 201 | 202 | // Dot product 203 | float DrVec3::dot(const DrVec3 &v_) const { 204 | return x*v_.x + y*v_.y + z*v_.z; 205 | } 206 | 207 | // Compute the cotangent (i.e. 1/tan) between 'this' and v_ 208 | float DrVec3::cotan(const DrVec3 &v_) const { 209 | return (this->dot(v_)) / (this->cross(v_)).norm(); 210 | } 211 | 212 | float DrVec3::normSquared() const { 213 | return dot(*this); 214 | } 215 | 216 | DrVec3 DrVec3::normalized() const { 217 | return (*this) * (1.f / std::sqrt(normSquared())); 218 | } 219 | 220 | float DrVec3::normalize() { 221 | float l = std::sqrt(normSquared()); 222 | float f = 1.f / l; 223 | x *= f; 224 | y *= f; 225 | z *= f; 226 | return l; 227 | } 228 | 229 | float DrVec3::norm() const { 230 | return std::sqrt(normSquared()); 231 | } 232 | 233 | float DrVec3::distance(const DrVec3 &v_) const { 234 | float d = sqrt(pow(v_.x - x, 2) + 235 | pow(v_.y - y, 2) + 236 | pow(v_.z - z, 2)); 237 | return d; 238 | } 239 | 240 | //#################################################################################### 241 | //## Accessors 242 | //#################################################################################### 243 | // Conversion returns the memory address at the index i 244 | const float& DrVec3::operator[](int i) const { 245 | return (reinterpret_cast(this))[i]; 246 | } 247 | 248 | // Conversion returns the memory address at the index i 249 | float& DrVec3::operator[](int i) { 250 | return (reinterpret_cast(this))[i]; 251 | } 252 | 253 | // Conversion returns the memory address of the vector 254 | // Very convenient to pass a DrVec3 pointer as a parameter to OpenGL, Example: 255 | // DrVec3 pos, normal; 256 | // glNormal3fv(normal); 257 | // glVertex3fv(pos); 258 | DrVec3::operator const float*() const { 259 | return &x; 260 | } 261 | 262 | // Conversion returns the memory address of the vector (non const version) 263 | DrVec3::operator float*() { 264 | return &x; 265 | } 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /example/input.h: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "3rd_party/sokol/sokol_app.h" 11 | 12 | static const char* eventtype_to_str(sapp_event_type event_type) { 13 | switch (event_type) { 14 | case SAPP_EVENTTYPE_INVALID: return "INVALID"; 15 | case SAPP_EVENTTYPE_KEY_DOWN: return "KEY_DOWN"; 16 | case SAPP_EVENTTYPE_KEY_UP: return "KEY_UP"; 17 | case SAPP_EVENTTYPE_CHAR: return "CHAR"; 18 | case SAPP_EVENTTYPE_MOUSE_DOWN: return "MOUSE_DOWN"; 19 | case SAPP_EVENTTYPE_MOUSE_UP: return "MOUSE_UP"; 20 | case SAPP_EVENTTYPE_MOUSE_SCROLL: return "MOUSE_SCROLL"; 21 | case SAPP_EVENTTYPE_MOUSE_MOVE: return "MOUSE_MOVE"; 22 | case SAPP_EVENTTYPE_MOUSE_ENTER: return "MOUSE_ENTER"; 23 | case SAPP_EVENTTYPE_MOUSE_LEAVE: return "MOUSE_LEAVE"; 24 | case SAPP_EVENTTYPE_TOUCHES_BEGAN: return "TOUCHES_BEGAN"; 25 | case SAPP_EVENTTYPE_TOUCHES_MOVED: return "TOUCHES_MOVED"; 26 | case SAPP_EVENTTYPE_TOUCHES_ENDED: return "TOUCHES_ENDED"; 27 | case SAPP_EVENTTYPE_TOUCHES_CANCELLED: return "TOUCHES_CANCELLED"; 28 | case SAPP_EVENTTYPE_RESIZED: return "RESIZED"; 29 | case SAPP_EVENTTYPE_ICONIFIED: return "ICONIFIED"; 30 | case SAPP_EVENTTYPE_RESTORED: return "RESTORED"; 31 | case SAPP_EVENTTYPE_SUSPENDED: return "SUSPENDED"; 32 | case SAPP_EVENTTYPE_RESUMED: return "RESUMED"; 33 | case SAPP_EVENTTYPE_UPDATE_CURSOR: return "UPDATE_CURSOR"; 34 | case SAPP_EVENTTYPE_QUIT_REQUESTED: return "QUIT_REQUESTED"; 35 | case SAPP_EVENTTYPE_CLIPBOARD_PASTED: return "CLIPBOARD_PASTED"; 36 | case SAPP_EVENTTYPE_FILES_DROPPED: return "FILES_DROPPED"; 37 | default: return "EVENT_UNKNOWN"; 38 | } 39 | } 40 | 41 | static const char* keycode_to_str(sapp_keycode keycode) { 42 | switch (keycode) { 43 | case SAPP_KEYCODE_INVALID: return "INVALID"; 44 | case SAPP_KEYCODE_SPACE: return "SPACE"; 45 | case SAPP_KEYCODE_APOSTROPHE: return "APOSTROPHE"; 46 | case SAPP_KEYCODE_COMMA: return "COMMA"; 47 | case SAPP_KEYCODE_MINUS: return "MINUS"; 48 | case SAPP_KEYCODE_PERIOD: return "PERIOD"; 49 | case SAPP_KEYCODE_SLASH: return "SLASH"; 50 | case SAPP_KEYCODE_0: return "0"; 51 | case SAPP_KEYCODE_1: return "1"; 52 | case SAPP_KEYCODE_2: return "2"; 53 | case SAPP_KEYCODE_3: return "3"; 54 | case SAPP_KEYCODE_4: return "4"; 55 | case SAPP_KEYCODE_5: return "5"; 56 | case SAPP_KEYCODE_6: return "6"; 57 | case SAPP_KEYCODE_7: return "7"; 58 | case SAPP_KEYCODE_8: return "8"; 59 | case SAPP_KEYCODE_9: return "9"; 60 | case SAPP_KEYCODE_SEMICOLON: return "SEMICOLON"; 61 | case SAPP_KEYCODE_EQUAL: return "EQUAL"; 62 | case SAPP_KEYCODE_A: return "A"; 63 | case SAPP_KEYCODE_B: return "B"; 64 | case SAPP_KEYCODE_C: return "C"; 65 | case SAPP_KEYCODE_D: return "D"; 66 | case SAPP_KEYCODE_E: return "E"; 67 | case SAPP_KEYCODE_F: return "F"; 68 | case SAPP_KEYCODE_G: return "G"; 69 | case SAPP_KEYCODE_H: return "H"; 70 | case SAPP_KEYCODE_I: return "I"; 71 | case SAPP_KEYCODE_J: return "J"; 72 | case SAPP_KEYCODE_K: return "K"; 73 | case SAPP_KEYCODE_L: return "L"; 74 | case SAPP_KEYCODE_M: return "M"; 75 | case SAPP_KEYCODE_N: return "N"; 76 | case SAPP_KEYCODE_O: return "O"; 77 | case SAPP_KEYCODE_P: return "P"; 78 | case SAPP_KEYCODE_Q: return "Q"; 79 | case SAPP_KEYCODE_R: return "R"; 80 | case SAPP_KEYCODE_S: return "S"; 81 | case SAPP_KEYCODE_T: return "T"; 82 | case SAPP_KEYCODE_U: return "U"; 83 | case SAPP_KEYCODE_V: return "V"; 84 | case SAPP_KEYCODE_W: return "W"; 85 | case SAPP_KEYCODE_X: return "X"; 86 | case SAPP_KEYCODE_Y: return "Y"; 87 | case SAPP_KEYCODE_Z: return "Z"; 88 | case SAPP_KEYCODE_LEFT_BRACKET: return "LEFT_BRACKET"; 89 | case SAPP_KEYCODE_BACKSLASH: return "BACKSLASH"; 90 | case SAPP_KEYCODE_RIGHT_BRACKET: return "RIGHT_BRACKET"; 91 | case SAPP_KEYCODE_GRAVE_ACCENT: return "ACCENT"; 92 | case SAPP_KEYCODE_WORLD_1: return "WORLD_1"; 93 | case SAPP_KEYCODE_WORLD_2: return "WORLD_2"; 94 | case SAPP_KEYCODE_ESCAPE: return "ESCAPE"; 95 | case SAPP_KEYCODE_ENTER: return "ENTER"; 96 | case SAPP_KEYCODE_TAB: return "TAB"; 97 | case SAPP_KEYCODE_BACKSPACE: return "BACKSPACE"; 98 | case SAPP_KEYCODE_INSERT: return "INSERT"; 99 | case SAPP_KEYCODE_DELETE: return "DELETE"; 100 | case SAPP_KEYCODE_RIGHT: return "RIGHT"; 101 | case SAPP_KEYCODE_LEFT: return "LEFT"; 102 | case SAPP_KEYCODE_DOWN: return "DOWN"; 103 | case SAPP_KEYCODE_UP: return "UP"; 104 | case SAPP_KEYCODE_PAGE_UP: return "PAGE_UP"; 105 | case SAPP_KEYCODE_PAGE_DOWN: return "PAGE_DOWN"; 106 | case SAPP_KEYCODE_HOME: return "HOME"; 107 | case SAPP_KEYCODE_END: return "END"; 108 | case SAPP_KEYCODE_CAPS_LOCK: return "CAPS_LOCK"; 109 | case SAPP_KEYCODE_SCROLL_LOCK: return "SCROLL_LOCK"; 110 | case SAPP_KEYCODE_NUM_LOCK: return "NUM_LOCK"; 111 | case SAPP_KEYCODE_PRINT_SCREEN: return "PRINT_SCREEN"; 112 | case SAPP_KEYCODE_PAUSE: return "PAUSE"; 113 | case SAPP_KEYCODE_F1: return "F1"; 114 | case SAPP_KEYCODE_F2: return "F2"; 115 | case SAPP_KEYCODE_F3: return "F3"; 116 | case SAPP_KEYCODE_F4: return "F4"; 117 | case SAPP_KEYCODE_F5: return "F5"; 118 | case SAPP_KEYCODE_F6: return "F6"; 119 | case SAPP_KEYCODE_F7: return "F7"; 120 | case SAPP_KEYCODE_F8: return "F8"; 121 | case SAPP_KEYCODE_F9: return "F9"; 122 | case SAPP_KEYCODE_F10: return "F10"; 123 | case SAPP_KEYCODE_F11: return "F11"; 124 | case SAPP_KEYCODE_F12: return "F12"; 125 | case SAPP_KEYCODE_F13: return "F13"; 126 | case SAPP_KEYCODE_F14: return "F14"; 127 | case SAPP_KEYCODE_F15: return "F15"; 128 | case SAPP_KEYCODE_F16: return "F16"; 129 | case SAPP_KEYCODE_F17: return "F17"; 130 | case SAPP_KEYCODE_F18: return "F18"; 131 | case SAPP_KEYCODE_F19: return "F19"; 132 | case SAPP_KEYCODE_F20: return "F20"; 133 | case SAPP_KEYCODE_F21: return "F21"; 134 | case SAPP_KEYCODE_F22: return "F22"; 135 | case SAPP_KEYCODE_F23: return "F23"; 136 | case SAPP_KEYCODE_F24: return "F24"; 137 | case SAPP_KEYCODE_F25: return "F25"; 138 | case SAPP_KEYCODE_KP_0: return "KP_0"; 139 | case SAPP_KEYCODE_KP_1: return "KP_1"; 140 | case SAPP_KEYCODE_KP_2: return "KP_2"; 141 | case SAPP_KEYCODE_KP_3: return "KP_3"; 142 | case SAPP_KEYCODE_KP_4: return "KP_4"; 143 | case SAPP_KEYCODE_KP_5: return "KP_5"; 144 | case SAPP_KEYCODE_KP_6: return "KP_6"; 145 | case SAPP_KEYCODE_KP_7: return "KP_7"; 146 | case SAPP_KEYCODE_KP_8: return "KP_8"; 147 | case SAPP_KEYCODE_KP_9: return "KP_9"; 148 | case SAPP_KEYCODE_KP_DECIMAL: return "KP_DECIMAL"; 149 | case SAPP_KEYCODE_KP_DIVIDE: return "KP_DIVIDE"; 150 | case SAPP_KEYCODE_KP_MULTIPLY: return "KP_MULTIPLY"; 151 | case SAPP_KEYCODE_KP_SUBTRACT: return "KP_SUBTRACT"; 152 | case SAPP_KEYCODE_KP_ADD: return "KP_ADD"; 153 | case SAPP_KEYCODE_KP_ENTER: return "KP_ENTER"; 154 | case SAPP_KEYCODE_KP_EQUAL: return "KP_EQUAL"; 155 | case SAPP_KEYCODE_LEFT_SHIFT: return "LEFT_SHIFT"; 156 | case SAPP_KEYCODE_LEFT_CONTROL: return "LEFT_CONTROL"; 157 | case SAPP_KEYCODE_LEFT_ALT: return "LEFT_ALT"; 158 | case SAPP_KEYCODE_LEFT_SUPER: return "LEFT_SUPER"; 159 | case SAPP_KEYCODE_RIGHT_SHIFT: return "RIGHT_SHIFT"; 160 | case SAPP_KEYCODE_RIGHT_CONTROL: return "RIGHT_CONTROL"; 161 | case SAPP_KEYCODE_RIGHT_ALT: return "RIGHT_ALT"; 162 | case SAPP_KEYCODE_RIGHT_SUPER: return "RIGHT_SUPER"; 163 | case SAPP_KEYCODE_MENU: return "MENU"; 164 | default: return "KEYCODE_UNKNOWN?"; 165 | } 166 | } 167 | 168 | static const char* button_to_str(sapp_mousebutton button) { 169 | switch (button) { 170 | case SAPP_MOUSEBUTTON_INVALID: return "INVALID"; 171 | case SAPP_MOUSEBUTTON_LEFT: return "LEFT"; 172 | case SAPP_MOUSEBUTTON_RIGHT: return "RIGHT"; 173 | case SAPP_MOUSEBUTTON_MIDDLE: return "MIDDLE"; 174 | default: return "MOUSE_UNKNOWN"; 175 | } 176 | } 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /src/types/bitmap.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "../3rd_party/stb/stb_image.h" 11 | #include "../3rd_party/stb/stb_image_resize.h" 12 | #include "../3rd_party/stb/stb_image_write.h" 13 | #include "bitmap.h" 14 | #include "color.h" 15 | #include "pointf.h" 16 | #include "polygonf.h" 17 | #include "rect.h" 18 | 19 | 20 | //#################################################################################### 21 | //## Constructors 22 | //#################################################################################### 23 | DrBitmap::DrBitmap(Bitmap_Format desired_format) { 24 | data.clear(); 25 | format = desired_format; 26 | switch (format) { 27 | case Bitmap_Format::Grayscale: channels = 1; break; 28 | case Bitmap_Format::ARGB: channels = 4; break; 29 | } 30 | } 31 | DrBitmap::~DrBitmap() { 32 | data.clear(); 33 | } 34 | 35 | DrBitmap::DrBitmap(const DrBitmap &bitmap, Bitmap_Format desired_format) : DrBitmap(bitmap.width, bitmap.height, desired_format) { 36 | if (bitmap.format == format && data.size()) { 37 | channels = bitmap.channels; 38 | width = bitmap.width; 39 | height = bitmap.height; 40 | data.resize(width * height * bitmap.channels); // Resize data vector 41 | memcpy(&data[0], &bitmap.data[0], data.size()); // Copy data 42 | } else { 43 | for (int x = 0; x < bitmap.width; ++x) { 44 | for (int y = 0; y < bitmap.height; ++y) { 45 | this->setPixel(x, y, bitmap.getPixel(x, y)); 46 | } 47 | } 48 | } 49 | } 50 | 51 | // Create empty bitmap 52 | DrBitmap::DrBitmap(int width_, int height_, Bitmap_Format desired_format) : DrBitmap(desired_format) { 53 | width = width_; 54 | height = height_; 55 | data.resize(width * height * channels); 56 | std::fill(data.begin(), data.end(), 0); 57 | } 58 | 59 | DrBitmap::DrBitmap(std::string filename, Bitmap_Format desired_format) { 60 | loadFromFile(filename, desired_format); 61 | } 62 | 63 | DrBitmap::DrBitmap(const unsigned char *from_data, const int &number_of_bytes, bool compressed, int width_, int height_) { 64 | loadFromMemory(from_data, number_of_bytes, compressed, width_, height_); 65 | } 66 | 67 | 68 | //#################################################################################### 69 | //## Manipulation 70 | //#################################################################################### 71 | DrBitmap DrBitmap::copy() { return (*this); } 72 | 73 | DrBitmap DrBitmap::copy(DrRect ©_rect) { 74 | // Bounds checking 75 | int check_left = copy_rect.left(); 76 | int check_top = copy_rect.top(); 77 | if (check_left < 0) { 78 | copy_rect.width -= abs(check_left); 79 | copy_rect.x += abs(check_left); 80 | } 81 | if (check_top < 0) { 82 | copy_rect.height -= abs(check_top); 83 | copy_rect.y += abs(check_top); 84 | } 85 | if (copy_rect.width <= 0 || copy_rect.height <= 0) return DrBitmap(0, 0); 86 | if (copy_rect.right() > this->width - 1) copy_rect.width = width - copy_rect.left(); 87 | if (copy_rect.bottom() > this->height - 1) copy_rect.height = height - copy_rect.top(); 88 | if (copy_rect.width <= 0 || copy_rect.height <= 0) return DrBitmap(0, 0); 89 | 90 | // Create empty DrBitmap 91 | DrBitmap copy(copy_rect.width, copy_rect.height, format); 92 | 93 | // Copy source 94 | int source_x = copy_rect.left(); 95 | for (int x = 0; x < copy.width; ++x) { 96 | int source_y = copy_rect.top(); 97 | for (int y = 0; y < copy.height; ++y) { 98 | copy.setPixel(x, y, this->getPixel(source_x, source_y)); 99 | ++source_y; 100 | } 101 | ++source_x; 102 | } 103 | return copy; 104 | } 105 | 106 | // Returns a clockwise polygon representing a box around this image 107 | DrPolygonF DrBitmap::polygon() const { 108 | DrPolygonF box; 109 | box.addPoint( DrPointF(0, 0) ); // Top Left 110 | box.addPoint( DrPointF(this->width - 1, 0) ); // Top Right 111 | box.addPoint( DrPointF(this->width - 1, this->height - 1) ); // Bottom Right 112 | box.addPoint( DrPointF(0, this->height - 1) ); // Bottom Left 113 | return box; 114 | } 115 | 116 | DrRect DrBitmap::rect() const { 117 | return DrRect(0, 0, width, height); 118 | } 119 | 120 | // !!!!! #WARNING: No out of bounds checks are done here for speed!! 121 | DrColor DrBitmap::getPixel(int x, int y) const { 122 | size_t index = (y * this->width * channels) + (x * channels); 123 | switch (format) { 124 | case Bitmap_Format::Grayscale: return DrColor(data[index+0], data[index+0], data[index+0], data[index+0]); 125 | case Bitmap_Format::ARGB: return DrColor(data[index+2], data[index+1], data[index+0], data[index+3]); 126 | } 127 | } 128 | 129 | // DrBitmap data is in the format (Format_ARGB32) 130 | void DrBitmap::setPixel(int x, int y, DrColor color) { 131 | size_t index = (y * this->width * channels) + (x * channels); 132 | switch (format) { 133 | case Bitmap_Format::Grayscale: 134 | data[index] = (color.redF() * 0.2126) + (color.greenF() * 0.7152) + (color.blueF() * 0.0722); 135 | break; 136 | case Bitmap_Format::ARGB: 137 | data[index] = color.blue(); 138 | data[index+1] = color.green(); 139 | data[index+2] = color.red(); 140 | data[index+3] = color.alpha(); 141 | break; 142 | } 143 | } 144 | 145 | 146 | //#################################################################################### 147 | //## Testing Alpha 148 | //#################################################################################### 149 | void DrBitmap::fuzzyAlpha() { 150 | for (int x = 0; x < width; ++x) { 151 | for (int y = 0; y < height; ++y) { 152 | DrColor color = getPixel(x, y); 153 | if ((color.red() < 10 && color.green() < 10 && color.blue() < 10) || 154 | (color.red() > 245 && color.green() > 245 && color.blue() > 245)) { 155 | switch (format) { 156 | case Bitmap_Format::Grayscale: color.setRgbF(0, 0, 0, 0); 157 | case Bitmap_Format::ARGB: color.setAlpha(0); 158 | } 159 | setPixel(x, y, color); 160 | } 161 | } 162 | } 163 | } 164 | 165 | 166 | //#################################################################################### 167 | //## Loading Images 168 | //#################################################################################### 169 | void DrBitmap::loadFromFile(std::string filename, Bitmap_Format desired_format) { 170 | // Load Image 171 | unsigned char* ptr = stbi_load(filename.data(), &width, &height, &channels, desired_format); 172 | 173 | // Error Check 174 | if (ptr == nullptr || width == 0 || height == 0) { 175 | width = 0; height = 0; 176 | return; 177 | } 178 | 179 | // Copy Image 180 | data.resize(width * height * channels); // Resize data vector 181 | memcpy(&data[0], ptr, data.size()); // Copy data 182 | stbi_image_free(ptr); // Free the loaded pixels 183 | } 184 | 185 | void DrBitmap::loadFromMemory(const unsigned char *from_data, const int &number_of_bytes, bool compressed, int width_, int height_) { 186 | // Load Raw Data 187 | if (compressed == false) { 188 | width = width_; 189 | height = height_; 190 | data.resize(number_of_bytes); // Resize data vector 191 | memcpy(&data[0], from_data, number_of_bytes); // Copy data 192 | 193 | // Load Image 194 | } else { 195 | const stbi_uc *compressed_data = reinterpret_cast(from_data); 196 | unsigned char* ptr = stbi_load_from_memory(compressed_data, number_of_bytes, &width, &height, &channels, channels); 197 | 198 | // Error Check 199 | if (ptr == nullptr || width == 0 || height == 0) { 200 | width = 0; height = 0; 201 | return; 202 | } 203 | 204 | // Copy Image 205 | data.resize(width * height * channels); // Resize data vector 206 | memcpy(&data[0], ptr, data.size()); // Copy data 207 | stbi_image_free(ptr); // Free the loaded pixels 208 | } 209 | } 210 | 211 | // Aligns pixel format (stb ABGR vs QImage ARGB) for stbi_write 212 | void DrBitmap::saveFormat(std::vector &formatted) { 213 | formatted.resize(width * height * channels); 214 | for (int x = 0; x < width; ++x) { 215 | for (int y = 0; y < height; ++y) { 216 | DrColor pixel = getPixel(x, y); 217 | 218 | size_t index = (y * width * channels) + (x * channels); 219 | formatted[index] = pixel.red(); 220 | formatted[index+1] = pixel.green(); 221 | formatted[index+2] = pixel.blue(); 222 | formatted[index+3] = pixel.alpha(); 223 | } 224 | } 225 | } 226 | 227 | int DrBitmap::saveAsBmp(std::string filename) { 228 | std::vector formatted; 229 | saveFormat(formatted); 230 | int result = stbi_write_bmp(filename.c_str(), width, height, channels, formatted.data()); 231 | return result; 232 | } 233 | 234 | int DrBitmap::saveAsJpg(std::string filename, int quality) { 235 | std::vector formatted; 236 | saveFormat(formatted); 237 | int result = stbi_write_jpg(filename.c_str(), width, height, channels, formatted.data(), quality); 238 | return result; 239 | } 240 | 241 | int DrBitmap::saveAsPng(std::string filename) { 242 | std::vector formatted; 243 | saveFormat(formatted); 244 | int result = stbi_write_png(filename.c_str(), width, height, channels, formatted.data(), width * channels); 245 | return result; 246 | } 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /example/3rd_party/sokol/sokol_time.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) 2 | #define SOKOL_TIME_IMPL 3 | #endif 4 | #ifndef SOKOL_TIME_INCLUDED 5 | /* 6 | sokol_time.h -- simple cross-platform time measurement 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_TIME_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | Optionally provide the following defines with your own implementations: 17 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 18 | SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) 19 | SOKOL_API_DECL - same as SOKOL_TIME_API_DECL 20 | SOKOL_API_IMPL - public function implementation prefix (default: -) 21 | 22 | If sokol_time.h is compiled as a DLL, define the following before 23 | including the declaration or implementation: 24 | 25 | SOKOL_DLL 26 | 27 | On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) 28 | or __declspec(dllimport) as needed. 29 | 30 | void stm_setup(); 31 | Call once before any other functions to initialize sokol_time 32 | (this calls for instance QueryPerformanceFrequency on Windows) 33 | 34 | uint64_t stm_now(); 35 | Get current point in time in unspecified 'ticks'. The value that 36 | is returned has no relation to the 'wall-clock' time and is 37 | not in a specific time unit, it is only useful to compute 38 | time differences. 39 | 40 | uint64_t stm_diff(uint64_t new, uint64_t old); 41 | Computes the time difference between new and old. This will always 42 | return a positive, non-zero value. 43 | 44 | uint64_t stm_since(uint64_t start); 45 | Takes the current time, and returns the elapsed time since start 46 | (this is a shortcut for "stm_diff(stm_now(), start)") 47 | 48 | uint64_t stm_laptime(uint64_t* last_time); 49 | This is useful for measuring frame time and other recurring 50 | events. It takes the current time, returns the time difference 51 | to the value in last_time, and stores the current time in 52 | last_time for the next call. If the value in last_time is 0, 53 | the return value will be zero (this usually happens on the 54 | very first call). 55 | 56 | uint64_t stm_round_to_common_refresh_rate(uint64_t duration) 57 | This oddly named function takes a measured frame time and 58 | returns the closest "nearby" common display refresh rate frame duration 59 | in ticks. If the input duration isn't close to any common display 60 | refresh rate, the input duration will be returned unchanged as a fallback. 61 | The main purpose of this function is to remove jitter/inaccuracies from 62 | measured frame times, and instead use the display refresh rate as 63 | frame duration. 64 | 65 | Use the following functions to convert a duration in ticks into 66 | useful time units: 67 | 68 | double stm_sec(uint64_t ticks); 69 | double stm_ms(uint64_t ticks); 70 | double stm_us(uint64_t ticks); 71 | double stm_ns(uint64_t ticks); 72 | Converts a tick value into seconds, milliseconds, microseconds 73 | or nanoseconds. Note that not all platforms will have nanosecond 74 | or even microsecond precision. 75 | 76 | Uses the following time measurement functions under the hood: 77 | 78 | Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() 79 | MacOS/iOS: mach_absolute_time() 80 | emscripten: performance.now() 81 | Linux+others: clock_gettime(CLOCK_MONOTONIC) 82 | 83 | zlib/libpng license 84 | 85 | Copyright (c) 2018 Andre Weissflog 86 | 87 | This software is provided 'as-is', without any express or implied warranty. 88 | In no event will the authors be held liable for any damages arising from the 89 | use of this software. 90 | 91 | Permission is granted to anyone to use this software for any purpose, 92 | including commercial applications, and to alter it and redistribute it 93 | freely, subject to the following restrictions: 94 | 95 | 1. The origin of this software must not be misrepresented; you must not 96 | claim that you wrote the original software. If you use this software in a 97 | product, an acknowledgment in the product documentation would be 98 | appreciated but is not required. 99 | 100 | 2. Altered source versions must be plainly marked as such, and must not 101 | be misrepresented as being the original software. 102 | 103 | 3. This notice may not be removed or altered from any source 104 | distribution. 105 | */ 106 | #define SOKOL_TIME_INCLUDED (1) 107 | #include 108 | 109 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) 110 | #define SOKOL_TIME_API_DECL SOKOL_API_DECL 111 | #endif 112 | #ifndef SOKOL_TIME_API_DECL 113 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) 114 | #define SOKOL_TIME_API_DECL __declspec(dllexport) 115 | #elif defined(_WIN32) && defined(SOKOL_DLL) 116 | #define SOKOL_TIME_API_DECL __declspec(dllimport) 117 | #else 118 | #define SOKOL_TIME_API_DECL extern 119 | #endif 120 | #endif 121 | 122 | #ifdef __cplusplus 123 | extern "C" { 124 | #endif 125 | 126 | SOKOL_TIME_API_DECL void stm_setup(void); 127 | SOKOL_TIME_API_DECL uint64_t stm_now(void); 128 | SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); 129 | SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); 130 | SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); 131 | SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); 132 | SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); 133 | SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); 134 | SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); 135 | SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); 136 | 137 | #ifdef __cplusplus 138 | } /* extern "C" */ 139 | #endif 140 | #endif // SOKOL_TIME_INCLUDED 141 | 142 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 143 | #ifdef SOKOL_TIME_IMPL 144 | #define SOKOL_TIME_IMPL_INCLUDED (1) 145 | #include /* memset */ 146 | 147 | #ifndef SOKOL_API_IMPL 148 | #define SOKOL_API_IMPL 149 | #endif 150 | #ifndef SOKOL_ASSERT 151 | #include 152 | #define SOKOL_ASSERT(c) assert(c) 153 | #endif 154 | #ifndef _SOKOL_PRIVATE 155 | #if defined(__GNUC__) || defined(__clang__) 156 | #define _SOKOL_PRIVATE __attribute__((unused)) static 157 | #else 158 | #define _SOKOL_PRIVATE static 159 | #endif 160 | #endif 161 | 162 | #if defined(_WIN32) 163 | #ifndef WIN32_LEAN_AND_MEAN 164 | #define WIN32_LEAN_AND_MEAN 165 | #endif 166 | #include 167 | typedef struct { 168 | uint32_t initialized; 169 | LARGE_INTEGER freq; 170 | LARGE_INTEGER start; 171 | } _stm_state_t; 172 | #elif defined(__APPLE__) && defined(__MACH__) 173 | #include 174 | typedef struct { 175 | uint32_t initialized; 176 | mach_timebase_info_data_t timebase; 177 | uint64_t start; 178 | } _stm_state_t; 179 | #elif defined(__EMSCRIPTEN__) 180 | #include 181 | typedef struct { 182 | uint32_t initialized; 183 | double start; 184 | } _stm_state_t; 185 | #else /* anything else, this will need more care for non-Linux platforms */ 186 | #ifdef ESP8266 187 | // On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined 188 | #define CLOCK_MONOTONIC 0 189 | #endif 190 | #include 191 | typedef struct { 192 | uint32_t initialized; 193 | uint64_t start; 194 | } _stm_state_t; 195 | #endif 196 | static _stm_state_t _stm; 197 | 198 | /* prevent 64-bit overflow when computing relative timestamp 199 | see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 200 | */ 201 | #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) 202 | _SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { 203 | int64_t q = value / denom; 204 | int64_t r = value % denom; 205 | return q * numer + r * numer / denom; 206 | } 207 | #endif 208 | 209 | #if defined(__EMSCRIPTEN__) 210 | EM_JS(double, stm_js_perfnow, (void), { 211 | return performance.now(); 212 | }); 213 | #endif 214 | 215 | SOKOL_API_IMPL void stm_setup(void) { 216 | memset(&_stm, 0, sizeof(_stm)); 217 | _stm.initialized = 0xABCDABCD; 218 | #if defined(_WIN32) 219 | QueryPerformanceFrequency(&_stm.freq); 220 | QueryPerformanceCounter(&_stm.start); 221 | #elif defined(__APPLE__) && defined(__MACH__) 222 | mach_timebase_info(&_stm.timebase); 223 | _stm.start = mach_absolute_time(); 224 | #elif defined(__EMSCRIPTEN__) 225 | _stm.start = stm_js_perfnow(); 226 | #else 227 | struct timespec ts; 228 | clock_gettime(CLOCK_MONOTONIC, &ts); 229 | _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; 230 | #endif 231 | } 232 | 233 | SOKOL_API_IMPL uint64_t stm_now(void) { 234 | SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); 235 | uint64_t now; 236 | #if defined(_WIN32) 237 | LARGE_INTEGER qpc_t; 238 | QueryPerformanceCounter(&qpc_t); 239 | now = (uint64_t) int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); 240 | #elif defined(__APPLE__) && defined(__MACH__) 241 | const uint64_t mach_now = mach_absolute_time() - _stm.start; 242 | now = (uint64_t) int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); 243 | #elif defined(__EMSCRIPTEN__) 244 | double js_now = stm_js_perfnow() - _stm.start; 245 | SOKOL_ASSERT(js_now >= 0.0); 246 | now = (uint64_t) (js_now * 1000000.0); 247 | #else 248 | struct timespec ts; 249 | clock_gettime(CLOCK_MONOTONIC, &ts); 250 | now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; 251 | #endif 252 | return now; 253 | } 254 | 255 | SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { 256 | if (new_ticks > old_ticks) { 257 | return new_ticks - old_ticks; 258 | } 259 | else { 260 | return 1; 261 | } 262 | } 263 | 264 | SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { 265 | return stm_diff(stm_now(), start_ticks); 266 | } 267 | 268 | SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { 269 | SOKOL_ASSERT(last_time); 270 | uint64_t dt = 0; 271 | uint64_t now = stm_now(); 272 | if (0 != *last_time) { 273 | dt = stm_diff(now, *last_time); 274 | } 275 | *last_time = now; 276 | return dt; 277 | } 278 | 279 | // first number is frame duration in ns, second number is tolerance in ns, 280 | // the resulting min/max values must not overlap! 281 | static const uint64_t _stm_refresh_rates[][2] = { 282 | { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms 283 | { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms 284 | { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms 285 | { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 286 | { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms 287 | { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms 288 | { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms 289 | { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms 290 | { 0, 0 }, // keep the last element always at zero 291 | }; 292 | 293 | SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { 294 | uint64_t ns; 295 | int i = 0; 296 | while (0 != (ns = _stm_refresh_rates[i][0])) { 297 | uint64_t tol = _stm_refresh_rates[i][1]; 298 | if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { 299 | return ns; 300 | } 301 | i++; 302 | } 303 | // fallthough: didn't fit into any buckets 304 | return ticks; 305 | } 306 | 307 | SOKOL_API_IMPL double stm_sec(uint64_t ticks) { 308 | return (double)ticks / 1000000000.0; 309 | } 310 | 311 | SOKOL_API_IMPL double stm_ms(uint64_t ticks) { 312 | return (double)ticks / 1000000.0; 313 | } 314 | 315 | SOKOL_API_IMPL double stm_us(uint64_t ticks) { 316 | return (double)ticks / 1000.0; 317 | } 318 | 319 | SOKOL_API_IMPL double stm_ns(uint64_t ticks) { 320 | return (double)ticks; 321 | } 322 | #endif /* SOKOL_TIME_IMPL */ 323 | 324 | -------------------------------------------------------------------------------- /src/types/color.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include 11 | 12 | #include "../compare.h" 13 | #include "color.h" 14 | 15 | 16 | inline constexpr unsigned char operator "" _uc( unsigned long long arg ) noexcept { 17 | return static_cast(arg); 18 | } 19 | 20 | //#################################################################################### 21 | //## Constructors 22 | //#################################################################################### 23 | DrColor::DrColor() { 24 | r = 0; g = 0; b = 0; a = 255; 25 | } 26 | DrColor::DrColor(const unsigned char &r_, const unsigned char &g_, const unsigned char &b_, const unsigned char &a_) { 27 | r = Dr::Clamp(static_cast(r_), 0_uc, 255_uc); 28 | g = Dr::Clamp(static_cast(g_), 0_uc, 255_uc); 29 | b = Dr::Clamp(static_cast(b_), 0_uc, 255_uc); 30 | a = Dr::Clamp(static_cast(a_), 0_uc, 255_uc); 31 | } 32 | DrColor::DrColor(int r_, int g_, int b_, int a_) { 33 | r = Dr::Clamp(static_cast(r_), 0_uc, 255_uc); 34 | g = Dr::Clamp(static_cast(g_), 0_uc, 255_uc); 35 | b = Dr::Clamp(static_cast(b_), 0_uc, 255_uc); 36 | a = Dr::Clamp(static_cast(a_), 0_uc, 255_uc); 37 | } 38 | DrColor::DrColor(float r_, float g_, float b_, float a_) { 39 | r = static_cast(Dr::Clamp(r_, 0.0f, 1.0f) * 255.0f); 40 | g = static_cast(Dr::Clamp(g_, 0.0f, 1.0f) * 255.0f); 41 | b = static_cast(Dr::Clamp(b_, 0.0f, 1.0f) * 255.0f); 42 | a = static_cast(Dr::Clamp(a_, 0.0f, 1.0f) * 255.0f); 43 | } 44 | DrColor::DrColor(double r_, double g_, double b_, double a_) { 45 | r = static_cast(Dr::Clamp(r_, 0.0, 1.0) * 255.0); 46 | g = static_cast(Dr::Clamp(g_, 0.0, 1.0) * 255.0); 47 | b = static_cast(Dr::Clamp(b_, 0.0, 1.0) * 255.0); 48 | a = static_cast(Dr::Clamp(a_, 0.0, 1.0) * 255.0); 49 | } 50 | DrColor::DrColor(unsigned int ui) { 51 | a = (ui & 0xFF000000) >> 24; 52 | r = (ui & 0x00FF0000) >> 16; 53 | g = (ui & 0x0000FF00) >> 8; 54 | b = (ui & 0x000000FF) >> 0; 55 | } 56 | 57 | 58 | //#################################################################################### 59 | //## Unsigned Int 60 | //#################################################################################### 61 | unsigned int DrColor::rgb() { 62 | unsigned int color = static_cast(b) | 63 | (static_cast(g) << 8) | 64 | (static_cast(r) << 16); 65 | return color; 66 | } 67 | unsigned int DrColor::rgba() { 68 | unsigned int color = static_cast(b) | 69 | (static_cast(g) << 8) | 70 | (static_cast(r) << 16) | 71 | (static_cast(a) << 24); 72 | return color; 73 | } 74 | 75 | 76 | //#################################################################################### 77 | //## Setters 78 | //#################################################################################### 79 | void DrColor::setRed(int red) { r = Dr::Clamp(static_cast(red), 0_uc, 255_uc); } 80 | void DrColor::setRedF(double red) { r = Dr::Clamp(static_cast(red * 255.0), 0_uc, 255_uc); } 81 | void DrColor::setGreen(int green) { g = Dr::Clamp(static_cast(green), 0_uc, 255_uc); } 82 | void DrColor::setGreenF(double green) { g = Dr::Clamp(static_cast(green * 255.0), 0_uc, 255_uc); } 83 | void DrColor::setBlue(int blue) { b = Dr::Clamp(static_cast(blue), 0_uc, 255_uc); } 84 | void DrColor::setBlueF(double blue) { b = Dr::Clamp(static_cast(blue * 255.0), 0_uc, 255_uc); } 85 | void DrColor::setAlpha(int alpha) { a = Dr::Clamp(static_cast(alpha), 0_uc, 255_uc); } 86 | void DrColor::setAlphaF(double alpha) { a = Dr::Clamp(static_cast(alpha * 255.0), 0_uc, 255_uc); } 87 | 88 | void DrColor::setRgbF(double red, double green, double blue, double alpha) { 89 | r = Dr::Clamp(static_cast(red * 255.0), 0_uc, 255_uc); 90 | g = Dr::Clamp(static_cast(green * 255.0), 0_uc, 255_uc); 91 | b = Dr::Clamp(static_cast(blue * 255.0), 0_uc, 255_uc); 92 | a = Dr::Clamp(static_cast(alpha * 255.0), 0_uc, 255_uc); 93 | } 94 | 95 | //#################################################################################### 96 | //## Color Editing 97 | //#################################################################################### 98 | // Values are 0 to 255+ range, redistributes overflow of highest value to other values (i.e. DrColor(260r, 0g, 0b) becomes DrColor(255r, 2.5b, 2.5g) 99 | DrColor DrColor::redistributeRgb(double r, double g, double b) { 100 | double extra = 0.0; 101 | if (r > 255.0) { 102 | extra = r - 255.0; 103 | b += extra * (b / (b + g)); 104 | g += extra * (g / (b + g)); 105 | } else if (g > 255.0) { 106 | extra = g - 255.0; 107 | r += extra * (r / (r + b)); 108 | b += extra * (b / (r + b)); 109 | } else if (b > 255.0) { 110 | extra = b - 255.0; 111 | r += extra * (r / (r + g)); 112 | g += extra * (g / (r + g)); 113 | } 114 | int ir = Dr::Clamp(static_cast(r), 0, 255); 115 | int ig = Dr::Clamp(static_cast(g), 0, 255); 116 | int ib = Dr::Clamp(static_cast(b), 0, 255); 117 | return DrColor(ir, ig, ib); 118 | } 119 | 120 | // Darkens color by percent 121 | DrColor DrColor::darker(int percent) { 122 | // Can't process negative percent 123 | if (percent <= 0) return *this; 124 | 125 | // Convert to multiplier, multiply rgb values, redistribute overflows 126 | double m = 1.0 / (static_cast(percent) / 100.0); 127 | return redistributeRgb(m * this->red(), m * this->green(), m * this->blue()); 128 | } 129 | 130 | // Lightens color by percent 131 | DrColor DrColor::lighter(int percent) { 132 | // Can't process negative percent 133 | if (percent <= 0) return (*this); 134 | 135 | // Convert to multiplier, multiply rgb values, redistribute overflows 136 | double m = static_cast(percent) / 100.0; 137 | return redistributeRgb(m * this->red(), m * this->green(), m * this->blue()); 138 | } 139 | 140 | 141 | //#################################################################################### 142 | //## Overload Operators 143 | //#################################################################################### 144 | DrColor& DrColor::operator=(const DrColor &other) { 145 | r = other.r; 146 | g = other.g; 147 | b = other.b; 148 | a = other.a; 149 | return *this; 150 | } 151 | 152 | DrColor DrColor::operator+(const DrColor &other) const { 153 | return DrColor(Dr::Clamp(r + other.r, 0, 255), 154 | Dr::Clamp(g + other.g, 0, 255), 155 | Dr::Clamp(b + other.b, 0, 255), 156 | Dr::Clamp(a + other.a, 0, 255)); 157 | } 158 | 159 | DrColor DrColor::operator-(const DrColor &other) const { 160 | return DrColor(Dr::Clamp(r - other.r, 0, 255), 161 | Dr::Clamp(g - other.g, 0, 255), 162 | Dr::Clamp(b - other.b, 0, 255), 163 | Dr::Clamp(a - other.a, 0, 255)); 164 | } 165 | 166 | DrColor DrColor::operator*(int k) const { 167 | return DrColor(Dr::Clamp(r * k, 0, 255), 168 | Dr::Clamp(g * k, 0, 255), 169 | Dr::Clamp(b * k, 0, 255), 170 | Dr::Clamp(a * k, 0, 255)); 171 | } 172 | 173 | DrColor DrColor::operator/(int k) const { 174 | if (k == 0) return DrColor(255, 255, 255, 255); 175 | return DrColor(Dr::Clamp(r / k, 0, 255), 176 | Dr::Clamp(g / k, 0, 255), 177 | Dr::Clamp(b / k, 0, 255), 178 | Dr::Clamp(a / k, 0, 255)); 179 | } 180 | 181 | bool DrColor::operator==(const DrColor &other) const { 182 | return (r == other.r) && (g == other.g) && (b == other.b) && (a == other.a); 183 | } 184 | 185 | bool DrColor::operator!=(const DrColor &other) const { 186 | return (r != other.r) || (g != other.g) || (b != other.b) || (a != other.a); 187 | } 188 | 189 | 190 | 191 | //#################################################################################### 192 | //## Rgb / Hsv Conversion Functions 193 | //## 194 | //## Source: 195 | //## http://web.mit.edu/GRAPHICS/src/tilemkrs/tile.c 196 | //## "fractile 1.1, placed in public domain by Steve Kirkendall, 8 April 1996" 197 | //## 198 | //#################################################################################### 199 | DrHsv DrColor::getHsv() { 200 | DrHsv hsv; /* the resulting HSV value */ 201 | double rd, gd, bd; /* the RGB components, in range 0.0 - 1.0 */ 202 | double max, min; /* extremes from r, g, b */ 203 | double delta; /* difference between max and min */ 204 | 205 | /* extract the RGB components from rgb */ 206 | rd = redF(); 207 | gd = greenF(); 208 | bd = blueF(); 209 | 210 | /* find max and min */ 211 | if (rd > gd) { 212 | max = (bd > rd) ? bd : rd; 213 | min = (gd > bd) ? bd : gd; 214 | } else { 215 | max = (bd > gd) ? bd : gd; 216 | min = (rd > bd) ? bd : rd; 217 | } 218 | 219 | hsv.value = max; /* compute "value" */ 220 | hsv.saturation = (max > 0.0) ? (max - min) / max : 0; /* compute "saturation" */ 221 | 222 | /* compute "hue". This is the hard one */ 223 | delta = max - min; 224 | if (delta <= 0.001) { 225 | hsv.hue = 0.0; /* gray - any hue will work */ 226 | } else { 227 | /* divide hexagonal color wheel into three sectors */ 228 | if (max == rd) 229 | hsv.hue = (gd - bd) / delta; /* color is between yellow and magenta */ 230 | else if (max == gd) 231 | hsv.hue = 2.0 + (bd - rd) / delta; /* color is between cyan and yellow */ 232 | else /* max == b */ 233 | hsv.hue = 4.0 + (rd - gd) / delta; /* color is between magenta and cyan */ 234 | 235 | hsv.hue *= 60.0; /* convert hue to degrees */ 236 | 237 | /* make sure hue is not negative */ 238 | if (hsv.hue < 0.0) hsv.hue += 360.0; 239 | } 240 | 241 | return hsv; 242 | } 243 | 244 | void DrColor::setFromHsv(DrHsv hsv) { 245 | DrRgb rgb; /* the new rgb value */ 246 | double h; /* copy of the "hsv.hue" */ 247 | double i, f; /* integer and fractional parts of "h" */ 248 | int p, q, t; /* permuted RGB values, in integer form */ 249 | int v; /* "hsv.value", in integer form */ 250 | 251 | if (hsv.saturation < 0.01) { 252 | /* simple gray conversion */ 253 | rgb.red = rgb.green = rgb.blue = (int)(hsv.value * 256.0); 254 | } else { 255 | /* convert hue to range [0,6) */ 256 | h = hsv.hue / 60.0; 257 | if (h >= 6.0) 258 | h -= 6.0; 259 | 260 | /* break "h" down into integer and fractional parts. */ 261 | i = floor(h); 262 | f = h - i; 263 | 264 | /* compute the permuted RGB values */ 265 | v = (int)(hsv.value * 256); 266 | p = (int)((hsv.value * (1.0 - hsv.saturation)) * 256.0); 267 | q = (int)((hsv.value * (1.0 - (hsv.saturation * f))) * 256.0); 268 | t = (int)((hsv.value * (1.0 - (hsv.saturation * (1.0 - f)))) * 256.0); 269 | 270 | /* map v, p, q, and t into red, green, and blue values */ 271 | switch ((int)i) { 272 | case 0: rgb.red = v, rgb.green = t, rgb.blue = p; break; 273 | case 1: rgb.red = q, rgb.green = v, rgb.blue = p; break; 274 | case 2: rgb.red = p, rgb.green = v, rgb.blue = t; break; 275 | case 3: rgb.red = p, rgb.green = q, rgb.blue = v; break; 276 | case 4: rgb.red = t, rgb.green = p, rgb.blue = v; break; 277 | case 5: rgb.red = v, rgb.green = p, rgb.blue = q; break; 278 | } 279 | } 280 | 281 | setRed(rgb.red); 282 | setGreen(rgb.green); 283 | setBlue(rgb.blue); 284 | } 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /src/mesh.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Description: 3D Extrusion 3 | // Author: Stephens Nunnally and Scidian Software 4 | // License: Distributed under the MIT License 5 | // Source(s): https://github.com/stevinz/extrude 6 | // 7 | // Copyright (c) 2021 Stephens Nunnally and Scidian Software 8 | // 9 | // 10 | #include "3rd_party/handmade_math.h" 11 | #include "compare.h" 12 | #include "mesh.h" 13 | #include "types/vec2.h" 14 | #include "types/vec3.h" 15 | 16 | 17 | //#################################################################################### 18 | //## Builds a Vertex 19 | //#################################################################################### 20 | Vertex Vertex::createVertex(DrVec3 pos, DrVec3 norm, DrVec3 uv, DrVec3 bary) { 21 | Vertex v; 22 | v.px = pos.x; 23 | v.py = pos.y; 24 | v.pz = pos.z; 25 | v.nx = norm.x; 26 | v.ny = norm.y; 27 | v.nz = norm.z; 28 | v.tx = uv.x; 29 | v.ty = uv.y; 30 | v.bx = bary.x; 31 | v.by = bary.y; 32 | v.bz = bary.z; 33 | return v; 34 | } 35 | 36 | 37 | //#################################################################################### 38 | //## Mesh Constructor 39 | //#################################################################################### 40 | DrMesh::DrMesh() { } 41 | 42 | 43 | //#################################################################################### 44 | //## Adds a Vertex, including: 45 | //## Vec3 Position 46 | //## Vec3 Normal 47 | //## Vec2 UV Texture Coordinates 48 | //## Vec3 Barycentric Coordinates (gives shader a number between 0.0 and 1.0 to lerp to) 49 | //#################################################################################### 50 | void DrMesh::add(const DrVec3 &vertex, const DrVec3 &normal, const DrVec2 &text_coord, Triangle_Point point_number) { 51 | Vertex v; 52 | v.px = vertex.x; 53 | v.py = vertex.y; 54 | v.pz = vertex.z; 55 | v.nx = normal.x; 56 | v.ny = normal.y; 57 | v.nz = normal.z; 58 | v.tx = text_coord.x; 59 | v.ty = text_coord.y; 60 | switch (point_number) { 61 | case Triangle_Point::Point1: v.bx = 1.0; v.by = 0.0; v.bz = 0.0; break; 62 | case Triangle_Point::Point2: v.bx = 0.0; v.by = 1.0; v.bz = 0.0; break; 63 | case Triangle_Point::Point3: v.bx = 0.0; v.by = 0.0; v.bz = 1.0; break; 64 | } 65 | vertices.push_back(v); 66 | } 67 | 68 | void DrMesh::set(Vertex &from_vertex, Vertex &to_vertex) { 69 | to_vertex.px = from_vertex.px; 70 | to_vertex.py = from_vertex.py; 71 | to_vertex.pz = from_vertex.pz; 72 | 73 | to_vertex.nx = from_vertex.nx; 74 | to_vertex.ny = from_vertex.ny; 75 | to_vertex.nz = from_vertex.nz; 76 | 77 | to_vertex.tx = from_vertex.tx; 78 | to_vertex.ty = from_vertex.ty; 79 | 80 | to_vertex.bx = from_vertex.bx; 81 | to_vertex.by = from_vertex.by; 82 | to_vertex.bz = from_vertex.bz; 83 | } 84 | 85 | 86 | //#################################################################################### 87 | //## Builds a Textured Quad 88 | //#################################################################################### 89 | void DrMesh::initializeTextureQuad(float size) { 90 | int width = size; 91 | int height = size; 92 | float w2 = width / 2.f; 93 | float h2 = height / 2.f; 94 | 95 | // EXAMPLE: Adding Triangles 96 | float x1 = +w2, y1 = +h2; // Top Right 97 | float x2 = -w2, y2 = +h2; // Top Left 98 | float x3 = +w2, y3 = -h2; // Bottom Right 99 | float x4 = -w2, y4 = -h2; // Bottom Left 100 | 101 | float tx1 = 1.0, ty1 = 1.0; 102 | float tx2 = 0.0, ty2 = 1.0; 103 | float tx3 = 1.0, ty3 = 0.0; 104 | float tx4 = 0.0, ty4 = 0.0; 105 | 106 | DrVec3 n = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x3, y3, 0.f), DrVec3(x2, y2, 0.f)); 107 | 108 | add(DrVec3(x1, y1, 0.f), n, DrVec2(tx1, ty1), Triangle_Point::Point1); 109 | add(DrVec3(x2, y2, 0.f), n, DrVec2(tx2, ty2), Triangle_Point::Point2); 110 | add(DrVec3(x3, y3, 0.f), n, DrVec2(tx3, ty3), Triangle_Point::Point3); 111 | add(DrVec3(x2, y2, 0.f), n, DrVec2(tx2, ty2), Triangle_Point::Point1); 112 | add(DrVec3(x4, y4, 0.f), n, DrVec2(tx4, ty4), Triangle_Point::Point2); 113 | add(DrVec3(x3, y3, 0.f), n, DrVec2(tx3, ty3), Triangle_Point::Point3); 114 | } 115 | 116 | 117 | //#################################################################################### 118 | //## Builds a Textured Cube 119 | //#################################################################################### 120 | void DrMesh::initializeTextureCube(float size) { 121 | int width = size; 122 | int height = size; 123 | float w2 = width / 2.f; 124 | float h2 = height / 2.f; 125 | float depth = size * c_cube_depth; 126 | 127 | // EXAMPLE: Adding Triangles 128 | float x1 = +w2, y1 = +h2; // Top Right 129 | float x2 = -w2, y2 = +h2; // Top Left 130 | float x3 = +w2, y3 = -h2; // Bottom Right 131 | float x4 = -w2, y4 = -h2; // Bottom Left 132 | 133 | float tx1 = 1.0, ty1 = 1.0; 134 | float tx2 = 0.0, ty2 = 1.0; 135 | float tx3 = 1.0, ty3 = 0.0; 136 | float tx4 = 0.0, ty4 = 0.0; 137 | 138 | cube( x1, y1, tx1, ty1, 139 | x2, y2, tx2, ty2, 140 | x3, y3, tx3, ty3, 141 | x4, y4, tx4, ty4, depth); 142 | } 143 | 144 | 145 | //#################################################################################### 146 | //## Adds a Cube, as 3 pairs (six sides) of front and back 147 | //#################################################################################### 148 | void DrMesh::cube(float x1, float y1, float tx1, float ty1, 149 | float x2, float y2, float tx2, float ty2, 150 | float x3, float y3, float tx3, float ty3, 151 | float x4, float y4, float tx4, float ty4, float depth) { 152 | hmm_m4 rotate = Dr::IdentityMatrix(); 153 | DrVec3 nf, nb; // Normal Front, Normal Back 154 | DrVec3 p1f, p2f, p3f, p4f; // Point 1 Front, etc 155 | DrVec3 p1b, p2b, p3b, p4b; // Point 1 Back, etc 156 | 157 | for (int i = 0; i <= 2; ++i) { 158 | nf = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x3, y3, 0.f), DrVec3(x2, y2, 0.f)); 159 | nb = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x2, y2, 0.f), DrVec3(x3, y3, 0.f)); 160 | p1f = DrVec3(x1, y1, +depth); 161 | p2f = DrVec3(x2, y2, +depth); 162 | p3f = DrVec3(x3, y3, +depth); 163 | p4f = DrVec3(x4, y4, +depth); 164 | p1b = DrVec3(x1, y1, -depth); 165 | p2b = DrVec3(x2, y2, -depth); 166 | p3b = DrVec3(x3, y3, -depth); 167 | p4b = DrVec3(x4, y4, -depth); 168 | 169 | if (i == 1) { 170 | rotate = HMM_MultiplyMat4(rotate, HMM_Rotate(90.f, { 0.0, 1.0, 0.0 })); // Angle is in degrees 171 | } else if (i == 2) { 172 | rotate = HMM_MultiplyMat4(rotate, HMM_Rotate(90.f, { 1.0, 0.0, 0.0 })); // Angle is in degrees 173 | } 174 | 175 | nf = rotate * nf; 176 | p1f = rotate * p1f; 177 | p2f = rotate * p2f; 178 | p3f = rotate * p3f; 179 | p4f = rotate * p4f; 180 | 181 | nb = rotate * nb; 182 | p1b = rotate * p1b; 183 | p2b = rotate * p2b; 184 | p3b = rotate * p3b; 185 | p4b = rotate * p4b; 186 | 187 | add(p1f, nf, DrVec2(tx1, ty1), Triangle_Point::Point1); 188 | add(p2f, nf, DrVec2(tx2, ty2), Triangle_Point::Point2); 189 | add(p3f, nf, DrVec2(tx3, ty3), Triangle_Point::Point3); 190 | add(p2f, nf, DrVec2(tx2, ty2), Triangle_Point::Point1); 191 | add(p4f, nf, DrVec2(tx4, ty4), Triangle_Point::Point2); 192 | add(p3f, nf, DrVec2(tx3, ty3), Triangle_Point::Point3); 193 | 194 | add(p1b, nb, DrVec2(tx1, ty1), Triangle_Point::Point1); 195 | add(p3b, nb, DrVec2(tx3, ty3), Triangle_Point::Point2); 196 | add(p2b, nb, DrVec2(tx2, ty2), Triangle_Point::Point3); 197 | add(p2b, nb, DrVec2(tx2, ty2), Triangle_Point::Point1); 198 | add(p3b, nb, DrVec2(tx3, ty3), Triangle_Point::Point2); 199 | add(p4b, nb, DrVec2(tx4, ty4), Triangle_Point::Point3); 200 | } 201 | } 202 | 203 | 204 | //#################################################################################### 205 | //## Adds a Quad, front and back 206 | //#################################################################################### 207 | void DrMesh::quad(float x1, float y1, float tx1, float ty1, 208 | float x2, float y2, float tx2, float ty2, 209 | float x3, float y3, float tx3, float ty3, 210 | float x4, float y4, float tx4, float ty4) { 211 | DrVec3 n; 212 | n = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x3, y3, 0.f), DrVec3(x2, y2, 0.f)); 213 | 214 | add(DrVec3(x1, y1, +c_extrude_depth), n, DrVec2(tx1, ty1), Triangle_Point::Point1); 215 | add(DrVec3(x2, y2, +c_extrude_depth), n, DrVec2(tx2, ty2), Triangle_Point::Point2); 216 | add(DrVec3(x3, y3, +c_extrude_depth), n, DrVec2(tx3, ty3), Triangle_Point::Point3); 217 | 218 | add(DrVec3(x2, y2, +c_extrude_depth), n, DrVec2(tx2, ty2), Triangle_Point::Point1); 219 | add(DrVec3(x4, y4, +c_extrude_depth), n, DrVec2(tx4, ty4), Triangle_Point::Point2); 220 | add(DrVec3(x3, y3, +c_extrude_depth), n, DrVec2(tx3, ty3), Triangle_Point::Point3); 221 | 222 | n = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x2, y2, 0.f), DrVec3(x3, y3, 0.f)); 223 | 224 | add(DrVec3(x1, y1, -c_extrude_depth), n, DrVec2(tx1, ty1), Triangle_Point::Point1); 225 | add(DrVec3(x3, y3, -c_extrude_depth), n, DrVec2(tx3, ty3), Triangle_Point::Point2); 226 | add(DrVec3(x2, y2, -c_extrude_depth), n, DrVec2(tx2, ty2), Triangle_Point::Point3); 227 | 228 | add(DrVec3(x2, y2, -c_extrude_depth), n, DrVec2(tx2, ty2), Triangle_Point::Point1); 229 | add(DrVec3(x3, y3, -c_extrude_depth), n, DrVec2(tx3, ty3), Triangle_Point::Point2); 230 | add(DrVec3(x4, y4, -c_extrude_depth), n, DrVec2(tx4, ty4), Triangle_Point::Point3); 231 | } 232 | 233 | 234 | //#################################################################################### 235 | //## Adds a Triangle, front and back 236 | //#################################################################################### 237 | void DrMesh::triangle(float x1, float y1, float tx1, float ty1, 238 | float x2, float y2, float tx2, float ty2, 239 | float x3, float y3, float tx3, float ty3, float depth_multiplier) { 240 | DrVec3 n; 241 | n = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x3, y3, 0.f), DrVec3(x2, y2, 0.f)); 242 | 243 | float depth = c_extrude_depth * depth_multiplier;; 244 | 245 | add(DrVec3(x1, y1, +depth), n, DrVec2(tx1, ty1), Triangle_Point::Point1); 246 | add(DrVec3(x2, y2, +depth), n, DrVec2(tx2, ty2), Triangle_Point::Point2); 247 | add(DrVec3(x3, y3, +depth), n, DrVec2(tx3, ty3), Triangle_Point::Point3); 248 | 249 | n = DrVec3::triangleNormal(DrVec3(x1, y1, 0.f), DrVec3(x2, y2, 0.f), DrVec3(x3, y3, 0.f)); 250 | 251 | add(DrVec3(x1, y1, -depth), n, DrVec2(tx1, ty1), Triangle_Point::Point1); 252 | add(DrVec3(x3, y3, -depth), n, DrVec2(tx3, ty3), Triangle_Point::Point2); 253 | add(DrVec3(x2, y2, -depth), n, DrVec2(tx2, ty2), Triangle_Point::Point3); 254 | } 255 | 256 | //#################################################################################### 257 | //## Adds a Quad extruded from an Edge 258 | //#################################################################################### 259 | void DrMesh::extrude(float x1, float y1, float tx1, float ty1, 260 | float x2, float y2, float tx2, float ty2, int steps, float depth_multiplier) { 261 | float depth = c_extrude_depth * depth_multiplier; 262 | 263 | float step = (depth * 2.0f) / static_cast(steps); 264 | float front = depth; 265 | float back = depth - step; 266 | 267 | for (int i = 0; i < steps; i++) { 268 | DrVec3 n; 269 | n = DrVec3::triangleNormal(DrVec3(x1, y1, front), DrVec3(x2, y2, front), DrVec3(x1, y1, back)); 270 | 271 | add(DrVec3(x1, y1, front), n, DrVec2(tx1, ty1), Triangle_Point::Point1); 272 | add(DrVec3(x1, y1, back), n, DrVec2(tx1, ty1), Triangle_Point::Point2); 273 | add(DrVec3(x2, y2, front), n, DrVec2(tx2, ty2), Triangle_Point::Point3); 274 | 275 | n = DrVec3::triangleNormal(DrVec3(x2, y2, front), DrVec3(x2, y2, back), DrVec3(x1, y1, back)); 276 | 277 | add(DrVec3(x2, y2, front), n, DrVec2(tx2, ty2), Triangle_Point::Point1); 278 | add(DrVec3(x1, y1, back), n, DrVec2(tx1, ty1), Triangle_Point::Point2); 279 | add(DrVec3(x2, y2, back), n, DrVec2(tx2, ty2), Triangle_Point::Point3); 280 | 281 | front -= step; 282 | back -= step; 283 | } 284 | 285 | } 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | --------------------------------------------------------------------------------