├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── CMakeSettings.json ├── LICENSE ├── README.md ├── include └── AobaAPI │ ├── AobaAPI.hpp │ ├── Core.hpp │ ├── Core │ ├── EulerOps.hpp │ ├── Mesh.hpp │ └── Mesh │ │ ├── Edge.hpp │ │ ├── Face.hpp │ │ ├── Loop.hpp │ │ ├── Mesh.hpp │ │ └── Vert.hpp │ ├── IO.hpp │ ├── IO │ ├── ExportObj.hpp │ ├── ExportStl.hpp │ └── IndexMesh.hpp │ ├── Math.hpp │ ├── Math │ ├── Euler.hpp │ ├── Matrix.hpp │ ├── Matrix │ │ ├── Matrix2.hpp │ │ ├── Matrix3.hpp │ │ └── Matrix4.hpp │ ├── Quaternion.hpp │ ├── Vector.hpp │ └── Vector │ │ ├── Vector2.hpp │ │ ├── Vector3.hpp │ │ └── Vector4.hpp │ ├── Ops.hpp │ └── Ops │ ├── Create.hpp │ ├── Modify.hpp │ ├── Select.hpp │ └── Transform.hpp └── src ├── AobaAPI.cpp ├── CMakeLists.txt ├── Core ├── CMakeLists.txt ├── EulerOps │ ├── CMakeLists.txt │ ├── DissolveEdge.cpp │ ├── EdgeSplit.cpp │ ├── EdgeSqueeze.cpp │ ├── GlueEdge.cpp │ ├── GlueFace.cpp │ ├── GlueVert.cpp │ ├── JoinMesh.cpp │ ├── KillEdge.cpp │ ├── KillFace.cpp │ ├── KillMesh.cpp │ ├── KillVert.cpp │ ├── MakeEdge.cpp │ ├── MakeEdgeVert.cpp │ ├── MakeFace.cpp │ ├── MakeLoop.cpp │ ├── MakeVert.cpp │ ├── ManifoldMakeEdge.cpp │ └── ValidateLoop.cpp ├── Mesh.h ├── Mesh │ ├── CMakeLists.txt │ ├── Edge.cpp │ ├── Face.cpp │ ├── Loop.cpp │ ├── Mesh.cpp │ └── Vert.cpp └── README.md ├── IO ├── CMakeLists.txt ├── ExportObj.cpp ├── ExportStl.cpp ├── IndexMesh.cpp └── README.md ├── Math ├── CMakeLists.txt ├── Euler.h ├── Euler │ ├── CMakeLists.txt │ ├── Euler.cpp │ └── Euler.h ├── Matrix │ ├── CMakeLists.txt │ ├── Matrix2.cpp │ ├── Matrix3.cpp │ └── Matrix4.cpp ├── Quaternion.h ├── Quaternion │ ├── CMakeLists.txt │ ├── Quaternion.cpp │ └── Quaternion.h ├── README.md └── Vector │ ├── CMakeLists.txt │ ├── Vector2.cpp │ ├── Vector3.cpp │ └── Vector4.cpp └── Ops ├── CMakeLists.txt ├── Create ├── CMakeLists.txt ├── CreateCircle.cpp ├── CreateCube.cpp ├── CreateGrid.cpp ├── CreateUVSphere.cpp └── CreateVert.cpp ├── Modify ├── CMakeLists.txt ├── Delete.cpp ├── Duplicate.cpp ├── ExtrudeEdges.cpp ├── ExtrudeFaceRegion.cpp ├── ExtrudeFaces.cpp ├── ExtrudeVerts.cpp ├── InsetFaces.cpp ├── MakeFace.cpp ├── Mirror.cpp ├── RecalculateFaceNormals.cpp ├── ReverseFaceOrder.cpp ├── SplitEdges.cpp ├── SubdivideSimple.cpp ├── SubdivideSmooth.cpp └── TriangulateFaces.cpp ├── README.md ├── Select ├── CMakeLists.txt ├── SelectExtend.cpp ├── SelectFromEdges.cpp ├── SelectFromFaces.cpp ├── SelectFromVerts.cpp └── SelectReduce.cpp └── Transform ├── CMakeLists.txt ├── Rotate.cpp ├── Scale.cpp ├── Transform.cpp └── Translate.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AlignAfterOpenBracket: DontAlign 3 | AlignConsecutiveMacros: 'false' 4 | AlignConsecutiveAssignments: 'false' 5 | AlignConsecutiveDeclarations: 'false' 6 | AlignEscapedNewlines: DontAlign 7 | AlignOperands: 'true' 8 | AlignTrailingComments: 'true' 9 | AllowAllArgumentsOnNextLine: 'true' 10 | AllowAllConstructorInitializersOnNextLine: 'true' 11 | AllowAllParametersOfDeclarationOnNextLine: 'true' 12 | AllowShortBlocksOnASingleLine: 'false' 13 | AllowShortCaseLabelsOnASingleLine: 'false' 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: Never 16 | AllowShortLambdasOnASingleLine: None 17 | AllowShortLoopsOnASingleLine: 'false' 18 | AlwaysBreakAfterDefinitionReturnType: None 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakBeforeMultilineStrings: 'false' 21 | AlwaysBreakTemplateDeclarations: 'Yes' 22 | BinPackArguments: 'true' 23 | BinPackParameters: 'true' 24 | BreakBeforeBinaryOperators: NonAssignment 25 | BreakBeforeBraces: Attach 26 | BreakBeforeTernaryOperators: 'true' 27 | BreakConstructorInitializers: AfterColon 28 | BreakInheritanceList: AfterColon 29 | BreakStringLiterals: 'true' 30 | ColumnLimit: '120' 31 | CompactNamespaces: 'false' 32 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' 33 | Cpp11BracedListStyle: 'true' 34 | DerivePointerAlignment: 'false' 35 | DisableFormat: 'false' 36 | ExperimentalAutoDetectBinPacking: 'true' 37 | FixNamespaceComments: 'true' 38 | IncludeBlocks: Regroup 39 | IndentCaseLabels: 'true' 40 | IndentPPDirectives: AfterHash 41 | IndentWidth: '4' 42 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 43 | Language: Cpp 44 | MaxEmptyLinesToKeep: '1' 45 | NamespaceIndentation: None 46 | PointerAlignment: Left 47 | ReflowComments: 'true' 48 | SortIncludes: 'true' 49 | SortUsingDeclarations: 'true' 50 | SpaceAfterCStyleCast: 'false' 51 | SpaceAfterLogicalNot: 'false' 52 | SpaceAfterTemplateKeyword: 'false' 53 | SpaceBeforeAssignmentOperators: 'true' 54 | SpaceBeforeCpp11BracedList: 'true' 55 | SpaceBeforeCtorInitializerColon: 'true' 56 | SpaceBeforeInheritanceColon: 'true' 57 | SpaceBeforeParens: Never 58 | SpaceBeforeRangeBasedForLoopColon: 'true' 59 | SpaceInEmptyParentheses: 'false' 60 | SpacesInAngles: 'false' 61 | SpacesInCStyleCastParentheses: 'false' 62 | SpacesInContainerLiterals: 'false' 63 | SpacesInParentheses: 'false' 64 | SpacesInSquareBrackets: 'false' 65 | Standard: Cpp11 66 | TabWidth: '4' 67 | UseTab: Never 68 | 69 | ... 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | /.vs -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for AobaAPI, include source and define 2 | # project specific logic here. 3 | # 4 | cmake_minimum_required(VERSION 3.11) 5 | set (CMAKE_CXX_STANDARD 11) 6 | 7 | project( 8 | AobaAPI 9 | VERSION 0.1 10 | DESCRIPTION "3D mesh modeling add_library" 11 | LANGUAGES CXX 12 | ) 13 | 14 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 15 | set(CMAKE_CXX_EXTENSIONS OFF) 16 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 17 | 18 | find_package(Doxygen) 19 | if(Doxygen_FOUND) 20 | add_subdirectory(docs) 21 | else() 22 | message(STATUS "Doxygen not found, not building docs.") 23 | endif() 24 | endif() 25 | 26 | add_subdirectory(src) 27 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "buildRoot": "${projectDir}\\out\\build\\${name}", 8 | "installRoot": "${projectDir}\\out\\install\\${name}", 9 | "cmakeCommandArgs": "", 10 | "buildCommandArgs": "", 11 | "ctestCommandArgs": "", 12 | "inheritEnvironments": [ "msvc_x64_x64" ], 13 | "variables": [] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Luka Šimić 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AobaAPI - a C++ API/library for 3D polygonal mesh modeling 2 | 3 | A standalone C++ library for 3D modeling 4 | 5 | Developed as a part of the Graduate(Master's) thesis at the *Faculty of Electrical Engineering, Computer Science and Information Technology Osijek*. 6 | 7 | ## Features 8 | - No dependencies 9 | - Lightweight 10 | - Custom mathematics library with 2d/3d/4d vectors and matrices 11 | - Flexible data structure supporting non-manifold geometry 12 | - Low level(Euler) operators for local topology modification and implementation of advanced tools 13 | - Advanced 3D modeling operators - Primitive creation, extrusion, geometric transformations 14 | - Subdivision 15 | - .obj, .stl file export 16 | - Conversion to index/triangle based type suitable for rendering. 17 | 18 | ## Example projects 19 | See the AobaExamples repository at 20 | https://github.com/lsimic/AobaExamples 21 | Includes several modeling examples and showcases how to include the API into other projects 22 | 23 | ## License 24 | MIT -------------------------------------------------------------------------------- /include/AobaAPI/AobaAPI.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_API_HPP 2 | #define AOBA_API_HPP 3 | 4 | #include "Core.hpp" 5 | #include "IO.hpp" 6 | #include "Math.hpp" 7 | #include "Ops.hpp" 8 | 9 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_HPP 2 | #define AOBA_CORE_HPP 3 | 4 | #include "Core/EulerOps.hpp" 5 | #include "Core/Mesh.hpp" 6 | 7 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core/Mesh.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_HPP 2 | #define AOBA_CORE_MESH_HPP 3 | 4 | #include "Mesh/Edge.hpp" 5 | #include "Mesh/Face.hpp" 6 | #include "Mesh/Loop.hpp" 7 | #include "Mesh/Mesh.hpp" 8 | #include "Mesh/Vert.hpp" 9 | 10 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core/Mesh/Edge.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_EDGE_HPP 2 | #define AOBA_CORE_MESH_EDGE_HPP 3 | 4 | #include "../EulerOps.hpp" 5 | #include "../../Math/Vector/Vector3.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Aoba { 12 | namespace Core { 13 | 14 | class Vert; 15 | class Loop; 16 | class Mesh; 17 | class Face; 18 | 19 | class Edge { 20 | friend class Vert; 21 | friend class Mesh; 22 | friend class Face; 23 | 24 | friend void EdgeSplit(Edge*, Vert*, Edge*, Vert*); 25 | friend void KillEdge(Edge*); 26 | friend void KillFace(Face*); 27 | friend void KillMesh(Mesh*); 28 | friend void MakeEdge(Vert*, Vert*, Edge*); 29 | friend void MakeEdgeVert(Vert*, Edge*, Vert*); 30 | friend void MakeLoop(std::vector, std::vector, Loop*); 31 | friend void GlueVert(Vert*, Vert*); 32 | friend void ManifoldMakeEdge(Vert*, Vert*, Face*, Edge*, Face*); 33 | friend void GlueEdge(Edge*, Edge*); 34 | friend void DissolveEdge(Edge*, Face*); 35 | friend void JoinMesh(Mesh*, Mesh*); 36 | 37 | public: 38 | std::size_t index; // index of this vert, not updated automatically, used for tools 39 | int32_t flags; // flags available for use in other tools 40 | int32_t flagsIntern; // flags for use in internal tools and operators 41 | private: 42 | Vert* v1; // Unordered verts of this edge. 43 | Vert* v2; // Unordered verts of this edge. 44 | Loop* l; // List of loops using this edge. Use l->eNext/ePrev for traversal. 45 | Edge* v1Next; // List of edges around v1 46 | Edge* v1Prev; // List of edges around v1 47 | Edge* v2Next; // List of edges around v2 48 | Edge* v2Prev; // List of edges around v2 49 | Mesh* m; // Pointer to parent mesh. Could potentialy be ommited but might pose a safety issue. 50 | Edge* mNext; // list of all edges in the mesh 51 | Edge* mPrev; // List of all edges in the mesh 52 | 53 | /// 54 | /// Get the next edge in the list of edges around vert v 55 | /// 56 | /// vert 57 | /// The next edge 58 | /// Thrown if given vert is not used by the edge 59 | Edge* Next(const Vert* v) const; 60 | 61 | /// 62 | /// Get the previous edge in the list of edges around vert v 63 | /// 64 | /// vert 65 | /// The previous edge 66 | /// Thrown if given vert is not used by the edge 67 | Edge* Prev(const Vert* v) const; 68 | 69 | /// 70 | /// Calculate the local face normal (adjecent quad, or triangle if triangular face) 71 | /// 72 | /// Loop of this edge to use for calculation. 73 | /// Local face normal 74 | Math::Vec3 CalcLocalNormal(Loop* loop) const; 75 | 76 | public: 77 | Edge(); 78 | 79 | /// 80 | /// Calculate angle between two faces. 81 | /// 82 | /// Angle between faces. 83 | /// Thrown if edge does not have exactly two adjacent faces 84 | float CalcFaceAngle() const; 85 | 86 | /// 87 | /// Calculate angle between two faces, negative for concave join. 88 | /// 89 | /// Angle between faces. 90 | /// Thrown if edge does not have exactly two adjacent faces 91 | float CalcFaceAngleSigned() const; 92 | 93 | /// 94 | /// Euclidean distance between the verts of this edge. 95 | /// 96 | /// Length of the edge. 97 | float CalcLength() const; 98 | 99 | /// 100 | /// Find the other vert of this edge. 101 | /// 102 | /// Any vert of this edge. 103 | /// Other vert of this edge. 104 | /// Thrown if given vert is not used by the edge 105 | Vert* Other(const Vert* v) const; 106 | 107 | /// 108 | /// Check wether the edge is boundary face. Edge is boundary if it is used by only one face, and is used only once 109 | /// in the loop 110 | /// 111 | /// True if edge is boundary, otherwise false. 112 | bool IsBoundary() const; 113 | 114 | /// 115 | /// Check wether the edge is contigous. Edge is not contigous if it has two faces pointing in separate directions. 116 | /// Wire edges and boundary edges are always contigous. Edges used three or more times are never contigous. 117 | /// 118 | /// True if edge is contigous, otherwise false. 119 | bool IsContigous() const; 120 | 121 | /// 122 | /// Check wether the edge is manifold. Edge is not manifold if it has three or more adjecent loops, or is a wire 123 | /// edge, or has adjecent faces pointing in separate directions. 124 | /// 125 | /// True if edge is manifold, otherwise false. 126 | bool IsManifold() const; 127 | 128 | /// 129 | /// Check wether the edge is a wire edge. Edge is a wire edge if it does not have any adjecent face. 130 | /// 131 | /// True if edge is wire, otherwise false. 132 | bool IsWire() const; 133 | 134 | /// 135 | /// List of all Faces that use this edge. Do not use this list to add new faces, use EulerOps instead. 136 | /// 137 | /// Unordered List containing references to all Faces which use this edge. 138 | const std::vector Faces() const; 139 | 140 | /// 141 | /// List of faces that use this edge and fulfill the criteria given by the filtering function. 142 | /// 143 | /// Filtering function 144 | /// Filtered adjacent faces 145 | const std::vector Faces(std::function func) const; 146 | 147 | /// 148 | /// List of all Loops that use this edge. Do not use this list to add new Loops, use EulerOps instead. 149 | /// 150 | /// Unordered List containing references to all Loops which use this edge. 151 | const std::vector Loops() const; 152 | 153 | /// 154 | /// list of loops that use this edge and fulfill the criteria given by the filtering function. 155 | /// 156 | /// Filtering function 157 | /// Filtered adjacent loops 158 | const std::vector Loops(std::function func) const; 159 | 160 | /// 161 | /// List of all Verts of the edge. Do not use this list to change verts, use EulerOps instead. 162 | /// 163 | /// Unordered List containing references to all verts of this edge. 164 | const std::vector Verts() const; 165 | 166 | /// 167 | /// List of verts of this edge that fulfill the criteria given by the filtering function. 168 | /// 169 | /// Filtering function 170 | /// Filtered verts 171 | const std::vector Verts(std::function func) const; 172 | 173 | /// 174 | /// V1 of this edge. Do not use this to change the verts, use EulerOps instead. 175 | /// 176 | /// V1 177 | Vert* V1() const; 178 | 179 | /// 180 | /// V1 of this edge. Do not use this to change the verts, use EulerOps instead. 181 | /// 182 | /// V2 183 | Vert* V2() const; 184 | }; 185 | } // namespace Core 186 | } // namespace Aoba 187 | 188 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core/Mesh/Face.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_FACE_HPP 2 | #define AOBA_CORE_MESH_FACE_HPP 3 | 4 | #include "../../Math/Vector/Vector3.hpp" 5 | #include "../EulerOps.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Aoba { 12 | namespace Core { 13 | 14 | class Loop; 15 | class Mesh; 16 | class Edge; 17 | class Vert; 18 | 19 | class Face { 20 | friend class Mesh; 21 | 22 | friend void KillFace(Face*); 23 | friend void KillMesh(Mesh*); 24 | friend void MakeFace(Loop*, Face*); 25 | friend void ManifoldMakeEdge(Vert*, Vert*, Face*, Edge*, Face*); 26 | friend void GlueVert(Vert*, Vert*); 27 | friend void DissolveEdge(Edge*, Face*); 28 | friend void JoinMesh(Mesh*, Mesh*); 29 | 30 | private: 31 | Loop* l; // First loop in a list of loops which form the face boundary. use fNext/fPrev for traversal 32 | Mesh* m; // Pointer to parent mesh. Could potentialy be ommited but might pose a safety issue. 33 | Face* mNext; // list of all faces in the mesh 34 | Face* mPrev; // List of all faces in the mesh 35 | public: 36 | std::size_t index; // index of this vert, not updated automatically, used for tools 37 | int32_t flags; // flags available for use in other tools 38 | int32_t flagsIntern; // flags for use in internal tools and operators 39 | Math::Vec3 no; // Face normal 40 | short materialIdx; // Face material index 41 | 42 | Face(); 43 | 44 | /// 45 | /// Calculate the area of the face. 46 | /// 47 | /// Area of the face. 48 | float CalcArea() const; 49 | 50 | /// 51 | /// Calculate the center of the face as an average value of it's verts. 52 | /// 53 | /// Center of face. 54 | Math::Vec3 CalcCenterAverage() const; 55 | 56 | /// 57 | /// Calculate the sum of lengths of all edges which form this face. 58 | /// 59 | /// Perimiter of the face. 60 | float CalcPerimiter() const; 61 | 62 | /// 63 | /// Flip the normal of this face by changing the winding of it's edges/loops. 64 | /// 65 | void NormalFlip(); 66 | 67 | /// 68 | /// Update the face's normal. 69 | /// 70 | void NormalUpdate(); 71 | 72 | /// 73 | /// List of all edges of the face. Do not use this list to change edges, use EulerOps instead. 74 | /// 75 | /// Unordered List containing references to all edges of this face. 76 | const std::vector Edges() const; 77 | 78 | /// 79 | /// List of all Verts of the face. Do not use this list to change verts, use EulerOps instead. 80 | /// 81 | /// Unordered List containing references to all verts of this face. 82 | const std::vector Verts() const; 83 | 84 | /// 85 | /// List of all loops of the face. Do not use this list to modify the face, use EulerOps instead. 86 | /// 87 | /// Ordered list of loops using this face. 88 | const std::vector Loops() const; 89 | 90 | /// 91 | /// List of edges which form this face and fulfill the criteria given by the filtering function. 92 | /// 93 | /// Filtering function 94 | /// Filtered edges 95 | const std::vector Edges(std::function func) const; 96 | 97 | /// 98 | /// List of verts which form this face and fulfill the criteria given by the filtering function. 99 | /// 100 | /// Filtering function 101 | /// Filtered verts 102 | const std::vector Verts(std::function func) const; 103 | 104 | /// 105 | /// List of loops which form this face and fulfill the criteria given by the filtering function. 106 | /// 107 | /// Filtering function 108 | /// Filtered loops 109 | const std::vector Loops(std::function func) const; 110 | }; 111 | 112 | } // namespace Core 113 | } // namespace Aoba 114 | 115 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core/Mesh/Loop.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_LOOP_HPP 2 | #define AOBA_CORE_MESH_LOOP_HPP 3 | 4 | #include "../EulerOps.hpp" 5 | 6 | #include 7 | 8 | namespace Aoba { 9 | namespace Core { 10 | 11 | class Vert; 12 | class Edge; 13 | class Face; 14 | class Mesh; 15 | 16 | class Loop { 17 | friend class Edge; 18 | friend class Face; 19 | friend class Vert; 20 | friend class Mesh; 21 | 22 | friend void EdgeSplit(Edge*, Vert*, Edge*, Vert*); 23 | friend void KillFace(Face*); 24 | friend void MakeFace(Loop*, Face*); 25 | friend void MakeLoop(std::vector, std::vector, Loop*); 26 | friend void GlueVert(Vert*, Vert*); 27 | friend void ManifoldMakeEdge(Vert*, Vert*, Face*, Edge*, Face*); 28 | friend void GlueFace(Face*, Edge*, Face*, Edge*); 29 | friend void DissolveEdge(Edge*, Face*); 30 | friend void JoinMesh(Mesh*, Mesh*); 31 | 32 | public: 33 | std::size_t index; // index of this vert, not updated automatically, used for tools 34 | int32_t flags; // flags available for use in other tools 35 | int32_t flagsIntern; // flags for use in internal tools and operators 36 | private: 37 | Vert* v; // First vert of the edge which this loop uses. Specifies edge orientation. 38 | Edge* e; // Edge which this loop uses. Uses verts v, next->v 39 | Face* f; // Face which is bound by this loop. 40 | Loop* eNext; // List of loops around edge e. 41 | Loop* ePrev; // List of loops around edge e. 42 | Loop* fNext; // List of loops which form the boundary of face f. 43 | Loop* fPrev; // List of loops which form the boundary of face f. 44 | Mesh* m; // Pointer to parent mesh. Could potentialy be ommited but might pose a safety issue. 45 | 46 | public: 47 | Loop(); 48 | 49 | /// 50 | /// Vert which defines the direction of this loop. 51 | /// 52 | /// Vert of the loop 53 | Vert* LoopVert() const; 54 | 55 | /// 56 | /// Edge which forms this loop. 57 | /// 58 | /// Edge of the loop 59 | Edge* LoopEdge() const; 60 | 61 | /// 62 | /// Face which this loop forms. 63 | /// 64 | /// Face of the loop 65 | Face* LoopFace() const; 66 | }; 67 | 68 | } // namespace Core 69 | } // namespace Aoba 70 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core/Mesh/Mesh.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_MESH_HPP 2 | #define AOBA_CORE_MESH_MESH_HPP 3 | 4 | #include "../../Math/Matrix/Matrix4.hpp" 5 | #include "../EulerOps.hpp" 6 | 7 | #include 8 | #include 9 | 10 | namespace Aoba { 11 | namespace Core { 12 | 13 | class Vert; 14 | class Edge; 15 | class Face; 16 | 17 | class Mesh { 18 | friend void EdgeSplit(Edge*, Vert*, Edge*, Vert*); 19 | friend void KillEdge(Edge*); 20 | friend void KillFace(Face*); 21 | friend void KillMesh(Mesh*); 22 | friend void KillVert(Vert*); 23 | friend void MakeEdge(Vert*, Vert*, Edge*); 24 | friend void MakeEdgeVert(Vert*, Edge*, Vert*); 25 | friend void MakeFace(Loop*, Face*); 26 | friend void MakeVert(Mesh*, Vert*); 27 | friend void GlueVert(Vert*, Vert*); 28 | friend void ManifoldMakeEdge(Vert*, Vert*, Face*, Edge*, Face*); 29 | friend void DissolveEdge(Edge*, Face*); 30 | friend void JoinMesh(Mesh*, Mesh*); 31 | 32 | private: 33 | Vert* verts; // List of all verts in the mesh. 34 | Edge* edges; // List of all edges in the mesh. 35 | Face* faces; // list of all faces in the mesh. 36 | // List of loops is omitted, because loops can be easily accessed using faces. 37 | public: 38 | /// 39 | /// Constructor, initializes empty lists for verts, edges and faces. 40 | /// 41 | Mesh(); 42 | 43 | /// 44 | /// Checks wether the mesh is in a valid state. 45 | /// 46 | /// True if the mesh is in a valid state, otherwise False. 47 | bool IsValid() const; 48 | 49 | /// 50 | /// Transform all mesh elements using a 4x4 transformation matrix. 51 | /// 52 | /// 4x4 transformation matrix to use.k 53 | void Transform(Math::Mat4 mat); 54 | 55 | /// 56 | /// List of all Verts inside the mesh. Do not use this list to add new verts to the mesh, use EulerOps instead. 57 | /// 58 | /// List containing references to all Verts inside the mesh. 59 | const std::vector Verts() const; 60 | 61 | /// 62 | /// List of all Edges inside the mesh. Do not use this list to add new Edges to the mesh, use EulerOps instead. 63 | /// 64 | /// List containing references to all Edges inside the mesh. 65 | const std::vector Edges() const; 66 | 67 | /// 68 | /// List of all Faces inside the mesh. Do not use this list to add new Faces to the mesh, use EulerOps instead. 69 | /// 70 | /// List containing references to all Faces inside the mesh. 71 | const std::vector Faces() const; 72 | 73 | /// 74 | /// List of verts iun the mesh which fulfill the criteria given by the filtering function. 75 | /// 76 | /// Filtering function 77 | /// Filtered verts 78 | const std::vector Verts(std::function func) const; 79 | 80 | /// 81 | /// List of edges iun the mesh which fulfill the criteria given by the filtering function. 82 | /// 83 | /// Filtering function 84 | /// Filtered edges 85 | const std::vector Edges(std::function func) const; 86 | 87 | /// 88 | /// List of faces iun the mesh which fulfill the criteria given by the filtering function. 89 | /// 90 | /// Filtering function 91 | /// Filtered faces 92 | const std::vector Faces(std::function func) const; 93 | }; 94 | 95 | } // namespace Core 96 | } // namespace Aoba 97 | 98 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Core/Mesh/Vert.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_VERT_HPP 2 | #define AOBA_CORE_MESH_VERT_HPP 3 | 4 | #include "../../Math/Vector/Vector3.hpp" 5 | #include "../EulerOps.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Aoba { 12 | namespace Core { 13 | 14 | class Edge; 15 | class Mesh; 16 | class Face; 17 | 18 | class Vert { 19 | friend class Mesh; 20 | 21 | friend void EdgeSplit(Edge*, Vert*, Edge*, Vert*); 22 | friend void KillEdge(Edge*); 23 | friend void KillMesh(Mesh*); 24 | friend void KillVert(Vert*); 25 | friend void MakeEdge(Vert*, Vert*, Edge*); 26 | friend void MakeEdgeVert(Vert*, Edge*, Vert*); 27 | friend void MakeVert(Mesh*, Vert*); 28 | friend void GlueVert(Vert*, Vert*); 29 | friend void JoinMesh(Mesh*, Mesh*); 30 | 31 | public: 32 | std::size_t index; // index of this vert, not updated automatically, used for tools 33 | int32_t flags; // flags available for use in other tools 34 | int32_t flagsIntern; // flags for use in internal tools and operators 35 | Math::Vec3 co; // X,Y,Z vertex coordinates 36 | Math::Vec3 no; // X,Y,Z vertex normals 37 | private: 38 | Edge* e; // List of edges using this vert. Use e->v1/v2 Next/Prev for traversal. 39 | Mesh* m; // Pointer to parent mesh. Could potentialy be ommited but might pose a safety issue. 40 | Vert* mNext; // list of all verts in the mesh 41 | Vert* mPrev; // List of all verts in the mesh 42 | public: 43 | Vert(); 44 | 45 | /// 46 | /// Check wether the vert is boundary. Vert is boundary if it is connected to a boundary edge. 47 | /// Isolated verts are not considered boundary verts. 48 | /// 49 | /// True if boundary vert, otherwise false. 50 | bool IsBoundary() const; 51 | 52 | /// 53 | /// Check wether the vert is manifold. Vert is not manifold if it belongs to multiple-face edges or wire edges, 54 | /// borders non-adjecent faces or is an isolated vert. 55 | /// 56 | /// 57 | /// This is sasauge. 58 | /// 59 | /// True if vert is manifold, otherwise false. 60 | bool IsManifold() const; 61 | 62 | /// 63 | /// Check wether the vert is wire. Vert is wire if it is adjecent to a wire edge. 64 | /// Isolated verts are not considered wire verts. 65 | /// 66 | /// True if vert is wire, otherwise false. 67 | bool IsWire() const; 68 | 69 | /// 70 | /// List of all Edges that use this vert. Do not use this list to add new Edges, use EulerOps instead. 71 | /// 72 | /// List containing references to all Edges using this vert. 73 | const std::vector Edges() const; 74 | 75 | /// 76 | /// List of all faces that use this vert. Do not use this list to add new faces, use EulerOps instead. 77 | /// 78 | /// List containing references to all faces using this vert. 79 | const std::vector Faces() const; 80 | 81 | /// 82 | /// List of all loops which start in this vert. Do not use this list to add new loops, use EulerOps instead. 83 | /// 84 | /// Loops using this vert 85 | const std::vector Loops() const; 86 | 87 | /// 88 | /// List of edges which are adjacent to this vert and fulfill the criteria given by the filtering function. 89 | /// 90 | /// Filtering function 91 | /// Filtered edges 92 | const std::vector Edges(std::function func) const; 93 | 94 | /// 95 | /// List of faces which are adjacent to this vert and fulfill the criteria given by the filtering function. 96 | /// 97 | /// Filtering function 98 | /// Filtered faces 99 | const std::vector Faces(std::function func) const; 100 | 101 | 102 | /// 103 | /// List of loops which are adjacent to this vert and fulfill the criteria given by the filtering function. 104 | /// 105 | /// Filtering function 106 | /// Filtered loops 107 | const std::vector Loops(std::function func) const; 108 | 109 | }; 110 | } // namespace Core 111 | } // namespace Aoba 112 | 113 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/IO.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_IO_HPP 2 | #define AOBA_IO_HPP 3 | 4 | #include "IO/ExportObj.hpp" 5 | #include "IO/ExportStl.hpp" 6 | #include "IO/IndexMesh.hpp" 7 | 8 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/IO/ExportObj.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_IO_EXPORT_OBJ_HPP 2 | #define AOBA_IO_EXPORT_OBJ_HPP 3 | 4 | #include "../Core.hpp" 5 | 6 | #include 7 | 8 | namespace Aoba { 9 | namespace IO { 10 | 11 | /// 12 | /// Export the given mesh into a text-based obj file stored at the given path. 13 | /// 14 | /// 15 | /// The functinality is currently rather limited. 16 | /// 17 | /// File path 18 | /// Mesh to export 19 | void ExportObj(std::string path, Core::Mesh* mesh); 20 | 21 | } // namespace IO 22 | } // namespace Aoba 23 | 24 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/IO/ExportStl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_IO_EXPORT_STL_HPP 2 | #define AOBA_IO_EXPORT_STL_HPP 3 | 4 | #include "../Core.hpp" 5 | 6 | #include 7 | 8 | namespace Aoba { 9 | namespace IO { 10 | 11 | /// 12 | /// Export the given mesh into a binary stl file stored at the given path. 13 | /// 14 | /// 15 | /// The functinality is currently rather limited. 16 | /// 17 | /// File path 18 | /// Mesh to export 19 | void ExportStl(std::string path, Core::Mesh* mesh); 20 | 21 | } // namespace IO 22 | } // namespace Aoba 23 | 24 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/IO/IndexMesh.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_IO_INDEX_MESH_HPP 2 | #define AOBA_IO_INDEX_MESH_HPP 3 | 4 | #include "../Core.hpp" 5 | 6 | namespace Aoba { 7 | namespace IO { 8 | 9 | class IndexMesh { 10 | public: 11 | std::vector vertexCoords; // packed vertex coordinates, in x,y,z order 12 | std::vector edges; // packed edge vertex indices, in v1,v2 order 13 | std::vector triangles; // indices of triangle coordinates, v1,v2,v3 order 14 | 15 | /// 16 | /// populate the data of this index mesh from mesh. 17 | /// 18 | /// Mesh to convert to index-based representation 19 | void FromMesh(Core::Mesh* m); 20 | }; 21 | 22 | } // namespace IO 23 | } // namespace Aoba 24 | 25 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_HPP 2 | #define AOBA_MATH_HPP 3 | 4 | constexpr float PI = 3.1415926535897932384626433f; //TODO: c++20 has pi constant built in ... 5 | 6 | #include "Math/Euler.hpp" 7 | #include "Math/Matrix.hpp" 8 | #include "Math/Quaternion.hpp" 9 | #include "Math/Vector.hpp" 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/AobaAPI/Math/Euler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_EULER_HPP 2 | #define AOBA_MATH_EULER_HPP 3 | 4 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Matrix.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_MATRIX_HPP 2 | #define AOBA_MATH_MATRIX_HPP 3 | 4 | #include "Matrix/Matrix2.hpp" 5 | #include "Matrix/Matrix3.hpp" 6 | #include "Matrix/Matrix4.hpp" 7 | 8 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Matrix/Matrix2.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_MATRIX_MATRIX2_HPP 2 | #define AOBA_MATH_MATRIX_MATRIX2_HPP 3 | 4 | #include "AobaAPI/Math/Vector/Vector2.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace Aoba { 10 | namespace Math { 11 | 12 | class Mat2 { 13 | private: 14 | float data[4]; 15 | 16 | public: 17 | Mat2(); 18 | Mat2(const std::array& vals); 19 | 20 | static Mat2 Diagonal(const Vec2& vec); 21 | static Mat2 Identity(); 22 | static Mat2 OrthoProjection(const Vec2& axis); 23 | static Mat2 Rotation(float angle); 24 | static Mat2 Scale(const Vec2& axis, float factor); 25 | static Mat2 Zero(); 26 | 27 | float Determinant() const; 28 | bool Equals(const Mat2& other, float epsilon); 29 | void Invert(); 30 | Mat2 Inverted() const; 31 | bool IsInvertible() const; 32 | void Transpose(); 33 | Mat2 Transposed() const; 34 | 35 | friend Mat2 operator*(const float& lhs, const Mat2& rhs); 36 | friend Mat2 operator*(const Mat2& lhs, const float& rhs); 37 | friend Mat2 operator/(const Mat2& lhs, const float& rhs); 38 | friend Mat2 operator-(const Mat2& lhs, const Mat2& rhs); 39 | friend Mat2 operator+(const Mat2& lhs, const Mat2& rhs); 40 | friend Mat2 operator*(const Mat2& lhs, const Mat2& rhs); 41 | friend Mat2& operator*=(Mat2& lhs, const float& rhs); 42 | friend Mat2& operator/=(Mat2& lhs, const float& rhs); 43 | friend Mat2& operator+=(Mat2& lhs, const Mat2& rhs); 44 | friend Mat2& operator-=(Mat2& lhs, const Mat2& rhs); 45 | friend Mat2& operator*=(Mat2& lhs, const Mat2& rhs); 46 | friend Vec2 operator*(const Mat2 lhs, const Vec2& rhs); 47 | 48 | float operator()(std::size_t row, std::size_t col) const; 49 | float& operator()(std::size_t row, std::size_t col); 50 | 51 | Vec2 GetCol(std::size_t idx) const; 52 | Vec2 GetRow(std::size_t idx) const; 53 | void SetCol(std::size_t idx, const Vec2& vec); 54 | void SetRow(std::size_t idx, const Vec2& vec); 55 | }; 56 | 57 | Mat2 operator*(const float& lhs, const Mat2& rhs); 58 | Mat2 operator*(const Mat2& lhs, const float& rhs); 59 | Mat2 operator/(const Mat2& lhs, const float& rhs); 60 | Mat2 operator-(const Mat2& lhs, const Mat2& rhs); 61 | Mat2 operator+(const Mat2& lhs, const Mat2& rhs); 62 | Mat2 operator*(const Mat2& lhs, const Mat2& rhs); 63 | Mat2& operator*=(Mat2& lhs, const float& rhs); 64 | Mat2& operator/=(Mat2& lhs, const float& rhs); 65 | Mat2& operator+=(Mat2& lhs, const Mat2& rhs); 66 | Mat2& operator-=(Mat2& lhs, const Mat2& rhs); 67 | Mat2& operator*=(Mat2& lhs, const Mat2& rhs); 68 | Vec2 operator*(const Mat2 lhs, const Vec2& rhs); 69 | 70 | } // namespace Math 71 | } // namespace Aoba 72 | 73 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Matrix/Matrix3.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_MATRIX_MATRIX3_HPP 2 | #define AOBA_MATH_MATRIX_MATRIX3_HPP 3 | 4 | #include "AobaAPI/Math/Vector/Vector3.hpp" 5 | 6 | #include 7 | 8 | namespace Aoba { 9 | namespace Math { 10 | 11 | class Mat3 { 12 | private: 13 | float data[9]; 14 | 15 | public: 16 | Mat3(); 17 | Mat3(const std::array& vals); 18 | 19 | static Mat3 Diagonal(const Vec3& vec); 20 | static Mat3 Identity(); 21 | static Mat3 OrthoProjection(const Vec3& axis); 22 | static Mat3 Rotation(const Vec3& axis, float angle); 23 | static Mat3 Scale(const Vec3& axis, float factor); 24 | static Mat3 Zero(); 25 | 26 | float Determinant() const; 27 | bool Equals(const Mat3& other, float epsilon); 28 | Vec3 GetScale() const; 29 | void Invert(); 30 | Mat3 Inverted() const; 31 | bool IsInvertible() const; 32 | // Euler ToEuler(); 33 | // Quaternion ToQuaternion(); 34 | void Transpose(); 35 | Mat3 Transposed() const; 36 | 37 | friend Mat3 operator*(const float& lhs, const Mat3& rhs); 38 | friend Mat3 operator*(const Mat3& lhs, const float& rhs); 39 | friend Mat3 operator/(const Mat3& lhs, const float& rhs); 40 | friend Mat3 operator-(const Mat3& lhs, const Mat3& rhs); 41 | friend Mat3 operator+(const Mat3& lhs, const Mat3& rhs); 42 | friend Mat3 operator*(const Mat3& lhs, const Mat3& rhs); 43 | friend Mat3& operator*=(Mat3& lhs, const float& rhs); 44 | friend Mat3& operator/=(Mat3& lhs, const float& rhs); 45 | friend Mat3& operator+=(Mat3& lhs, const Mat3& rhs); 46 | friend Mat3& operator-=(Mat3& lhs, const Mat3& rhs); 47 | friend Mat3& operator*=(Mat3& lhs, const Mat3& rhs); 48 | friend Vec3 operator*(const Mat3 lhs, const Vec3& rhs); 49 | 50 | float operator()(std::size_t row, std::size_t col) const; 51 | float& operator()(std::size_t row, std::size_t col); 52 | 53 | Vec3 GetCol(std::size_t idx) const; 54 | Vec3 GetRow(std::size_t idx) const; 55 | void SetCol(std::size_t idx, const Vec3& vec); 56 | void SetRow(std::size_t idx, const Vec3& vec); 57 | }; 58 | 59 | Mat3 operator*(const float& lhs, const Mat3& rhs); 60 | Mat3 operator*(const Mat3& lhs, const float& rhs); 61 | Mat3 operator/(const Mat3& lhs, const float& rhs); 62 | Mat3 operator-(const Mat3& lhs, const Mat3& rhs); 63 | Mat3 operator+(const Mat3& lhs, const Mat3& rhs); 64 | Mat3 operator*(const Mat3& lhs, const Mat3& rhs); 65 | Mat3& operator*=(Mat3& lhs, const float& rhs); 66 | Mat3& operator/=(Mat3& lhs, const float& rhs); 67 | Mat3& operator+=(Mat3& lhs, const Mat3& rhs); 68 | Mat3& operator-=(Mat3& lhs, const Mat3& rhs); 69 | Mat3& operator*=(Mat3& lhs, const Mat3& rhs); 70 | Vec3 operator*(const Mat3 lhs, const Vec3& rhs); 71 | 72 | } // namespace Math 73 | } // namespace Aoba 74 | 75 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Matrix/Matrix4.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_MATRIX_MATRIX4_HPP 2 | #define AOBA_MATH_MATRIX_MATRIX4_HPP 3 | 4 | #include "AobaAPI/Math/Vector/Vector4.hpp" 5 | 6 | #include 7 | 8 | namespace Aoba { 9 | namespace Math { 10 | 11 | class Mat4 { 12 | private: 13 | float data[16]; 14 | 15 | public: 16 | Mat4(); 17 | Mat4(const std::array& vals); 18 | 19 | static Mat4 Diagonal(const Vec4& vec); 20 | static Mat4 Identity(); 21 | static Mat4 OrthoProjection(const Vec4& axis); 22 | static Mat4 Rotation(const Vec4& axis, float angle); 23 | static Mat4 Scale(const Vec4& axis, float factor); 24 | static Mat4 Translation(const Vec4& vec); 25 | static Mat4 Zero(); 26 | 27 | float Determinant() const; 28 | bool Equals(const Mat4& other, float epsilon); 29 | Vec4 GetScale() const; 30 | Vec4 GetTranslation() const; 31 | void Invert(); 32 | Mat4 Inverted() const; 33 | bool IsInvertible() const; 34 | // Euler ToEuler(); 35 | // Quaternion ToQuaternion(); 36 | void Transpose(); 37 | Mat4 Transposed() const; 38 | 39 | friend Mat4 operator*(const float& lhs, const Mat4& rhs); 40 | friend Mat4 operator*(const Mat4& lhs, const float& rhs); 41 | friend Mat4 operator/(const Mat4& lhs, const float& rhs); 42 | friend Mat4 operator-(const Mat4& lhs, const Mat4& rhs); 43 | friend Mat4 operator+(const Mat4& lhs, const Mat4& rhs); 44 | friend Mat4 operator*(const Mat4& lhs, const Mat4& rhs); 45 | friend Mat4& operator*=(Mat4& lhs, const float& rhs); 46 | friend Mat4& operator/=(Mat4& lhs, const float& rhs); 47 | friend Mat4& operator+=(Mat4& lhs, const Mat4& rhs); 48 | friend Mat4& operator-=(Mat4& lhs, const Mat4& rhs); 49 | friend Mat4& operator*=(Mat4& lhs, const Mat4& rhs); 50 | friend Vec4 operator*(const Mat4 lhs, const Vec4& rhs); 51 | 52 | float operator()(std::size_t row, std::size_t col) const; 53 | float& operator()(std::size_t row, std::size_t col); 54 | 55 | Vec4 GetCol(std::size_t idx) const; 56 | Vec4 GetRow(std::size_t idx) const; 57 | void SetCol(std::size_t idx, const Vec4& vec); 58 | void SetRow(std::size_t idx, const Vec4& vec); 59 | }; 60 | 61 | Mat4 operator*(const float& lhs, const Mat4& rhs); 62 | Mat4 operator*(const Mat4& lhs, const float& rhs); 63 | Mat4 operator/(const Mat4& lhs, const float& rhs); 64 | Mat4 operator-(const Mat4& lhs, const Mat4& rhs); 65 | Mat4 operator+(const Mat4& lhs, const Mat4& rhs); 66 | Mat4 operator*(const Mat4& lhs, const Mat4& rhs); 67 | Mat4& operator*=(Mat4& lhs, const float& rhs); 68 | Mat4& operator/=(Mat4& lhs, const float& rhs); 69 | Mat4& operator+=(Mat4& lhs, const Mat4& rhs); 70 | Mat4& operator-=(Mat4& lhs, const Mat4& rhs); 71 | Mat4& operator*=(Mat4& lhs, const Mat4& rhs); 72 | Vec4 operator*(const Mat4 lhs, const Vec4& rhs); 73 | 74 | } // namespace Math 75 | } // namespace Aoba 76 | 77 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Quaternion.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_QUATERNION_HPP 2 | #define AOBA_MATH_QUATERNION_HPP 3 | 4 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Vector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_VECTOR_HPP 2 | #define AOBA_MATH_VECTOR_HPP 3 | 4 | #include "Vector/Vector2.hpp" 5 | #include "Vector/Vector3.hpp" 6 | #include "Vector/Vector4.hpp" 7 | 8 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Vector/Vector2.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_VECTOR_VECTOR2_HPP 2 | #define AOBA_MATH_VECTOR_VECTOR2_HPP 3 | 4 | #include "Vector3.hpp" 5 | 6 | namespace Aoba { 7 | namespace Math { 8 | 9 | class Vec2 { 10 | public: 11 | float x; 12 | float y; 13 | 14 | Vec2(); 15 | Vec2(float x, float y); 16 | explicit Vec2(const Vec3& vec); 17 | explicit Vec2(const Vec4& vec); 18 | 19 | float Angle(const Vec2& other) const; 20 | float AngleSigned(const Vec2& other) const; 21 | float Dot(const Vec2& other) const; 22 | bool Equals(const Vec2& other, float epsilon) const; 23 | void Negate(); 24 | Vec2 Negated() const; 25 | void Normalize(); 26 | Vec2 Normalized() const; 27 | float Length() const; 28 | float LengthSquared() const; 29 | float Magnitude() const; 30 | 31 | friend Vec2 operator*(const float& lhs, const Vec2& rhs); 32 | friend Vec2 operator*(const Vec2& lhs, const float& rhs); 33 | friend Vec2 operator/(const Vec2& lhs, const float& rhs); 34 | friend Vec2 operator+(const Vec2& lhs, const Vec2& rhs); 35 | friend Vec2 operator-(const Vec2& lhs, const Vec2& rhs); 36 | friend Vec2& operator*=(Vec2& lhs, const float& rhs); 37 | friend Vec2& operator/=(Vec2& lhs, const float& rhs); 38 | friend Vec2& operator+=(Vec2& lhs, const Vec2& rhs); 39 | friend Vec2& operator-=(Vec2& lhs, const Vec2& rhs); 40 | 41 | float operator()(std::size_t idx) const; 42 | float& operator()(std::size_t idx); 43 | }; 44 | 45 | Vec2 operator*(const float& lhs, const Vec2& rhs); 46 | Vec2 operator*(const Vec2& lhs, const float& rhs); 47 | Vec2 operator/(const Vec2& lhs, const float& rhs); 48 | Vec2 operator+(const Vec2& lhs, const Vec2& rhs); 49 | Vec2 operator-(const Vec2& lhs, const Vec2& rhs); 50 | Vec2& operator*=(Vec2& lhs, const float& rhs); 51 | Vec2& operator/=(Vec2& lhs, const float& rhs); 52 | Vec2& operator+=(Vec2& lhs, const Vec2& rhs); 53 | Vec2& operator-=(Vec2& lhs, const Vec2& rhs); 54 | 55 | } // namespace Math 56 | } // namespace Aoba 57 | 58 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Vector/Vector3.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_VECTOR_VECTOR3_HPP 2 | #define AOBA_MATH_VECTOR_VECTOR3_HPP 3 | 4 | #include "Vector4.hpp" 5 | 6 | namespace Aoba { 7 | namespace Math { 8 | 9 | class Vec3 { 10 | public: 11 | float x; 12 | float y; 13 | float z; 14 | 15 | Vec3(); 16 | Vec3(float x, float y, float z); 17 | explicit Vec3(const Vec4& vec); 18 | 19 | float Angle(const Vec3& other) const; 20 | Vec3 Cross(const Vec3& other) const; 21 | float Dot(const Vec3& other) const; 22 | bool Equals(const Vec3& other, float epsilon) const; 23 | void Negate(); 24 | Vec3 Negated() const; 25 | void Normalize(); 26 | Vec3 Normalized() const; 27 | float Length() const; 28 | float LengthSquared() const; 29 | float Magnitude() const; 30 | 31 | friend Vec3 operator*(const float& lhs, const Vec3& rhs); 32 | friend Vec3 operator*(const Vec3& lhs, const float& rhs); 33 | friend Vec3 operator/(const Vec3& lhs, const float& rhs); 34 | friend Vec3 operator+(const Vec3& lhs, const Vec3& rhs); 35 | friend Vec3 operator-(const Vec3& lhs, const Vec3& rhs); 36 | friend Vec3& operator*=(Vec3& lhs, const float& rhs); 37 | friend Vec3& operator/=(Vec3& lhs, const float& rhs); 38 | friend Vec3& operator+=(Vec3& lhs, const Vec3& rhs); 39 | friend Vec3& operator-=(Vec3& lhs, const Vec3& rhs); 40 | 41 | float operator()(std::size_t idx) const; 42 | float& operator()(std::size_t idx); 43 | }; 44 | 45 | Vec3 operator*(const float& lhs, const Vec3& rhs); 46 | Vec3 operator*(const Vec3& lhs, const float& rhs); 47 | Vec3 operator/(const Vec3& lhs, const float& rhs); 48 | Vec3 operator+(const Vec3& lhs, const Vec3& rhs); 49 | Vec3 operator-(const Vec3& lhs, const Vec3& rhs); 50 | Vec3& operator*=(Vec3& lhs, const float& rhs); 51 | Vec3& operator/=(Vec3& lhs, const float& rhs); 52 | Vec3& operator+=(Vec3& lhs, const Vec3& rhs); 53 | Vec3& operator-=(Vec3& lhs, const Vec3& rhs); 54 | 55 | } // namespace Math 56 | } // namespace Aoba 57 | 58 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Math/Vector/Vector4.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_MATH_VECTOR_VECTOR4_HPP 2 | #define AOBA_MATH_VECTOR_VECTOR4_HPP 3 | 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Math { 8 | 9 | class Vec4 { 10 | public: 11 | float x; 12 | float y; 13 | float z; 14 | float w; 15 | 16 | Vec4(); 17 | Vec4(float x, float y, float z, float w); 18 | 19 | float Dot(const Vec4& other) const; 20 | bool Equals(const Vec4& other, float epsilon) const; 21 | void Negate(); 22 | Vec4 Negated() const; 23 | void Normalize(); 24 | Vec4 Normalized() const; 25 | float Length() const; 26 | float LengthSquared() const; 27 | float Magnitude() const; 28 | 29 | friend Vec4 operator*(const float& lhs, const Vec4& rhs); 30 | friend Vec4 operator*(const Vec4& lhs, const float& rhs); 31 | friend Vec4 operator/(const Vec4& lhs, const float& rhs); 32 | friend Vec4 operator+(const Vec4& lhs, const Vec4& rhs); 33 | friend Vec4 operator-(const Vec4& lhs, const Vec4& rhs); 34 | friend Vec4& operator*=(Vec4& lhs, const float& rhs); 35 | friend Vec4& operator/=(Vec4& lhs, const float& rhs); 36 | friend Vec4& operator+=(Vec4& lhs, const Vec4& rhs); 37 | friend Vec4& operator-=(Vec4& lhs, const Vec4& rhs); 38 | 39 | float operator()(std::size_t idx) const; 40 | float& operator()(std::size_t idx); 41 | }; 42 | 43 | Vec4 operator*(const float& lhs, const Vec4& rhs); 44 | Vec4 operator*(const Vec4& lhs, const float& rhs); 45 | Vec4 operator/(const Vec4& lhs, const float& rhs); 46 | Vec4 operator+(const Vec4& lhs, const Vec4& rhs); 47 | Vec4 operator-(const Vec4& lhs, const Vec4& rhs); 48 | Vec4& operator*=(Vec4& lhs, const float& rhs); 49 | Vec4& operator/=(Vec4& lhs, const float& rhs); 50 | Vec4& operator+=(Vec4& lhs, const Vec4& rhs); 51 | Vec4& operator-=(Vec4& lhs, const Vec4& rhs); 52 | 53 | } // namespace Math 54 | } // namespace Aoba 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/AobaAPI/Ops.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_OPS_HPP 2 | #define AOBA_OPS_HPP 3 | 4 | #include "Ops/Create.hpp" 5 | #include "Ops/Modify.hpp" 6 | #include "Ops/Select.hpp" 7 | #include "Ops/Transform.hpp" 8 | 9 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Ops/Create.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_OPS_CREATE_HPP 2 | #define AOBA_OPS_CREATE_HPP 3 | 4 | #include "AobaAPI/Core.hpp" 5 | #include "AobaAPI/Math.hpp" 6 | 7 | #include 8 | 9 | namespace Aoba { 10 | namespace Ops { 11 | 12 | class CreateCircleResult { 13 | public: 14 | std::vector verts; // circle verts 15 | std::vector edges; // circle edges 16 | }; 17 | 18 | /// 19 | /// Create circle of specified radius at the world origin 20 | /// 21 | /// Mesh on which to operate on 22 | /// Number of verts which form the circle 23 | /// Radius of the circle 24 | /// New verts and edges 25 | const CreateCircleResult CreateCircle(Core::Mesh* m, unsigned vertCount, float radius); 26 | 27 | class CreateCubeResult { 28 | public: 29 | std::vector verts; // cube verts 30 | std::vector edges; // cube edges 31 | std::vector Faces; // cube faces 32 | }; 33 | 34 | /// 35 | /// Create cube of specified size centered at the world origin 36 | /// 37 | /// Mesh on which to operate on 38 | /// Size of the cube(same for all dimensions) 39 | /// new verts, edges and faces 40 | const CreateCubeResult CreateCube(Core::Mesh* m, float size); 41 | 42 | class CreateGridResult { 43 | public: 44 | std::vector innerVerts; // grid verts which do not touch the boundary 45 | std::vector outerVerts; // grid verts found on the boundary 46 | std::vector innerEdges; // grid verts not found on the boundary 47 | std::vector outerEdges; // grid verts found on the boundary, forming the outer loop. 48 | std::vector innerFaces; // grid faces not found on the boundary 49 | std::vector outerFaces; // grid faces found on the boundary 50 | }; 51 | 52 | /// 53 | /// Create a new grid at the world origin, with specified divisions and size for each axis, in the x-y plane 54 | /// if divisions are 0, creates a plane consisting of 4 verts, 4 edges and a single face 55 | /// 56 | /// Mesh on which to operate on 57 | /// Number of divisions on the x axis 58 | /// Number of divisions on the y axis 59 | /// Size on the x axis 60 | /// Size on the y axis 61 | /// new verts, edges and faces 62 | const CreateGridResult CreateGrid(Core::Mesh* m, int divisionsX, int divisionsY, float sizeX, float sizeY); 63 | 64 | class CreateIcoSphereResult { 65 | public: 66 | std::vector verts; // sphere verts 67 | std::vector edges; // sphere edges 68 | std::vector Faces; // sphere faces 69 | }; 70 | 71 | /// 72 | /// TODO: 73 | /// Create a new IcoSphere of the given radius, centered at the world origin 74 | /// 75 | /// Mesh on which to operate on 76 | /// Radius of the sphere 77 | /// Number of divisions(for each edge) 78 | /// new edges, verts and faces 79 | /// TODO: 80 | const CreateIcoSphereResult CreateIcoSphere(Core::Mesh* m, float radius, unsigned divisions); 81 | 82 | class CreateUvSphereResult { 83 | public: 84 | std::vector verts; // sphere verts 85 | std::vector edges; // sphere edges 86 | std::vector Faces; // sphere faces 87 | }; 88 | 89 | /// 90 | /// Creates a new UV sphere centered at the world origin 91 | /// 92 | /// Mesh on which to operate on 93 | /// Radius of the sphere 94 | /// Number of rings(vertical slices) 95 | /// Number of slices(orthogonal to x-y plane) 96 | /// New edges, verts and faces 97 | const CreateUvSphereResult CreateUVSphere(Core::Mesh* m, float radius, std::size_t rings, std::size_t segments); 98 | 99 | /// 100 | /// Create a single vertex at the given coordinates. 101 | /// 102 | /// Mesh on which to operate on 103 | /// Coordinate of the new vertex 104 | /// The new vertex 105 | Core::Vert* CreateVert(Core::Mesh* m, const Math::Vec3 co); 106 | 107 | } // namespace Ops 108 | } // namespace Aoba 109 | 110 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Ops/Select.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_OPS_SELECT_HPP 2 | #define AOBA_OPS_SELECT_HPP 3 | 4 | #include "AobaAPI/Core.hpp" 5 | #include "AobaAPI/Math.hpp" 6 | 7 | #include 8 | 9 | namespace Aoba { 10 | namespace Ops { 11 | 12 | class SelectResult { 13 | public: 14 | std::vector verts; // selected verts 15 | std::vector edges; // selected edges 16 | std::vector faces; // selected faces 17 | }; 18 | 19 | /// 20 | /// Grow the input geometry using the defined step. The output geometry is returned together with input elements 21 | /// Faces and edges are also selected if their boundaries are selected(both input and grown) 22 | /// 23 | /// Mesh on which to operate on 24 | /// Input verts 25 | /// Input edges 26 | /// Input faces 27 | /// Wether to step using faces(or edges) 28 | /// Newly selected geometry along with previously selected geometry 29 | const SelectResult SelectExtend(Core::Mesh* m, const std::vector& verts, 30 | const std::vector& edges, const std::vector& faces, bool faceStep); 31 | 32 | /// 33 | /// Reduce the input geometry using the defined step. 34 | /// Faces and edges are also selected if their boundaries are selected(both input and grown) 35 | /// 36 | /// Mesh on which to operate on 37 | /// Input verts 38 | /// Input edges 39 | /// Input faces 40 | /// Wether to step using faces(or edges) 41 | /// Selected geometry. can be empty. 42 | const SelectResult SelectReduce(Core::Mesh* m, const std::vector& verts, 43 | const std::vector& edges, const std::vector& faces, bool faceStep); 44 | 45 | /// 46 | /// Transform input edges into other elements - verts and faces 47 | /// 48 | /// Mesh on which to operate on 49 | /// Input edges 50 | /// The transformed selection. edges countain input edges. 51 | const SelectResult SelectFromEdges(Core::Mesh* m, const std::vector& edges); 52 | 53 | /// 54 | /// Transform input faces into other elements - edged and verts 55 | /// 56 | /// Mesh on which to operate on 57 | /// Input faces 58 | /// The transformed selection. faces countain input faces. 59 | const SelectResult SelectFromFaces(Core::Mesh* m, const std::vector& faces); 60 | 61 | /// 62 | /// Transform input verts into other elements - edges and faces 63 | /// 64 | /// Mesh on which to operate on 65 | /// Input verts 66 | /// The transformed selection. verts countain input verts. 67 | const SelectResult SelectFromVerts(Core::Mesh* m, const std::vector& verts); 68 | 69 | } // namespace Ops 70 | } // namespace Aoba 71 | 72 | #endif -------------------------------------------------------------------------------- /include/AobaAPI/Ops/Transform.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_OPS_TRANSFORM_HPP 2 | #define AOBA_OPS_TRANSFORM_HPP 3 | 4 | #include "AobaAPI/Core.hpp" 5 | #include "AobaAPI/Math.hpp" 6 | 7 | #include 8 | 9 | namespace Aoba { 10 | namespace Ops { 11 | 12 | /// 13 | /// Rotate the given verts around the given rotation center using the rotation specified by the rotation matrix 14 | /// 15 | /// Mesh on which to operate on 16 | /// Verts to rotate 17 | /// Center of rotation 18 | /// rotation matrix 19 | void Rotate(Core::Mesh* m, const std::vector& verts, const Math::Vec3& center, const Math::Mat3& mat); 20 | 21 | /// 22 | /// Translate the given verts using the given vector. 23 | /// 24 | /// Mesh on which to operate on 25 | /// Verts to translate 26 | /// Translation vector 27 | void Translate(Core::Mesh* m, const std::vector& verts, const Math::Vec3& vec); 28 | 29 | /// 30 | /// Scale the given verts around the given center using the scale factors for x,y,z axis defined in the vector 31 | /// 32 | /// Mesh on which to operate on 33 | /// Verts to rotate 34 | /// Center of scaling 35 | /// X,Y,Z axis scale factor 36 | void Scale(Core::Mesh* m, const std::vector& verts, const Math::Vec3& center, const Math::Vec3& vec); 37 | 38 | /// 39 | /// Transform the mesh using the transform matrix. 40 | /// 41 | /// Mesh on which to operate on 42 | /// Verts to rotate 43 | /// Transform matrix 44 | void Transform(Core::Mesh* m, const std::vector& verts, const Math::Mat4 matrix); 45 | 46 | } // namespace Ops 47 | } // namespace Aoba 48 | 49 | #endif -------------------------------------------------------------------------------- /src/AobaAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/AobaAPI.hpp" -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADER_LIST "${PROJECT_SOURCE_DIR}/include/AobaAPI/AobaAPI.hpp") 2 | 3 | add_library(AobaAPI AobaAPI.cpp ${HEADER_LIST}) 4 | 5 | target_include_directories(AobaAPI PUBLIC ../include) 6 | 7 | target_compile_features(AobaAPI PUBLIC cxx_std_11) 8 | 9 | source_group( 10 | TREE "${PROJECT_SOURCE_DIR}/include" 11 | PREFIX "Header Files" 12 | FILES ${HEADER_LIST} 13 | ) 14 | 15 | add_subdirectory(Math) 16 | add_subdirectory(Core) 17 | add_subdirectory(Ops) 18 | add_subdirectory(IO) -------------------------------------------------------------------------------- /src/Core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Mesh) 2 | add_subdirectory(EulerOps) 3 | -------------------------------------------------------------------------------- /src/Core/EulerOps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/DissolveEdge.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/EdgeSplit.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/EdgeSqueeze.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/GlueEdge.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/GlueFace.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/GlueVert.cpp 10 | ${CMAKE_CURRENT_SOURCE_DIR}/JoinMesh.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/KillEdge.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/KillFace.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/KillMesh.cpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/KillVert.cpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/MakeEdge.cpp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/MakeEdgeVert.cpp 17 | ${CMAKE_CURRENT_SOURCE_DIR}/MakeFace.cpp 18 | ${CMAKE_CURRENT_SOURCE_DIR}/MakeLoop.cpp 19 | ${CMAKE_CURRENT_SOURCE_DIR}/MakeVert.cpp 20 | ${CMAKE_CURRENT_SOURCE_DIR}/ManifoldMakeEdge.cpp 21 | ${CMAKE_CURRENT_SOURCE_DIR}/ValidateLoop.cpp 22 | ) -------------------------------------------------------------------------------- /src/Core/EulerOps/DissolveEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Core { 8 | 9 | void DissolveEdge(Edge* e, Face* fSurvivor) { 10 | // check if the edge is dissolvable 11 | // edge is not dissolvable if 12 | // 1. it is not manifold 13 | if(e->Loops().size() != 2 || !(e->IsManifold())) { 14 | throw std::invalid_argument("Edge must have two adjecent faces and must be manifold."); 15 | } 16 | // get the ohter face and loops around this edge 17 | Face* other; 18 | Loop* survLoop; 19 | Loop* otherLoop; 20 | if(e->l->f == fSurvivor) { 21 | survLoop = e->l; 22 | otherLoop = e->l->eNext; 23 | other = e->l->eNext->f; 24 | } else { 25 | other = e->l->f; 26 | survLoop = e->l->eNext; 27 | otherLoop = e->l; 28 | } 29 | 30 | // 2. there exists another edge which is a boundary between the same two faces 31 | Loop* faceLoop = survLoop->fNext; 32 | do { 33 | Loop* edgeLoop = faceLoop; 34 | do { 35 | if(edgeLoop->f == other) { 36 | throw std::invalid_argument("Another shared edge exists between the two faces."); 37 | } 38 | edgeLoop = edgeLoop->eNext; 39 | } while(edgeLoop != faceLoop); 40 | faceLoop = faceLoop->fNext; 41 | } while(faceLoop != survLoop); 42 | 43 | // replace fnext/fprev pointers inside both loops 44 | Loop* otherNext = otherLoop->fNext; 45 | Loop* otherPrev = otherLoop->fPrev; 46 | Loop* survNext = survLoop->fNext; 47 | Loop* survPrev = survLoop->fPrev; 48 | survPrev->fNext = otherNext; 49 | survNext->fPrev = otherPrev; 50 | otherNext->fPrev = survPrev; 51 | otherPrev->fNext = survNext; 52 | 53 | // make the loop point to surviving face 54 | Loop* current = survPrev; 55 | do { 56 | current->f = fSurvivor; 57 | current = current->fNext; 58 | } while(current != survNext); 59 | 60 | // make sure that the face won't point to the deleted loop 61 | fSurvivor->l = survPrev; 62 | 63 | // kill the dissolved edge 64 | e->l = nullptr; 65 | KillEdge(e); 66 | // delete unused face loops. 67 | delete otherLoop; 68 | delete survLoop; 69 | 70 | // delete the other face 71 | other->mPrev->mNext = other->mNext; 72 | other->mNext->mPrev = other->mPrev; 73 | if(other->m->faces == other) { 74 | other->m->faces = other->mNext; 75 | } 76 | delete other; 77 | 78 | return; 79 | } 80 | 81 | } // namespace Core 82 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/EdgeSplit.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void EdgeSplit(Edge* e, Vert* v, Edge* newe, Vert* newv) { 8 | newe->m = e->m; 9 | newv->m = e->m; // v can be nullptr 10 | // add the new edge between the appropriate verts 11 | // if v is not specified, add the new edge around v1. 12 | 13 | if(v == nullptr || v == e->v1) { 14 | // replace e with newe in list of verts around v1 15 | if(e->v1Prev == e && e->v1Next == e) { 16 | // e is the only edge around e->v1, handle this case differently 17 | e->v1->e = newe; 18 | newe->v1Next = newe; 19 | newe->v1Prev = newe; 20 | newv->e = e; 21 | // edges around e->v2 are unchanged 22 | } else { 23 | newv->e = newe; 24 | // e is not the only edge around e->v1 25 | newe->v1Next = e->v1Next; 26 | newe->v1Prev = e->v1Prev; 27 | if(e->v1->e == e) { 28 | e->v1->e = newe; 29 | } 30 | 31 | if(newe->v1Next->v1 == e->v1) { 32 | newe->v1Next->v1Prev = newe; 33 | } else { 34 | newe->v1Next->v2Prev = newe; 35 | } 36 | 37 | if(newe->v1Prev->v1 == e->v1) { 38 | newe->v1Prev->v1Next = newe; 39 | } else { 40 | newe->v1Prev->v2Next = newe; 41 | } 42 | } 43 | 44 | // list of edges around newv 45 | e->v1Next = newe; 46 | e->v1Prev = newe; 47 | newe->v2Next = e; 48 | newe->v2Prev = e; 49 | 50 | // loops 51 | if(e->l != nullptr) { 52 | Loop* current = e->l; 53 | std::vector newLoops = std::vector(); 54 | do { 55 | Loop* newl = new Loop(); 56 | newl->f = current->f; 57 | newl->e = newe; 58 | newl->m = e->m; 59 | if(current->v == e->v1) { 60 | // change loop vert 61 | current->v = newv; 62 | // add new loop as fPrev, with v in e->v1 and e in newe 63 | newl->v = e->v1; 64 | newl->fNext = current; 65 | newl->fPrev = current->fPrev; 66 | current->fPrev->fNext = newl; 67 | current->fPrev = newl; 68 | } else { 69 | // add new loop as fNext, with v in newv and e in newe 70 | newl->v = newv; 71 | newl->fPrev = current; 72 | newl->fNext = current->fNext; 73 | current->fNext->fPrev = newl; 74 | current->fNext = newl; 75 | } 76 | newLoops.push_back(newl); 77 | 78 | current = current->eNext; 79 | } while(current != e->l); 80 | for(std::size_t i = 1; i < newLoops.size(); ++i) { 81 | newLoops.at(i)->ePrev = newLoops.at(i - 1); 82 | newLoops.at(i - 1)->eNext = newLoops.at(i); 83 | } 84 | newLoops.at(0)->ePrev = newLoops.back(); 85 | newLoops.back()->eNext = newLoops.at(0); 86 | newe->l = newLoops.at(0); 87 | } 88 | 89 | // set verts around edges 90 | newe->v1 = e->v1; 91 | newe->v2 = newv; 92 | e->v1 = newv; 93 | 94 | } else { 95 | // replace e with newe in list of verts around v2 96 | if(e->v2Prev == e && e->v2Next == e) { 97 | // e is the only edge around e->v2, handle this case differently 98 | e->v2->e = newe; 99 | newe->v2Next = newe; 100 | newe->v2Prev = newe; 101 | newv->e = e; 102 | // edges around e->v1 are unchanged 103 | } else { 104 | newv->e = newe; 105 | // e is not the only edge around e->v2 106 | newe->v2Next = e->v2Next; 107 | newe->v2Prev = e->v2Prev; 108 | if(e->v2->e == e) { 109 | e->v2->e = newe; 110 | } 111 | 112 | if(newe->v2Next->v2 == e->v2) { 113 | newe->v2Next->v2Prev = newe; 114 | } else { 115 | newe->v2Next->v1Prev = newe; 116 | } 117 | 118 | if(newe->v2Prev->v2 == e->v2) { 119 | newe->v2Prev->v2Next = newe; 120 | } else { 121 | newe->v2Prev->v1Next = newe; 122 | } 123 | } 124 | 125 | // list of edges around newv 126 | e->v2Next = newe; 127 | e->v2Prev = newe; 128 | newe->v1Next = e; 129 | newe->v1Prev = e; 130 | 131 | // loops 132 | if(e->l != nullptr) { 133 | Loop* current = e->l; 134 | std::vector newLoops = std::vector(); 135 | do { 136 | Loop* newl = new Loop(); 137 | newl->f = current->f; 138 | newl->e = newe; 139 | newl->m = e->m; 140 | if(current->v == e->v2) { 141 | // change loop vert 142 | current->v = newv; 143 | // add new loop as fPrev, with v in e->v1 and e in newe 144 | newl->v = e->v2; 145 | newl->fNext = current; 146 | newl->fPrev = current->fPrev; 147 | current->fPrev->fNext = newl; 148 | current->fPrev = newl; 149 | } else { 150 | // add new loop as fNext, with v in newv and e in newe 151 | newl->v = newv; 152 | newl->fPrev = current; 153 | newl->fNext = current->fNext; 154 | current->fNext->fPrev = newl; 155 | current->fNext = newl; 156 | } 157 | newLoops.push_back(newl); 158 | 159 | current = current->eNext; 160 | } while(current != e->l); 161 | for(std::size_t i = 1; i < newLoops.size(); ++i) { 162 | newLoops.at(i)->ePrev = newLoops.at(i - 1); 163 | newLoops.at(i - 1)->eNext = newLoops.at(i); 164 | } 165 | newLoops.at(0)->ePrev = newLoops.back(); 166 | newLoops.back()->eNext = newLoops.at(0); 167 | newe->l = newLoops.at(0); 168 | } 169 | 170 | // set verts around edges 171 | newe->v2 = e->v2; 172 | newe->v1 = newv; 173 | e->v2 = newv; 174 | } 175 | 176 | // add newe, newv to mesh... 177 | Mesh* m = e->m; 178 | m->edges->mPrev->mNext = newe; 179 | newe->mPrev = m->edges->mPrev; 180 | m->edges->mPrev = newe; 181 | newe->mNext = m->edges; 182 | m->edges = newe; 183 | 184 | m->verts->mPrev->mNext = newv; 185 | newv->mPrev = m->verts->mPrev; 186 | m->verts->mPrev = newv; 187 | newv->mNext = m->verts; 188 | m->verts = newv; 189 | } 190 | 191 | } // namespace Core 192 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/EdgeSqueeze.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void EdgeSqueeze(Edge* e, Vert* vSurvivor) { 8 | // EdgeSqueeze is implemented using the GlueVert operator 9 | // it performs the same action, but it is known before hand that v1 and v2 have a common edge 10 | // this means that search for common edge can be omitted 11 | // that search is performed in O(m*n), where m and n are the number of edges aroud v1 and v2 12 | // the performance could be improved slightly, but m, n are rarely large, and in quad mesh, manifold scenario 13 | // m, n = 4, which means worst case - 16 iterations, which isn't much 14 | 15 | GlueVert(vSurvivor, e->Other(vSurvivor)); 16 | return; 17 | } 18 | 19 | } // namespace Core 20 | } // namespace Aoba 21 | -------------------------------------------------------------------------------- /src/Core/EulerOps/GlueEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void GlueEdge(Edge* e1, Edge* e2) { 8 | // Glue edge is implemented using the gluevert operator 9 | // it performs the same actions as the glue vert operator, but it is known that the references to e2 will be 10 | // replaced with references to e1 11 | 12 | // since other check must be performed, some speedup might be achieved by implementing this operator separately 13 | 14 | // check wether edge e1 and edge e2 have any shared verts 15 | // if they have a shared vert, the way in which they will be glued is fixed 16 | // and only one glueVert operation is required 17 | if(e1->v1 == e2->v1) { 18 | GlueVert(e1->v2, e2->v2); 19 | return; 20 | } 21 | if(e1->v1 == e2->v2) { 22 | GlueVert(e1->v2, e2->v1); 23 | return; 24 | } 25 | if(e1->v2 == e2->v1) { 26 | GlueVert(e1->v1, e2->v2); 27 | return; 28 | } 29 | if(e1->v2 == e2->v2) { 30 | GlueVert(e1->v1, e2->v1); 31 | return; 32 | } 33 | 34 | GlueVert(e1->v1, e2->v1); 35 | GlueVert(e1->v2, e2->v2); 36 | return; 37 | 38 | // verts of v1 are always kept, in order to always keep the edges of v1 alive 39 | } 40 | 41 | } // namespace Core 42 | } // namespace Aoba 43 | -------------------------------------------------------------------------------- /src/Core/EulerOps/GlueFace.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Core/EulerOps/GlueFace.cpp -------------------------------------------------------------------------------- /src/Core/EulerOps/JoinMesh.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void JoinMesh(Mesh* m1, Mesh* m2) { 8 | // join verts 9 | if(m2->verts) { 10 | // make all m2 verts point to m1 11 | Vert* current = m2->verts; 12 | do { 13 | current->m = m1; 14 | current = current->mNext; 15 | } while(current != m2->verts); 16 | 17 | // merge the lists 18 | if(m1->verts) { 19 | Vert* m1Start = m1->verts; 20 | Vert* m1End = m1->verts->mPrev; 21 | Vert* m2Start = m2->verts; 22 | Vert* m2End = m2->verts->mPrev; 23 | m1Start->mPrev = m2End; 24 | m1End->mNext = m2Start; 25 | m2Start->mPrev = m1End; 26 | m2End->mNext = m1Start; 27 | } else { 28 | m1->verts = m2->verts; 29 | } 30 | 31 | // reset pointer(not necessary, but just in case) 32 | m2->verts = nullptr; 33 | } 34 | 35 | // join edges 36 | if(m2->edges) { 37 | // make all m2 edges point to m1 38 | Edge* current = m2->edges; 39 | do { 40 | current->m = m1; 41 | current = current->mNext; 42 | } while(current != m2->edges); 43 | 44 | // merge the lists 45 | if(m1->verts) { 46 | Edge* m1Start = m1->edges; 47 | Edge* m1End = m1->edges->mPrev; 48 | Edge* m2Start = m2->edges; 49 | Edge* m2End = m2->edges->mPrev; 50 | m1Start->mPrev = m2End; 51 | m1End->mNext = m2Start; 52 | m2Start->mPrev = m1End; 53 | m2End->mNext = m1Start; 54 | } else { 55 | m1->edges = m2->edges; 56 | } 57 | 58 | // reset pointer(not necessary, but just in case) 59 | m2->edges = nullptr; 60 | } 61 | 62 | // join faces and loops 63 | if(m2->faces) { 64 | // make all m2 faces and their loops point to m1 65 | Face* current = m2->faces; 66 | do { 67 | Loop* currentLoop = current->l; 68 | while(currentLoop->fNext != current->l) { 69 | currentLoop->m = m1; 70 | currentLoop = currentLoop->fNext; 71 | } 72 | current->m = m1; 73 | current = current->mNext; 74 | } while(current != m2->faces); 75 | 76 | // merge the lists 77 | if(m1->verts) { 78 | Face* m1Start = m1->faces; 79 | Face* m1End = m1->faces->mPrev; 80 | Face* m2Start = m2->faces; 81 | Face* m2End = m2->faces->mPrev; 82 | m1Start->mPrev = m2End; 83 | m1End->mNext = m2Start; 84 | m2Start->mPrev = m1End; 85 | m2End->mNext = m1Start; 86 | } else { 87 | m1->faces = m2->faces; 88 | } 89 | 90 | // reset pointer(not necessary, but just in case) 91 | m2->faces = nullptr; 92 | } 93 | 94 | delete m2; 95 | } 96 | 97 | } // namespace Core 98 | } // namespace Aoba 99 | -------------------------------------------------------------------------------- /src/Core/EulerOps/KillEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void KillEdge(Edge* e) { 8 | // Kill all faces using this edge 9 | if(e->l != nullptr) { 10 | std::vector edgeFaces = e->Faces(); 11 | for(Face* f : edgeFaces) { 12 | KillFace(f); 13 | } 14 | } 15 | 16 | // for v1, check if this is the only edge 17 | if(e->v1->e == e && e->v1Next == e && e->v1Prev == e) { 18 | e->v1->e = nullptr; 19 | e->v1Next = nullptr; // likely not needed 20 | e->v1Prev = nullptr; // likely not needed 21 | } else { 22 | // no, v1 has more than one edge 23 | // if v1 points to this edge as entry point to the list, change that to the next edge 24 | e->v1->e = e->v1Next; 25 | 26 | // remove edge from v1's list 27 | // in the next edge in the list, v1 might be stored as v2... 28 | if(e->v1Prev->v1 == e->v1) { 29 | e->v1Prev->v1Next = e->v1Next; 30 | } else { 31 | e->v1Prev->v2Next = e->v1Next; 32 | } 33 | if(e->v1Next->v1 == e->v1) { 34 | e->v1Next->v1Prev = e->v1Prev; 35 | } else { 36 | e->v1Next->v2Prev = e->v1Prev; 37 | } 38 | } 39 | 40 | // for v2, check if this is the only edge 41 | if(e->v2->e == e && e->v2Next == e && e->v2Prev == e) { 42 | e->v2->e = nullptr; 43 | e->v2Next = nullptr; // likely not needed 44 | e->v2Prev = nullptr; // likely not needed 45 | } else { 46 | // no, v2 has more than one edge 47 | // if v2 points to this edge as entry point to the list, change that to the next edge 48 | e->v2->e = e->v2Next; 49 | 50 | // remove edge from v2's list 51 | // in the next edge in the list, v2 might be stored as v1... 52 | if(e->v2Prev->v2 == e->v2) { 53 | e->v2Prev->v2Next = e->v2Next; 54 | } else { 55 | e->v2Prev->v1Next = e->v2Next; 56 | } 57 | if(e->v2Next->v2 == e->v2) { 58 | e->v2Next->v2Prev = e->v2Prev; 59 | } else { 60 | e->v2Next->v1Prev = e->v2Prev; 61 | } 62 | } 63 | // TODO: does this work with single vertex edges? 64 | 65 | // remove edge from list of edges in mesh. 66 | if(e->mNext == e && e->mPrev == e) { 67 | e->m->edges = nullptr; 68 | } else { 69 | e->mPrev->mNext = e->mNext; 70 | e->mNext->mPrev = e->mPrev; 71 | if(e->m->edges == e) { 72 | e->m->edges = e->mNext; 73 | } 74 | } 75 | 76 | delete e; 77 | } 78 | 79 | } // namespace Core 80 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/KillFace.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void KillFace(Face* f) { 8 | // iterate over all face loops 9 | Loop* currentLoop = f->l; 10 | do { 11 | // remove loop from list of loops around an edge. 12 | if(currentLoop->eNext == currentLoop && currentLoop->ePrev == currentLoop) { 13 | // this is the only face using this edge. 14 | currentLoop->e->l = nullptr; 15 | } else { 16 | // the edge of this loop has more than one loop. 17 | currentLoop->ePrev->eNext = currentLoop->eNext; 18 | currentLoop->eNext->ePrev = currentLoop->ePrev; 19 | if(currentLoop->e->l == currentLoop) { 20 | currentLoop->e->l = currentLoop->eNext; 21 | } 22 | } 23 | 24 | Loop* toDelete = currentLoop; 25 | currentLoop = currentLoop->fNext; 26 | delete toDelete; 27 | } while(currentLoop != f->l); 28 | 29 | // remove face from list of faces in mesh. 30 | if(f->mNext == f && f->mPrev == f) { 31 | f->m->faces = nullptr; 32 | } else { 33 | f->mPrev->mNext = f->mNext; 34 | f->mNext->mPrev = f->mPrev; 35 | if(f->m->faces == f) { 36 | f->m->faces = f->mNext; 37 | } 38 | } 39 | 40 | // delete face 41 | delete f; 42 | } 43 | 44 | } // namespace Core 45 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/KillMesh.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void KillMesh(Mesh* m) { 8 | // could be done faster, but im lazy... 9 | 10 | // kill all faces (therefore loops and looplists) 11 | if(m->faces != nullptr) { 12 | std::vector faces = m->Faces(); 13 | for(Face* f : faces) { 14 | KillFace(f); 15 | } 16 | } 17 | 18 | // delete all edges(without killing faces as there are none) 19 | if(m->edges != nullptr) { 20 | std::vector edges = m->Edges(); 21 | for(Edge* e : edges) { 22 | KillEdge(e); 23 | } 24 | } 25 | 26 | // delete all verts(without deleting edges as there are none) 27 | if(m->verts != nullptr) { 28 | std::vector verts = m->Verts(); 29 | for(Vert* v : verts) { 30 | KillVert(v); 31 | } 32 | } 33 | 34 | delete m; 35 | } 36 | 37 | } // namespace Core 38 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/KillVert.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void KillVert(Vert* v) { 8 | // Kill all edges (and faces) using this edge 9 | std::vector vertEdges = v->Edges(); 10 | if(vertEdges.size() > 0) { 11 | for(auto it = vertEdges.begin(); it != vertEdges.end(); ++it) { 12 | KillEdge(*it); 13 | } 14 | } 15 | 16 | // remove vert from list of verts in mesh. 17 | if(v->mNext == v && v->mPrev == v) { 18 | v->m->verts = nullptr; 19 | } else { 20 | v->mPrev->mNext = v->mNext; 21 | v->mNext->mPrev = v->mPrev; 22 | if(v->m->verts == v) { 23 | v->m->verts = v->mNext; 24 | } 25 | } 26 | 27 | delete v; 28 | } 29 | 30 | } // namespace Core 31 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/MakeEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Core { 7 | 8 | void MakeEdge(Vert* v1, Vert* v2, Edge* newe) { 9 | // v1 and v2 are already in the mesh 10 | 11 | if(v1 == v2) { 12 | throw std::invalid_argument("Self-loop edges are not allowed"); 13 | } 14 | 15 | for(Core::Edge* edge : v1->Edges()) { 16 | if(edge->Other(v1) == v2) { 17 | throw std::invalid_argument("Edge already exists between v1 and v2"); 18 | } 19 | } 20 | 21 | // add newe to the mesh. 22 | // mesh might not have any edges at this point. 23 | Mesh* m = v1->m; 24 | if(m->edges == nullptr) { 25 | // empty mesh case 26 | m->edges = newe; 27 | newe->mNext = newe; 28 | newe->mPrev = newe; 29 | } else { 30 | // some edges already exist 31 | m->edges->mPrev->mNext = newe; 32 | newe->mPrev = m->edges->mPrev; 33 | m->edges->mPrev = newe; 34 | newe->mNext = m->edges; 35 | m->edges = newe; 36 | } 37 | 38 | // add verts v1 and v2 to edge, set loops to nullptr 39 | newe->v1 = v1; 40 | newe->v2 = v2; 41 | newe->l = nullptr; 42 | newe->m = v1->m; 43 | 44 | // set list of edges around v1. 45 | if(v1->e == nullptr) { 46 | // no existing edges around v 47 | v1->e = newe; 48 | newe->v1Prev = newe; 49 | newe->v1Next = newe; 50 | } else { 51 | // v already has some edges 52 | Edge* current = v1->e; 53 | Edge* prev = current->Prev(v1); 54 | v1->e = newe; 55 | 56 | newe->v1Prev = prev; 57 | newe->v1Next = current; 58 | 59 | // must check wether v1 is v1 or v2 in current and previous edge 60 | if(current->v1 == v1) { 61 | current->v1Prev = newe; 62 | } else { 63 | current->v2Prev = newe; 64 | } 65 | 66 | if(prev->v1 == v1) { 67 | prev->v1Next = newe; 68 | } else { 69 | prev->v2Next = newe; 70 | } 71 | } 72 | 73 | // set list of edges around v2. 74 | if(v2->e == nullptr) { 75 | // no existing edges around v 76 | v2->e = newe; 77 | newe->v2Prev = newe; 78 | newe->v2Next = newe; 79 | } else { 80 | // v already has some edges 81 | Edge* current = v2->e; 82 | Edge* prev = v2->e->Prev(v2); 83 | v2->e = newe; 84 | 85 | newe->v2Prev = prev; 86 | newe->v2Next = current; 87 | 88 | // must check wether v2 is v1 or v2 in current and previous edge 89 | if(current->v1 == v2) { 90 | current->v1Prev = newe; 91 | } else { 92 | current->v2Prev = newe; 93 | } 94 | 95 | if(prev->v1 == v2) { 96 | prev->v1Next = newe; 97 | } else { 98 | prev->v2Next = newe; 99 | } 100 | } 101 | 102 | // TODO: this likely does not work if v1 == v2 103 | } 104 | 105 | } // namespace Core 106 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/MakeEdgeVert.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | namespace Aoba { 5 | namespace Core { 6 | 7 | void MakeEdgeVert(Vert* v, Edge* newe, Vert* newv) { 8 | // v is already in the mesh 9 | 10 | // add newv to the mesh. 11 | // no need to check for nullptr since v is already in the mesh. 12 | Mesh* m = v->m; 13 | newv->m = m; 14 | m->verts->mPrev->mNext = newv; 15 | newv->mPrev = m->verts->mPrev; 16 | m->verts->mPrev = newv; 17 | newv->mNext = m->verts; 18 | m->verts = newv; 19 | 20 | // add newe to the mesh. 21 | // mesh might not have any edges at this point. 22 | newe->m = m; 23 | if(m->edges == nullptr) { 24 | // empty mesh case 25 | m->edges = newe; 26 | newe->mNext = newe; 27 | newe->mPrev = newe; 28 | } else { 29 | // some edges already exist 30 | m->edges->mPrev->mNext = newe; 31 | newe->mPrev = m->edges->mPrev; 32 | m->edges->mPrev = newe; 33 | newe->mNext = m->edges; 34 | m->edges = newe; 35 | } 36 | 37 | // add existing vert v as v1, newv as v2 of the edge, set loops to nullptr 38 | newe->v1 = v; 39 | newe->v2 = newv; 40 | newe->l = nullptr; 41 | 42 | // set list of edges around newv - v2. 43 | // since v2 is a new vert, it only has this single edge that is adjecent. 44 | newv->e = newe; 45 | newe->v2Next = newe; 46 | newe->v2Prev = newe; 47 | 48 | // set list of edges around v - v1. 49 | if(v->e == nullptr) { 50 | // no existing edges around v 51 | v->e = newe; 52 | newe->v1Prev = newe; 53 | newe->v1Next = newe; 54 | } else { 55 | // v already has some edges 56 | Edge* current = v->e; 57 | Edge* prev = current->Prev(v); 58 | v->e = newe; 59 | 60 | newe->v1Prev = prev; 61 | newe->v1Next = current; 62 | 63 | // must check wether v is v1 or v2 in current and previous edge 64 | if(current->v1 == v) { 65 | current->v1Prev = newe; 66 | } else { 67 | current->v2Prev = newe; 68 | } 69 | 70 | if(prev->v1 == v) { 71 | prev->v1Next = newe; 72 | } else { 73 | prev->v2Next = newe; 74 | } 75 | } 76 | } 77 | 78 | } // namespace Core 79 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/MakeFace.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Core { 8 | 9 | void MakeFace(Loop* loop, Face* newf) { 10 | Mesh* m = loop->m; 11 | 12 | // set the first loop of the face 13 | newf->l = loop; 14 | Loop* current = loop; 15 | 16 | do { 17 | current->f = newf; 18 | current = current->fNext; 19 | } while(current != loop); 20 | 21 | // add newf to mesh 22 | if(m->faces == nullptr) { 23 | m->faces = newf; 24 | newf->mNext = newf; 25 | newf->mPrev = newf; 26 | } else { 27 | m->faces->mPrev->mNext = newf; 28 | newf->mPrev = m->faces->mPrev; 29 | m->faces->mPrev = newf; 30 | newf->mNext = m->faces; 31 | m->faces = newf; 32 | } 33 | 34 | // link face to mesh 35 | newf->m = m; 36 | } 37 | 38 | } // namespace Core 39 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/MakeLoop.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Core { 8 | 9 | void MakeLoop(std::vector edges, std::vector verts, Loop* first) { 10 | if(edges.size() < 3 || verts.size() < 3) { 11 | throw std::invalid_argument("Face must have at least 3 distinct edges."); 12 | } 13 | 14 | std::vector newLoops = std::vector(); 15 | newLoops.reserve(edges.size()); 16 | 17 | newLoops.push_back(first); 18 | for(std::size_t i = 0; i < edges.size() - 1; i++) { 19 | newLoops.push_back(new Loop()); 20 | } 21 | 22 | // create loops for each vert-edge pair 23 | for(std::size_t i = 0; i < edges.size(); ++i) { 24 | Edge* currentEdge = edges.at(i); 25 | 26 | // create the new loop 27 | Loop* newl = newLoops.at(i); 28 | // TODO: check wether vert is adjecent to edge 29 | newl->e = currentEdge; 30 | newl->v = verts.at(i); 31 | newl->m = currentEdge->m; 32 | 33 | // add the new loop to edge 34 | // no existing loops using edge 35 | if(currentEdge->l == nullptr) { 36 | currentEdge->l = newl; 37 | newl->eNext = newl; 38 | newl->ePrev = newl; 39 | } else { 40 | // edge already has some adjecent loops 41 | Loop* currentLoop = currentEdge->l; 42 | Loop* prevLoop = currentLoop->ePrev; 43 | currentEdge->l = newl; 44 | 45 | newl->ePrev = prevLoop; 46 | newl->eNext = currentLoop; 47 | currentLoop->ePrev = newl; 48 | prevLoop->eNext = newl; 49 | } 50 | } 51 | 52 | for(std::size_t i = 1; i < newLoops.size(); i++) { 53 | newLoops.at(i)->fPrev = newLoops.at(i - 1); 54 | newLoops.at(i - 1)->fNext = newLoops.at(i); 55 | } 56 | newLoops.at(0)->fPrev = newLoops.back(); 57 | newLoops.back()->fNext = newLoops.at(0); 58 | } 59 | 60 | } // namespace Core 61 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/MakeVert.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | 5 | namespace Aoba { 6 | namespace Core { 7 | 8 | void MakeVert(Mesh* m, Vert* newv) { 9 | newv->e = nullptr; // new verts don't have any edges 10 | newv->m = m; // set pointer to mesh from vert. 11 | 12 | if(m->verts == nullptr) { 13 | // empty mesh case 14 | m->verts = newv; 15 | newv->mNext = newv; 16 | newv->mPrev = newv; 17 | } else { 18 | // some verts already exist 19 | m->verts->mPrev->mNext = newv; 20 | newv->mPrev = m->verts->mPrev; 21 | m->verts->mPrev = newv; 22 | newv->mNext = m->verts; 23 | m->verts = newv; 24 | } 25 | } 26 | 27 | } // namespace Core 28 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/ManifoldMakeEdge.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Core { 8 | 9 | void ManifoldMakeEdge(Vert* v1, Vert* v2, Face* f, Edge* newe, Face* newf) { 10 | // TODO: check wether inputs are valid 11 | 12 | // make the edge between v1, v2 13 | MakeEdge(v1, v2, newe); 14 | 15 | // find loops with starting point in l1, l2 16 | Loop* loop1 = nullptr; 17 | Loop* loop2 = nullptr; 18 | for(Core::Loop* loop : f->Loops()) { 19 | if(loop->v == v1) { 20 | loop1 = loop; 21 | } 22 | if(loop->v == v2) { 23 | loop2 = loop; 24 | } 25 | } 26 | if(loop1 == nullptr || loop2 == nullptr) { 27 | throw std::invalid_argument("V1 or V2 not found in face loop"); 28 | } 29 | 30 | // Make two new loops 31 | Loop* newl1 = new Loop(); 32 | Loop* newl2 = new Loop(); 33 | newl1->v = v2; 34 | newl2->v = v1; 35 | newl1->m = f->m; 36 | newl2->m = f->m; 37 | 38 | // add the new loops to face loops 39 | newl1->fPrev = loop2->fPrev; 40 | newl2->fPrev = loop1->fPrev; 41 | newl1->fNext = loop1; 42 | newl2->fNext = loop2; 43 | loop1->fPrev->fNext = newl2; 44 | loop1->fPrev = newl1; 45 | loop2->fPrev->fNext = newl1; 46 | loop2->fPrev = newl2; 47 | 48 | // add newl1, newl2 around edge newe 49 | newl1->eNext = newl2; 50 | newl1->ePrev = newl2; 51 | newl2->eNext = newl1; 52 | newl2->ePrev = newl1; 53 | newl1->e = newe; 54 | newl2->e = newe; 55 | newe->l = newl1; 56 | 57 | // make one of the loops point to the new face. 58 | newf->l = newl1; 59 | newl1->f = newf; 60 | Loop* current = newl1->fNext; 61 | while(current != newl1) { 62 | current->f = newf; 63 | current = current->fNext; 64 | } 65 | f->l = newl2; 66 | newl2->f = f; 67 | current = newl2->fNext; 68 | while(current != newl2) { 69 | current->f = f; 70 | current = current->fNext; 71 | } 72 | 73 | // add newf to list of faces in mesh 74 | Mesh* m = f->m; 75 | newf->m = m; 76 | m->faces->mPrev->mNext = newf; 77 | newf->mPrev = m->faces->mPrev; 78 | m->faces->mPrev = newf; 79 | newf->mNext = m->faces; 80 | m->faces = newf; 81 | 82 | return; 83 | } 84 | 85 | } // namespace Core 86 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/EulerOps/ValidateLoop.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/EulerOps.hpp" 2 | #include "AobaAPI/Core/Mesh.hpp" 3 | 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Core { 8 | 9 | bool ValidateLoop(std::vector edges, std::vector verts) { 10 | // faces must have at least three edges 11 | if(edges.size() < 3 || verts.size() < 3) { 12 | return false; 13 | } 14 | 15 | // verts and edges must be of same size 16 | if(edges.size() != verts.size()) { 17 | return false; 18 | } 19 | 20 | // edges and verts must form a continous closed loop. 21 | std::size_t vertIdx = 1; 22 | for(Edge* e : edges) { 23 | if(vertIdx == verts.size()) { 24 | if(verts.at(0) != e->Other(verts.back())) { 25 | return false; 26 | } 27 | } else { 28 | if(verts.at(vertIdx - 1) != e->Other(verts.at(vertIdx))) { 29 | return false; 30 | } 31 | vertIdx++; 32 | } 33 | } 34 | 35 | // check for duplicate verts 36 | // no need to explicitly check edges as well 37 | // if an edge is used twice, a vert must have been used twice as well. 38 | // this could be done in O(n) if i used flags to do it. but i don't want to use flags in eulerops 39 | for(std::size_t i = 0; i < verts.size(); ++i) { 40 | for(std::size_t j = 1; j < verts.size(); ++j) { 41 | if(verts.at(i) == verts.at(j)) { 42 | return false; 43 | } 44 | } 45 | } 46 | 47 | // seems ok 48 | return true; 49 | } 50 | 51 | } // namespace Core 52 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/Mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef AOBA_CORE_MESH_H 2 | #define AOBA_CORE_MESH_H 3 | 4 | #include "Mesh/Edge.h" 5 | #include "Mesh/Face.h" 6 | #include "Mesh/Loop.h" 7 | #include "Mesh/Mesh.h" 8 | #include "Mesh/Vert.h" 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/Core/Mesh/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Edge.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Face.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Loop.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/Mesh.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/Vert.cpp 9 | ) -------------------------------------------------------------------------------- /src/Core/Mesh/Edge.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/Mesh/Edge.hpp" 2 | 3 | #include "AobaAPI/Core/Mesh/Loop.hpp" 4 | #include "AobaAPI/Core/Mesh/Vert.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace Aoba { 10 | namespace Core { 11 | 12 | Edge::Edge() { 13 | index = 0; 14 | flags = 0; 15 | flagsIntern = 0; 16 | v1 = nullptr; 17 | v2 = nullptr; 18 | l = nullptr; 19 | v1Next = nullptr; 20 | v1Prev = nullptr; 21 | v2Next = nullptr; 22 | v2Prev = nullptr; 23 | m = nullptr; 24 | mNext = nullptr; 25 | mPrev = nullptr; 26 | } 27 | 28 | Edge* Edge::Next(const Vert* v) const { 29 | if(v == this->v1) { 30 | return this->v1Next; 31 | } 32 | if(v == this->v2) { 33 | return this->v2Next; 34 | } 35 | throw std::invalid_argument("Specified vert not used by the edge."); 36 | } 37 | 38 | Edge* Edge::Prev(const Vert* v) const { 39 | if(v == this->v1) { 40 | return this->v1Prev; 41 | } 42 | if(v == this->v2) { 43 | return this->v2Prev; 44 | } 45 | throw std::invalid_argument("Specified vert not used by the edge."); 46 | } 47 | 48 | ;Math::Vec3 Edge::CalcLocalNormal(Loop* loop) const { 49 | // use v1, v2, loop->fPrev.v 50 | Math::Vec3 result = Math::Vec3(); 51 | 52 | Loop* current = loop->fPrev; 53 | do { 54 | Math::Vec3& vc = current->v->co; 55 | Math::Vec3& vn = current->fNext->v->co; 56 | result.x += (vc.y - vn.y) * (vc.z + vn.z); 57 | result.y += (vc.z - vn.z) * (vc.x + vn.x); 58 | result.z += (vc.x - vn.x) * (vc.y + vn.y); 59 | current = current->fNext; 60 | } while(current != loop->fNext->fNext); 61 | //if face is not triangular, additional iteration to make a quad 62 | if(loop->fNext->fNext != loop->fPrev) { 63 | Math::Vec3& vc = loop->fNext->fNext->v->co; 64 | Math::Vec3& vn = loop->fPrev->v->co; 65 | result.x += (vc.y - vn.y) * (vc.z + vn.z); 66 | result.y += (vc.z - vn.z) * (vc.x + vn.x); 67 | result.z += (vc.x - vn.x) * (vc.y + vn.y); 68 | } 69 | 70 | result.Normalize(); 71 | return result; 72 | } 73 | 74 | float Edge::CalcFaceAngle() const { 75 | // check if edge has exactly two faces 76 | if(l == nullptr || l->eNext != l || l->eNext != l->ePrev) { 77 | throw std::invalid_argument("Edge must have exactly two faces"); 78 | } 79 | 80 | // calculate nearby normals for this edge. Usefull if face is not flat 81 | Math::Vec3 no1 = CalcLocalNormal(l); 82 | Math::Vec3 no2 = CalcLocalNormal(l->eNext); 83 | return no1.Angle(no2); 84 | } 85 | 86 | float Edge::CalcFaceAngleSigned() const { 87 | // check if edge has exactly two faces 88 | if(l == nullptr || l->eNext != l || l->eNext != l->ePrev) { 89 | throw std::invalid_argument("Edge must have exactly two faces"); 90 | } 91 | 92 | // calculate nearby normals for this edge. Usefull if face is not flat 93 | Math::Vec3 no1 = CalcLocalNormal(l); 94 | Math::Vec3 no2 = CalcLocalNormal(l->eNext); 95 | 96 | Math::Vec3 cross = no1.Cross(no2); 97 | Math::Vec3 dir = l->fNext->v->co - l->v->co; 98 | 99 | if(dir.Dot(cross) > 0.0f) { 100 | return no1.Angle(no2); 101 | } else { 102 | return -(no1.Angle(no2)); 103 | } 104 | } 105 | 106 | float Edge::CalcLength() const { 107 | return (this->v1->co - this->v2->co).Magnitude(); 108 | } 109 | 110 | Vert* Edge::Other(const Vert* v) const { 111 | if(this->v1 == v) { 112 | return this->v2; 113 | } else if(this->v2 == v) { 114 | return this->v1; 115 | } else { 116 | throw std::invalid_argument("Specified vert not used by the edge."); 117 | } 118 | } 119 | 120 | bool Edge::IsBoundary() const { 121 | if(this->l == nullptr) { 122 | return false; 123 | } 124 | if(this->l == this->l->eNext && this->l == this->l->ePrev) { 125 | return true; 126 | } 127 | return false; 128 | } 129 | 130 | bool Edge::IsContigous() const { 131 | if(this->l == nullptr) { 132 | return true; // wire edge 133 | } 134 | if(this->l == this->l->eNext) { 135 | // not checking ePrev here. if it is not the same, mesh is in invalid state 136 | return true; // boundary edge 137 | } 138 | int count = 1; 139 | Loop* current = this->l; 140 | while(current->eNext != this->l) { 141 | count++; 142 | if(count > 2) { 143 | return false; // more than 2 loops per edge 144 | // there will always be two faces pointing in separate direction in this case. 145 | } 146 | current = current->eNext; 147 | } 148 | if(count == 2) { 149 | if(this->l->v == this->l->eNext->v) { 150 | return false; // adjecent faces pointing in separate direction 151 | } 152 | } 153 | return true; 154 | } 155 | 156 | bool Edge::IsManifold() const { 157 | if(this->l == nullptr) { 158 | return false; // wire edge 159 | } 160 | int count = 1; 161 | Loop* currentLoop = this->l; 162 | while(currentLoop->eNext != this->l) { 163 | count++; 164 | if(count > 2) { 165 | return false; // more than 2 loops per edge 166 | } 167 | currentLoop = currentLoop->eNext; 168 | } 169 | if(count == 2) { 170 | if(this->l->v == this->l->eNext->v) { 171 | return false; // adjecent faces pointing in separate direction 172 | // (if edge is used twice, it should be used in different orientations) 173 | } 174 | } 175 | return true; 176 | } 177 | 178 | bool Edge::IsWire() const { 179 | if(this->l == nullptr) { 180 | return true; 181 | } 182 | return false; 183 | } 184 | 185 | const std::vector Edge::Faces() const { 186 | std::vector result = std::vector(); 187 | if(this->l == nullptr) { 188 | return result; 189 | } 190 | 191 | Loop* currentLoop = this->l; 192 | do { 193 | // check if the face already added 194 | // handles edges which are used by the same face in multiple orientations 195 | if(std::find(result.begin(), result.end(), currentLoop->f) == result.end()) { 196 | // face not present, add to result 197 | result.push_back(currentLoop->f); 198 | } 199 | currentLoop = currentLoop->eNext; 200 | } while(currentLoop != this->l); 201 | return result; 202 | } 203 | 204 | const std::vector Edge::Loops() const { 205 | std::vector result = std::vector(); 206 | if(this->l == nullptr) { 207 | return result; 208 | } 209 | 210 | Loop* currentLoop = this->l; 211 | do { 212 | result.push_back(currentLoop); 213 | currentLoop = currentLoop->eNext; 214 | } while(currentLoop != this->l); 215 | return result; 216 | } 217 | 218 | const std::vector Edge::Verts() const { 219 | return std::vector {this->v1, this->v2}; 220 | } 221 | 222 | const std::vector Edge::Faces(std::function func) const { 223 | std::vector result = std::vector(); 224 | if(this->l == nullptr) { 225 | return result; 226 | } 227 | 228 | Loop* currentLoop = this->l; 229 | do { 230 | if(func(currentLoop->f)) { 231 | result.push_back(currentLoop->f); 232 | } 233 | currentLoop = currentLoop->eNext; 234 | } while(currentLoop != this->l); 235 | return result; 236 | } 237 | 238 | const std::vector Edge::Loops(std::function func) const { 239 | std::vector result = std::vector(); 240 | if(this->l == nullptr) { 241 | return result; 242 | } 243 | 244 | Loop* currentLoop = this->l; 245 | do { 246 | if(func(currentLoop)) { 247 | result.push_back(currentLoop); 248 | } 249 | currentLoop = currentLoop->eNext; 250 | } while(currentLoop != this->l); 251 | return result; 252 | } 253 | 254 | const std::vector Edge::Verts(std::function func) const { 255 | std::vector result = std::vector(); 256 | 257 | if(func(v1)) { 258 | result.push_back(v1); 259 | } 260 | if(func(v2)) { 261 | result.push_back(v2); 262 | } 263 | return result; 264 | } 265 | 266 | Vert* Edge::V1() const { 267 | return v1; 268 | } 269 | 270 | Vert* Edge::V2() const { 271 | return v2; 272 | } 273 | 274 | } // namespace Core 275 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/Mesh/Face.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Core/Mesh/Face.hpp" 2 | 3 | #include "AobaAPI/Core/Mesh/Edge.hpp" 4 | #include "AobaAPI/Core/Mesh/Loop.hpp" 5 | #include "AobaAPI/Core/Mesh/Vert.hpp" 6 | 7 | #include 8 | 9 | namespace Aoba { 10 | namespace Core { 11 | 12 | Face::Face() { 13 | l = nullptr; 14 | m = nullptr; 15 | mNext = nullptr; 16 | mPrev = nullptr; 17 | index = 0; 18 | flags = 0; 19 | flagsIntern = 0; 20 | materialIdx = 0; 21 | } 22 | 23 | float Face::CalcArea() const { 24 | // TODO: 25 | // low priority 26 | // possible solution is to triangulate the face, and sum the areas of all triangles. 27 | return 0; 28 | } 29 | 30 | Math::Vec3 Face::CalcCenterAverage() const { 31 | std::vector faceVerts = Verts(); 32 | Math::Vec3 result = Math::Vec3(); 33 | for(int i = 0; i < faceVerts.size(); i++) { 34 | result += faceVerts.at(i)->co; 35 | } 36 | result /= float(faceVerts.size()); 37 | return result; 38 | } 39 | 40 | float Face::CalcPerimiter() const { 41 | float result = 0; 42 | std::vector faceEdges = Edges(); 43 | for(int i = 0; i < faceEdges.size(); i++) { 44 | result += faceEdges.at(i)->CalcLength(); 45 | } 46 | return result; 47 | } 48 | 49 | void Face::NormalFlip() { 50 | Loop* currentLoop = l; 51 | do { 52 | if(currentLoop->v == currentLoop->e->v1) { 53 | currentLoop->v = currentLoop->e->v2; 54 | } else { 55 | currentLoop->v = currentLoop->e->v1; 56 | } 57 | Loop* toModify = currentLoop; 58 | currentLoop = currentLoop->fNext; 59 | std::swap(toModify->fNext, toModify->fPrev); 60 | } while(currentLoop != l); 61 | } 62 | 63 | void Face::NormalUpdate() { 64 | // source: https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal 65 | // newell's algorithm 66 | 67 | no.x = 0; 68 | no.y = 0; 69 | no.z = 0; 70 | 71 | Loop* current = l; 72 | do { 73 | Math::Vec3& vc = current->v->co; 74 | Math::Vec3& vn = current->fNext->v->co; 75 | no.x += (vc.y - vn.y) * (vc.z + vn.z); 76 | no.y += (vc.z - vn.z) * (vc.x + vn.x); 77 | no.z += (vc.x - vn.x) * (vc.y + vn.y); 78 | current = current->fNext; 79 | } while(current != l); 80 | 81 | no.Normalize(); 82 | } 83 | 84 | const std::vector Face::Edges() const { 85 | std::vector result = std::vector(); 86 | // iterate over all loops in face 87 | Loop* currentLoop = l; 88 | do { 89 | if(std::find(result.begin(), result.end(), currentLoop->e) == result.end()) { // check if already added 90 | result.push_back(currentLoop->e); 91 | } 92 | currentLoop = currentLoop->fNext; 93 | } while(currentLoop != l); 94 | return result; 95 | } 96 | 97 | const std::vector Face::Verts() const { 98 | std::vector result = std::vector(); 99 | // iterate over all loops in current list 100 | Loop* currentLoop = l; 101 | do { 102 | if(std::find(result.begin(), result.end(), currentLoop->v) == result.end()) { // check if already added 103 | result.push_back(currentLoop->v); 104 | } 105 | currentLoop = currentLoop->fNext; 106 | } while(currentLoop != l); 107 | return result; 108 | } 109 | 110 | const std::vector Face::Loops() const { 111 | std::vector result = std::vector(); 112 | Loop* current = l; 113 | 114 | result.push_back(current); 115 | while(current->fNext != l) { 116 | current = current->fNext; 117 | result.push_back(current); 118 | } 119 | 120 | return result; 121 | } 122 | 123 | const std::vector Face::Edges(std::function func) const { 124 | std::vector result = std::vector(); 125 | // iterate over all loops in face 126 | Loop* currentLoop = l; 127 | do { 128 | if(func(currentLoop->e)) { 129 | if(std::find(result.begin(), result.end(), currentLoop->e) == result.end()) { // check if already added 130 | result.push_back(currentLoop->e); 131 | } 132 | } 133 | currentLoop = currentLoop->fNext; 134 | } while(currentLoop != l); 135 | return result; 136 | } 137 | 138 | const std::vector Face::Verts(std::function func) const { 139 | std::vector result = std::vector(); 140 | // iterate over all loops in current list 141 | Loop* currentLoop = l; 142 | do { 143 | if(func(currentLoop->v)) { 144 | if(std::find(result.begin(), result.end(), currentLoop->v) == result.end()) { // check if already added 145 | result.push_back(currentLoop->v); 146 | } 147 | } 148 | currentLoop = currentLoop->fNext; 149 | } while(currentLoop != l); 150 | return result; 151 | } 152 | 153 | const std::vector Face::Loops(std::function func) const { 154 | std::vector result = std::vector(); 155 | Loop* current = l; 156 | 157 | if(func(current)) { 158 | result.push_back(current); 159 | } 160 | while(current->fNext != l) { 161 | current = current->fNext; 162 | if(func(current)) { 163 | result.push_back(current); 164 | } 165 | } 166 | 167 | return result; 168 | } 169 | 170 | } // namespace Core 171 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/Mesh/Loop.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "AobaAPI/Core/Mesh/Loop.hpp" 4 | 5 | #include "AobaAPI/Core/Mesh/Edge.hpp" 6 | #include "AobaAPI/Core/Mesh/Vert.hpp" 7 | 8 | #include 9 | 10 | namespace Aoba { 11 | namespace Core { 12 | 13 | Vert* Loop::LoopVert() const { 14 | return v; 15 | } 16 | 17 | Edge* Loop::LoopEdge() const { 18 | return e; 19 | } 20 | 21 | Loop::Loop() { 22 | index = 0; 23 | flags = 0; 24 | flagsIntern = 0; 25 | v = nullptr; 26 | e = nullptr; 27 | f = nullptr; 28 | eNext = nullptr; 29 | ePrev = nullptr; 30 | fNext = nullptr; 31 | fPrev = nullptr; 32 | m = nullptr; 33 | } 34 | 35 | Face* Loop::LoopFace() const { 36 | return f; 37 | } 38 | 39 | } // namespace Core 40 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Core/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Core/README.md -------------------------------------------------------------------------------- /src/IO/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/ExportObj.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/ExportStl.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/IndexMesh.cpp 7 | ) -------------------------------------------------------------------------------- /src/IO/ExportObj.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/IO/ExportObj.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace IO { 7 | 8 | void ExportObj(std::string path, Core::Mesh* mesh) { 9 | std::ofstream outFile(path); 10 | 11 | outFile << "# AobaAPI test file \n"; 12 | outFile << "o AobaAPIObject \n"; 13 | 14 | std::vector verts = mesh->Verts(); 15 | std::vector faces = mesh->Faces(); 16 | std::vector edges = mesh->Edges(); 17 | 18 | for(std::size_t i = 0; i < verts.size(); ++i) { 19 | outFile << "v " << verts.at(i)->co.x << " " << verts.at(i)->co.y << " " << verts.at(i)->co.z << " \n"; 20 | verts.at(i)->index = i + 1; 21 | } 22 | 23 | outFile << "s off \n"; 24 | 25 | for(std::size_t i = 0; i < faces.size(); ++i) { 26 | outFile << "f "; 27 | 28 | // basic obj export, only do the single/first loop 29 | std::vector loops = faces.at(i)->Loops(); 30 | 31 | for(std::size_t j = 0; j < loops.size(); ++j) { 32 | outFile << loops.at(j)->LoopVert()->index << " "; 33 | } 34 | 35 | outFile << "\n"; 36 | } 37 | 38 | for(std::size_t i = 0; i < edges.size(); ++i) { 39 | if(edges.at(i)->IsWire()) { 40 | outFile << "l "; 41 | for(std::size_t j = 0; j < 2; ++j) { 42 | outFile << edges.at(i)->Verts().at(j)->index << " "; 43 | } 44 | 45 | outFile << "\n"; 46 | } 47 | } 48 | 49 | outFile.close(); 50 | } 51 | 52 | } // namespace IO 53 | } // namespace Aoba -------------------------------------------------------------------------------- /src/IO/ExportStl.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/IO/ExportStl.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace IO { 7 | 8 | void ExportStl(std::string path, Core::Mesh* mesh) { 9 | std::ofstream outFile(path, std::ios::out | std::ios::binary); 10 | 11 | unsigned int triangleCount = 0; 12 | 13 | // write header, empty (80 bytes) 14 | for(int i = 0; i < 80; ++i) { 15 | outFile.put(0); 16 | } 17 | 18 | // write triangle count, populate later (4 bytes, uint32) 19 | std::streampos triangleCountPos = outFile.tellp(); 20 | outFile.write(reinterpret_cast(&triangleCount), sizeof(unsigned int)); 21 | 22 | // using simple triangle fan triangulation 23 | float zero = 0.0f; 24 | std::vector faces = mesh->Faces(); 25 | for(Core::Face* f : faces) { 26 | std::vector fLoops = f->Loops(); 27 | std::size_t idx = 1; 28 | while(idx + 1 < fLoops.size()) { 29 | // increment triangle count 30 | triangleCount++; 31 | 32 | // write face normal (12 bytes, fp32) 33 | outFile.write(reinterpret_cast(&zero), sizeof(float)); 34 | outFile.write(reinterpret_cast(&zero), sizeof(float)); 35 | outFile.write(reinterpret_cast(&zero), sizeof(float)); 36 | 37 | // write vertex coordinates (3 x 12 bytesm fp32) 38 | outFile.write(reinterpret_cast(&fLoops.at(0)->LoopVert()->co.x), sizeof(float)); 39 | outFile.write(reinterpret_cast(&fLoops.at(0)->LoopVert()->co.y), sizeof(float)); 40 | outFile.write(reinterpret_cast(&fLoops.at(0)->LoopVert()->co.z), sizeof(float)); 41 | 42 | outFile.write(reinterpret_cast(&fLoops.at(idx)->LoopVert()->co.x), sizeof(float)); 43 | outFile.write(reinterpret_cast(&fLoops.at(idx)->LoopVert()->co.y), sizeof(float)); 44 | outFile.write(reinterpret_cast(&fLoops.at(idx)->LoopVert()->co.z), sizeof(float)); 45 | 46 | outFile.write(reinterpret_cast(&fLoops.at(idx + 1)->LoopVert()->co.x), sizeof(float)); 47 | outFile.write(reinterpret_cast(&fLoops.at(idx + 1)->LoopVert()->co.y), sizeof(float)); 48 | outFile.write(reinterpret_cast(&fLoops.at(idx + 1)->LoopVert()->co.z), sizeof(float)); 49 | 50 | // write attribute byte (2 bytes, uint16) 51 | outFile.put(0); 52 | outFile.put(0); 53 | 54 | // increment loop idx 55 | ++idx; 56 | } 57 | } 58 | 59 | // go back, populate triangle count 60 | outFile.seekp(triangleCountPos); 61 | outFile.write(reinterpret_cast(&triangleCount), sizeof(unsigned int)); 62 | 63 | outFile.close(); 64 | } 65 | 66 | } // namespace IO 67 | } // namespace Aoba -------------------------------------------------------------------------------- /src/IO/IndexMesh.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/IO/IndexMesh.hpp" 2 | 3 | namespace Aoba { 4 | namespace IO { 5 | 6 | void IndexMesh::FromMesh(Core::Mesh* m) { 7 | std::vector mVerts = m->Verts(); 8 | std::vector mEdges = m->Edges(); 9 | std::vector mFaces = m->Faces(); 10 | 11 | vertexCoords = std::vector(); 12 | edges = std::vector(); 13 | triangles = std::vector(); 14 | 15 | vertexCoords.reserve(mVerts.size() * 3); 16 | edges.reserve(mEdges.size() * 2); 17 | triangles.reserve(mFaces.size() * 2); // expecting mostly quads 18 | 19 | // populate all vertex coordinates, set vertex index. 20 | std::size_t vertIdx = 0; 21 | for(Core::Vert* v : mVerts) { 22 | vertexCoords.push_back(v->co.x); 23 | vertexCoords.push_back(v->co.y); 24 | vertexCoords.push_back(v->co.z); 25 | v->index = vertIdx; 26 | vertIdx++; 27 | } 28 | 29 | // populate edges. this will include both wire and non-wire edges together 30 | for(Core::Edge* e : mEdges) { 31 | edges.push_back(e->V1()->index); 32 | edges.push_back(e->V2()->index); 33 | } 34 | 35 | // populate faces 36 | // use simple triangle-fan triangulation for non-triangular faces. 37 | for(Core::Face* f : mFaces) { 38 | std::vector fLoops = f->Loops(); 39 | std::size_t idx = 1; 40 | while(idx + 1 < fLoops.size()) { 41 | triangles.push_back(fLoops.at(0)->LoopVert()->index); 42 | triangles.push_back(fLoops.at(idx)->LoopVert()->index); 43 | triangles.push_back(fLoops.at(idx + 1)->LoopVert()->index); 44 | ++idx; 45 | } 46 | } 47 | 48 | // reset vertex index. 49 | for(Core::Vert* v : mVerts) { 50 | v->index = 0; 51 | } 52 | } 53 | 54 | } // namespace IO 55 | } // namespace Aoba -------------------------------------------------------------------------------- /src/IO/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/IO/README.md -------------------------------------------------------------------------------- /src/Math/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Vector) 2 | add_subdirectory(Matrix) -------------------------------------------------------------------------------- /src/Math/Euler.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Euler.h -------------------------------------------------------------------------------- /src/Math/Euler/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Euler/CMakeLists.txt -------------------------------------------------------------------------------- /src/Math/Euler/Euler.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Euler/Euler.cpp -------------------------------------------------------------------------------- /src/Math/Euler/Euler.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Euler/Euler.h -------------------------------------------------------------------------------- /src/Math/Matrix/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Matrix2.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Matrix3.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Matrix4.cpp 7 | ) -------------------------------------------------------------------------------- /src/Math/Matrix/Matrix2.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Math/Matrix/Matrix2.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Aoba { 8 | namespace Math { 9 | 10 | Mat2::Mat2() { 11 | data[0] = 0; 12 | data[1] = 0; 13 | data[2] = 0; 14 | data[3] = 0; 15 | } 16 | 17 | Mat2::Mat2(const std::array& vals) { 18 | data[0] = vals.at(0); 19 | data[1] = vals.at(1); 20 | data[2] = vals.at(2); 21 | data[3] = vals.at(3); 22 | } 23 | 24 | Mat2 Mat2::Diagonal(const Vec2& vec) { 25 | Mat2 result = Mat2(); 26 | result.data[0] = vec.x; 27 | result.data[3] = vec.y; 28 | return result; 29 | } 30 | 31 | Mat2 Mat2::Identity() { 32 | Mat2 result = Mat2(); 33 | result.data[0] = 1; 34 | result.data[3] = 1; 35 | return result; 36 | } 37 | 38 | Mat2 Mat2::OrthoProjection(const Vec2& axis) { 39 | Vec2 norm = axis.Normalized(); 40 | Mat2 result = Mat2(); 41 | result.data[0] = powf(norm.x, 2); 42 | result.data[1] = norm.x * norm.y; 43 | result.data[2] = norm.x * norm.y; 44 | result.data[3] = powf(norm.y, 2); 45 | return result; 46 | } 47 | 48 | Mat2 Mat2::Rotation(float angle) { 49 | Mat2 result = Mat2(); 50 | result.data[0] = cosf(angle); 51 | result.data[1] = -sinf(angle); 52 | result.data[2] = sinf(angle); 53 | result.data[3] = cosf(angle); 54 | return result; 55 | } 56 | 57 | Mat2 Mat2::Scale(const Vec2& axis, float factor) { 58 | Vec2 norm = axis.Normalized(); 59 | Mat2 result = Mat2(); 60 | result.data[0] = 1 + (factor - 1) * powf(norm.x, 2); 61 | result.data[1] = (factor - 1) * norm.x * norm.y; 62 | result.data[2] = (factor - 1) * norm.x * norm.y; 63 | result.data[3] = 1 + (factor - 1) * powf(norm.y, 2); 64 | return result; 65 | } 66 | 67 | Mat2 Mat2::Zero() { 68 | return Mat2(); 69 | } 70 | 71 | float Mat2::Determinant() const { 72 | return data[0] * data[3] - data[1] * data[2]; 73 | } 74 | 75 | bool Mat2::Equals(const Mat2& other, float epsilon) { 76 | for(int i = 0; i < 4; i++) { 77 | if(fabsf(data[i] - other.data[i]) > epsilon) { 78 | return false; 79 | } 80 | } 81 | return true; 82 | } 83 | 84 | void Mat2::Invert() { 85 | float determinant = Determinant(); 86 | std::swap(data[0], data[3]); 87 | data[1] = -data[1]; 88 | data[2] = -data[2]; 89 | data[0] /= determinant; 90 | data[1] /= determinant; 91 | data[2] /= determinant; 92 | data[3] /= determinant; 93 | } 94 | 95 | Mat2 Mat2::Inverted() const { 96 | float determinant = Determinant(); 97 | Mat2 result = Mat2(); 98 | result.data[0] = data[3] / determinant; 99 | result.data[1] = -data[1] / determinant; 100 | result.data[2] = -data[2] / determinant; 101 | result.data[3] = data[0] / determinant; 102 | return result; 103 | } 104 | 105 | bool Mat2::IsInvertible() const { 106 | return Determinant() != 0; 107 | } 108 | 109 | void Mat2::Transpose() { 110 | std::swap(data[1], data[2]); 111 | } 112 | 113 | Mat2 Mat2::Transposed() const { 114 | Mat2 result = Mat2(); 115 | result.data[0] = data[0]; 116 | result.data[1] = data[2]; 117 | result.data[2] = data[1]; 118 | result.data[3] = data[3]; 119 | return result; 120 | } 121 | 122 | Mat2 operator*(const float& lhs, const Mat2& rhs) { 123 | Mat2 result = Mat2(); 124 | result.data[0] = lhs * rhs.data[0]; 125 | result.data[1] = lhs * rhs.data[1]; 126 | result.data[2] = lhs * rhs.data[2]; 127 | result.data[3] = lhs * rhs.data[3]; 128 | return result; 129 | } 130 | 131 | Mat2 operator*(const Mat2& lhs, const float& rhs) { 132 | Mat2 result = Mat2(); 133 | result.data[0] = lhs.data[0] * rhs; 134 | result.data[1] = lhs.data[1] * rhs; 135 | result.data[2] = lhs.data[2] * rhs; 136 | result.data[3] = lhs.data[3] * rhs; 137 | return result; 138 | } 139 | 140 | Mat2 operator/(const Mat2& lhs, const float& rhs) { 141 | Mat2 result = Mat2(); 142 | result.data[0] = lhs.data[0] / rhs; 143 | result.data[1] = lhs.data[1] / rhs; 144 | result.data[2] = lhs.data[2] / rhs; 145 | result.data[3] = lhs.data[3] / rhs; 146 | return result; 147 | } 148 | 149 | Mat2 operator-(const Mat2& lhs, const Mat2& rhs) { 150 | Mat2 result = Mat2(); 151 | result.data[0] = lhs.data[0] - rhs.data[0]; 152 | result.data[1] = lhs.data[1] - rhs.data[1]; 153 | result.data[2] = lhs.data[2] - rhs.data[2]; 154 | result.data[3] = lhs.data[3] - rhs.data[3]; 155 | return result; 156 | } 157 | 158 | Mat2 operator+(const Mat2& lhs, const Mat2& rhs) { 159 | Mat2 result = Mat2(); 160 | result.data[0] = lhs.data[0] + rhs.data[0]; 161 | result.data[1] = lhs.data[1] + rhs.data[1]; 162 | result.data[2] = lhs.data[2] + rhs.data[2]; 163 | result.data[3] = lhs.data[3] + rhs.data[3]; 164 | return result; 165 | } 166 | 167 | Mat2 operator*(const Mat2& lhs, const Mat2& rhs) { 168 | Mat2 result = Mat2(); 169 | result.data[0] = lhs.data[0] * rhs.data[0] + lhs.data[1] * rhs.data[2]; 170 | result.data[1] = lhs.data[0] * rhs.data[1] + lhs.data[1] * rhs.data[3]; 171 | result.data[2] = lhs.data[2] * rhs.data[0] + lhs.data[3] * rhs.data[2]; 172 | result.data[3] = lhs.data[2] * rhs.data[1] + lhs.data[3] * rhs.data[3]; 173 | return result; 174 | } 175 | 176 | Mat2& operator*=(Mat2& lhs, const float& rhs) { 177 | lhs.data[0] *= rhs; 178 | lhs.data[1] *= rhs; 179 | lhs.data[2] *= rhs; 180 | lhs.data[3] *= rhs; 181 | return lhs; 182 | } 183 | 184 | Mat2& operator/=(Mat2& lhs, const float& rhs) { 185 | lhs.data[0] /= rhs; 186 | lhs.data[1] /= rhs; 187 | lhs.data[2] /= rhs; 188 | lhs.data[3] /= rhs; 189 | return lhs; 190 | } 191 | 192 | Mat2& operator+=(Mat2& lhs, const Mat2& rhs) { 193 | lhs.data[0] += rhs.data[0]; 194 | lhs.data[1] += rhs.data[1]; 195 | lhs.data[2] += rhs.data[2]; 196 | lhs.data[3] += rhs.data[3]; 197 | return lhs; 198 | } 199 | 200 | Mat2& operator-=(Mat2& lhs, const Mat2& rhs) { 201 | lhs.data[0] -= rhs.data[0]; 202 | lhs.data[1] -= rhs.data[1]; 203 | lhs.data[2] -= rhs.data[2]; 204 | lhs.data[3] -= rhs.data[3]; 205 | return lhs; 206 | } 207 | 208 | Mat2& operator*=(Mat2& lhs, const Mat2& rhs) { 209 | float result[4] = {0}; 210 | result[0] = lhs.data[0] * rhs.data[0] + lhs.data[1] * rhs.data[2]; 211 | result[1] = lhs.data[0] * rhs.data[1] + lhs.data[1] * rhs.data[3]; 212 | result[2] = lhs.data[2] * rhs.data[0] + lhs.data[3] * rhs.data[2]; 213 | result[3] = lhs.data[2] * rhs.data[1] + lhs.data[3] * rhs.data[3]; 214 | lhs.data[0] = result[0]; 215 | lhs.data[0] = result[1]; 216 | lhs.data[0] = result[2]; 217 | lhs.data[0] = result[3]; 218 | return lhs; 219 | } 220 | 221 | Vec2 operator*(const Mat2 lhs, const Vec2& rhs) { 222 | Vec2 result = Vec2(); 223 | result.x = lhs.data[0] * rhs.x + lhs.data[1] * rhs.y; 224 | result.y = lhs.data[2] * rhs.x + lhs.data[3] * rhs.y; 225 | return result; 226 | } 227 | 228 | float Mat2::operator()(std::size_t row, std::size_t col) const { 229 | return data[row * 2 + col]; 230 | } 231 | 232 | float& Mat2::operator()(std::size_t row, std::size_t col) { 233 | return data[row * 2 + col]; 234 | } 235 | 236 | Vec2 Mat2::GetCol(std::size_t idx) const { 237 | return Vec2(data[idx], data[idx + 2]); 238 | } 239 | 240 | Vec2 Mat2::GetRow(std::size_t idx) const { 241 | return Vec2(data[idx * 2], data[idx * 2 + 1]); 242 | } 243 | 244 | void Mat2::SetCol(std::size_t idx, const Vec2& vec) { 245 | data[idx] = vec.x; 246 | data[idx + 2] = vec.y; 247 | } 248 | 249 | void Mat2::SetRow(std::size_t idx, const Vec2& vec) { 250 | data[idx * 2 + 0] = vec.x; 251 | data[idx * 2 + 1] = vec.y; 252 | } 253 | 254 | } // namespace Math 255 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Math/Quaternion.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Quaternion.h -------------------------------------------------------------------------------- /src/Math/Quaternion/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Quaternion/CMakeLists.txt -------------------------------------------------------------------------------- /src/Math/Quaternion/Quaternion.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Quaternion/Quaternion.cpp -------------------------------------------------------------------------------- /src/Math/Quaternion/Quaternion.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/Quaternion/Quaternion.h -------------------------------------------------------------------------------- /src/Math/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Math/README.md -------------------------------------------------------------------------------- /src/Math/Vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Vector2.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Vector3.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Vector4.cpp 7 | ) -------------------------------------------------------------------------------- /src/Math/Vector/Vector2.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Math/Vector/Vector2.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Math { 8 | 9 | Vec2::Vec2() : x(0), y(0) { 10 | } 11 | 12 | Vec2::Vec2(float x, float y) : x(x), y(y) { 13 | } 14 | 15 | Vec2::Vec2(const Vec3& vec) : x(vec.x), y(vec.y) { 16 | } 17 | 18 | Vec2::Vec2(const Vec4& vec) : x(vec.x), y(vec.y) { 19 | } 20 | 21 | float Vec2::Angle(const Vec2& other) const { 22 | return acosf(Dot(other) / (Magnitude() * other.Magnitude())); 23 | } 24 | 25 | float Vec2::AngleSigned(const Vec2& other) const { 26 | return atan2f(y, x) - atan2f(other.y, other.x); 27 | } 28 | 29 | float Vec2::Dot(const Vec2& other) const { 30 | return x * other.x + y * other.y; 31 | } 32 | 33 | bool Vec2::Equals(const Vec2& other, float epsilon) const { 34 | if(fabsf(x - other.x) > epsilon) { 35 | return false; 36 | } 37 | if(fabsf(y - other.y) > epsilon) { 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | void Vec2::Negate() { 44 | x = -x; 45 | y = -y; 46 | } 47 | 48 | Vec2 Vec2::Negated() const { 49 | return Vec2(-x, -y); 50 | } 51 | 52 | void Vec2::Normalize() { 53 | float magnitude = Magnitude(); 54 | x /= magnitude; 55 | y /= magnitude; 56 | } 57 | 58 | Vec2 Vec2::Normalized() const { 59 | float magnitude = Magnitude(); 60 | return Vec2(x / magnitude, y / magnitude); 61 | } 62 | 63 | float Vec2::Length() const { 64 | return sqrtf(powf(x, 2) + powf(y, 2)); 65 | } 66 | 67 | float Vec2::LengthSquared() const { 68 | return powf(x, 2) + powf(y, 2); 69 | } 70 | 71 | float Vec2::Magnitude() const { 72 | return sqrtf(powf(x, 2) + powf(y, 2)); 73 | } 74 | 75 | Vec2 operator*(const float& lhs, const Vec2& rhs) { 76 | return Vec2(lhs * rhs.x, lhs * rhs.y); 77 | } 78 | 79 | Vec2 operator*(const Vec2& lhs, const float& rhs) { 80 | return Vec2(lhs.x * rhs, lhs.y * rhs); 81 | } 82 | 83 | Vec2 operator/(const Vec2& lhs, const float& rhs) { 84 | return Vec2(lhs.x / rhs, lhs.y / rhs); 85 | } 86 | 87 | Vec2 operator+(const Vec2& lhs, const Vec2& rhs) { 88 | return Vec2(lhs.x + rhs.x, lhs.y + rhs.y); 89 | } 90 | 91 | Vec2 operator-(const Vec2& lhs, const Vec2& rhs) { 92 | return Vec2(lhs.x - rhs.x, lhs.y - rhs.y); 93 | } 94 | 95 | Vec2& operator*=(Vec2& lhs, const float& rhs) { 96 | lhs.x *= rhs; 97 | lhs.y *= rhs; 98 | return lhs; 99 | } 100 | 101 | Vec2& operator/=(Vec2& lhs, const float& rhs) { 102 | lhs.x /= rhs; 103 | lhs.y /= rhs; 104 | return lhs; 105 | } 106 | 107 | Vec2& operator+=(Vec2& lhs, const Vec2& rhs) { 108 | lhs.x += rhs.x; 109 | lhs.y += rhs.y; 110 | return lhs; 111 | } 112 | 113 | Vec2& operator-=(Vec2& lhs, const Vec2& rhs) { 114 | lhs.x -= rhs.x; 115 | lhs.y -= rhs.y; 116 | return lhs; 117 | } 118 | 119 | float Vec2::operator()(std::size_t idx) const { 120 | if(idx > 1) { 121 | // TODO: should I throw my own exception? 122 | throw std::domain_error("Vector index out of range. Use values in [0-1]."); 123 | } else if(idx == 0) { 124 | return x; 125 | } else { 126 | return y; 127 | } 128 | } 129 | 130 | float& Vec2::operator()(std::size_t idx) { 131 | if(idx > 1) { 132 | // TODO: should I throw my own exception? 133 | throw std::domain_error("Vector index out of range. Use values in [0-1]."); 134 | } else if(idx == 0) { 135 | return x; 136 | } else { 137 | return y; 138 | } 139 | } 140 | 141 | } // namespace Math 142 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Math/Vector/Vector3.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Math/Vector/Vector3.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Math { 8 | 9 | Vec3::Vec3() : x(0), y(0), z(0) { 10 | } 11 | 12 | Vec3::Vec3(float x, float y, float z) : x(x), y(y), z(z) { 13 | } 14 | 15 | Vec3::Vec3(const Vec4& vec) : x(vec.x), y(vec.y), z(vec.z) { 16 | } 17 | 18 | float Vec3::Angle(const Vec3& other) const { 19 | return acosf(Dot(other) / (Length() * other.Length())); 20 | } 21 | 22 | Vec3 Vec3::Cross(const Vec3& other) const { 23 | return Vec3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); 24 | } 25 | 26 | float Vec3::Dot(const Vec3& other) const { 27 | return x * other.x + y * other.y + z * other.z; 28 | } 29 | 30 | bool Vec3::Equals(const Vec3& other, float epsilon) const { 31 | if(fabsf(x - other.x) > epsilon) { 32 | return false; 33 | } 34 | if(fabsf(y - other.y) > epsilon) { 35 | return false; 36 | } 37 | if(fabsf(z - other.z) > epsilon) { 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | void Vec3::Negate() { 44 | x = -x; 45 | y = -y; 46 | z = -z; 47 | } 48 | 49 | Vec3 Vec3::Negated() const { 50 | return Vec3(-x, -y, -z); 51 | } 52 | 53 | void Vec3::Normalize() { 54 | float magnitude = Magnitude(); 55 | x /= magnitude; 56 | y /= magnitude; 57 | z /= magnitude; 58 | } 59 | 60 | Vec3 Vec3::Normalized() const { 61 | float magnitude = Magnitude(); 62 | return Vec3(x / magnitude, y / magnitude, z / magnitude); 63 | } 64 | 65 | float Vec3::Length() const { 66 | return sqrtf(powf(x, 2) + powf(y, 2) + powf(z, 2)); 67 | } 68 | 69 | float Vec3::LengthSquared() const { 70 | return powf(x, 2) + powf(y, 2) + powf(z, 2); 71 | } 72 | 73 | float Vec3::Magnitude() const { 74 | return sqrt(powf(x, 2) + powf(y, 2) + powf(z, 2)); 75 | } 76 | 77 | Vec3 operator*(const float& lhs, const Vec3& rhs) { 78 | return Vec3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); 79 | } 80 | 81 | Vec3 operator*(const Vec3& lhs, const float& rhs) { 82 | return Vec3(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs); 83 | } 84 | 85 | Vec3 operator/(const Vec3& lhs, const float& rhs) { 86 | return Vec3(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs); 87 | } 88 | 89 | Vec3 operator+(const Vec3& lhs, const Vec3& rhs) { 90 | return Vec3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); 91 | } 92 | 93 | Vec3 operator-(const Vec3& lhs, const Vec3& rhs) { 94 | return Vec3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z); 95 | } 96 | 97 | Vec3& operator*=(Vec3& lhs, const float& rhs) { 98 | lhs.x *= rhs; 99 | lhs.y *= rhs; 100 | lhs.z *= rhs; 101 | return lhs; 102 | } 103 | 104 | Vec3& operator/=(Vec3& lhs, const float& rhs) { 105 | lhs.x /= rhs; 106 | lhs.y /= rhs; 107 | lhs.z /= rhs; 108 | return lhs; 109 | } 110 | 111 | Vec3& operator+=(Vec3& lhs, const Vec3& rhs) { 112 | lhs.x += rhs.x; 113 | lhs.y += rhs.y; 114 | lhs.z += rhs.z; 115 | return lhs; 116 | } 117 | 118 | Vec3& operator-=(Vec3& lhs, const Vec3& rhs) { 119 | lhs.x -= rhs.x; 120 | lhs.y -= rhs.y; 121 | lhs.z -= rhs.z; 122 | return lhs; 123 | } 124 | 125 | float Vec3::operator()(std::size_t idx) const { 126 | if(idx > 2) { 127 | // TODO: should I throw my own exception? 128 | throw std::domain_error("Vector index out of range. Use values in [0-2]."); 129 | } else if(idx == 0) { 130 | return x; 131 | } else if(idx == 1) { 132 | return y; 133 | } else { 134 | return z; 135 | } 136 | } 137 | 138 | float& Vec3::operator()(std::size_t idx) { 139 | if(idx > 2) { 140 | // TODO: should I throw my own exception? 141 | throw std::domain_error("Vector index out of range. Use values in [0-2]."); 142 | } else if(idx == 0) { 143 | return x; 144 | } else if(idx == 1) { 145 | return y; 146 | } else { 147 | return z; 148 | } 149 | } 150 | 151 | } // namespace Math 152 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Math/Vector/Vector4.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Math/Vector/Vector4.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Math { 8 | 9 | Vec4::Vec4() : x(0), y(0), z(0), w(0) { 10 | } 11 | 12 | Vec4::Vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) { 13 | } 14 | 15 | float Vec4::Dot(const Vec4& other) const { 16 | return x * other.x + y * other.y + z * other.z + w * other.w; 17 | } 18 | 19 | bool Vec4::Equals(const Vec4& other, float epsilon) const { 20 | if(fabsf(x - other.x) > epsilon) { 21 | return false; 22 | } 23 | if(fabsf(y - other.y) > epsilon) { 24 | return false; 25 | } 26 | if(fabsf(z - other.z) > epsilon) { 27 | return false; 28 | } 29 | if(fabsf(w - other.w) > epsilon) { 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | void Vec4::Negate() { 36 | x = -x; 37 | y = -y; 38 | z = -z; 39 | w = -w; 40 | } 41 | 42 | Vec4 Vec4::Negated() const { 43 | return Vec4(-x, -y, -z, -w); 44 | } 45 | 46 | void Vec4::Normalize() { 47 | float magnitude = Magnitude(); 48 | x /= magnitude; 49 | y /= magnitude; 50 | z /= magnitude; 51 | w /= magnitude; 52 | } 53 | 54 | Vec4 Vec4::Normalized() const { 55 | float magnitude = Magnitude(); 56 | return Vec4(x / magnitude, y / magnitude, z / magnitude, w / magnitude); 57 | } 58 | 59 | float Vec4::Length() const { 60 | return sqrtf(powf(x, 2) + powf(y, 2) + powf(z, 2) + powf(w, 2)); 61 | } 62 | 63 | float Vec4::LengthSquared() const { 64 | return powf(x, 2) + powf(y, 2) + powf(z, 2) + powf(w, 2); 65 | } 66 | 67 | float Vec4::Magnitude() const { 68 | return sqrtf(powf(x, 2) + powf(y, 2) + powf(z, 2) + powf(w, 2)); 69 | } 70 | 71 | Vec4 operator*(const float& lhs, const Vec4& rhs) { 72 | return Vec4(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z, lhs * rhs.w); 73 | } 74 | 75 | Vec4 operator*(const Vec4& lhs, const float& rhs) { 76 | return Vec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); 77 | } 78 | 79 | Vec4 operator/(const Vec4& lhs, const float& rhs) { 80 | return Vec4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs); 81 | } 82 | 83 | Vec4 operator+(const Vec4& lhs, const Vec4& rhs) { 84 | return Vec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); 85 | } 86 | 87 | Vec4 operator-(const Vec4& lhs, const Vec4& rhs) { 88 | return Vec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); 89 | } 90 | 91 | Vec4& operator*=(Vec4& lhs, const float& rhs) { 92 | lhs.x *= rhs; 93 | lhs.y *= rhs; 94 | lhs.z *= rhs; 95 | lhs.w *= rhs; 96 | return lhs; 97 | } 98 | 99 | Vec4& operator/=(Vec4& lhs, const float& rhs) { 100 | lhs.x /= rhs; 101 | lhs.y /= rhs; 102 | lhs.z /= rhs; 103 | lhs.w /= rhs; 104 | return lhs; 105 | } 106 | 107 | Vec4& operator+=(Vec4& lhs, const Vec4& rhs) { 108 | lhs.x += rhs.x; 109 | lhs.y += rhs.y; 110 | lhs.z += rhs.z; 111 | lhs.w += rhs.w; 112 | return lhs; 113 | } 114 | 115 | Vec4& operator-=(Vec4& lhs, const Vec4& rhs) { 116 | lhs.x -= rhs.x; 117 | lhs.y -= rhs.y; 118 | lhs.z -= rhs.z; 119 | lhs.w -= rhs.w; 120 | return lhs; 121 | } 122 | 123 | float Vec4::operator()(std::size_t idx) const { 124 | if(idx > 3) { 125 | // TODO: should I throw my own exception? 126 | throw std::domain_error("Vector index out of range. Use values in [0-3]."); 127 | } else if(idx == 0) { 128 | return x; 129 | } else if(idx == 1) { 130 | return y; 131 | } else if(idx == 2) { 132 | return z; 133 | } else { 134 | return w; 135 | } 136 | } 137 | 138 | float& Vec4::operator()(std::size_t idx) { 139 | if(idx > 3) { 140 | // TODO: should I throw my own exception? 141 | throw std::domain_error("Vector index out of range. Use values in [0-3]."); 142 | } else if(idx == 0) { 143 | return x; 144 | } else if(idx == 1) { 145 | return y; 146 | } else if(idx == 2) { 147 | return z; 148 | } else { 149 | return w; 150 | } 151 | } 152 | 153 | } // namespace Math 154 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Create) 2 | add_subdirectory(Transform) 3 | add_subdirectory(Modify) 4 | add_subdirectory(Select) -------------------------------------------------------------------------------- /src/Ops/Create/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/CreateCircle.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/CreateCube.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/CreateGrid.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/CreateUVSphere.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/CreateVert.cpp 9 | ) -------------------------------------------------------------------------------- /src/Ops/Create/CreateCircle.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Create.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Ops { 7 | 8 | const CreateCircleResult CreateCircle(Core::Mesh* m, unsigned vertCount, float radius) { 9 | CreateCircleResult result = CreateCircleResult(); 10 | std::vector edges = std::vector(); 11 | edges.reserve(vertCount); 12 | std::vector verts = std::vector(); 13 | verts.reserve(vertCount); 14 | 15 | float angle_step = 2 * PI / vertCount; 16 | 17 | for(unsigned i = 0; i < vertCount; ++i) { 18 | Core::Vert* newv = new Core::Vert(); 19 | newv->co.x = sinf(i * angle_step) * radius; 20 | newv->co.y = cosf(i * angle_step) * radius; 21 | newv->co.z = 0; 22 | Core::MakeVert(m, newv); 23 | verts.push_back(newv); 24 | } 25 | 26 | for(unsigned i = 1; i < vertCount; ++i) { 27 | Core::Edge* newe = new Core::Edge(); 28 | Core::MakeEdge(verts.at(i - 1), verts.at(i), newe); 29 | edges.push_back(newe); 30 | } 31 | 32 | Core::Edge* newe = new Core::Edge(); 33 | Core::MakeEdge(verts.back(), verts.at(0), newe); 34 | edges.push_back(newe); 35 | 36 | result.edges = edges; 37 | result.verts = verts; 38 | 39 | return result; 40 | } 41 | 42 | 43 | } // namespace Ops 44 | } // namespace Aoba 45 | -------------------------------------------------------------------------------- /src/Ops/Create/CreateCube.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Create.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Ops { 7 | 8 | const CreateCubeResult CreateCube(Core::Mesh* m, float size) { 9 | // first create all vertices 10 | std::vector verts = std::vector(); 11 | verts.reserve(8); 12 | 13 | // add new verts, add them to mesh 14 | for(int i = 0; i < 8; ++i) { 15 | Core::Vert* newv = new Core::Vert(); 16 | Core::MakeVert(m, newv); 17 | verts.push_back(newv); 18 | } 19 | 20 | // set up vert coordinates 21 | // perhaps this could be done inside a loop, but this is readable and efficient 22 | float halfSize = size / 2; 23 | verts.at(0)->co = Math::Vec3(-halfSize, -halfSize, -halfSize); 24 | verts.at(1)->co = Math::Vec3(halfSize, -halfSize, -halfSize); 25 | verts.at(2)->co = Math::Vec3(halfSize, -halfSize, halfSize); 26 | verts.at(3)->co = Math::Vec3(-halfSize, -halfSize, halfSize); 27 | verts.at(4)->co = Math::Vec3(-halfSize, halfSize, -halfSize); 28 | verts.at(5)->co = Math::Vec3(halfSize, halfSize, -halfSize); 29 | verts.at(6)->co = Math::Vec3(halfSize, halfSize, halfSize); 30 | verts.at(7)->co = Math::Vec3(-halfSize, halfSize, halfSize); 31 | 32 | // create all edges 33 | std::vector edges = std::vector(); 34 | edges.reserve(12); 35 | for(std::size_t i = 0; i < 4; ++i) { 36 | Core::Edge* newe = new Core::Edge(); 37 | if(i == 3) { 38 | Core::MakeEdge(verts.at(i), verts.at(0), newe); 39 | } else { 40 | Core::MakeEdge(verts.at(i), verts.at(i + 1), newe); 41 | } 42 | edges.push_back(newe); 43 | } 44 | for(std::size_t i = 4; i < 8; ++i) { 45 | Core::Edge* newe = new Core::Edge(); 46 | if(i == 7) { 47 | Core::MakeEdge(verts.at(i), verts.at(4), newe); 48 | } else { 49 | Core::MakeEdge(verts.at(i), verts.at(i + 1), newe); 50 | } 51 | edges.push_back(newe); 52 | } 53 | for(std::size_t i = 0; i < 4; ++i) { 54 | Core::Edge* newe = new Core::Edge(); 55 | Core::MakeEdge(verts.at(i), verts.at(i + 4), newe); 56 | edges.push_back(newe); 57 | } 58 | 59 | // create faces 60 | std::vector faces = std::vector(); 61 | faces.reserve(6); 62 | for(int i = 0; i < 6; ++i) { 63 | faces.push_back(new Core::Face()); 64 | } 65 | 66 | // perhaps this could be done in a loop, but this works for now. 67 | // not the most elegant solution 68 | Core::Loop* newl = new Core::Loop(); 69 | std::vector loopEdges = {edges.at(0), edges.at(1), edges.at(2), edges.at(3)}; 70 | std::vector loopVerts = {verts.at(0), verts.at(1), verts.at(2), verts.at(3)}; 71 | Core::MakeLoop(loopEdges, loopVerts, newl); 72 | Core::MakeFace(newl, faces.at(0)); 73 | 74 | newl = new Core::Loop(); 75 | loopEdges = {edges.at(4), edges.at(7), edges.at(6), edges.at(5)}; 76 | loopVerts = {verts.at(5), verts.at(4), verts.at(7), verts.at(6)}; 77 | Core::MakeLoop(loopEdges, loopVerts, newl); 78 | Core::MakeFace(newl, faces.at(1)); 79 | 80 | newl = new Core::Loop(); 81 | loopEdges = {edges.at(9), edges.at(5), edges.at(10), edges.at(1)}; 82 | loopVerts = {verts.at(1), verts.at(5), verts.at(6), verts.at(2)}; 83 | Core::MakeLoop(loopEdges, loopVerts, newl); 84 | Core::MakeFace(newl, faces.at(2)); 85 | 86 | newl = new Core::Loop(); 87 | loopEdges = {edges.at(8), edges.at(3), edges.at(11), edges.at(7)}; 88 | loopVerts = {verts.at(4), verts.at(0), verts.at(3), verts.at(7)}; 89 | Core::MakeLoop(loopEdges, loopVerts, newl); 90 | Core::MakeFace(newl, faces.at(3)); 91 | 92 | newl = new Core::Loop(); 93 | loopEdges = {edges.at(10), edges.at(6), edges.at(11), edges.at(2)}; 94 | loopVerts = {verts.at(2), verts.at(6), verts.at(7), verts.at(3)}; 95 | Core::MakeLoop(loopEdges, loopVerts, newl); 96 | Core::MakeFace(newl, faces.at(4)); 97 | 98 | newl = new Core::Loop(); 99 | loopEdges = {edges.at(8), edges.at(4), edges.at(9), edges.at(0)}; 100 | loopVerts = {verts.at(0), verts.at(4), verts.at(5), verts.at(1)}; 101 | Core::MakeLoop(loopEdges, loopVerts, newl); 102 | Core::MakeFace(newl, faces.at(5)); 103 | 104 | CreateCubeResult result = CreateCubeResult(); 105 | result.edges = edges; 106 | result.Faces = faces; 107 | result.verts = verts; 108 | 109 | return result; 110 | } 111 | 112 | } // namespace Ops 113 | } // namespace Aoba 114 | -------------------------------------------------------------------------------- /src/Ops/Create/CreateGrid.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Create.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const CreateGridResult CreateGrid(Core::Mesh* m, int divisionsX, int divisionsY, float sizeX, float sizeY) { 7 | std::size_t divsX = divisionsX; 8 | std::size_t divsY = divisionsY; 9 | // Create verts. 10 | std::vector verts = std::vector(); 11 | verts.reserve((2 + divsX) * (2 + divsY)); 12 | std::vector boundaryVerts = std::vector(); 13 | boundaryVerts.reserve(divsX + 2 * (2 + divsX)); 14 | std::vector innerVerts = std::vector(); 15 | innerVerts.reserve(divsX * divsY); 16 | 17 | float xStep = sizeX / (divsX + 1); 18 | float yStep = sizeY / (divsY + 1); 19 | for(int x = 0; x <= divsX + 1; ++x) { 20 | for(int y = 0; y <= divsY + 1; ++y) { 21 | Core::Vert* newv = new Core::Vert(); 22 | Core::MakeVert(m, newv); 23 | newv->co = Math::Vec3(-sizeX / 2 + x * xStep, -sizeY / 2 + y * yStep, 0); 24 | verts.push_back(newv); 25 | 26 | // push to inner/boundary vert 27 | if(x == 0 || x == divsX + 1 || y == 0 || y == divsY + 1) { 28 | boundaryVerts.push_back(newv); 29 | } else { 30 | innerVerts.push_back(newv); 31 | } 32 | } 33 | } 34 | 35 | std::vector boundaryEdges = std::vector(); 36 | boundaryEdges.reserve(2 * (divsX + 2 + divsY)); 37 | std::vector innerEdges = std::vector(); 38 | innerEdges.reserve(divsX * (1 + divsY) + divsY * (1 + divsX)); 39 | 40 | // create edges paralel to x axi 41 | std::vector xEdges = std::vector(); 42 | xEdges.reserve((divsX + 1) * (divsY + 2)); 43 | for(std::size_t y = 0; y <= divsY + 1; ++y) { 44 | for(std::size_t x = 0; x <= divsX; ++x) { 45 | std::size_t idx = y * (2 + divsX) + x; 46 | Core::Edge* newe = new Core::Edge(); 47 | Core::MakeEdge(verts.at(idx), verts.at(idx + 1), newe); 48 | xEdges.push_back(newe); 49 | if(y == 0 || y == divsY + 1) { 50 | boundaryEdges.push_back(newe); 51 | } else { 52 | innerEdges.push_back(newe); 53 | } 54 | } 55 | } 56 | 57 | // create edges paralel to y axis 58 | std::vector yEdges = std::vector(); 59 | yEdges.reserve((divsY + 1) * (divsX + 2)); 60 | for(std::size_t y = 0; y <= divsY; ++y) { 61 | for(std::size_t x = 0; x <= divsX + 1; ++x) { 62 | std::size_t idx = y * (2 + divsX) + x; 63 | Core::Edge* newe = new Core::Edge(); 64 | Core::MakeEdge(verts.at(idx), verts.at(idx + 2 + divsX), newe); 65 | yEdges.push_back(newe); 66 | if(x == 0 || x == divsX + 1) { 67 | boundaryEdges.push_back(newe); 68 | } else { 69 | innerEdges.push_back(newe); 70 | } 71 | } 72 | } 73 | 74 | std::vector boundaryFaces = std::vector(); 75 | std::size_t innerFaceCount = 0; 76 | if(divsX >= 2 && divsY >= 2) { 77 | innerFaceCount = (divsX - 1) * (divsY - 1); 78 | } 79 | boundaryFaces.reserve((divsX + 1) * (divsY + 1) - innerFaceCount); 80 | std::vector innerFaces = std::vector(); 81 | innerFaces.reserve(innerFaceCount); 82 | 83 | // Create edges and faces. 84 | for(std::size_t x = 0; x <= divsX; ++x) { 85 | for(std::size_t y = 0; y <= divsY; ++y) { 86 | Core::Edge* e0 = xEdges.at(x + y * (1 + divsX)); 87 | Core::Edge* e1 = yEdges.at(x + (2 + divsX) * y + 1); 88 | Core::Edge* e2 = xEdges.at(x + (1 + y) * (1 + divsX)); 89 | Core::Edge* e3 = yEdges.at(x + (2 + divsX) * y); 90 | 91 | Core::Vert* v0 = verts.at(y * (2 + divsX) + x); 92 | Core::Vert* v1 = verts.at(y * (2 + divsX) + x + 1); 93 | Core::Vert* v2 = verts.at((1 + y) * (2 + divsX) + x + 1); 94 | Core::Vert* v3 = verts.at((1 + y) * (2 + divsX) + x); 95 | 96 | std::vector loopEdges = {e0, e1, e2, e3}; 97 | std::vector loopVerts = {v0, v1, v2, v3}; 98 | 99 | Core::Loop* newl = new Core::Loop(); 100 | Core::MakeLoop(loopEdges, loopVerts, newl); 101 | Core::Face* newf = new Core::Face(); 102 | Core::MakeFace(newl, newf); 103 | 104 | // TODO: push faces, edges to appropriate vectors... 105 | bool isBoundaryFace = false; 106 | if(x == 0 || x == divsX) { 107 | isBoundaryFace = true; 108 | } else if(y == 0 || y == divsY) { 109 | isBoundaryFace = true; 110 | } else { 111 | isBoundaryFace = false; 112 | } 113 | 114 | if(isBoundaryFace) { 115 | boundaryFaces.push_back(newf); 116 | } else { 117 | innerFaces.push_back(newf); 118 | } 119 | } 120 | } 121 | 122 | CreateGridResult result = CreateGridResult(); 123 | result.innerEdges = innerEdges; 124 | result.innerFaces = innerFaces; 125 | result.innerVerts = innerVerts; 126 | result.outerEdges = boundaryEdges; 127 | result.outerFaces = boundaryFaces; 128 | result.outerVerts = boundaryVerts; 129 | 130 | return result; 131 | } 132 | 133 | } // namespace Ops 134 | } // namespace Aoba 135 | -------------------------------------------------------------------------------- /src/Ops/Create/CreateUVSphere.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Create.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Ops { 7 | 8 | const CreateUvSphereResult CreateUVSphere(Core::Mesh* m, float radius, std::size_t rings, std::size_t segments) { 9 | // todo: check if radius, number of rings and number of segments are valid values 10 | // must have at least three segments and at least two rings 11 | 12 | float segmentAngleStep = (2 * PI) / segments; 13 | float ringAngleStep = PI / (rings + 1); 14 | 15 | std::vector verts = std::vector(); 16 | verts.reserve(rings * segments + 2); 17 | 18 | // create verts except the cap verts 19 | for(int ring = 0; ring < rings; ring++) { 20 | float ringAngle = ringAngleStep * (1 + ring) - PI / 2; 21 | float z = radius * sinf(ringAngle); 22 | float r = radius * cosf(ringAngle); 23 | for(int segment = 0; segment < segments; segment++) { 24 | float segmentAngle = segment * segmentAngleStep; 25 | float x = r * cosf(segmentAngle); 26 | float y = r * sinf(segmentAngle); 27 | 28 | Core::Vert* newv = new Core::Vert(); 29 | Core::MakeVert(m, newv); 30 | newv->co = Math::Vec3(x, y, z); 31 | verts.push_back(newv); 32 | } 33 | } 34 | 35 | // create cap verts 36 | Core::Vert* capBottom = new Core::Vert(); 37 | Core::MakeVert(m, capBottom); 38 | capBottom->co = Math::Vec3(0, 0, -radius); 39 | Core::Vert* capTop = new Core::Vert(); 40 | Core::MakeVert(m, capTop); 41 | capTop->co = Math::Vec3(0, 0, radius); 42 | verts.push_back(capBottom); 43 | verts.push_back(capTop); 44 | 45 | std::vector edges = std::vector(); 46 | std::size_t edgeCount = (2 * rings + 1) * segments; 47 | edges.reserve(edgeCount); 48 | 49 | // create ring edges 50 | std::vector rEdges = std::vector(); 51 | rEdges.reserve(rings * segments); 52 | for(std::size_t ring = 0; ring < rings; ++ring) { 53 | for(int i = 0; i < segments; ++i) { 54 | std::size_t idx = ring * segments + i; 55 | Core::Edge* newe = new Core::Edge(); 56 | if(i < segments - 1) { 57 | Core::MakeEdge(verts.at(idx), verts.at(idx + 1), newe); 58 | 59 | } else { 60 | Core::MakeEdge(verts.at(idx), verts.at(ring * segments), newe); 61 | } 62 | rEdges.push_back(newe); 63 | edges.push_back(newe); 64 | } 65 | } 66 | 67 | // create segment edges 68 | std::vector sEdges = std::vector(); 69 | sEdges.reserve((rings - 1) * segments); 70 | for(int ring = 0; ring < rings - 1; ++ring) { 71 | for(int segment = 0; segment < segments; ++segment) { 72 | std::size_t idx = ring * segments + segment; 73 | Core::Edge* newe = new Core::Edge(); 74 | Core::MakeEdge(verts.at(idx), verts.at(idx + segments), newe); 75 | sEdges.push_back(newe); 76 | edges.push_back(newe); 77 | } 78 | } 79 | 80 | // create bottom cap edges 81 | std::vector bottomEdges = std::vector(); 82 | bottomEdges.reserve(segments); 83 | for(int i = 0; i < segments; ++i) { 84 | Core::Edge* newe = new Core::Edge(); 85 | Core::MakeEdge(capBottom, verts.at(i), newe); 86 | edges.push_back(newe); 87 | bottomEdges.push_back(newe); 88 | } 89 | // create top cap edges 90 | std::vector topEdges = std::vector(); 91 | topEdges.reserve(segments); 92 | for(std::size_t i = (rings - 1) * segments; i < rings * segments; ++i) { 93 | Core::Edge* newe = new Core::Edge(); 94 | Core::MakeEdge(verts.at(i), capTop, newe); 95 | edges.push_back(newe); 96 | topEdges.push_back(newe); 97 | } 98 | 99 | std::vector faces = std::vector(); 100 | faces.reserve((rings + 1) * segments); 101 | 102 | // create bottom cap faces 103 | for(std::size_t i = 0; i < segments; ++i) { 104 | std::vector loopVerts; 105 | std::vector loopEdges; 106 | if(i != segments - 1) { 107 | loopVerts = {verts.at(i), capBottom, verts.at(i + 1)}; 108 | loopEdges = {bottomEdges.at(i), bottomEdges.at(i + 1), rEdges.at(i)}; 109 | } else { 110 | loopVerts = {verts.at(i), capBottom, verts.at(0)}; 111 | loopEdges = {bottomEdges.at(i), bottomEdges.at(0), rEdges.at(i)}; 112 | } 113 | Core::Loop* newl = new Core::Loop(); 114 | Core::MakeLoop(loopEdges, loopVerts, newl); 115 | Core::Face* newf = new Core::Face(); 116 | Core::MakeFace(newl, newf); 117 | faces.push_back(newf); 118 | } 119 | 120 | // create top cap faces 121 | for(std::size_t i = 0; i < segments; ++i) { 122 | std::size_t idx = (rings - 1) * segments + i; 123 | std::vector loopVerts; 124 | std::vector loopEdges; 125 | if(i != segments - 1) { 126 | loopVerts = {capTop, verts.at(idx), verts.at(idx + 1)}; 127 | loopEdges = {topEdges.at(i), rEdges.at(idx), topEdges.at(i + 1)}; 128 | } else { 129 | loopVerts = {capTop, verts.at(idx), verts.at((rings - 1) * segments)}; 130 | loopEdges = {topEdges.at(i), rEdges.at(idx), topEdges.at(0)}; 131 | } 132 | Core::Loop* newl = new Core::Loop(); 133 | Core::MakeLoop(loopEdges, loopVerts, newl); 134 | Core::Face* newf = new Core::Face(); 135 | Core::MakeFace(newl, newf); 136 | faces.push_back(newf); 137 | } 138 | 139 | // create segment faces 140 | for(std::size_t ring = 0; ring < rings - 1; ++ring) { 141 | for(std::size_t segment = 0; segment < segments; ++segment) { 142 | std::size_t idx = ring * segments + segment; 143 | Core::Vert* v0 = verts.at(idx); 144 | Core::Vert* v1; 145 | Core::Vert* v2; 146 | Core::Vert* v3 = verts.at(idx + segments); 147 | 148 | Core::Edge* e0 = rEdges.at(idx); 149 | Core::Edge* e1; 150 | Core::Edge* e2 = rEdges.at(idx + segments); 151 | Core::Edge* e3 = sEdges.at(idx); 152 | 153 | if(segment == segments - 1) { 154 | v1 = verts.at(ring * segments); 155 | v2 = verts.at((ring + 1) * segments); 156 | e1 = sEdges.at(ring * segments); 157 | } else { 158 | v1 = verts.at(idx + 1); 159 | v2 = verts.at(idx + segments + 1); 160 | e1 = sEdges.at(idx + 1); 161 | } 162 | 163 | std::vector loopEdges = {e0, e1, e2, e3}; 164 | std::vector loopVerts = {v0, v1, v2, v3}; 165 | 166 | Core::Loop* newl = new Core::Loop(); 167 | Core::MakeLoop(loopEdges, loopVerts, newl); 168 | Core::Face* newf = new Core::Face(); 169 | Core::MakeFace(newl, newf); 170 | 171 | faces.push_back(newf); 172 | } 173 | } 174 | 175 | CreateUvSphereResult result = CreateUvSphereResult(); 176 | result.verts = verts; 177 | result.edges = edges; 178 | result.Faces = faces; 179 | 180 | return result; 181 | } 182 | 183 | } // namespace Ops 184 | } // namespace Aoba 185 | -------------------------------------------------------------------------------- /src/Ops/Create/CreateVert.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Create.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Ops { 7 | 8 | Core::Vert* CreateVert(Core::Mesh* m, const Math::Vec3 co) { 9 | Core::Vert* newv = new Core::Vert(); 10 | 11 | Core::MakeVert(m, newv); 12 | newv->co = co; 13 | 14 | return newv; 15 | } 16 | 17 | } // namespace Ops 18 | } // namespace Aoba 19 | -------------------------------------------------------------------------------- /src/Ops/Modify/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Delete.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Duplicate.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/ExtrudeEdges.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/ExtrudeFaceRegion.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/ExtrudeFaces.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/ExtrudeVerts.cpp 10 | ${CMAKE_CURRENT_SOURCE_DIR}/InsetFaces.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/MakeFace.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/Mirror.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/RecalculateFaceNormals.cpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/ReverseFaceOrder.cpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/SplitEdges.cpp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/SubdivideSimple.cpp 17 | ${CMAKE_CURRENT_SOURCE_DIR}/SubdivideSmooth.cpp 18 | ${CMAKE_CURRENT_SOURCE_DIR}/TriangulateFaces.cpp 19 | ) -------------------------------------------------------------------------------- /src/Ops/Modify/Delete.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Ops { 7 | 8 | void Delete(Core::Mesh* m, const std::vector& verts, const std::vector& edges, 9 | const std::vector& faces, DeleteMode mode) { 10 | // delete flag 11 | const int32_t DELETE = 1 << 0; 12 | 13 | // TODO: some cases which might seem intuitive are not handled somewhat gracefully 14 | // think about wanting to delete two adjecent faces and the (manifold) edge that they share 15 | // while this could be achieved by calling delete on that manifold edge, it is not intuitive 16 | // rather, it is intuitive to call delete on those two adjecent faces, with some delete mode 17 | // and expect that the shared edge is deleted, while keeping other(boundary) edges. 18 | 19 | // the same can be said when, for example, deleting wire edges joined at a single vertex 20 | // an intuitive option would be to delete the shared vertex, while keeping the boundary vertices 21 | // even though the same effect can be achieved by only deleting the shared vertex. 22 | 23 | // verify that no duplicate faces exist in input 24 | std::vector facesToDelete = std::vector(); 25 | facesToDelete.reserve(faces.size()); 26 | for(Core::Face* f : faces) { 27 | if(f->flagsIntern != DELETE) { 28 | facesToDelete.push_back(f); 29 | f->flagsIntern = DELETE; 30 | } 31 | } 32 | 33 | std::vector edgesToDelete = std::vector(); 34 | if(mode != DeleteMode::FacesOnly) { 35 | edgesToDelete.reserve(edges.size()); 36 | } 37 | 38 | std::vector vertsToDelete = std::vector(); 39 | if(mode == DeleteMode::All) { 40 | vertsToDelete.reserve(verts.size()); 41 | } 42 | 43 | // delete faces 44 | for(Core::Face* f : facesToDelete) { 45 | // if using DeleteMode other than FaceOnly, mark edges(and verts) of this face for deletion 46 | if(mode != DeleteMode::FacesOnly) { 47 | for(Core::Loop* loop : f->Loops()) { 48 | Core::Edge* edge = loop->LoopEdge(); 49 | if(edge->flagsIntern != DELETE) { 50 | edge->flagsIntern = DELETE; 51 | edgesToDelete.push_back(edge); 52 | } 53 | if(mode == DeleteMode::All) { 54 | Core::Vert* vert = loop->LoopVert(); 55 | if(vert->flagsIntern != DELETE) { 56 | vert->flagsIntern = DELETE; 57 | vertsToDelete.push_back(vert); 58 | } 59 | } 60 | } 61 | } 62 | // kill the face, keeping its edges and verts alive (for now) 63 | KillFace(f); 64 | } 65 | 66 | if(mode == DeleteMode::FacesOnly) { 67 | return; 68 | } 69 | 70 | for(Core::Edge* e : edges) { 71 | if(e->flagsIntern != DELETE) { 72 | edgesToDelete.push_back(e); 73 | e->flagsIntern = DELETE; 74 | } 75 | } 76 | 77 | // delete edges 78 | // edges from deleted faces have also been added to edgesToDelete. 79 | for(Core::Edge* e : edgesToDelete) { 80 | // if using DeleteMode All, mark verts of this edge for deletion 81 | if(mode == DeleteMode::All) { 82 | for(Core::Vert* v : e->Verts()) { 83 | if(v->flagsIntern != DELETE) { 84 | v->flagsIntern = DELETE; 85 | vertsToDelete.push_back(v); 86 | } 87 | } 88 | } 89 | 90 | // kill the edge, keeping its verts alive(for now), but killing all adjecent faces 91 | KillEdge(e); 92 | } 93 | 94 | if(mode != DeleteMode::All) { 95 | return; 96 | } 97 | 98 | for(Core::Vert* v : verts) { 99 | if(v->flagsIntern != DELETE) { 100 | vertsToDelete.push_back(v); 101 | v->flagsIntern = DELETE; 102 | } 103 | } 104 | 105 | // kill all verts marked for deletion(either directly or implicit through edges/faces) 106 | // this will kill other faces and edges as necessary. 107 | for(Core::Vert* v : vertsToDelete) { 108 | KillVert(v); 109 | } 110 | } 111 | 112 | } // namespace Ops 113 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Modify/Duplicate.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const DuplicateResult Duplicate(Core::Mesh* m, const std::vector& verts, 7 | const std::vector& edges, const std::vector& faces) { 8 | const int32_t COPIED = 1 << 0; 9 | 10 | // duplicate verts from the list 11 | std::vector newVerts = std::vector(); 12 | newVerts.reserve(verts.size()); 13 | int vertIdx = 0; 14 | 15 | for(int i = 0; i < verts.size(); ++i) { 16 | Core::Vert* newv = new Core::Vert(); 17 | Core::MakeVert(m, newv); 18 | 19 | newv->co = verts.at(i)->co; 20 | verts.at(i)->flagsIntern = COPIED; 21 | 22 | verts.at(i)->index = vertIdx; 23 | vertIdx++; 24 | 25 | newVerts.push_back(newv); 26 | } 27 | 28 | // duplicate edges from the list, and any verts not previously duplicated 29 | std::vector newEdges = std::vector(); 30 | newEdges.reserve(edges.size()); 31 | int edgeIdx = 0; 32 | 33 | for(int i = 0; i < edges.size(); ++i) { 34 | Core::Edge* newe = new Core::Edge(); 35 | Core::Edge* current = edges.at(i); 36 | current->index = edgeIdx; 37 | current->flagsIntern = COPIED; 38 | edgeIdx++; 39 | 40 | Core::Vert* v1; 41 | Core::Vert* v2; 42 | 43 | if(current->Verts().at(0)->flagsIntern & COPIED) { 44 | v1 = newVerts.at(current->Verts().at(0)->index); 45 | } else { 46 | v1 = new Core::Vert(); 47 | Core::MakeVert(m, v1); 48 | 49 | v1->co = current->Verts().at(0)->co; 50 | current->Verts().at(0)->flagsIntern = COPIED; 51 | 52 | current->Verts().at(0)->index = vertIdx; 53 | vertIdx++; 54 | newVerts.push_back(v1); 55 | } 56 | 57 | if(current->Verts().at(1)->flagsIntern & COPIED) { 58 | v2 = newVerts.at(current->Verts().at(1)->index); 59 | } else { 60 | v2 = new Core::Vert(); 61 | Core::MakeVert(m, v2); 62 | 63 | v2->co = current->Verts().at(1)->co; 64 | current->Verts().at(1)->flagsIntern = COPIED; 65 | 66 | current->Verts().at(1)->index = vertIdx; 67 | vertIdx++; 68 | newVerts.push_back(v2); 69 | } 70 | 71 | Core::MakeEdge(v1, v2, newe); 72 | newEdges.push_back(newe); 73 | } 74 | 75 | // duplicate Faces from the list, and any verts and edges not previously duplicated 76 | std::vector newFaces = std::vector(); 77 | newFaces.reserve(edges.size()); 78 | 79 | for(int i = 0; i < faces.size(); ++i) { 80 | Core::Face* newf = new Core::Face(); 81 | 82 | std::vector newFaceLoops = std::vector(); 83 | 84 | std::vector loops = faces.at(i)->Loops(); 85 | std::vector loopVerts = std::vector(); 86 | std::vector loopEdges = std::vector(); 87 | 88 | for(int k = 0; k < loops.size(); ++k) { 89 | // check if loop vert is copied, push to loopverts 90 | if(loops.at(k)->LoopVert()->flagsIntern & COPIED) { 91 | loopVerts.push_back(newVerts.at(loops.at(k)->LoopVert()->index)); 92 | } else { 93 | Core::Vert* newv = new Core::Vert(); 94 | Core::MakeVert(m, newv); 95 | 96 | newv->co = loops.at(k)->LoopVert()->co; 97 | loops.at(k)->LoopVert()->flagsIntern = COPIED; 98 | loops.at(k)->LoopVert()->index = vertIdx; 99 | vertIdx++; 100 | newVerts.push_back(newv); 101 | loopVerts.push_back(newv); 102 | } 103 | 104 | // check if loop edge is copied, push to loopedges 105 | if(loops.at(k)->LoopEdge()->flagsIntern & COPIED) { 106 | loopEdges.push_back(newEdges.at(loops.at(k)->LoopEdge()->index)); 107 | } else { 108 | Core::Edge* newe = new Core::Edge(); 109 | Core::Edge* current = loops.at(k)->LoopEdge(); 110 | current->index = edgeIdx; 111 | current->flagsIntern = COPIED; 112 | edgeIdx++; 113 | 114 | Core::Vert* v1; 115 | Core::Vert* v2; 116 | 117 | if(current->Verts().at(0)->flagsIntern & COPIED) { 118 | v1 = newVerts.at(current->Verts().at(0)->index); 119 | } else { 120 | v1 = new Core::Vert(); 121 | Core::MakeVert(m, v1); 122 | 123 | v1->co = current->Verts().at(0)->co; 124 | current->Verts().at(0)->flagsIntern = COPIED; 125 | 126 | current->Verts().at(0)->index = vertIdx; 127 | vertIdx++; 128 | newVerts.push_back(v1); 129 | } 130 | 131 | if(current->Verts().at(1)->flagsIntern & COPIED) { 132 | v2 = newVerts.at(current->Verts().at(1)->index); 133 | } else { 134 | v2 = new Core::Vert(); 135 | Core::MakeVert(m, v2); 136 | 137 | v2->co = current->Verts().at(1)->co; 138 | current->Verts().at(1)->flagsIntern = COPIED; 139 | 140 | current->Verts().at(1)->index = vertIdx; 141 | vertIdx++; 142 | newVerts.push_back(v2); 143 | } 144 | 145 | Core::MakeEdge(v1, v2, newe); 146 | newEdges.push_back(newe); 147 | loopEdges.push_back(newe); 148 | } 149 | } 150 | 151 | Core::Loop* newl = new Core::Loop(); 152 | Core::MakeLoop(loopEdges, loopVerts, newl); 153 | 154 | Core::MakeFace(newl, newf); 155 | newFaces.push_back(newf); 156 | } 157 | 158 | DuplicateResult result = DuplicateResult(); 159 | result.verts = newVerts; 160 | result.faces = newFaces; 161 | result.edges = newEdges; 162 | 163 | // TODO: perhaps this could be done faster 164 | // clear all flags and indexes 165 | for(int i = 0; i < verts.size(); ++i) { 166 | verts.at(i)->flagsIntern = 0; 167 | verts.at(i)->index = 0; 168 | vertIdx++; 169 | } 170 | 171 | for(int i = 0; i < edges.size(); ++i) { 172 | edges.at(i)->flagsIntern = 0; 173 | edges.at(i)->index = 0; 174 | 175 | edges.at(i)->Verts().at(0)->index = 0; 176 | edges.at(i)->Verts().at(0)->flagsIntern = 0; 177 | edges.at(i)->Verts().at(1)->index = 0; 178 | edges.at(i)->Verts().at(1)->flagsIntern = 0; 179 | } 180 | 181 | for(int i = 0; i < faces.size(); ++i) { 182 | std::vector loops = faces.at(i)->Loops(); 183 | 184 | for(int k = 0; k < loops.size(); ++k) { 185 | loops.at(k)->LoopVert()->flagsIntern = 0; 186 | loops.at(k)->LoopVert()->index = 0; 187 | 188 | if(loops.at(k)->LoopEdge()->flagsIntern) { 189 | loops.at(k)->LoopEdge()->Verts().at(0)->index = 0; 190 | loops.at(k)->LoopEdge()->Verts().at(0)->flagsIntern = 0; 191 | loops.at(k)->LoopEdge()->Verts().at(1)->index = 0; 192 | loops.at(k)->LoopEdge()->Verts().at(1)->flagsIntern = 0; 193 | } 194 | 195 | loops.at(k)->LoopEdge()->flagsIntern = 0; 196 | loops.at(k)->LoopEdge()->index = 0; 197 | } 198 | } 199 | 200 | return result; 201 | } 202 | 203 | } // namespace Ops 204 | } // namespace Aoba 205 | -------------------------------------------------------------------------------- /src/Ops/Modify/ExtrudeEdges.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const ExtrudeEdgesResult ExtrudeEdges(Core::Mesh* m, const std::vector& edges) { 7 | // TODO: check if all elements belong to the mesh 8 | 9 | ExtrudeEdgesResult result = ExtrudeEdgesResult(); 10 | 11 | std::vector verticalEdges = std::vector(); 12 | verticalEdges.reserve(edges.size()); 13 | std::vector horizontalEdges = std::vector(); 14 | horizontalEdges.reserve(edges.size()); 15 | std::vector verts = std::vector(); 16 | verts.reserve(edges.size()); // not exact cause some verts might be shared, and some not 17 | std::vector faces = std::vector(); 18 | faces.reserve(edges.size()); 19 | 20 | const int32_t EXTRUDED_VERT = 1 << 0; 21 | 22 | int vertIdx = 0; 23 | for(int i = 0; i < edges.size(); ++i) { 24 | std::vector currentVerts = edges.at(i)->Verts(); 25 | // extrude all individual verts into vertical edges ig not already extruded 26 | for(int j = 0; j < 2; ++j) { 27 | if(!(currentVerts.at(j)->flagsIntern & EXTRUDED_VERT)) { 28 | Core::Vert* newv = new Core::Vert(); 29 | Core::Edge* newe = new Core::Edge(); 30 | 31 | Core::MakeEdgeVert(currentVerts.at(j), newe, newv); 32 | newv->co = currentVerts.at(j)->co; 33 | 34 | // set flags and index on the vert. 35 | currentVerts.at(j)->flagsIntern = EXTRUDED_VERT; 36 | currentVerts.at(j)->index = vertIdx; 37 | vertIdx++; 38 | 39 | // push new edge, vert 40 | verticalEdges.push_back(newe); 41 | verts.push_back(newv); 42 | } 43 | } 44 | 45 | // create the horizontal edge, accessing the new verts by index stored on existing verts of the current edge. 46 | Core::Edge* newe = new Core::Edge(); 47 | Core::MakeEdge(verts.at(currentVerts.at(0)->index), verts.at(currentVerts.at(1)->index), newe); 48 | horizontalEdges.push_back(newe); 49 | 50 | // find edges and verts which form the face boundary, keeping in mind existing loops 51 | // try to avoid discontinued normals 52 | std::vector faceEdges = std::vector(); 53 | faceEdges.reserve(4); 54 | faceEdges.push_back(edges.at(i)); 55 | Core::Vert* loopVert = currentVerts.at(0); 56 | if(edges.at(i)->IsBoundary()) { 57 | loopVert = edges.at(i)->Loops().at(0)->LoopVert(); 58 | } 59 | Core::Vert* first = edges.at(i)->Other(loopVert); 60 | faceEdges.push_back(verticalEdges.at(loopVert->index)); 61 | faceEdges.push_back(horizontalEdges.at(i)); 62 | faceEdges.push_back(verticalEdges.at(first->index)); 63 | std::vector faceVerts = {first, loopVert, verts.at(loopVert->index), verts.at(first->index)}; 64 | 65 | // create the new face 66 | Core::Face* newf = new Core::Face(); 67 | Core::Loop* newl = new Core::Loop(); 68 | Core::MakeLoop(faceEdges, faceVerts, newl); 69 | Core::MakeFace(newl, newf); 70 | faces.push_back(newf); 71 | } 72 | 73 | // clear flags and index from verts 74 | for(int i = 0; i < edges.size(); ++i) { 75 | std::vector currentVerts = edges.at(i)->Verts(); 76 | for(int j = 0; j < 2; ++j) { 77 | currentVerts.at(j)->index = 0; 78 | currentVerts.at(j)->flagsIntern = 0; 79 | } 80 | } 81 | 82 | result.faces = faces; 83 | result.horizontalEdges = horizontalEdges; 84 | result.verticalEdges = verticalEdges; 85 | result.verts = verts; 86 | 87 | return result; 88 | } 89 | 90 | } // namespace Ops 91 | } // namespace Aoba 92 | -------------------------------------------------------------------------------- /src/Ops/Modify/ExtrudeFaces.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const ExtrudeFacesResult ExtrudeFaces(Core::Mesh* m, const std::vector& faces, bool keepOrig) { 7 | // flags 8 | const int32_t EXTRUDED = 1 << 0; 9 | 10 | // verts and edges 11 | std::vector newVerts = std::vector(); 12 | std::size_t newVertIdx = 0; 13 | std::vector newVerticalEdges = std::vector(); 14 | std::vector newHorizontalEdges = std::vector(); 15 | std::size_t newEdgeIdx = 0; 16 | std::vector newVerticalFaces = std::vector(); 17 | std::vector newHorizontalFaces = std::vector(); 18 | 19 | // iterate over all input faces 20 | for(Core::Face* face : faces) { 21 | std::vector faceLoops = std::vector(); 22 | std::vector loopEdges = std::vector(); 23 | std::vector loopVerts = std::vector(); 24 | 25 | // iterate over all loops inside this face 26 | for(Core::Loop* loop : face->Loops()) { 27 | Core::Edge* topEdge; 28 | Core::Vert* v2; 29 | Core::Vert* v3; 30 | // get the edge and verts and of current loop 31 | Core::Edge* loopEdge = loop->LoopEdge(); 32 | Core::Vert* v0 = loop->LoopVert(); 33 | Core::Vert* v1 = loopEdge->Other(v0); 34 | // check if the edge of current loop was already extruded 35 | if(loopEdge->flagsIntern & EXTRUDED) { 36 | // get verts, edges using indices 37 | v2 = newVerts.at(v1->index); 38 | v3 = newVerts.at(v0->index); 39 | topEdge = newHorizontalEdges.at(loopEdge->index); 40 | } else { 41 | Core::Edge* leftEdge; 42 | Core::Edge* rightEdge; 43 | // extrude each vertex of the edge which was not extruded 44 | if(v0->flagsIntern & EXTRUDED) { 45 | leftEdge = newVerticalEdges.at(v0->index); 46 | v3 = newVerts.at(v0->index); 47 | } else { 48 | leftEdge = new Core::Edge(); 49 | v3 = new Core::Vert(); 50 | v3->co = v0->co; 51 | Core::MakeEdgeVert(v0, leftEdge, v3); 52 | v0->index = newVertIdx; 53 | v0->flagsIntern = EXTRUDED; 54 | newVerts.push_back(v3); 55 | newVerticalEdges.push_back(leftEdge); 56 | newVertIdx++; 57 | } 58 | if(v1->flagsIntern & EXTRUDED) { 59 | rightEdge = newVerticalEdges.at(v1->index); 60 | v2 = newVerts.at(v1->index); 61 | } else { 62 | rightEdge = new Core::Edge(); 63 | v2 = new Core::Vert(); 64 | v2->co = v1->co; 65 | Core::MakeEdgeVert(v1, rightEdge, v2); 66 | v1->index = newVertIdx; 67 | v1->flagsIntern = EXTRUDED; 68 | newVerts.push_back(v2); 69 | newVerticalEdges.push_back(rightEdge); 70 | newVertIdx++; 71 | } 72 | // make an edge at the top using the new vertices of the extruded edges 73 | topEdge = new Core::Edge(); 74 | Core::MakeEdge(v3, v2, topEdge); 75 | newHorizontalEdges.push_back(topEdge); 76 | loopEdge->index = newEdgeIdx; 77 | loopEdge->flagsIntern = EXTRUDED; 78 | newEdgeIdx++; 79 | 80 | // make the extruded face 81 | std::vector loopEdges = {loopEdge, rightEdge, topEdge, leftEdge}; 82 | std::vector loopVerts = {v0, v1, v2, v3}; 83 | 84 | Core::Loop* newl = new Core::Loop(); 85 | Core::Face* newf = new Core::Face(); 86 | Core::MakeLoop(loopEdges, loopVerts, newl); 87 | Core::MakeFace(newl, newf); 88 | newVerticalFaces.push_back(newf); 89 | } 90 | 91 | // add the top edge and v3(same order) to the list of loop verts, edges 92 | loopEdges.push_back(topEdge); 93 | loopVerts.push_back(v3); 94 | } 95 | 96 | // create a new loop with MakeLoop using the loopverts and loopeedges to create the top face 97 | Core::Loop* faceLoop = new Core::Loop(); 98 | Core::MakeLoop(loopEdges, loopVerts, faceLoop); 99 | // push the new loop to the list of new loops 100 | faceLoops.push_back(faceLoop); 101 | 102 | // create the face using the list of new loops 103 | Core::Face* newf = new Core::Face(); 104 | Core::MakeFace(faceLoop, newf); 105 | newHorizontalFaces.push_back(newf); 106 | } 107 | 108 | // loop over all faces again, clear indices and flags 109 | for(Core::Face* face : faces) { 110 | // iterate over all loops inside this face 111 | for(Core::Loop* loop : face->Loops()) { 112 | Core::Edge* edge = loop->LoopEdge(); 113 | edge->flagsIntern = 0; 114 | edge->index = 0; 115 | edge->V1()->index = 0; 116 | edge->V1()->flagsIntern = 0; 117 | edge->V2()->index = 0; 118 | edge->V2()->flagsIntern = 0; 119 | } 120 | 121 | // if specified, kill original face 122 | if(!keepOrig) { 123 | Core::KillFace(face); 124 | } 125 | } 126 | 127 | ExtrudeFacesResult result = ExtrudeFacesResult(); 128 | result.verts = newVerts; 129 | result.horizontalEdges = newHorizontalEdges; 130 | result.horizontalFaces = newHorizontalFaces; 131 | result.verticalEdges = newVerticalEdges; 132 | result.verticalFaces = newVerticalFaces; 133 | 134 | return result; 135 | } 136 | 137 | } // namespace Ops 138 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Modify/ExtrudeVerts.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const ExtrudeVertsResult ExtrudeVerts(Core::Mesh* m, const std::vector& verts) { 7 | // TODO: check if all elements belong to the mesh 8 | // TODO: check for repeating verts 9 | 10 | ExtrudeVertsResult result = ExtrudeVertsResult(); 11 | 12 | std::vector newVerts = std::vector(); 13 | newVerts.reserve(verts.size()); 14 | 15 | std::vector newEdges = std::vector(); 16 | newEdges.reserve(verts.size()); 17 | 18 | for(Core::Vert* v : verts) { 19 | Core::Vert* newv = new Core::Vert(); 20 | newv->co = v->co; 21 | Core::Edge* newe = new Core::Edge(); 22 | Core::MakeEdgeVert(v, newe, newv); 23 | newVerts.push_back(newv); 24 | newEdges.push_back(newe); 25 | } 26 | 27 | result.verts = newVerts; 28 | result.edges = newEdges; 29 | 30 | return result; 31 | } 32 | 33 | } // namespace Ops 34 | } // namespace Aoba 35 | -------------------------------------------------------------------------------- /src/Ops/Modify/InsetFaces.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace Aoba { 8 | namespace Ops { 9 | 10 | Math::Vec3 CalcEdgeNo(Math::Vec3 faceNo, Core::Loop* loop) { 11 | Math::Vec3 coStart = loop->LoopVert()->co; 12 | Math::Vec3 coEnd = loop->LoopEdge()->Other(loop->LoopVert())->co; 13 | 14 | return faceNo.Cross((coEnd - coStart).Normalized()); 15 | } 16 | 17 | Math::Vec3 CalcVertOffset(Math::Vec3 no1, Math::Vec3 no2) { 18 | Math::Vec3 result = (no1 + no2); 19 | result.Normalize(); 20 | 21 | float angle = no1.Angle(no2); 22 | // this will error if no1 and no2 point in the s 23 | if(angle < 0.0000001f) { 24 | return no1; // no1 and no2 are practically equal, therefore edges are paralel 25 | } 26 | if(fabsf(PI - angle) < 0.00001f) { 27 | // the edges in question are really pointy, and the vector would go towards infinity 28 | // TODO: what t return in this case? 29 | return result; 30 | } 31 | result *= fabsf(1 / (cosf((angle) / 2))); 32 | return result; 33 | } 34 | 35 | const InsetFacesResult InsetFaces(Core::Mesh* m, const std::vector& faces, float distance) { 36 | std::vector verts = std::vector(); 37 | std::vector centerEdges = std::vector(); 38 | std::vector boundaryEdges = std::vector(); 39 | std::vector centerFaces = std::vector(); 40 | std::vector boundaryFaces = std::vector(); 41 | 42 | for(Core::Face* face : faces) { 43 | face->NormalUpdate(); 44 | std::vector faceLoops = face->Loops(); 45 | 46 | // vectors for edges and faces, in the same order as original face loops 47 | std::vector newVerts = std::vector(); 48 | std::vector newEdges = std::vector(); 49 | 50 | // calculate vectors pointing from edges to center of face 51 | Math::Vec3 edgeNoPrev = CalcEdgeNo(face->no, faceLoops.back()); 52 | Math::Vec3 edgeNoCurrent; 53 | 54 | // create inner verts and edges spanning to outer verts 55 | for(Core::Loop* loop : faceLoops) { 56 | edgeNoCurrent = CalcEdgeNo(face->no, loop); 57 | Core::Vert* newv = new Core::Vert(); 58 | Core::Edge* newe = new Core::Edge(); 59 | Core::MakeEdgeVert(loop->LoopVert(), newe, newv); 60 | 61 | newv->co = loop->LoopVert()->co + CalcVertOffset(edgeNoPrev, edgeNoCurrent) * distance; 62 | 63 | newVerts.push_back(newv); 64 | newEdges.push_back(newe); 65 | verts.push_back(newv); 66 | boundaryEdges.push_back(newe); 67 | edgeNoPrev = edgeNoCurrent; 68 | } 69 | 70 | // create inner edge loop using the existing verts 71 | // create outer ring faces 72 | std::vector innerEdges = std::vector(); 73 | for(std::size_t i = 0; i < newVerts.size() - 1; ++i) { 74 | Core::Edge* newe = new Core::Edge(); 75 | Core::MakeEdge(newVerts.at(i), newVerts.at(i + 1), newe); 76 | innerEdges.push_back(newe); 77 | centerEdges.push_back(newe); 78 | 79 | // make the face as well 80 | std::vector fVerts = { 81 | faceLoops.at(i)->LoopVert(), faceLoops.at(i + 1)->LoopVert(), newVerts.at(i + 1), newVerts.at(i)}; 82 | std::vector fEdges = {faceLoops.at(i)->LoopEdge(), newEdges.at(i + 1), newe, newEdges.at(i)}; 83 | 84 | Core::Loop* newl = new Core::Loop(); 85 | Core::MakeLoop(fEdges, fVerts, newl); 86 | Core::Face* newf = new Core::Face(); 87 | Core::MakeFace(newl, newf); 88 | boundaryFaces.push_back(newf); 89 | } 90 | // final iteration 91 | Core::Edge* newe = new Core::Edge(); 92 | Core::MakeEdge(newVerts.back(), newVerts.at(0), newe); 93 | innerEdges.push_back(newe); 94 | centerEdges.push_back(newe); 95 | std::vector fVerts = { 96 | faceLoops.back()->LoopVert(), faceLoops.at(0)->LoopVert(), newVerts.at(0), newVerts.back()}; 97 | std::vector fEdges = {faceLoops.back()->LoopEdge(), newEdges.at(0), newe, newEdges.back()}; 98 | 99 | Core::Loop* newl = new Core::Loop(); 100 | Core::MakeLoop(fEdges, fVerts, newl); 101 | Core::Face* newf = new Core::Face(); 102 | Core::MakeFace(newl, newf); 103 | boundaryFaces.push_back(newf); 104 | 105 | // create inner face 106 | newl = new Core::Loop(); 107 | Core::MakeLoop(innerEdges, newVerts, newl); 108 | newf = new Core::Face(); 109 | Core::MakeFace(newl, newf); 110 | 111 | centerFaces.push_back(newf); 112 | 113 | // kill original face 114 | Core::KillFace(face); 115 | } 116 | 117 | InsetFacesResult result = InsetFacesResult(); 118 | result.verts = verts; 119 | result.boundaryEdges = boundaryEdges; 120 | result.boundaryFaces = boundaryFaces; 121 | result.centerEdges = centerEdges; 122 | result.centerFaces = centerFaces; 123 | return result; 124 | } 125 | 126 | } // namespace Ops 127 | } // namespace Aoba 128 | -------------------------------------------------------------------------------- /src/Ops/Modify/MakeFace.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | #include 4 | 5 | namespace Aoba { 6 | namespace Ops { 7 | 8 | Core::Face* MakeFace(Core::Mesh* m, std::vector edges) { 9 | 10 | if(edges.size() < 3) { 11 | throw std::invalid_argument("Face must have at least 3 distinct edges."); 12 | } 13 | 14 | // arrays to hold ordered edges and verts. 15 | std::vector loopEdges = std::vector(); 16 | std::vector loopVerts = std::vector(); 17 | 18 | loopEdges.push_back(edges.at(0)); 19 | if(edges.at(0)->IsBoundary()) { 20 | if(edges.at(0)->Loops().at(0)->LoopVert() == edges.at(0)->V1()) { 21 | loopVerts.push_back(edges.at(0)->V2()); 22 | loopVerts.push_back(edges.at(0)->V1()); 23 | } else { 24 | loopVerts.push_back(edges.at(0)->V1()); 25 | loopVerts.push_back(edges.at(0)->V2()); 26 | } 27 | } else { 28 | loopVerts.push_back(edges.at(0)->V1()); 29 | loopVerts.push_back(edges.at(0)->V2()); 30 | } 31 | 32 | edges.erase(edges.begin()); // remove first edge from input edges 33 | 34 | // find edge which contains last vert in the loop 35 | // push that edge into the loop 36 | // add another vert into the loop which is the other vert of the found edge 37 | while(!edges.empty()) { 38 | bool found = false; 39 | for(auto it = edges.begin(); it != edges.end(); ++it) { 40 | if((*it)->V1() == loopVerts.back()) { 41 | loopEdges.push_back((*it)); 42 | loopVerts.push_back((*it)->V2()); 43 | found = true; 44 | edges.erase(it); 45 | break; 46 | } else if((*it)->V2() == loopVerts.back()) { 47 | loopEdges.push_back((*it)); 48 | loopVerts.push_back((*it)->V1()); 49 | found = true; 50 | edges.erase(it); 51 | break; 52 | } 53 | } 54 | if(!found) { 55 | throw std::invalid_argument("Specified edges do not form a true circuit"); 56 | } 57 | } 58 | if(loopVerts.at(0) != loopVerts.back()) { 59 | throw std::invalid_argument("Specified edges do not form a true circuit"); 60 | } 61 | loopVerts.pop_back(); 62 | 63 | // use flags to check for duplicates 64 | bool duplicate = false; 65 | int32_t MARKED = 1 << 0; 66 | for(Core::Vert* v : loopVerts) { 67 | if(v->flagsIntern & MARKED) { 68 | duplicate = true; 69 | break; 70 | } 71 | v->flagsIntern = MARKED; 72 | } 73 | for(Core::Vert* v : loopVerts) { 74 | v->flagsIntern = 0; 75 | } 76 | 77 | if(duplicate) { 78 | throw std::invalid_argument("Duplicate verts found"); 79 | } 80 | 81 | Core::Face* newf = new Core::Face(); 82 | Core::Loop* newl = new Core::Loop(); 83 | Core::MakeLoop(loopEdges, loopVerts, newl); 84 | Core::MakeFace(newl, newf); 85 | return newf; 86 | } 87 | 88 | } // namespace Ops 89 | } // namespace Aoba 90 | -------------------------------------------------------------------------------- /src/Ops/Modify/RecalculateFaceNormals.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | void RecalculateFaceNormals(Core::Mesh* m, const std::vector& faces) { 7 | for(Core::Face* face : faces) { 8 | face->NormalUpdate(); 9 | } 10 | } 11 | 12 | } // namespace Ops 13 | } // namespace Aoba 14 | -------------------------------------------------------------------------------- /src/Ops/Modify/ReverseFaceOrder.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | void ReverseFaceOrder(Core::Mesh* m, const std::vector& faces) { 7 | // TODO: check if all elements belong to the mesh 8 | 9 | for(Core::Face* f : faces) { 10 | f->NormalFlip(); 11 | } 12 | 13 | } 14 | 15 | } // namespace Ops 16 | } // namespace Aoba 17 | -------------------------------------------------------------------------------- /src/Ops/Modify/SplitEdges.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace Aoba { 7 | namespace Ops { 8 | 9 | const SplitEdgesResult SplitEdges( 10 | Core::Mesh* m, const std::vector& edges, unsigned cuts, const std::vector& ratios) { 11 | if((std::size_t)cuts + 1 != ratios.size()) { 12 | throw std::invalid_argument("Invalid number of ratios given"); 13 | } 14 | 15 | // todo: check that the sum of all ratios == 1 16 | float sum = 0; 17 | for(float ratio : ratios) { 18 | sum += ratio; 19 | } 20 | if(sum < 0.999 || sum > 1.001) { 21 | throw std::invalid_argument("ratios must add up to 1.0"); 22 | } 23 | 24 | std::vector newEdges = std::vector(); 25 | newEdges.reserve(edges.size() * cuts); 26 | std::vector newVerts = std::vector(); 27 | newVerts.reserve(edges.size() * cuts); 28 | 29 | for(Core::Edge* edge : edges) { 30 | Math::Vec3 dist = edge->V2()->co - edge->V1()->co; 31 | Core::Edge* edgeToSplit = edge; 32 | float ratioSum = 0; 33 | for(unsigned i = 0; i < cuts; ++i) { 34 | ratioSum += ratios.at(i); 35 | Core::Edge* newe = new Core::Edge(); 36 | Core::Vert* newv = new Core::Vert(); 37 | newv->co = edge->V1()->co + (ratioSum * dist); 38 | Core::EdgeSplit(edgeToSplit, edgeToSplit->Verts().at(1), newe, newv); 39 | edgeToSplit = newe; 40 | newEdges.push_back(newe); 41 | newVerts.push_back(newv); 42 | } 43 | } 44 | 45 | SplitEdgesResult result = SplitEdgesResult(); 46 | result.verts = newVerts; 47 | result.edges = newEdges; 48 | return result; 49 | } 50 | 51 | } // namespace Ops 52 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Modify/SubdivideSimple.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const SubdivideResult SubdivideSimple( 7 | Core::Mesh* m, const std::vector& edges, const std::vector& faces) { 8 | // flags 9 | const int32_t EDGE_SPLIT = 1 << 0; 10 | const int32_t EDGE_NEW = 1 << 1; 11 | const int32_t VERT_NEW = 1 << 0; 12 | 13 | SubdivideResult result = SubdivideResult(); 14 | result.edges = std::vector(); 15 | result.edgeVerts = std::vector(); 16 | result.faceVerts = std::vector(); 17 | result.faces = std::vector(); 18 | 19 | // subdivide the edges of all input faces 20 | for(Core::Face* face : faces) { 21 | for(Core::Edge* edge : face->Edges()) { 22 | if(edge->flagsIntern == 0) { 23 | Core::Vert* newv = new Core::Vert(); 24 | Core::Edge* newe = new Core::Edge(); 25 | newv->co = (edge->V1()->co + edge->V2()->co) / 2; 26 | 27 | Core::EdgeSplit(edge, edge->V1(), newe, newv); 28 | newv->flagsIntern = VERT_NEW; 29 | newe->flagsIntern = EDGE_NEW; 30 | edge->flagsIntern = EDGE_SPLIT; 31 | 32 | result.edges.push_back(newe); 33 | result.edges.push_back(edge); 34 | result.edgeVerts.push_back(newv); 35 | } 36 | } 37 | } 38 | 39 | // subdivide other input edges 40 | for(Core::Edge* edge : edges) { 41 | if(!(edge->flagsIntern & EDGE_SPLIT)) { 42 | Core::Vert* newv = new Core::Vert(); 43 | Core::Edge* newe = new Core::Edge(); 44 | newv->co = (edge->V1()->co + edge->V2()->co) / 2; 45 | 46 | Core::EdgeSplit(edge, edge->V1(), newe, newv); 47 | // no need to set tags here, as this is not used by subdivided faces 48 | 49 | result.edges.push_back(newe); 50 | result.edges.push_back(edge); 51 | result.edgeVerts.push_back(newv); 52 | } 53 | } 54 | 55 | // subdivide faces 56 | for(Core::Face* face : faces) { 57 | // find newly created verts which must be connected to face center. this list should be ordered 58 | std::vector verts = face->Verts([VERT_NEW](const Core::Vert* const v) { 59 | return (v->flagsIntern == VERT_NEW); 60 | }); 61 | // find old verts used for center calculation 62 | std::vector oldVerts = face->Verts([VERT_NEW](const Core::Vert* const v) { 63 | return (v->flagsIntern != VERT_NEW); 64 | }); 65 | 66 | // calculate original face center 67 | Math::Vec3 center = Math::Vec3(); 68 | for(Core::Vert* oldVert : oldVerts) { 69 | center += oldVert->co; 70 | } 71 | center = center / static_cast(oldVerts.size()); 72 | 73 | // initial face split: 74 | Core::Edge* newe = new Core::Edge(); 75 | Core::Face* newf = new Core::Face(); 76 | Core::ManifoldMakeEdge(verts.at(0), verts.at(1), face, newe, newf); 77 | 78 | Core::Edge* split = new Core::Edge(); 79 | Core::Vert* centerVert = new Core::Vert(); 80 | Core::EdgeSplit(newe, verts.at(0), split, centerVert); 81 | centerVert->co = center; 82 | 83 | result.edges.push_back(newe); 84 | result.edges.push_back(split); 85 | result.faceVerts.push_back(centerVert); 86 | 87 | Core::Face* faceToSplit = face; 88 | if(face->Loops().size() == 4) { 89 | faceToSplit = newf; 90 | result.faces.push_back(face); 91 | } else { 92 | result.faces.push_back(newf); 93 | } 94 | 95 | for(std::size_t idx = 2; idx < verts.size(); ++idx) { 96 | Core::Edge* newSplit = new Core::Edge(); 97 | Core::Face* newFace = new Core::Face(); 98 | result.faces.push_back(newFace); 99 | result.edges.push_back(newSplit); 100 | Core::ManifoldMakeEdge(centerVert, verts.at(idx), faceToSplit, newSplit, newFace); 101 | if(newFace->Loops().size() > 4) { 102 | faceToSplit = newFace; 103 | } 104 | } 105 | } 106 | 107 | // clear flags 108 | for(Core::Edge* edge : result.edges) { 109 | edge->flagsIntern = 0; 110 | } 111 | 112 | for(Core::Vert* vert : result.edgeVerts) { 113 | vert->flagsIntern = 0; 114 | } 115 | 116 | return result; 117 | } 118 | 119 | } // namespace Ops 120 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Modify/TriangulateFaces.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Modify.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const TriangulateFacesResult TriangulateFaces(Core::Mesh* m, const std::vector& faces) { 7 | // TODO: use a more advanced triangulation algorithm, which can handle concave cases and complex polygons 8 | // this currently generates a simple fan triangulation from a single vertex 9 | std::vector newEdges = std::vector(); 10 | std::vector triangularFaces = std::vector(); 11 | 12 | for(Core::Face* face : faces) { 13 | if(face->Loops().size() > 3) { 14 | // face not a triangle, mark for splitting 15 | std::vector facesToSplit = {face}; 16 | 17 | while(!facesToSplit.empty()) { 18 | // take the last face in list of splittable faces 19 | Core::Face* current = facesToSplit.back(); 20 | facesToSplit.pop_back(); 21 | 22 | // split face using manifoldMakeEdge 23 | Core::Vert* v1 = current->Loops().at(0)->LoopVert(); 24 | Core::Vert* v2 = current->Loops().at(2)->LoopVert(); 25 | Core::Edge* newe = new Core::Edge(); 26 | Core::Face* newf = new Core::Face(); 27 | Core::ManifoldMakeEdge(v1, v2, current, newe, newf); 28 | newEdges.push_back(newe); 29 | 30 | if(current->Loops().size() > 3) { 31 | facesToSplit.push_back(current); 32 | } else { 33 | triangularFaces.push_back(current); 34 | } 35 | if(newf->Loops().size() > 3) { 36 | facesToSplit.push_back(newf); 37 | } else { 38 | triangularFaces.push_back(newf); 39 | } 40 | } 41 | } else { 42 | // face already a triangle 43 | triangularFaces.push_back(face); 44 | } 45 | } 46 | 47 | TriangulateFacesResult result = TriangulateFacesResult(); 48 | result.edges = newEdges; 49 | result.faces = triangularFaces; 50 | 51 | return result; 52 | } 53 | 54 | } // namespace Ops 55 | } // namespace Aoba 56 | -------------------------------------------------------------------------------- /src/Ops/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsimic/AobaAPI/5bb17a1055cd1621eebeb91844c2f8b4a3572cb8/src/Ops/README.md -------------------------------------------------------------------------------- /src/Ops/Select/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/SelectExtend.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/SelectReduce.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/SelectFromEdges.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/SelectFromFaces.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/SelectFromVerts.cpp 9 | ) -------------------------------------------------------------------------------- /src/Ops/Select/SelectExtend.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Select.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const SelectResult SelectExtend(Core::Mesh* m, const std::vector& verts, 7 | const std::vector& edges, const std::vector& faces, bool faceStep) { 8 | // flags 9 | const int32_t SELECTED_FACE = 1 << 0; 10 | const int32_t SELECTED_VERT = 1 << 0; 11 | const int32_t SELECTED_EDGE = 1 << 0; 12 | 13 | std::vector selectedVerts = std::vector(); 14 | std::vector selectedEdges = std::vector(); 15 | std::vector selectedFaces = std::vector(); 16 | std::vector newSelectedVerts = std::vector(); 17 | std::vector adjecentFaces = std::vector(); 18 | 19 | // mark input verts as selected 20 | for(Core::Vert* vert : verts) { 21 | if(!(vert->flagsIntern & SELECTED_VERT)) { 22 | vert->flagsIntern = SELECTED_VERT; 23 | selectedVerts.push_back(vert); 24 | } 25 | } 26 | 27 | // mark input edges and their verts as selected 28 | for(Core::Edge* edge : edges) { 29 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 30 | edge->flagsIntern = SELECTED_EDGE; 31 | selectedEdges.push_back(edge); 32 | for(Core::Vert* vert : edge->Verts()) { 33 | if(!(vert->flagsIntern & SELECTED_VERT)) { 34 | vert->flagsIntern = SELECTED_VERT; 35 | selectedVerts.push_back(vert); 36 | } 37 | } 38 | } 39 | } 40 | 41 | // mark input faces, their edges and verts as selected 42 | for(Core::Face* face : faces) { 43 | if(!(face->flagsIntern & SELECTED_FACE)) { 44 | face->flagsIntern = SELECTED_FACE; 45 | selectedFaces.push_back(face); 46 | for(Core::Edge* edge : face->Edges()) { 47 | edge->flagsIntern = SELECTED_EDGE; 48 | selectedEdges.push_back(edge); 49 | } 50 | for(Core::Vert* vert : face->Verts()) { 51 | if(!(vert->flagsIntern & SELECTED_VERT)) { 52 | vert->flagsIntern = SELECTED_VERT; 53 | selectedVerts.push_back(vert); 54 | } 55 | } 56 | } 57 | } 58 | 59 | // step using edges 60 | for(Core::Vert* vert : selectedVerts) { 61 | for(Core::Edge* edge : vert->Edges()) { 62 | if(!(edge->flagsIntern % SELECTED_EDGE)) { 63 | edge->flagsIntern = SELECTED_EDGE; 64 | selectedEdges.push_back(edge); 65 | Core::Vert* other = edge->Other(vert); 66 | if(!(other->flagsIntern & SELECTED_VERT)) { 67 | other->flagsIntern = SELECTED_VERT; 68 | newSelectedVerts.push_back(other); 69 | } 70 | } 71 | } 72 | for(Core::Face* face : vert->Faces()) { 73 | if(!(face->flagsIntern % SELECTED_FACE)) { 74 | adjecentFaces.push_back(face); 75 | } 76 | } 77 | } 78 | 79 | // concat new selected verts to selected verts 80 | selectedVerts.insert(selectedVerts.end(), newSelectedVerts.begin(), newSelectedVerts.end()); 81 | 82 | // check adjecent faces 83 | for(Core::Face* face : adjecentFaces) { 84 | // if using face step, mark all edges and verts as selected 85 | if(faceStep) { 86 | for(Core::Edge* edge : face->Edges()) { 87 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 88 | edge->flagsIntern = SELECTED_EDGE; 89 | selectedEdges.push_back(edge); 90 | } 91 | } 92 | for(Core::Vert* vert : face->Verts()) { 93 | if(!(vert->flagsIntern & SELECTED_VERT)) { 94 | vert->flagsIntern = SELECTED_VERT; 95 | selectedVerts.push_back(vert); 96 | } 97 | } 98 | } else { 99 | bool selected = true; 100 | for(Core::Edge* edge : face->Edges()) { 101 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 102 | selected = false; 103 | break; 104 | } 105 | } 106 | if(selected) { 107 | selectedFaces.push_back(face); 108 | } 109 | } 110 | } 111 | 112 | // clear flags, return 113 | for(Core::Edge* edge : selectedEdges) { 114 | edge->flagsIntern = 0; 115 | } 116 | for(Core::Face* face : selectedFaces) { 117 | face->flagsIntern = 0; 118 | } 119 | for(Core::Vert* vert : selectedVerts) { 120 | vert->flagsIntern = 0; 121 | } 122 | 123 | SelectResult result = SelectResult(); 124 | result.edges = selectedEdges; 125 | result.verts = selectedVerts; 126 | result.faces = selectedFaces; 127 | 128 | return result; 129 | } 130 | 131 | } // namespace Ops 132 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Select/SelectFromEdges.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Select.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const SelectResult SelectFromEdges(Core::Mesh* m, const std::vector& edges) { 7 | // flags 8 | const int32_t ADJECENT_FACE = 1 << 0; 9 | const int32_t SELECTED_VERT = 1 << 0; 10 | const int32_t SELECTED_EDGE = 1 << 0; 11 | 12 | std::vector verts = std::vector(); 13 | std::vector faces = std::vector(); 14 | std::vector adjecentFaces = std::vector(); 15 | 16 | // iterate over all edges, mark them as selected, select verts 17 | // add all adjecent faces to a list 18 | for(Core::Edge* edge : edges) { 19 | edge->flagsIntern = SELECTED_EDGE; 20 | for(Core::Vert* vert : edge->Verts()) { 21 | if(!(vert->flagsIntern & SELECTED_VERT)) { 22 | vert->flagsIntern = SELECTED_VERT; 23 | verts.push_back(vert); 24 | } 25 | } 26 | for(Core::Face* face : edge->Faces()) { 27 | if(!(face->flagsIntern & ADJECENT_FACE)) { 28 | face->flagsIntern = ADJECENT_FACE; 29 | adjecentFaces.push_back(face); 30 | } 31 | } 32 | } 33 | 34 | // check all adjecent faces 35 | // all loop's edges must be selected to mark the face as selected 36 | // remove flags from all faces 37 | for(Core::Face* face : adjecentFaces) { 38 | bool selected = true; 39 | for(Core::Edge* edge : face->Edges()) { 40 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 41 | selected = false; 42 | break; 43 | } 44 | } 45 | if(selected) { 46 | faces.push_back(face); 47 | } 48 | face->flagsIntern = 0; 49 | } 50 | 51 | // remove flags from vert 52 | for(Core::Vert* vert : verts) { 53 | vert->flagsIntern = 0; 54 | } 55 | 56 | // remove flags from edges 57 | for(Core::Edge* edge : edges) { 58 | edge->flagsIntern = 0; 59 | } 60 | 61 | SelectResult result = SelectResult(); 62 | result.edges = edges; 63 | result.faces = faces; 64 | result.verts = verts; 65 | 66 | return result; 67 | 68 | // TODO: if an edge is not inside input edge, but some of its adjecemt edges are selected on either side 69 | // should it be selected as well because both of it's verts are selected trough input edges 70 | // currently it is not selected 71 | } 72 | 73 | } // namespace Ops 74 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Select/SelectFromFaces.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Select.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const SelectResult SelectFromFaces(Core::Mesh* m, const std::vector& faces) { 7 | // flags 8 | const int32_t SELECTED_EDGE = 1 << 0; 9 | const int32_t SELECTED_VERT = 1 << 0; 10 | 11 | std::vector verts = std::vector(); 12 | std::vector edges = std::vector(); 13 | 14 | // iterate over input faces and select all edges exactly once 15 | for(Core::Face* face : faces) { 16 | for(Core::Edge* edge : face->Edges()) { 17 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 18 | edge->flagsIntern = SELECTED_EDGE; 19 | edges.push_back(edge); 20 | } 21 | } 22 | } 23 | 24 | // iterate over all edges, remove flags and select all verts exactly once 25 | for(Core::Edge* edge : edges) { 26 | for(Core::Vert* vert : edge->Verts()) { 27 | if(!(vert->flagsIntern & SELECTED_VERT)) { 28 | vert->flagsIntern = SELECTED_VERT; 29 | verts.push_back(vert); 30 | } 31 | } 32 | edge->flagsIntern = 0; 33 | } 34 | 35 | // remove flags from vert 36 | for(Core::Vert* vert : verts) { 37 | vert->flagsIntern = 0; 38 | } 39 | 40 | SelectResult result = SelectResult(); 41 | result.edges = edges; 42 | result.faces = faces; 43 | result.verts = verts; 44 | 45 | return result; 46 | 47 | // TODO: if a face is not inside input faces, but all of its adjecemt faces are selected 48 | // should it be selected as well because all of it's edges are selected trough input faces 49 | // currently it is not selected 50 | } 51 | 52 | } // namespace Ops 53 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Select/SelectFromVerts.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Select.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const SelectResult SelectFromVerts(Core::Mesh* m, const std::vector& verts) { 7 | // flags 8 | const int32_t ADJECENT_FACE = 1 << 0; 9 | const int32_t SELECTED_VERT = 1 << 0; 10 | const int32_t SELECTED_EDGE = 1 << 0; 11 | const int32_t ADJECENT_EDGE = 1 << 1; 12 | 13 | std::vector edges = std::vector(); 14 | std::vector adjecentEdges = std::vector(); 15 | std::vector faces = std::vector(); 16 | std::vector adjecentFaces = std::vector(); 17 | 18 | // iterate over all verts, mark them as selected 19 | // add all adjecent edges to a list 20 | for(Core::Vert* vert : verts) { 21 | vert->flagsIntern = SELECTED_VERT; 22 | 23 | for(Core::Edge* edge : vert->Edges()) { 24 | if(!(edge->flagsIntern & ADJECENT_EDGE)) { 25 | edge->flagsIntern = ADJECENT_EDGE; 26 | adjecentEdges.push_back(edge); 27 | } 28 | } 29 | } 30 | 31 | // check all adjecent edges 32 | // both verts must be selected to mark the edge as selected 33 | // if edge is selected, add it's adjecent faces to a list. 34 | for(Core::Edge* edge : adjecentEdges) { 35 | bool selected = true; 36 | for(Core::Vert* vert : edge->Verts()) { 37 | if(!(vert->flagsIntern & SELECTED_VERT)) { 38 | selected = false; 39 | break; 40 | } 41 | } 42 | if(selected) { 43 | edges.push_back(edge); 44 | edge->flagsIntern = SELECTED_EDGE; 45 | for(Core::Face* face : edge->Faces()) { 46 | if(!(face->flagsIntern & ADJECENT_FACE)) { 47 | face->flagsIntern = ADJECENT_FACE; 48 | adjecentFaces.push_back(face); 49 | } 50 | } 51 | } else { 52 | edge->flagsIntern = 0; 53 | } 54 | } 55 | 56 | // check all adjecent faces 57 | // all loop's edges must be selected to mark the face as selected 58 | // remove flags from all faces 59 | for(Core::Face* face : adjecentFaces) { 60 | bool selected = true; 61 | for(Core::Edge* edge : face->Edges()) { 62 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 63 | selected = false; 64 | break; 65 | } 66 | } 67 | if(selected) { 68 | faces.push_back(face); 69 | } 70 | face->flagsIntern = 0; 71 | } 72 | 73 | // remove flags from vert 74 | for(Core::Vert* vert : verts) { 75 | vert->flagsIntern = 0; 76 | } 77 | 78 | // remove flags from edges 79 | for(Core::Edge* edge : edges) { 80 | edge->flagsIntern = 0; 81 | } 82 | 83 | SelectResult result = SelectResult(); 84 | result.edges = edges; 85 | result.faces = faces; 86 | result.verts = verts; 87 | 88 | return result; 89 | } 90 | 91 | } // namespace Ops 92 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Select/SelectReduce.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Select.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | const SelectResult SelectReduce(Core::Mesh* m, const std::vector& verts, 7 | const std::vector& edges, const std::vector& faces, bool faceStep) { 8 | 9 | // mark all input edges, verts and faces as selected 10 | // flags 11 | const int32_t SELECTED_FACE = 1 << 0; 12 | const int32_t SELECTED_VERT = 1 << 0; 13 | const int32_t SELECTED_EDGE = 1 << 0; 14 | const int32_t DESELECTED_FACE = 1 << 1; 15 | const int32_t DESELECTED_VERT = 1 << 1; 16 | const int32_t DESELECTED_EDGE = 1 << 1; 17 | 18 | std::vector selectedVerts = std::vector(); 19 | std::vector selectedEdges = std::vector(); 20 | std::vector selectedFaces = std::vector(); 21 | 22 | // mark input verts as selected 23 | for(Core::Vert* vert : verts) { 24 | if(!(vert->flagsIntern & SELECTED_VERT)) { 25 | vert->flagsIntern = SELECTED_VERT; 26 | selectedVerts.push_back(vert); 27 | } 28 | } 29 | 30 | // mark input edges and their verts as selected 31 | for(Core::Edge* edge : edges) { 32 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 33 | edge->flagsIntern = SELECTED_EDGE; 34 | selectedEdges.push_back(edge); 35 | for(Core::Vert* vert : edge->Verts()) { 36 | if(!(vert->flagsIntern & SELECTED_VERT)) { 37 | vert->flagsIntern = SELECTED_VERT; 38 | selectedVerts.push_back(vert); 39 | } 40 | } 41 | } 42 | } 43 | 44 | // mark input faces, their edges and verts as selected 45 | for(Core::Face* face : faces) { 46 | if(!(face->flagsIntern & SELECTED_FACE)) { 47 | face->flagsIntern = SELECTED_FACE; 48 | selectedFaces.push_back(face); 49 | for(Core::Edge* edge : face->Edges()) { 50 | edge->flagsIntern = SELECTED_EDGE; 51 | selectedEdges.push_back(edge); 52 | } 53 | for(Core::Vert* vert : face->Verts()) { 54 | if(!(vert->flagsIntern & SELECTED_VERT)) { 55 | vert->flagsIntern = SELECTED_VERT; 56 | selectedVerts.push_back(vert); 57 | } 58 | } 59 | } 60 | } 61 | 62 | // find boundary verts 63 | std::vector boundaryVerts = std::vector(); 64 | if(!faceStep) { 65 | // boundary vert is one which does not have all it's edges selected 66 | for(Core::Vert* vert : selectedVerts) { 67 | for(Core::Edge* edge : vert->Edges()) { 68 | if(!(edge->flagsIntern & SELECTED_EDGE)) { 69 | boundaryVerts.push_back(vert); 70 | vert->flagsIntern += DESELECTED_VERT; 71 | } 72 | } 73 | } 74 | } else { 75 | // boundary vert is one which does not have all it's faces selected 76 | std::vector boundaryVerts = std::vector(); 77 | for(Core::Vert* vert : selectedVerts) { 78 | for(Core::Face* face : vert->Faces()) { 79 | if(!(face->flagsIntern & SELECTED_FACE)) { 80 | boundaryVerts.push_back(vert); 81 | vert->flagsIntern += DESELECTED_VERT; 82 | } 83 | } 84 | } 85 | } 86 | 87 | // deselect boundary vert's edges which were selected previously 88 | for(Core::Vert* vert : boundaryVerts) { 89 | for(Core::Edge* edge : vert->Edges()) { 90 | if(edge->flagsIntern & SELECTED_EDGE) { 91 | edge->flagsIntern = SELECTED_EDGE + DESELECTED_EDGE; 92 | } 93 | } 94 | } 95 | 96 | // deselect each face which had one of it's edges deselected 97 | for(Core::Face* face : faces) { 98 | for(Core::Edge* edge : face->Edges()) { 99 | if(edge->flagsIntern & DESELECTED_EDGE) { 100 | face->flagsIntern += DESELECTED_EDGE; 101 | break; 102 | } 103 | } 104 | } 105 | 106 | // create result, clear flags, return 107 | 108 | std::vector resultVerts = std::vector(); 109 | std::vector resultEdges = std::vector(); 110 | std::vector resultFaces = std::vector(); 111 | 112 | for(Core::Vert* vert : selectedVerts) { 113 | if(!(vert->flagsIntern & DESELECTED_VERT)) { 114 | resultVerts.push_back(vert); 115 | } 116 | vert->flagsIntern = 0; 117 | } 118 | 119 | for(Core::Edge* edge : selectedEdges) { 120 | if(!(edge->flagsIntern & DESELECTED_EDGE)) { 121 | resultEdges.push_back(edge); 122 | } 123 | edge->flagsIntern = 0; 124 | } 125 | 126 | for(Core::Face* face : selectedFaces) { 127 | if(!(face->flagsIntern & DESELECTED_FACE)) { 128 | resultFaces.push_back(face); 129 | } 130 | face->flagsIntern = 0; 131 | } 132 | 133 | 134 | SelectResult result = SelectResult(); 135 | result.edges = resultEdges; 136 | result.faces = resultFaces; 137 | result.verts = resultVerts; 138 | return result; 139 | } 140 | 141 | } // namespace Ops 142 | } // namespace Aoba -------------------------------------------------------------------------------- /src/Ops/Transform/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${PROJECT_NAME} 3 | PRIVATE 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Rotate.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Scale.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Transform.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/Translate.cpp 8 | ) -------------------------------------------------------------------------------- /src/Ops/Transform/Rotate.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Transform.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | void Rotate(Core::Mesh* m, const std::vector& verts, const Math::Vec3& center, const Math::Mat3& mat) { 7 | for(auto it = verts.begin(); it != verts.end(); ++it) { 8 | (*it)->co -= center; 9 | (*it)->co = mat * (*it)->co; 10 | (*it)->co += center; 11 | 12 | // TODO: check if coordinate is a part of the mesh 13 | // TODO: can at one point run this in paralel 14 | } 15 | } 16 | 17 | } // namespace Ops 18 | } // namespace Aoba 19 | -------------------------------------------------------------------------------- /src/Ops/Transform/Scale.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Transform.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | void Scale(Core::Mesh* m, const std::vector& verts, const Math::Vec3& center, const Math::Vec3& vec) { 7 | for(auto it = verts.begin(); it != verts.end(); ++it) { 8 | (*it)->co -= center; 9 | (*it)->co.x *= vec.x; 10 | (*it)->co.y *= vec.y; 11 | (*it)->co.z *= vec.z; 12 | (*it)->co += center; 13 | 14 | // TODO: check if coordinate is a part of the mesh 15 | // TODO: can at one point run this in paralel 16 | } 17 | } 18 | 19 | } // namespace Ops 20 | } // namespace Aoba 21 | -------------------------------------------------------------------------------- /src/Ops/Transform/Transform.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Transform.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | void Transform(Core::Mesh* m, const std::vector& verts, const Math::Mat4 matrix) { 7 | Math::Mat3 transformMatrix = Math::Mat3(); 8 | for(int i = 0; i < 3; i++) { 9 | for(int j = 0; j < 3; j++) { 10 | transformMatrix(i, j) = matrix(i, j); 11 | } 12 | } 13 | 14 | for(Core::Vert* v : verts) { 15 | v->co = transformMatrix * v->co; 16 | v->co.x += matrix(0, 3); 17 | v->co.y += matrix(1, 3); 18 | v->co.z += matrix(2, 3); 19 | } 20 | } 21 | 22 | } // namespace Ops 23 | } // namespace Aoba 24 | -------------------------------------------------------------------------------- /src/Ops/Transform/Translate.cpp: -------------------------------------------------------------------------------- 1 | #include "AobaAPI/Ops/Transform.hpp" 2 | 3 | namespace Aoba { 4 | namespace Ops { 5 | 6 | void Translate(Core::Mesh* m, const std::vector& verts, const Math::Vec3& vec) { 7 | for(auto it = verts.begin(); it != verts.end(); ++it) { 8 | // TODO: check if coordinate is a part of the mesh 9 | // TODO: can at one point run this in paralel 10 | ((*it)->co) += vec; 11 | } 12 | } 13 | 14 | } // namespace Ops 15 | } // namespace Aoba 16 | --------------------------------------------------------------------------------