├── assets └── images │ ├── banner.png │ ├── input.png │ ├── title.png │ ├── accuracy.gif │ ├── italy-66.png │ └── italy-100.png ├── TestBed ├── assets │ ├── images │ │ ├── bat.png │ │ ├── dog.png │ │ ├── human.png │ │ ├── circle.png │ │ ├── marioBig.png │ │ ├── square.png │ │ ├── silhouette.png │ │ └── small-square.png │ └── fonts │ │ └── LinBiolinum.ttf ├── FileSystem.hpp ├── main.cpp ├── FileSystem.cpp ├── Example.hpp └── Example.cpp ├── cmake ├── ResolveSFML.cmake ├── EnableTests.cmake ├── EnableClangTidy.cmake ├── Sources.cmake ├── FindClangTidy.cmake ├── CompilerFlags.cmake ├── CheckCompilerFlags.cmake └── FindSFML.cmake ├── include ├── detail │ ├── BoundingBoxBuilder.hpp │ ├── Math.hpp │ ├── CoordinateTransformer.hpp │ ├── PolygonBuilder.hpp │ ├── PolygonPartitioner.hpp │ ├── Triangulator.hpp │ └── ContourBuilder.hpp ├── Types.hpp └── Module.hpp ├── .gitignore ├── src ├── detail │ ├── CoordinateTransformer.cpp │ ├── BoundingBoxBuilder.cpp │ ├── Math.cpp │ ├── PolygonBuilder.cpp │ ├── PolygonPartitioner.cpp │ ├── Triangulator.cpp │ └── ContourBuilder.cpp └── Module.cpp ├── LICENSE ├── CMakeLists.txt ├── configure.sh ├── README.md ├── .clang-format └── .clang-tidy /assets/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/assets/images/banner.png -------------------------------------------------------------------------------- /assets/images/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/assets/images/input.png -------------------------------------------------------------------------------- /assets/images/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/assets/images/title.png -------------------------------------------------------------------------------- /assets/images/accuracy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/assets/images/accuracy.gif -------------------------------------------------------------------------------- /assets/images/italy-66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/assets/images/italy-66.png -------------------------------------------------------------------------------- /TestBed/assets/images/bat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/bat.png -------------------------------------------------------------------------------- /TestBed/assets/images/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/dog.png -------------------------------------------------------------------------------- /assets/images/italy-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/assets/images/italy-100.png -------------------------------------------------------------------------------- /TestBed/assets/images/human.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/human.png -------------------------------------------------------------------------------- /TestBed/assets/images/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/circle.png -------------------------------------------------------------------------------- /TestBed/assets/images/marioBig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/marioBig.png -------------------------------------------------------------------------------- /TestBed/assets/images/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/square.png -------------------------------------------------------------------------------- /TestBed/assets/fonts/LinBiolinum.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/fonts/LinBiolinum.ttf -------------------------------------------------------------------------------- /TestBed/assets/images/silhouette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/silhouette.png -------------------------------------------------------------------------------- /TestBed/assets/images/small-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/HEAD/TestBed/assets/images/small-square.png -------------------------------------------------------------------------------- /TestBed/FileSystem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TESTBED_FILE_SYSTEM_HPP 2 | #define TESTBED_FILE_SYSTEM_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace TestBed { 8 | 9 | std::vector readFiles(const std::string& path, const std::string& ext); 10 | 11 | } /* namespace TestBed */ 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /cmake/ResolveSFML.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "[SFML] Resolving internal dependency") 2 | 3 | set(MIN_SFML_VERSION 2.0.0) 4 | 5 | find_package(SFML ${MIN_SFML_VERSION} REQUIRED system window graphics audio) 6 | 7 | list(APPEND EXTERNAL_LIBRARIES ${SFML_LIBRARIES}) 8 | 9 | message(STATUS "[SFML] Using ${SFML_VERSION}") 10 | -------------------------------------------------------------------------------- /cmake/EnableTests.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "Configuring target '${TESTER_NAME}'") 2 | 3 | set(MIN_SFML_VERSION 20400) 4 | 5 | if(SFML_VERSION LESS MIN_SFML_VERSION) 6 | MESSAGE(FATAL_ERROR "Testbed requires minimal SFML version '${MIN_SFML_VERSION}', is '${SFML_VERSION}'") 7 | endif() 8 | 9 | add_executable(${TESTER_NAME} ${TESTBED_SOURCE_FILES}) 10 | target_link_libraries (${TESTER_NAME} ${EXTERNAL_LIBRARIES} ${PROJECT_NAME}) 11 | -------------------------------------------------------------------------------- /cmake/EnableClangTidy.cmake: -------------------------------------------------------------------------------- 1 | function(enable_clangtidy) 2 | cmake_parse_arguments(ARG "" "" "SOURCES" ${ARGN}) 3 | 4 | message(STATUS "Configuring clang-tidy static analysis") 5 | 6 | # Attempt to find clang-tidy 7 | find_package(ClangTidy) 8 | 9 | if(ClangTidy_FOUND) 10 | # Add clang-tidy static analysis target 11 | add_custom_target(clang-tidy COMMAND ${RunClangTidy_EXECUTABLE} 12 | -header-filter=.* 13 | ${ARG_SOURCES} 14 | ) 15 | endif() 16 | endfunction() 17 | -------------------------------------------------------------------------------- /cmake/Sources.cmake: -------------------------------------------------------------------------------- 1 | # Source files 2 | 3 | file(GLOB_RECURSE LIB_SOURCE_FILES "${PROJECT_SOURCE_DIR}/${SOURCE_DIR}/*pp") 4 | file(GLOB_RECURSE LIB_HEADER_FILES "${PROJECT_SOURCE_DIR}/${INCLUDE_DIR}/*pp") 5 | file(GLOB_RECURSE TESTBED_SOURCE_FILES "${PROJECT_SOURCE_DIR}/${TESTBED_DIR}/*pp") 6 | 7 | foreach(F ${LIB_HEADER_FILES}) 8 | # Filter out non exposed header files 9 | if(NOT F MATCHES ".*/${IMPLEMENTATION_DIR}/*") 10 | list(APPEND LIB_EXPOSED_HEADER_FILES ${F}) 11 | endif() 12 | endforeach() 13 | -------------------------------------------------------------------------------- /include/detail/BoundingBoxBuilder.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_DETAIL_BOUNDING_BOX_BUILDER_HPP 2 | #define HITBOX_BUILDER_DETAIL_BOUNDING_BOX_BUILDER_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "Types.hpp" 9 | 10 | namespace HitboxBuilder { 11 | namespace Detail { 12 | 13 | class BoundingBoxBuilder { 14 | public: 15 | Polygon make(const Polygon& polygon) const; 16 | }; 17 | 18 | } /* namespace Detail */ 19 | } /* namespace HitboxBuilder */ 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Build directory 35 | build/ 36 | 37 | # Test assets 38 | TestBed/assets/images/test/ 39 | -------------------------------------------------------------------------------- /include/detail/Math.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_DETAIL_MATH_HPP 2 | #define HITBOX_BUILDER_DETAIL_MATH_HPP 3 | 4 | #include 5 | 6 | namespace HitboxBuilder { 7 | namespace Detail { 8 | namespace Math { 9 | 10 | template 11 | float vectorDot(const sf::Vector2& a, const sf::Vector2& b); 12 | 13 | template 14 | float pointDistance(const sf::Vector2& a, const sf::Vector2& b); 15 | 16 | } /* namespace Math */ 17 | } /* namespace Detail */ 18 | } /* namespace HitboxBuilder */ 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /TestBed/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "TestBed/Example.hpp" 5 | #include "TestBed/FileSystem.hpp" 6 | 7 | int main() { 8 | const auto fontPath = std::string("../TestBed/assets/fonts/"); 9 | const auto images = TestBed::readFiles("../TestBed/assets/images/", ".png"); 10 | const auto fonts = TestBed::readFiles(fontPath, ".ttf"); 11 | 12 | if (fonts.empty()) { 13 | throw std::runtime_error("No font found in '" + fontPath + "'"); 14 | } 15 | TestBed::Example example(fonts); 16 | 17 | example.run(images); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /include/detail/CoordinateTransformer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_DETAIL_COORDINATE_TRANSFORMER_HPP 2 | #define HITBOX_BUILDER_DETAIL_COORDINATE_TRANSFORMER_HPP 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "Types.hpp" 10 | 11 | namespace HitboxBuilder { 12 | namespace Detail { 13 | 14 | void toCarthesian(const sf::Sprite& sprite, Polygon& polygon); 15 | void toCarthesian(const sf::Sprite& sprite, std::vector& polygons); 16 | 17 | } /* namespace Detail */ 18 | } /* namespace HitboxBuilder */ 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/detail/CoordinateTransformer.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/CoordinateTransformer.hpp" 2 | 3 | namespace HitboxBuilder { 4 | namespace Detail { 5 | 6 | void toCarthesian(const sf::Sprite& sprite, Polygon& polygon) { 7 | const auto bound = sprite.getLocalBounds(); 8 | 9 | std::transform(polygon.begin(), polygon.end(), polygon.begin(), [&bound](auto& v) -> sf::Vector2i { 10 | return { v.x, -1 * (v.y - static_cast(bound.height)) }; 11 | }); 12 | } 13 | 14 | void toCarthesian(const sf::Sprite& sprite, std::vector& polygons) { 15 | for (auto& polygon : polygons) { 16 | toCarthesian(sprite, polygon); 17 | } 18 | } 19 | 20 | } /* namespace Detail */ 21 | } /* namespace HitboxBuilder */ 22 | -------------------------------------------------------------------------------- /include/Types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_TYPES_HPP 2 | #define HITBOX_BUILDER_TYPES_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace HitboxBuilder { 10 | 11 | using Polygon = std::vector; 12 | 13 | class Hitbox { 14 | public: 15 | Hitbox(std::vector body, Polygon bound) 16 | : _body(std::move(body)) 17 | , _bound(std::move(bound)) {} 18 | 19 | const std::vector& body() const { 20 | return _body; 21 | } 22 | const Polygon& bound() const { 23 | return _bound; 24 | } 25 | 26 | private: 27 | std::vector _body; 28 | Polygon _bound; 29 | }; 30 | 31 | } /* namespace HitboxBuilder */ 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/detail/BoundingBoxBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/BoundingBoxBuilder.hpp" 2 | 3 | #include 4 | 5 | namespace HitboxBuilder { 6 | namespace Detail { 7 | 8 | Polygon BoundingBoxBuilder::make(const Polygon& polygon) const { 9 | sf::Vector2i tl = polygon.front(), br = polygon.front(); 10 | 11 | for (const auto& v : polygon) { 12 | tl.x = std::min(tl.x, v.x); 13 | tl.y = std::min(tl.y, v.y); 14 | br.x = std::max(br.x, v.x); 15 | br.y = std::max(br.y, v.y); 16 | } 17 | 18 | return Polygon{ 19 | sf::Vector2i{ tl.x, tl.y }, 20 | sf::Vector2i{ tl.x, br.y }, 21 | sf::Vector2i{ br.x, br.y }, 22 | sf::Vector2i{ br.x, tl.y }, 23 | }; 24 | } 25 | 26 | } /* namespace Detail */ 27 | } /* namespace HitboxBuilder */ 28 | -------------------------------------------------------------------------------- /include/Module.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_MODULE_HPP 2 | #define HITBOX_BUILDER_MODULE_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "Types.hpp" 9 | 10 | namespace HitboxBuilder { 11 | 12 | //! Initialize the hitbox builder module. 13 | void init(); 14 | 15 | //! Factory function that creates a hitbox from the given sprite. 16 | //! @param sprite The sprite to build the hitbox from. 17 | //! @param accuracy a number ranging from 0 to 100. 18 | //! @param isCartesian A boolean value that defines where to put the origin. 19 | //! [bottom, left] if @c true, [top, left] if @c false. 20 | //! @return The hitbox matching the sprite. 21 | Hitbox make(const sf::Sprite& sprite, size_t accuracy, bool isCartesian); 22 | 23 | } /* namespace HitboxBuilder */ 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /TestBed/FileSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "TestBed/FileSystem.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace TestBed { 8 | 9 | std::vector readFiles(const std::string& path, const std::string& ext) { 10 | DIR* dir; 11 | struct dirent* entry; 12 | std::vector files; 13 | 14 | if ((dir = opendir(path.c_str())) == nullptr) { 15 | std::perror(""); 16 | return files; 17 | } 18 | 19 | while ((entry = readdir(dir)) != nullptr) { 20 | std::string filename(entry->d_name); 21 | 22 | if (filename.size() > ext.size() && // 23 | filename.compare(filename.size() - ext.size(), filename.size(), ext) == 0) { 24 | files.push_back(path + entry->d_name); 25 | } 26 | } 27 | closedir(dir); 28 | 29 | return files; 30 | } 31 | 32 | } /* namespace TestBed */ 33 | -------------------------------------------------------------------------------- /cmake/FindClangTidy.cmake: -------------------------------------------------------------------------------- 1 | # Find-module for clang-tidy 2 | # Defines: 3 | # 4 | # ClangTidy_FOUND - clang-tidy is available 5 | # ClangTidy_EXECUTABLE - the clang-tidy binary 6 | # ClangTidy_VERSION - the clang-tidy version 7 | 8 | cmake_minimum_required(VERSION 3.3 FATAL_ERROR) 9 | 10 | include(FindPackageHandleStandardArgs) 11 | 12 | find_program(ClangTidy_EXECUTABLE NAMES clang-tidy HINTS ${ClangTidy_HINT_PATH}) 13 | if(ClangTidy_EXECUTABLE) 14 | execute_process(COMMAND ${ClangTidy_EXECUTABLE} --version OUTPUT_VARIABLE ClangTidy_VERSION) 15 | 16 | find_program(RunClangTidy_EXECUTABLE NAMES run-clang-tidy.py) 17 | if(RunClangTidy_EXECUTABLE) 18 | set(ClangTidy_FOUND) 19 | endif() 20 | endif() 21 | 22 | find_package_handle_standard_args(ClangTidy DEFAULT_MSG RunClangTidy_EXECUTABLE ClangTidy_VERSION) 23 | 24 | # only visible in advanced view 25 | mark_as_advanced(ClangTidy_EXECUTABLE ClangTidy_VERSION) 26 | -------------------------------------------------------------------------------- /src/detail/Math.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/Math.hpp" 2 | 3 | #include 4 | 5 | namespace HitboxBuilder { 6 | namespace Detail { 7 | namespace Math { 8 | 9 | template 10 | float vectorDot(const sf::Vector2& a, const sf::Vector2& b) { 11 | return static_cast(a.x * b.x + a.y * b.y); 12 | } 13 | 14 | template 15 | float pointDistance(const sf::Vector2& a, const sf::Vector2& b) { 16 | T dx = a.x - b.x; 17 | T dy = a.y - b.y; 18 | 19 | return static_cast(std::sqrt(dx * dx + dy * dy)); 20 | } 21 | 22 | template float vectorDot(const sf::Vector2i& a, const sf::Vector2i& b); 23 | template float vectorDot(const sf::Vector2f& a, const sf::Vector2f& b); 24 | 25 | template float pointDistance(const sf::Vector2i& a, const sf::Vector2i& b); 26 | template float pointDistance(const sf::Vector2f& a, const sf::Vector2f& b); 27 | 28 | } /* namespace Math */ 29 | } /* namespace Detail */ 30 | } /* namespace HitboxBuilder */ 31 | -------------------------------------------------------------------------------- /include/detail/PolygonBuilder.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_DETAIL_POLYGON_BUILDER_HPP 2 | #define HITBOX_BUILDER_DETAIL_POLYGON_BUILDER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "Types.hpp" 12 | 13 | namespace HitboxBuilder { 14 | namespace Detail { 15 | 16 | class PolygonBuilder { 17 | private: 18 | using Contour = Polygon; 19 | 20 | const float kAverageDiagonal = 400.f * static_cast(std::sqrt(2)); 21 | 22 | public: 23 | Polygon make(Contour contour, const Polygon& boundingBox, size_t accuracy) const; 24 | 25 | private: 26 | float computeThreshold(const Polygon& polygon, size_t accuracy) const; 27 | void fetchFurthestPoints(const Contour& contour, size_t i, size_t j, std::vector& vertices, 28 | float threshold) const; 29 | float pointSegmentLineDistance(const sf::Vector2i& p, const sf::Vector2i& a, const sf::Vector2i& b) const; 30 | }; 31 | 32 | } /* namespace Detail */ 33 | } // namespace HitboxBuilder 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Luc Sinet 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 | -------------------------------------------------------------------------------- /include/detail/PolygonPartitioner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_DETAIL_POLYGON_PARTITIONER_HPP 2 | #define HITBOX_BUILDER_DETAIL_POLYGON_PARTITIONER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "Triangulator.hpp" 11 | 12 | namespace HitboxBuilder { 13 | namespace Detail { 14 | 15 | class PolygonPartitioner { 16 | private: 17 | using Triangle = Polygon; 18 | 19 | public: 20 | std::vector make(Polygon polygon) const; 21 | 22 | private: 23 | std::vector make(std::vector triangles) const; 24 | template 25 | std::pair findDiagonal(const std::vector& triangles, PolyItB& polygonB, 26 | const PolyItA& polygonA, size_t i) const; 27 | bool isPolygonConvex(const Polygon& polygon) const; 28 | 29 | private: 30 | Triangulator _triangulator; 31 | 32 | private: 33 | mutable size_t _polygonASize; 34 | mutable size_t _polygonBSize; 35 | }; 36 | 37 | } /* namespace Detail */ 38 | } /* namespace HitboxBuilder */ 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /cmake/CompilerFlags.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCompilerFlags) 2 | 3 | # Check and define C compiler flags 4 | check_cxx_compiler_flags( 5 | FLAGS 6 | "-std=c++1y" 7 | "-Wall" "-pedantic" "-Wextra" 8 | "-Wcast-align" 9 | "-Wcast-qual" "-Wconversion" 10 | "-Wdisabled-optimization" 11 | "-Wdocumentation" 12 | "-Wformat=2" 13 | "-Wformat-nonliteral" "-Wformat-security" 14 | "-Wimplicit" "-Wimport" "-Winit-self" "-Winline" 15 | "-Wmissing-field-initializers" "-Wmissing-format-attribute" 16 | "-Wmissing-include-dirs" "-Wmissing-noreturn" 17 | "-Wpacked" "-Wpointer-arith" 18 | "-Wredundant-decls" 19 | "-Wstack-protector" 20 | "-Wstrict-aliasing=2" "-Wswitch-default" 21 | "-Wunreachable-code" "-Wunused" 22 | "-Wunused-parameter" 23 | "-Wvariadic-macros" 24 | "-Wwrite-strings" 25 | "-Wno-builtin-macro-redefined" 26 | "-Wno-unknown-pragmas" 27 | "-Wno-suggest-attribute=noreturn" 28 | "-Wno-parentheses" 29 | # Enable colorized output 30 | "-fdiagnostics-color=auto" 31 | 32 | FLAGS_RELEASE 33 | "-march=native" 34 | "-Wno-inline" 35 | "-Wno-unused-parameter" 36 | "-Wno-unused-variable" 37 | "-Wno-unused-but-set-variable" 38 | "-Wno-maybe-uninitialized" 39 | "-Os" 40 | ) 41 | -------------------------------------------------------------------------------- /TestBed/Example.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TESTBED_EXAMPLE_HPP 2 | #define TESTBED_EXAMPLE_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "Types.hpp" 14 | 15 | namespace hb = HitboxBuilder; 16 | 17 | namespace TestBed { 18 | 19 | class Example { 20 | public: 21 | const int kAccIncrement = 2; 22 | 23 | public: 24 | explicit Example(const std::vector& fonts); 25 | 26 | void run(const std::vector& images); 27 | 28 | private: 29 | void updateDrawables(std::vector& bodyVertices, sf::VertexArray& boundingBox); 30 | void loadSprites(const std::vector& images); 31 | std::vector buildPolygon(const std::vector& polygons); 32 | sf::VertexArray buildBoundingBox(const hb::Polygon& boundingBox) const; 33 | bool handleEvents(const sf::Event& event); 34 | void close(); 35 | 36 | private: 37 | struct Text { 38 | sf::Text text; 39 | sf::Font font; 40 | }; 41 | 42 | private: 43 | sf::RenderWindow _window; 44 | std::vector _textures; 45 | std::vector _sprites; 46 | 47 | private: 48 | size_t _accuracy{ 50 }; 49 | size_t _spriteIdx{ 0 }; 50 | bool _displaySprite{ true }; 51 | sf::Vector2i _center; 52 | Text _polygonCount; 53 | }; 54 | 55 | } /* namespace TestBed */ 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/Module.cpp: -------------------------------------------------------------------------------- 1 | #include "Module.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "detail/BoundingBoxBuilder.hpp" 7 | #include "detail/ContourBuilder.hpp" 8 | #include "detail/CoordinateTransformer.hpp" 9 | #include "detail/PolygonBuilder.hpp" 10 | #include "detail/PolygonPartitioner.hpp" 11 | 12 | namespace HitboxBuilder { 13 | namespace { 14 | 15 | std::unique_ptr contourBuilder; 16 | std::unique_ptr polygonBuilder; 17 | std::unique_ptr boundingBoxBuilder; 18 | std::unique_ptr polygonPartitioner; 19 | bool init_called{false}; 20 | 21 | } /* namespace */ 22 | 23 | void init() { 24 | contourBuilder = std::make_unique(); 25 | polygonBuilder = std::make_unique(); 26 | boundingBoxBuilder = std::make_unique(); 27 | polygonPartitioner = std::make_unique(); 28 | init_called = true; 29 | } 30 | 31 | Hitbox make(const sf::Sprite& sprite, size_t accuracy, bool isCartesian) { 32 | assert(init_called); 33 | 34 | auto contour = contourBuilder->make(sprite, accuracy); 35 | auto boundingBox = boundingBoxBuilder->make(contour); 36 | auto polygon = polygonBuilder->make(std::move(contour), boundingBox, accuracy); 37 | auto polygons = polygonPartitioner->make(std::move(polygon)); 38 | 39 | if (isCartesian) { 40 | Detail::toCarthesian(sprite, polygons); 41 | Detail::toCarthesian(sprite, boundingBox); 42 | } 43 | 44 | return Hitbox(std::move(polygons), std::move(boundingBox)); 45 | } 46 | 47 | } /* namespace HitboxBuilder */ 48 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8.1) 2 | cmake_policy(SET CMP0042 NEW) 3 | 4 | set(PROJECT_NAME "HitboxBuilder") 5 | project (${PROJECT_NAME} CXX) 6 | 7 | # Global variables 8 | set(MODULE_DIR cmake) 9 | set(SOURCE_DIR src) 10 | set(INCLUDE_DIR include) 11 | set(LIB_DIR lib) 12 | set(IMPLEMENTATION_DIR detail) 13 | set(TESTBED_DIR TestBed) 14 | set(TESTER_NAME "${PROJECT_NAME}-tester") 15 | 16 | set(CLANG_TIDY_CONFIG_SOURCE "${PROJECT_SOURCE_DIR}/.clang-tidy") 17 | 18 | # CMake properties 19 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 20 | set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) 21 | set(PREDEFINED_TARGETS_FOLDER "CMake") 22 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/${MODULE_DIR}) 23 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 24 | set(CMAKE_BUILD_TYPE ${BUILD_TYPE}) 25 | 26 | 27 | message(STATUS "Building ${BUILD_TYPE} ${PROJECT_NAME}\n----------------------------") 28 | 29 | # Resolve internal dependency 30 | include(ResolveSFML) 31 | 32 | # Set compiler flags 33 | include(CompilerFlags) 34 | 35 | # Source files. 36 | include(Sources) 37 | 38 | include_directories (${PROJECT_SOURCE_DIR}) 39 | include_directories (${PROJECT_SOURCE_DIR}/${INCLUDE_DIR}) 40 | 41 | add_library(${PROJECT_NAME} STATIC ${LIB_SOURCE_FILES}) 42 | 43 | if(HITBOX_BUILDER_ENABLE_CLANGTIDY AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") 44 | include(EnableClangTidy) 45 | enable_clangtidy(SOURCES "${LIB_SOURCE_FILES}") 46 | endif() 47 | 48 | if(HITBOX_BUILDER_BUILD_TESTBED) 49 | include(EnableTests) 50 | endif() 51 | 52 | install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_DIR}) 53 | install(FILES ${LIB_EXPOSED_HEADER_FILES} DESTINATION "${INCLUDE_DIR}/${PROJECT_NAME}") 54 | -------------------------------------------------------------------------------- /include/detail/Triangulator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILDER_DETAIL_TRIANGULATOR_HPP 2 | #define HITBOX_BUILDER_DETAIL_TRIANGULATOR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Types.hpp" 9 | 10 | namespace HitboxBuilder { 11 | namespace Detail { 12 | 13 | class Triangulator { 14 | private: 15 | using Triangle = Polygon; 16 | using Point = sf::Vector2i; 17 | 18 | private: 19 | struct Vertex { 20 | explicit Vertex(Point&& p) 21 | : p(std::forward(p)) {} 22 | 23 | Point p; 24 | Vertex* prev; 25 | Vertex* next; 26 | bool isActive{ true }; 27 | bool isConvex; 28 | bool isEar; 29 | float angle; 30 | }; 31 | 32 | public: 33 | std::vector convert(Polygon polygon) const; 34 | 35 | public: 36 | bool isAngleReflex(const Point& a, const Point& b, const Point& c) const { 37 | return !this->isAngleConvex(a, b, c); 38 | } 39 | bool isAngleConvex(const Point& a, const Point& b, const Point& c) const { 40 | return (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y) < 0; 41 | } 42 | 43 | private: 44 | void initVertices(std::vector& vertices) const; 45 | void updateVertex(const std::vector& vertices, Vertex& vertex) const; 46 | std::pair nextEar(std::vector& polygon, size_t vertexCount, size_t vertexIndex) const; 47 | bool isPointContained(const Point& a, const Point& b, const Point& c, const Point& p) const; 48 | sf::Vector2f normalize(const Point& p) const; 49 | 50 | private: 51 | bool isPointVertice(const Point& a, const Point& b, const Point& c, const Point& point) const { 52 | return (point == a || point == b || point == c); 53 | } 54 | }; 55 | 56 | } /* namespace Detail */ 57 | } /* namespace HitboxBuilder */ 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/detail/ContourBuilder.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_BUILER_DETAIL_CONTOUR_BUILDER_HPP 2 | #define HITBOX_BUILER_DETAIL_CONTOUR_BUILDER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace HitboxBuilder { 15 | namespace Detail { 16 | 17 | class ContourBuilder { 18 | private: 19 | enum class StepDirection { None, N, W, S, E }; 20 | 21 | const std::array directions{ { 22 | StepDirection::None, 23 | StepDirection::N, 24 | StepDirection::E, 25 | StepDirection::E, 26 | StepDirection::W, 27 | StepDirection::N, 28 | StepDirection::None, 29 | StepDirection::E, 30 | StepDirection::S, 31 | StepDirection::None, 32 | StepDirection::S, 33 | StepDirection::S, 34 | StepDirection::W, 35 | StepDirection::N, 36 | StepDirection::W, 37 | StepDirection::None, 38 | } }; 39 | 40 | public: 41 | std::vector make(const sf::Sprite& sprite, size_t accuracy) const; 42 | 43 | private: 44 | sf::Vector2i findStartPoint() const; 45 | std::vector walkPerimeter(const sf::Vector2i& start) const; 46 | void step(std::vector& contour, const sf::Vector2i& point, StepDirection& nextStep) const; 47 | void savePoint(std::vector& contour, const sf::Vector2i& p) const; 48 | size_t pixelState(const sf::Vector2i& p) const; 49 | bool isPixelSolid(int x, int y) const; 50 | bool isPixelSolid(const sf::Vector2i& p) const { 51 | return this->isPixelSolid(p.x, p.y); 52 | } 53 | 54 | private: 55 | mutable sf::Image* _image; 56 | mutable sf::IntRect _bound; 57 | mutable uint8_t _alphaThreshold; 58 | }; 59 | 60 | } /* namespace Detail */ 61 | } /* namespace HitboxBuilder */ 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit # set -e: exit the script if any statement returns a non-true value 4 | 5 | # Program constants 6 | ROOT_DIR="$( 7 | cd "$(dirname "${BASH_SOURCE[0]}")" 8 | pwd -P 9 | )" 10 | BUILD_DIR="$ROOT_DIR/build" 11 | 12 | # Program options 13 | CXX=${CXX:-"g++"} 14 | 15 | function usage() { 16 | echo -e "Usage:\n$0 [options]" 17 | echo -e "\nConfigure a build directory using the provided options." 18 | echo -e "\nGeneral options:" 19 | echo -e " -h,--help Display this help message" 20 | echo -e " -c,--clang Use clang as compiler" 21 | echo -e " -t,--test Enable the testbed build target" 22 | echo -e "\nClang-specific options:" 23 | echo -e " -y,--tidy Enable clang-tidy static analysis target" 24 | echo 25 | exit "${1:-1}" 26 | } 27 | 28 | function configure() { 29 | cmake \ 30 | -DCMAKE_CXX_COMPILER="$CXX" \ 31 | -DBUILD_TYPE="$HITBOX_BUILDER_BUILD_TYPE" \ 32 | -DHITBOX_BUILDER_ENABLE_CLANGTIDY="$HITBOX_BUILDER_ENABLE_CLANGTIDY" \ 33 | -DHITBOX_BUILDER_BUILD_TESTBED="$HITBOX_BUILDER_BUILD_TESTBED" \ 34 | "$ROOT_DIR" 35 | } 36 | 37 | # Process arguments 38 | while [[ $# -gt 0 ]]; do 39 | case "$1" in 40 | -h | --help) 41 | usage 0 42 | ;; 43 | -c | --clang) 44 | CXX="clang++" 45 | shift 46 | ;; 47 | -t | --test) 48 | HITBOX_BUILDER_BUILD_TESTBED="ON" 49 | shift 50 | ;; 51 | -y | --tidy) 52 | HITBOX_BUILDER_ENABLE_CLANGTIDY="ON" 53 | shift 54 | ;; 55 | -*) 56 | echo -e "unknown option '$1'" 57 | usage 58 | ;; 59 | esac 60 | done 61 | 62 | # Prevent use of clang-only options with other compilers 63 | if [[ "$HITBOX_BUILDER_ENABLE_CLANGTIDY" == "ON" && "$CXX" != "clang++" ]]; then 64 | echo -e "cannot use clang-tidy if compiler is not clang" 65 | usage 66 | fi 67 | 68 | mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR" && configure 69 | -------------------------------------------------------------------------------- /cmake/CheckCompilerFlags.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | # Check whether the CXX compiler supports the given flags. 4 | # Usage: check_cxx_compiler_flags(FLAGS;FLAGS_RELEASE;FLAGS_DEBUG) 5 | macro(check_cxx_compiler_flags) 6 | cmake_parse_arguments(ARG "" "" "FLAGS;FLAGS_RELEASE;FLAGS_DEBUG" ${ARGN}) 7 | 8 | # General flags 9 | # NOTE: We use a separate variable to hold the checked CXX flags to workaround a bug 10 | # when using -stdlib=libc++ that makes all of the subsequent checks fail. 11 | set(COMPILER_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 12 | set(CMAKE_CXX_FLAGS "") 13 | foreach(F ${ARG_FLAGS}) 14 | # Make a good unique variable name for the check 15 | string(REGEX REPLACE "[-+=]" "_" F_CHECK_NAME ${F}) 16 | set(F_CHECK_CXX_NAME CHECK_CXX_FLAG${F_CHECK_NAME}) 17 | # Do the check and add the definition if it passes 18 | check_cxx_compiler_flag(${F} ${F_CHECK_CXX_NAME} HAS_FLAG) 19 | if(${F_CHECK_CXX_NAME}) 20 | set(COMPILER_CXX_FLAGS "${COMPILER_CXX_FLAGS} ${F}") 21 | endif(${F_CHECK_CXX_NAME}) 22 | endforeach(F ${ARG_FLAGS}) 23 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_CXX_FLAGS}") 24 | 25 | # Release flags 26 | foreach(F ${ARG_FLAGS_RELEASE}) 27 | # Make a good unique variable name for the check 28 | string(REGEX REPLACE "[-+=]" "_" F_CHECK_NAME ${F}) 29 | set(F_CHECK_CXX_NAME CHECK_CXX_FLAG${F_CHECK_NAME}) 30 | # Do the check and add the definition if it passes 31 | check_cxx_compiler_flag(${F} ${F_CHECK_CXX_NAME}) 32 | if(${F_CHECK_CXX_NAME}) 33 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${F}") 34 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${F}") 35 | endif(${F_CHECK_CXX_NAME}) 36 | endforeach(F ${ARG_FLAGS}) 37 | 38 | # Debug flags 39 | foreach(F ${ARG_FLAGS_DEBUG}) 40 | # Make a good unique variable name for the check 41 | string(REGEX REPLACE "[-+=]" "_" F_CHECK_NAME ${F}) 42 | set(F_CHECK_CXX_NAME CHECK_CXX_FLAG${F_CHECK_NAME}) 43 | # Do the check and add the definition if it passes 44 | check_cxx_compiler_flag(${F} ${F_CHECK_CXX_NAME}) 45 | if(${F_CHECK_CXX_NAME}) 46 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${F}") 47 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${F}") 48 | endif(${F_CHECK_CXX_NAME}) 49 | endforeach(F ${ARG_FLAGS}) 50 | endmacro() 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | logo 3 |
4 | 5 | 6 | ## Introduction 7 | Hitbox Builder is a lightweight C++ library aiming to automatizing the process of generating hitboxes composed by only convex polygons. 8 | 9 | It was originally designed for 2D games, but it could also serve different purposes. 10 | 11 | ## License 12 | Hitbox Builder is distributed under the [MIT License](https://raw.githubusercontent.com/Tastyep/SFML-HitboxBuilder-2D/develop/LICENSE) 13 | 14 | ## Requirements 15 | - [SFML](https://www.sfml-dev.org/) 16 | - [CMake](https://github.com/Kitware/CMake) 17 | - [GCC](https://gcc.gnu.org/) or [Clang](https://clang.llvm.org/) 18 | 19 | ## Features 20 | - Contour detection ([Marching squares](https://en.wikipedia.org/wiki/Marching_squares)) 21 | - Contour simplification ([Douglas–Peucker](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm)) 22 | - Polygon triangulation ([Ear clipping](https://en.wikipedia.org/wiki/Two_ears_theorem)) 23 | - Polygon partitioning (Hertel-Mehlhorn) 24 | 25 | Below is a representation of each stage of transformation of the data. 26 | 27 |
28 | logo 29 |
30 | 31 | ## Usage 32 | 33 | ```C++ 34 | // Initialize the builder. 35 | HitboxBuilder::init(); 36 | 37 | // Create a hitbox for the given sprite with an accuracy of 70%. 38 | const auto hitbox = HitboxBuilder::make(sprite, 70, false); 39 | 40 | const auto& convexPolygons = hitbox.body(); 41 | const auto& boundingBox = hitbox.bound(); 42 | 43 | // Use these data as you like. 44 | ``` 45 | 46 | ## Demos 47 | 48 | To run the demos, execute the configure script with the option '-t'. 49 | 50 | Execute the target HitboxBuilder-tester located in the build directory. 51 | 52 | Some test bed commands are: 53 | - keys to slide between the images. 54 | - keys to increase/decrease the accuracy of the 55 | algorithm. 56 | - Space key to toggle the display of the sprite. 57 | 58 | You can load your own images by adding them to the TestBed/assets/ directory. You will just need to restart the program as they are loaded at the beginning. 59 | 60 | ##### Output: 61 | 62 | Input | Output 63 | :-----------------------:|:-------------------------: 64 | ![silhouette](https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/master/assets/images/input.png) | ![Accuracy gif](https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/master/assets/images/accuracy.gif) 65 | **Accuracy = 100** | **Accuracy = 66** 66 | ![Italy 100](https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/master/assets/images/italy-100.png) | ![Italy 66](https://raw.githubusercontent.com/Tastyep/HitboxBuilder-2D/master/assets/images/italy-66.png) 67 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | DisableFormat: false 4 | 5 | AccessModifierOffset: -1 6 | AlignAfterOpenBracket: Align 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlinesLeft: true 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllParametersOfDeclarationOnNextLine: true 13 | AllowShortBlocksOnASingleLine: false 14 | AllowShortCaseLabelsOnASingleLine: true 15 | AllowShortFunctionsOnASingleLine: Empty 16 | AllowShortIfStatementsOnASingleLine: false 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterDefinitionReturnType: None 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakBeforeMultilineStrings: false 21 | AlwaysBreakTemplateDeclarations: true 22 | BinPackArguments: true 23 | BinPackParameters: true 24 | BraceWrapping: 25 | AfterClass: false 26 | AfterControlStatement: false 27 | AfterEnum: false 28 | AfterFunction: false 29 | AfterNamespace: false 30 | AfterObjCDeclaration: false 31 | AfterStruct: false 32 | AfterUnion: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | BreakAfterJavaFieldAnnotations: false 37 | BreakBeforeBinaryOperators: None 38 | BreakBeforeBraces: Attach 39 | BreakBeforeTernaryOperators: true 40 | BreakConstructorInitializersBeforeComma: true 41 | BreakStringLiterals: true 42 | ColumnLimit: 120 43 | CommentPragmas: '^ IWYU pragma:' 44 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 45 | ConstructorInitializerIndentWidth: 2 46 | ContinuationIndentWidth: 2 47 | Cpp11BracedListStyle: false 48 | DerivePointerAlignment: false 49 | ExperimentalAutoDetectBinPacking: true 50 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 51 | IncludeCategories: 52 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 53 | Priority: 2 54 | - Regex: '^(<|"(boost|soci|gtest|grpc|grpc++|experimental|spdlog|llvm)/)' 55 | Priority: 3 56 | - Regex: '^(<|"(catch.hpp))' 57 | Priority: 4 58 | - Regex: '.\*' 59 | Priority: 1 60 | IncludeIsMainRegex: '$' 61 | IndentCaseLabels: false 62 | IndentWidth: 2 63 | IndentWrappedFunctionNames: false 64 | JavaScriptQuotes: Leave 65 | JavaScriptWrapImports: true 66 | KeepEmptyLinesAtTheStartOfBlocks: true 67 | MacroBlockBegin: '' 68 | MacroBlockEnd: '' 69 | MaxEmptyLinesToKeep: 1 70 | NamespaceIndentation: None 71 | ObjCBlockIndentWidth: 2 72 | ObjCSpaceAfterProperty: false 73 | ObjCSpaceBeforeProtocolList: true 74 | PenaltyBreakBeforeFirstCallParameter: 19 75 | PenaltyBreakComment: 300 76 | PenaltyBreakFirstLessLess: 120 77 | PenaltyBreakString: 1000 78 | PenaltyExcessCharacter: 1000000 79 | PenaltyReturnTypeOnItsOwnLine: 60 80 | PointerAlignment: Left 81 | ReflowComments: true 82 | SortIncludes: true 83 | SpaceAfterCStyleCast: false 84 | #SpaceAfterTemplateKeyword: false 85 | SpaceBeforeAssignmentOperators: true 86 | SpaceBeforeParens: ControlStatements 87 | SpaceInEmptyParentheses: false 88 | SpacesBeforeTrailingComments: 1 89 | SpacesInAngles: false 90 | SpacesInContainerLiterals: false 91 | SpacesInCStyleCastParentheses: false 92 | SpacesInParentheses: false 93 | SpacesInSquareBrackets: false 94 | Standard: Cpp11 95 | TabWidth: 8 96 | UseTab: Never 97 | ... 98 | -------------------------------------------------------------------------------- /src/detail/PolygonBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/PolygonBuilder.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "detail/Math.hpp" 7 | 8 | namespace HitboxBuilder { 9 | namespace Detail { 10 | 11 | Polygon PolygonBuilder::make(Contour contour, const Polygon& boundingBox, size_t accuracy) const { 12 | if (contour.size() <= 3) { 13 | return contour; 14 | } 15 | 16 | const auto threshold = this->computeThreshold(boundingBox, accuracy); 17 | 18 | Polygon polygon; 19 | std::vector vertices(contour.size() - 1, 1); 20 | this->fetchFurthestPoints(contour, 0, vertices.size(), vertices, threshold); 21 | 22 | for (size_t i = 0; i < vertices.size(); ++i) { 23 | if (vertices[i]) { 24 | polygon.push_back(contour[i]); 25 | } 26 | } 27 | return polygon; 28 | } 29 | 30 | float PolygonBuilder::computeThreshold(const Polygon& polygon, size_t accuracy) const { 31 | const auto h = std::abs(polygon[0].y - polygon[1].y); 32 | const auto w = std::abs(polygon[1].x - polygon[2].x); 33 | const auto d = static_cast(std::sqrt(h * h + w * w)); 34 | const auto scaleFactor = std::sqrt(d) / std::sqrt(kAverageDiagonal); 35 | const auto minDistance = 1.5f * scaleFactor; 36 | const auto maxDistance = 40.f * scaleFactor; 37 | 38 | return minDistance + maxDistance - (static_cast(accuracy) / 100.f) * maxDistance; 39 | } 40 | 41 | void PolygonBuilder::fetchFurthestPoints(const Contour& contour, size_t i, size_t j, std::vector& vertices, 42 | float threshold) const { 43 | std::vector> indexes; 44 | 45 | indexes.reserve(contour.size()); 46 | indexes.emplace_back(i, j); 47 | while (!indexes.empty()) { 48 | std::tie(i, j) = indexes.back(); 49 | indexes.pop_back(); 50 | const auto& a = contour[i]; 51 | const auto& b = contour[j]; 52 | float maxDistance = 0.f; 53 | size_t maxIndex = i; 54 | 55 | for (size_t k = i + 1; k < j; ++k) { 56 | const auto& p = contour[k]; 57 | const auto distance = this->pointSegmentLineDistance(p, a, b); 58 | 59 | if (distance >= maxDistance) { 60 | maxDistance = distance; 61 | maxIndex = k; 62 | } 63 | } 64 | 65 | if (maxDistance < threshold) { 66 | size_t k = i + 1; 67 | std::fill_n(std::next(vertices.begin(), static_cast(k)), j - k, 0); 68 | continue; 69 | } 70 | indexes.emplace_back(i, maxIndex); 71 | indexes.emplace_back(maxIndex, j); 72 | } 73 | } 74 | 75 | float PolygonBuilder::pointSegmentLineDistance(const sf::Vector2i& p, const sf::Vector2i& a, 76 | const sf::Vector2i& b) const { 77 | if (a == b) { 78 | return Math::pointDistance(p, a); 79 | } 80 | 81 | const auto v = b - a; 82 | const auto w = p - a; 83 | float c1 = Math::vectorDot(w, v); 84 | if (c1 <= 0) { 85 | return Math::pointDistance(p, a); 86 | } 87 | 88 | float c2 = Math::vectorDot(v, v); 89 | if (c2 <= c1) { 90 | return Math::pointDistance(p, b); 91 | } 92 | 93 | float t = c1 / c2; 94 | const auto shiftedPoint = static_cast(a) + (static_cast(v) * t); 95 | return Math::pointDistance(static_cast(p), shiftedPoint); 96 | } 97 | 98 | } /* namespace Detail */ 99 | } /* namespace HitboxBuilder */ 100 | -------------------------------------------------------------------------------- /src/detail/PolygonPartitioner.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/PolygonPartitioner.hpp" 2 | 3 | #include 4 | 5 | namespace HitboxBuilder { 6 | namespace Detail { 7 | 8 | std::vector PolygonPartitioner::make(Polygon polygon) const { 9 | if (this->isPolygonConvex(polygon)) { 10 | return { polygon }; 11 | } 12 | 13 | auto triangles = _triangulator.convert(polygon); 14 | auto polygons = this->make(std::move(triangles)); 15 | 16 | return polygons; 17 | } 18 | 19 | std::vector PolygonPartitioner::make(std::vector triangles) const { 20 | for (auto polygonA = triangles.begin(); polygonA != triangles.end(); ++polygonA) { 21 | _polygonASize = polygonA->size(); 22 | 23 | for (int verticeIdx = 0; static_cast(verticeIdx) < _polygonASize; ++verticeIdx) { 24 | decltype(polygonA) polygonB; 25 | bool diagonalFound; 26 | const auto i = static_cast(verticeIdx); // Make a copy to cast only once. 27 | size_t j; 28 | 29 | std::tie(diagonalFound, j) = this->findDiagonal(triangles, polygonB, polygonA, i); 30 | if (diagonalFound == false) { 31 | continue; 32 | } 33 | size_t i2 = (i + 1) % _polygonASize; // End of the diagonal for polygonA 34 | size_t i0 = (i + _polygonASize - 1) % _polygonASize; // Point preceding the diagonal for polygonA 35 | size_t j2 = (j + 1) % _polygonBSize; // End of the diagonal for polygonB 36 | size_t j3 = (j2 + 1) % _polygonBSize; // Point after the diagonal for polygonB 37 | 38 | // Check the left side of the diagonal 39 | if (_triangulator.isAngleReflex((*polygonA)[i0], (*polygonA)[i], (*polygonB)[j3])) { 40 | continue; 41 | } 42 | 43 | size_t i3 = (i2 + 1) % _polygonASize; // Point after the diagonal for polygonA 44 | size_t j0 = (j + _polygonBSize - 1) % _polygonBSize; // Point preceding the diagonal for polygonB 45 | 46 | // Check the right side of the diagonal 47 | if (_triangulator.isAngleReflex((*polygonB)[j0], (*polygonA)[i2], (*polygonA)[i3])) { 48 | continue; 49 | } 50 | 51 | Polygon polygon; 52 | 53 | polygon.reserve(_polygonASize + _polygonBSize - 2); 54 | for (size_t k = i2; k != i; k = (k + 1) % _polygonASize) { 55 | polygon.push_back((*polygonA)[k]); 56 | } 57 | for (size_t k = j2; k != j; k = (k + 1) % _polygonBSize) { 58 | polygon.push_back((*polygonB)[k]); 59 | } 60 | *polygonA = std::move(polygon); 61 | triangles.erase(polygonB); 62 | _polygonASize = polygonA->size(); 63 | verticeIdx = 0; 64 | } 65 | } 66 | return triangles; 67 | } 68 | 69 | template 70 | std::pair PolygonPartitioner::findDiagonal(const std::vector& triangles, PolyItB& polygonB, 71 | const PolyItA& polygonA, size_t i) const { 72 | const auto& pA1 = (*polygonA)[i]; 73 | const auto& pA2 = (*polygonA)[(i + 1) % _polygonASize]; 74 | size_t j; 75 | 76 | for (polygonB = std::next(polygonA); polygonB != triangles.end(); ++polygonB) { 77 | _polygonBSize = polygonB->size(); 78 | 79 | for (j = 0; j < _polygonBSize; ++j) { 80 | const auto& pB1 = (*polygonB)[j]; 81 | const auto& pB2 = (*polygonB)[(j + 1) % _polygonBSize]; 82 | 83 | if (pA2 == pB1 && pA1 == pB2) { // Diagonal found 84 | return { true, j }; 85 | } 86 | } 87 | } 88 | return { false, j }; 89 | } 90 | 91 | bool PolygonPartitioner::isPolygonConvex(const Polygon& polygon) const { 92 | size_t polygonSize = polygon.size(); 93 | size_t i; 94 | 95 | for (i = 0; i < polygonSize; ++i) { 96 | size_t prev = (i + polygonSize - 1) % polygonSize; 97 | size_t next = (i + 1) % polygonSize; 98 | if (_triangulator.isAngleReflex(polygon[prev], polygon[i], polygon[next])) { 99 | break; 100 | } 101 | } 102 | 103 | return (i == polygonSize); 104 | } 105 | 106 | } /* namespace Detail */ 107 | } /* namespace HitboxBuilder */ 108 | -------------------------------------------------------------------------------- /src/detail/Triangulator.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/Triangulator.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace HitboxBuilder { 9 | namespace Detail { 10 | 11 | std::vector Triangulator::convert(Polygon polygon) const { 12 | std::vector vertices; 13 | std::vector triangles; 14 | Triangle ear; 15 | bool success; 16 | 17 | std::transform(std::make_move_iterator(polygon.begin()), 18 | std::make_move_iterator(polygon.end()), 19 | std::back_inserter(vertices), 20 | [](auto&& p) { return Vertex(std::forward(p)); }); 21 | this->initVertices(vertices); 22 | for (auto& vertex : vertices) { 23 | this->updateVertex(vertices, vertex); 24 | } 25 | 26 | size_t i = 0; 27 | size_t vertexCount = vertices.size(); 28 | while ((std::tie(success, ear) = this->nextEar(vertices, vertexCount, i)), success) { 29 | triangles.push_back(std::move(ear)); 30 | ++i; 31 | } 32 | 33 | return triangles; 34 | } 35 | 36 | void Triangulator::initVertices(std::vector& vertices) const { 37 | const size_t verticesSize = vertices.size(); 38 | 39 | for (size_t i = 0; i < verticesSize; ++i) { 40 | auto& vertice = vertices[i]; 41 | 42 | vertice.prev = &vertices[(i + verticesSize - 1) % verticesSize]; 43 | vertice.next = &vertices[(i + 1) % verticesSize]; 44 | } 45 | } 46 | 47 | void Triangulator::updateVertex(const std::vector& vertices, Vertex& vertex) const { 48 | const Vertex& prev = *vertex.prev; 49 | const Vertex& next = *vertex.next; 50 | const auto vPrev = this->normalize(prev.p - vertex.p); 51 | const auto vNext = this->normalize(next.p - vertex.p); 52 | 53 | vertex.angle = vPrev.x * vNext.x + vPrev.y * vNext.y; 54 | vertex.isConvex = this->isAngleConvex(prev.p, vertex.p, next.p); 55 | vertex.isEar = vertex.isConvex; 56 | if (vertex.isConvex) { 57 | for (const auto& v : vertices) { 58 | if (!this->isPointVertice(prev.p, vertex.p, next.p, v.p) && // 59 | this->isPointContained(prev.p, vertex.p, next.p, v.p)) { 60 | vertex.isEar = false; 61 | break; 62 | } 63 | } 64 | } 65 | } 66 | 67 | std::pair 68 | Triangulator::nextEar(std::vector& polygon, size_t vertexCount, size_t vertexIndex) const { 69 | Triangle triangle; 70 | Vertex* ear = nullptr; 71 | 72 | if (vertexIndex < vertexCount - 3) { 73 | for (size_t j = 0; j < vertexCount; ++j) { 74 | auto& v = polygon[j]; 75 | 76 | if (!v.isActive || !v.isEar) { 77 | continue; 78 | } 79 | if (ear == nullptr || v.angle > ear->angle) { 80 | ear = &v; 81 | } 82 | } 83 | 84 | if (ear == nullptr) { 85 | polygon.clear(); 86 | return { false, {} }; 87 | } 88 | 89 | triangle = Triangle{ ear->prev->p, ear->p, ear->next->p }; 90 | ear->isActive = false; 91 | ear->prev->next = ear->next; 92 | ear->next->prev = ear->prev; 93 | 94 | if (vertexIndex < vertexCount - 4) { 95 | this->updateVertex(polygon, *ear->prev); 96 | this->updateVertex(polygon, *ear->next); 97 | } 98 | } else if (vertexIndex == vertexCount - 3) { 99 | for (size_t i = 0; i < vertexCount; i++) { 100 | const auto& v = polygon[i]; 101 | if (v.isActive) { 102 | triangle = Triangle{ v.prev->p, v.p, v.next->p }; 103 | break; 104 | } 105 | } 106 | } else { 107 | return { false, {} }; 108 | } 109 | return { true, std::move(triangle) }; 110 | } 111 | 112 | bool Triangulator::isPointContained(const Point& a, const Point& b, const Point& c, const Point& p) const { 113 | return !this->isAngleConvex(a, p, b) && // 114 | !this->isAngleConvex(b, p, c) && // 115 | !this->isAngleConvex(c, p, a); 116 | } 117 | 118 | sf::Vector2f Triangulator::normalize(const Point& p) const { 119 | float n = std::sqrt(static_cast(p.x * p.x + p.y * p.y)); 120 | 121 | return (n == 0) ? sf::Vector2f{ 0, 0 } // 122 | : (static_cast(p) / n); 123 | } 124 | 125 | } /* namespace Detail */ 126 | } /* namespace HitboxBuilder */ 127 | -------------------------------------------------------------------------------- /src/detail/ContourBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "detail/ContourBuilder.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace HitboxBuilder { 9 | namespace Detail { 10 | 11 | std::vector ContourBuilder::make(const sf::Sprite& sprite, size_t accuracy) const { 12 | const sf::Texture* texture = sprite.getTexture(); 13 | sf::Image image = texture->copyToImage(); 14 | 15 | _alphaThreshold = static_cast(10 + 4 * (accuracy / 10)); 16 | _bound = sprite.getTextureRect(); 17 | _image = ℑ 18 | 19 | auto start = this->findStartPoint(); 20 | return this->walkPerimeter(start); 21 | } 22 | 23 | sf::Vector2i ContourBuilder::findStartPoint() const { 24 | sf::Vector2i pos; 25 | 26 | for (pos.y = 0; pos.y < _bound.height - 1; ++pos.y) { 27 | for (pos.x = 0; pos.x < _bound.width - 1; ++pos.x) { 28 | if (this->isPixelSolid(pos.x, pos.y) && // 29 | !this->isPixelSolid(pos.x, pos.y - 1)) { 30 | --pos.y; 31 | return pos; 32 | } 33 | if (this->isPixelSolid(pos.x + 1, pos.y)) { 34 | return pos; 35 | } 36 | } 37 | } 38 | return { 0, 0 }; 39 | } 40 | 41 | std::vector ContourBuilder::walkPerimeter(const sf::Vector2i& start) const { 42 | std::vector contour; 43 | StepDirection nextStep{ StepDirection::None }; 44 | sf::Vector2i point = start; 45 | 46 | do { 47 | this->step(contour, point, nextStep); 48 | 49 | // clang-format off 50 | switch (nextStep) { 51 | case StepDirection::N: 52 | --point.y; 53 | break; 54 | case StepDirection::W: 55 | --point.x; 56 | break; 57 | case StepDirection::S: 58 | ++point.y; 59 | break; 60 | case StepDirection::E: 61 | ++point.x; 62 | break; 63 | default: 64 | return contour; 65 | } 66 | // clang-format on 67 | } while (point != start); 68 | 69 | return contour; 70 | } 71 | 72 | void ContourBuilder::step(std::vector& contour, 73 | const sf::Vector2i& point, 74 | StepDirection& nextStep) const { 75 | StepDirection cuStep = nextStep; 76 | const auto state = this->pixelState(point); 77 | 78 | this->savePoint(contour, point); 79 | // clang-format off 80 | switch (state) { 81 | case 6: 82 | nextStep = (cuStep == StepDirection::N ? StepDirection::W 83 | : StepDirection::E); 84 | break; 85 | case 9: 86 | nextStep = (cuStep == StepDirection::E ? StepDirection::N 87 | : StepDirection::S); 88 | break; 89 | default: 90 | nextStep = this->directions[state]; 91 | } 92 | // clang-format on 93 | } 94 | 95 | void ContourBuilder::savePoint(std::vector& contour, const sf::Vector2i& p) const { 96 | if (this->isPixelSolid(p)) { 97 | if (contour.empty() || contour.back() != p) { 98 | contour.push_back(p); 99 | } 100 | } else { 101 | sf::Vector2i pr{ p.x + 1, p.y }; 102 | if (this->isPixelSolid(pr)) { 103 | if (contour.empty() || (contour.back() != pr && contour.front() != pr)) { 104 | contour.push_back(pr); 105 | } 106 | } 107 | 108 | sf::Vector2i pu{ p.x, p.y + 1 }; 109 | if (this->isPixelSolid(pu)) { 110 | if (contour.empty() || (contour.back() != pu && contour.front() != pu)) { 111 | contour.push_back(pu); 112 | } 113 | } 114 | } 115 | } 116 | 117 | size_t ContourBuilder::pixelState(const sf::Vector2i& p) const { 118 | bool upLeft = this->isPixelSolid(p.x, p.y); 119 | bool upRight = this->isPixelSolid(p.x + 1, p.y); 120 | bool downRight = this->isPixelSolid(p.x + 1, p.y + 1); 121 | bool downLeft = this->isPixelSolid(p.x, p.y + 1); 122 | 123 | return static_cast(upLeft | upRight << 1 | downRight << 3 | downLeft << 2); 124 | } 125 | 126 | bool ContourBuilder::isPixelSolid(int x, int y) const { 127 | if (x < 0 || y < 0 || x >= _bound.width || y >= _bound.height) { 128 | return false; 129 | } 130 | 131 | return _image->getPixel(static_cast(x + _bound.left), static_cast(y + _bound.top)).a > 132 | _alphaThreshold; 133 | } 134 | 135 | } /* namespace Detail */ 136 | } /* namespace HitboxBuilder */ 137 | -------------------------------------------------------------------------------- /TestBed/Example.cpp: -------------------------------------------------------------------------------- 1 | #include "TestBed/Example.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "Module.hpp" 10 | 11 | namespace TestBed { 12 | 13 | Example::Example(const std::vector& fonts) 14 | : _window(sf::VideoMode(1280, 1024), "HitboxBuilder") 15 | , _center(1280 / 2, 1024 / 2) { 16 | _window.setFramerateLimit(30); 17 | 18 | _polygonCount.font.loadFromFile(fonts[0]); 19 | _polygonCount.text.setFont(_polygonCount.font); 20 | _polygonCount.text.setCharacterSize(40); 21 | _polygonCount.text.setFillColor(sf::Color::Black); 22 | _polygonCount.text.setPosition(1.3f * _center.x, 1.8f * _center.y); 23 | 24 | HitboxBuilder::init(); 25 | } 26 | 27 | void Example::run(const std::vector& images) { 28 | std::vector bodyVertices; 29 | sf::VertexArray boundingBox; 30 | sf::Event event; 31 | 32 | this->loadSprites(images); 33 | this->updateDrawables(bodyVertices, boundingBox); 34 | while (_window.isOpen()) { 35 | while (_window.pollEvent(event)) { 36 | if (event.type == sf::Event::Closed) { 37 | this->close(); 38 | } 39 | if (this->handleEvents(event)) { 40 | this->updateDrawables(bodyVertices, boundingBox); 41 | } 42 | } 43 | _window.clear(sf::Color(255, 255, 255)); 44 | if (_displaySprite) { 45 | _window.draw(_sprites[_spriteIdx]); 46 | } 47 | for (const auto& v : bodyVertices) { 48 | _window.draw(v); 49 | } 50 | _window.draw(boundingBox); 51 | _window.draw(_polygonCount.text); 52 | _window.display(); 53 | } 54 | } 55 | 56 | void Example::updateDrawables(std::vector& bodyVertices, sf::VertexArray& boundingBox) { 57 | const auto hitbox = HitboxBuilder::make(_sprites[_spriteIdx], _accuracy, false); 58 | 59 | bodyVertices = this->buildPolygon(hitbox.body()); 60 | boundingBox = this->buildBoundingBox(hitbox.bound()); 61 | 62 | _polygonCount.text.setString("Acc: " + std::to_string(_accuracy) + "%, " + std::to_string(bodyVertices.size()) + 63 | " polygons"); 64 | } 65 | 66 | void Example::loadSprites(const std::vector& images) { 67 | _textures.reserve(images.size()); 68 | 69 | for (const auto& image : images) { 70 | sf::Texture texture; 71 | 72 | if (!texture.loadFromFile(image)) { 73 | this->close(); 74 | throw std::runtime_error("Could not find the texture '" + image + "'"); 75 | } 76 | 77 | _textures.push_back(std::move(texture)); 78 | sf::Sprite sprite(_textures.back()); 79 | // Move the sprite in the center of the screen. 80 | const auto spriteSize = sprite.getTextureRect(); 81 | sprite.setOrigin(spriteSize.width / 2, spriteSize.height / 2); 82 | sprite.setPosition(static_cast(_center)); 83 | 84 | _sprites.push_back(std::move(sprite)); 85 | } 86 | } 87 | 88 | std::vector Example::buildPolygon(const std::vector& polygons) { 89 | // Compute the values to center the drawing. 90 | const auto origin = _sprites[_spriteIdx].getOrigin(); 91 | const auto offset = static_cast(_center) - origin; 92 | 93 | std::vector bodyVertices; 94 | 95 | // Convert the polygons into drawable ones. 96 | for (const auto& polygon : polygons) { 97 | sf::VertexArray poly(sf::PrimitiveType::LineStrip, 0); 98 | 99 | for (size_t j = 0; j < polygon.size(); ++j) { 100 | auto p = static_cast(polygon[j]); 101 | 102 | p += offset; 103 | poly.append(sf::Vertex(p, sf::Color::Black)); 104 | } 105 | poly.append(sf::Vertex(static_cast(polygon.front()) + offset, sf::Color::Black)); 106 | bodyVertices.push_back(std::move(poly)); 107 | } 108 | 109 | return bodyVertices; 110 | } 111 | 112 | sf::VertexArray Example::buildBoundingBox(const hb::Polygon& boundingBox) const { 113 | sf::VertexArray bound(sf::PrimitiveType::LineStrip, 0); 114 | 115 | // Compute the values to center the drawing. 116 | const auto origin = _sprites[_spriteIdx].getOrigin(); 117 | const auto offset = static_cast(_center) - origin; 118 | 119 | for (const auto& p : boundingBox) { 120 | bound.append(sf::Vertex(static_cast(p) + offset, sf::Color::Black)); 121 | } 122 | bound.append(sf::Vertex(static_cast(boundingBox.front()) + offset, sf::Color::Black)); 123 | 124 | return bound; 125 | } 126 | 127 | bool Example::handleEvents(const sf::Event& event) { 128 | if (event.type == sf::Event::KeyPressed) { 129 | // clang-format off 130 | switch (event.key.code) { 131 | case sf::Keyboard::Down: 132 | _accuracy = static_cast(std::max(0, static_cast(_accuracy) - kAccIncrement)); 133 | break; 134 | case sf::Keyboard::Up: 135 | _accuracy = static_cast(std::min(100, static_cast(_accuracy) + kAccIncrement)); 136 | break; 137 | case sf::Keyboard::Right: 138 | _spriteIdx = static_cast(std::min(static_cast(_sprites.size()) - 1, static_cast(_spriteIdx) + 1)); 139 | break; 140 | case sf::Keyboard::Left: 141 | _spriteIdx = static_cast(std::max(0, static_cast(_spriteIdx) - 1)); 142 | break; 143 | case sf::Keyboard::Space: 144 | _displaySprite = !_displaySprite; 145 | default: 146 | break; 147 | } 148 | return true; 149 | } 150 | return false; 151 | } 152 | 153 | void Example::close() { 154 | _window.close(); 155 | } 156 | 157 | } /* namespace TestBed */ 158 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-cert-err58-cpp,-clang-analyzer-alpha*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-member-init,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-special-member-functions,- cppcoreguidelines-no-malloc ,-google-readability-todo,-google-runtime-references,-hicpp-member-init,-hicpp-special-member-functions,-hicpp-no-assembler,-llvm-include-order,-llvm-namespace-comment,-llvm-header-guard,-modernize-use-using,-modernize-pass-by-value,-performance-unnecessary-value-param,-readability-identifier-naming,-readability-named-parameter,-readability-implicit-bool-cast,-readability-redundant-declaration,-readability-simplify-boolean-expr' 3 | WarningsAsErrors: '*' 4 | HeaderFilterRegex: '' 5 | AnalyzeTemporaryDtors: false 6 | FormatStyle: none 7 | CheckOptions: 8 | - key: cert-dcl59-cpp.HeaderFileExtensions 9 | value: h,hh,hpp,hxx 10 | - key: cert-err09-cpp.CheckThrowTemporaries 11 | value: '1' 12 | - key: cert-err61-cpp.CheckThrowTemporaries 13 | value: '1' 14 | - key: cert-oop11-cpp.IncludeStyle 15 | value: llvm 16 | - key: google-build-namespaces.HeaderFileExtensions 17 | value: h,hh,hpp,hxx 18 | - key: google-global-names-in-headers.HeaderFileExtensions 19 | value: h 20 | - key: google-readability-braces-around-statements.ShortStatementLines 21 | value: '1' 22 | - key: google-readability-function-size.BranchThreshold 23 | value: '4294967295' 24 | - key: google-readability-function-size.LineThreshold 25 | value: '4294967295' 26 | - key: google-readability-function-size.NestingThreshold 27 | value: '4294967295' 28 | - key: google-readability-function-size.ParameterThreshold 29 | value: '4294967295' 30 | - key: google-readability-function-size.StatementThreshold 31 | value: '800' 32 | - key: google-readability-namespace-comments.ShortNamespaceLines 33 | value: '10' 34 | - key: google-readability-namespace-comments.SpacesBeforeComments 35 | value: '2' 36 | - key: google-runtime-int.SignedTypePrefix 37 | value: int 38 | - key: google-runtime-int.TypeSuffix 39 | value: _t 40 | - key: google-runtime-int.UnsignedTypePrefix 41 | value: uint 42 | - key: hicpp-function-size.BranchThreshold 43 | value: '4294967295' 44 | - key: hicpp-function-size.LineThreshold 45 | value: '4294967295' 46 | - key: hicpp-function-size.NestingThreshold 47 | value: '4294967295' 48 | - key: hicpp-function-size.ParameterThreshold 49 | value: '4294967295' 50 | - key: hicpp-function-size.StatementThreshold 51 | value: '800' 52 | - key: hicpp-named-parameter.IgnoreFailedSplit 53 | value: '0' 54 | - key: misc-argument-comment.StrictMode 55 | value: '0' 56 | - key: misc-assert-side-effect.AssertMacros 57 | value: assert 58 | - key: misc-assert-side-effect.CheckFunctionCalls 59 | value: '0' 60 | - key: misc-dangling-handle.HandleClasses 61 | value: 'std::basic_string_view;std::experimental::basic_string_view' 62 | - key: misc-definitions-in-headers.HeaderFileExtensions 63 | value: ',h,hh,hpp,hxx' 64 | - key: misc-definitions-in-headers.UseHeaderFileExtension 65 | value: '1' 66 | - key: misc-misplaced-widening-cast.CheckImplicitCasts 67 | value: '0' 68 | - key: misc-move-constructor-init.IncludeStyle 69 | value: llvm 70 | - key: misc-sizeof-expression.WarnOnSizeOfCompareToConstant 71 | value: '1' 72 | - key: misc-sizeof-expression.WarnOnSizeOfConstant 73 | value: '1' 74 | - key: misc-sizeof-expression.WarnOnSizeOfThis 75 | value: '1' 76 | - key: misc-string-constructor.LargeLengthThreshold 77 | value: '8388608' 78 | - key: misc-string-constructor.WarnOnLargeLength 79 | value: '1' 80 | - key: misc-suspicious-enum-usage.StrictMode 81 | value: '0' 82 | - key: misc-suspicious-missing-comma.MaxConcatenatedTokens 83 | value: '5' 84 | - key: misc-suspicious-missing-comma.RatioThreshold 85 | value: '0.200000' 86 | - key: misc-suspicious-missing-comma.SizeThreshold 87 | value: '5' 88 | - key: misc-suspicious-string-compare.StringCompareLikeFunctions 89 | value: '' 90 | - key: misc-suspicious-string-compare.WarnOnImplicitComparison 91 | value: '1' 92 | - key: misc-suspicious-string-compare.WarnOnLogicalNotComparison 93 | value: '0' 94 | - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries 95 | value: '1' 96 | - key: modernize-loop-convert.MaxCopySize 97 | value: '16' 98 | - key: modernize-loop-convert.MinConfidence 99 | value: reasonable 100 | - key: modernize-loop-convert.NamingStyle 101 | value: CamelCase 102 | - key: modernize-make-shared.IncludeStyle 103 | value: '1' 104 | - key: modernize-make-shared.MakeSmartPtrFunction 105 | value: 'std::make_shared' 106 | - key: modernize-make-shared.MakeSmartPtrFunctionHeader 107 | value: memory 108 | - key: modernize-make-unique.IncludeStyle 109 | value: '1' 110 | - key: modernize-make-unique.MakeSmartPtrFunction 111 | value: 'std::make_unique' 112 | - key: modernize-make-unique.MakeSmartPtrFunctionHeader 113 | value: memory 114 | - key: modernize-pass-by-value.IncludeStyle 115 | value: llvm 116 | - key: modernize-raw-string-literal.ReplaceShorterLiterals 117 | value: '0' 118 | - key: modernize-replace-auto-ptr.IncludeStyle 119 | value: llvm 120 | - key: modernize-replace-random-shuffle.IncludeStyle 121 | value: llvm 122 | - key: modernize-use-auto.RemoveStars 123 | value: '0' 124 | - key: modernize-use-default-member-init.IgnoreMacros 125 | value: '1' 126 | - key: modernize-use-default-member-init.UseAssignment 127 | value: '0' 128 | - key: modernize-use-emplace.ContainersWithPushBack 129 | value: '::std::vector;::std::list;::std::deque' 130 | - key: modernize-use-emplace.SmartPointers 131 | value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' 132 | - key: modernize-use-emplace.TupleMakeFunctions 133 | value: '::std::make_pair;::std::make_tuple' 134 | - key: modernize-use-emplace.TupleTypes 135 | value: '::std::pair;::std::tuple' 136 | - key: modernize-use-noexcept.ReplacementString 137 | value: '' 138 | - key: modernize-use-noexcept.UseNoexceptFalse 139 | value: '1' 140 | - key: modernize-use-nullptr.NullMacros 141 | value: 'NULL' 142 | - key: modernize-use-transparent-functors.SafeMode 143 | value: '0' 144 | - key: performance-faster-string-find.StringLikeClasses 145 | value: 'std::basic_string' 146 | - key: performance-for-range-copy.WarnOnAllAutoCopies 147 | value: '0' 148 | - key: performance-inefficient-string-concatenation.StrictMode 149 | value: '0' 150 | - key: performance-inefficient-vector-operation.VectorLikeClasses 151 | value: '::std::vector' 152 | - key: performance-type-promotion-in-math-fn.IncludeStyle 153 | value: llvm 154 | - key: readability-braces-around-statements.ShortStatementLines 155 | value: '0' 156 | - key: readability-function-size.BranchThreshold 157 | value: '4294967295' 158 | - key: readability-function-size.LineThreshold 159 | value: '4294967295' 160 | - key: readability-function-size.NestingThreshold 161 | value: '4294967295' 162 | - key: readability-function-size.ParameterThreshold 163 | value: '4294967295' 164 | - key: readability-function-size.StatementThreshold 165 | value: '800' 166 | ... 167 | -------------------------------------------------------------------------------- /cmake/FindSFML.cmake: -------------------------------------------------------------------------------- 1 | # This script locates the SFML library 2 | # ------------------------------------ 3 | # 4 | # Usage 5 | # ----- 6 | # 7 | # When you try to locate the SFML libraries, you must specify which modules you want to use (system, window, graphics, network, audio, main). 8 | # If none is given, the SFML_LIBRARIES variable will be empty and you'll end up linking to nothing. 9 | # example: 10 | # find_package(SFML COMPONENTS graphics window system) # find the graphics, window and system modules 11 | # 12 | # You can enforce a specific version, either MAJOR.MINOR or only MAJOR. 13 | # If nothing is specified, the version won't be checked (i.e. any version will be accepted). 14 | # example: 15 | # find_package(SFML COMPONENTS ...) # no specific version required 16 | # find_package(SFML 2 COMPONENTS ...) # any 2.x version 17 | # find_package(SFML 2.4 COMPONENTS ...) # version 2.4 or greater 18 | # 19 | # By default, the dynamic libraries of SFML will be found. To find the static ones instead, 20 | # you must set the SFML_STATIC_LIBRARIES variable to TRUE before calling find_package(SFML ...). 21 | # Since you have to link yourself all the SFML dependencies when you link it statically, the following 22 | # additional variables are defined: SFML_XXX_DEPENDENCIES and SFML_DEPENDENCIES (see their detailed 23 | # description below). 24 | # In case of static linking, the SFML_STATIC macro will also be defined by this script. 25 | # example: 26 | # set(SFML_STATIC_LIBRARIES TRUE) 27 | # find_package(SFML 2 COMPONENTS network system) 28 | # 29 | # On Mac OS X if SFML_STATIC_LIBRARIES is not set to TRUE then by default CMake will search for frameworks unless 30 | # CMAKE_FIND_FRAMEWORK is set to "NEVER" for example. Please refer to CMake documentation for more details. 31 | # Moreover, keep in mind that SFML frameworks are only available as release libraries unlike dylibs which 32 | # are available for both release and debug modes. 33 | # 34 | # If SFML is not installed in a standard path, you can use the SFML_ROOT CMake (or environment) variable 35 | # to tell CMake where SFML is. 36 | # 37 | # Output 38 | # ------ 39 | # 40 | # This script defines the following variables: 41 | # - For each specified module XXX (system, window, graphics, network, audio, main): 42 | # - SFML_XXX_LIBRARY_DEBUG: the name of the debug library of the xxx module (set to SFML_XXX_LIBRARY_RELEASE is no debug version is found) 43 | # - SFML_XXX_LIBRARY_RELEASE: the name of the release library of the xxx module (set to SFML_XXX_LIBRARY_DEBUG is no release version is found) 44 | # - SFML_XXX_LIBRARY: the name of the library to link to for the xxx module (includes both debug and optimized names if necessary) 45 | # - SFML_XXX_FOUND: true if either the debug or release library of the xxx module is found 46 | # - SFML_XXX_DEPENDENCIES: the list of libraries the module depends on, in case of static linking 47 | # - SFML_LIBRARIES: the list of all libraries corresponding to the required modules 48 | # - SFML_FOUND: true if all the required modules are found 49 | # - SFML_INCLUDE_DIR: the path where SFML headers are located (the directory containing the SFML/Config.hpp file) 50 | # - SFML_DEPENDENCIES: the list of libraries SFML depends on, in case of static linking 51 | # 52 | # example: 53 | # find_package(SFML 2 COMPONENTS system window graphics audio REQUIRED) 54 | # include_directories(${SFML_INCLUDE_DIR}) 55 | # add_executable(myapp ...) 56 | # target_link_libraries(myapp ${SFML_LIBRARIES}) 57 | 58 | # define the SFML_STATIC macro if static build was chosen 59 | if(SFML_STATIC_LIBRARIES) 60 | add_definitions(-DSFML_STATIC) 61 | endif() 62 | 63 | # define the list of search paths for headers and libraries 64 | set(FIND_SFML_PATHS 65 | ${SFML_ROOT} 66 | $ENV{SFML_ROOT} 67 | ~/Library/Frameworks 68 | /Library/Frameworks 69 | /usr/local 70 | /usr 71 | /sw 72 | /opt/local 73 | /opt/csw 74 | /opt) 75 | 76 | # find the SFML include directory 77 | find_path(SFML_INCLUDE_DIR SFML/Config.hpp 78 | PATH_SUFFIXES include 79 | PATHS ${FIND_SFML_PATHS}) 80 | 81 | # check the version number 82 | set(SFML_VERSION_OK TRUE) 83 | if(SFML_FIND_VERSION AND SFML_INCLUDE_DIR) 84 | # extract the major and minor version numbers from SFML/Config.hpp 85 | # we have to handle framework a little bit differently: 86 | if("${SFML_INCLUDE_DIR}" MATCHES "SFML.framework") 87 | set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/Headers/Config.hpp") 88 | else() 89 | set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/SFML/Config.hpp") 90 | endif() 91 | FILE(READ "${SFML_CONFIG_HPP_INPUT}" SFML_CONFIG_HPP_CONTENTS) 92 | STRING(REGEX REPLACE ".*#define SFML_VERSION_MAJOR ([0-9]+).*" "\\1" SFML_VERSION_MAJOR "${SFML_CONFIG_HPP_CONTENTS}") 93 | STRING(REGEX REPLACE ".*#define SFML_VERSION_MINOR ([0-9]+).*" "\\1" SFML_VERSION_MINOR "${SFML_CONFIG_HPP_CONTENTS}") 94 | STRING(REGEX REPLACE ".*#define SFML_VERSION_PATCH ([0-9]+).*" "\\1" SFML_VERSION_PATCH "${SFML_CONFIG_HPP_CONTENTS}") 95 | if (NOT "${SFML_VERSION_PATCH}" MATCHES "^[0-9]+$") 96 | set(SFML_VERSION_PATCH 0) 97 | endif() 98 | math(EXPR SFML_REQUESTED_VERSION "${SFML_FIND_VERSION_MAJOR} * 10000 + ${SFML_FIND_VERSION_MINOR} * 100 + ${SFML_FIND_VERSION_PATCH}") 99 | 100 | # if we could extract them, compare with the requested version number 101 | if (SFML_VERSION_MAJOR) 102 | # transform version numbers to an integer 103 | math(EXPR SFML_VERSION "${SFML_VERSION_MAJOR} * 10000 + ${SFML_VERSION_MINOR} * 100 + ${SFML_VERSION_PATCH}") 104 | 105 | # compare them 106 | if(SFML_VERSION LESS SFML_REQUESTED_VERSION) 107 | set(SFML_VERSION_OK FALSE) 108 | endif() 109 | else() 110 | # SFML version is < 2.0 111 | if (SFML_REQUESTED_VERSION GREATER 10900) 112 | set(SFML_VERSION_OK FALSE) 113 | set(SFML_VERSION_MAJOR 1) 114 | set(SFML_VERSION_MINOR x) 115 | set(SFML_VERSION_PATCH x) 116 | endif() 117 | endif() 118 | endif() 119 | 120 | # find the requested modules 121 | set(SFML_FOUND TRUE) # will be set to false if one of the required modules is not found 122 | foreach(FIND_SFML_COMPONENT ${SFML_FIND_COMPONENTS}) 123 | string(TOLOWER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_LOWER) 124 | string(TOUPPER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_UPPER) 125 | set(FIND_SFML_COMPONENT_NAME sfml-${FIND_SFML_COMPONENT_LOWER}) 126 | 127 | # no suffix for sfml-main, it is always a static library 128 | if(FIND_SFML_COMPONENT_LOWER STREQUAL "main") 129 | # release library 130 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE 131 | NAMES ${FIND_SFML_COMPONENT_NAME} 132 | PATH_SUFFIXES lib64 lib 133 | PATHS ${FIND_SFML_PATHS}) 134 | 135 | # debug library 136 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG 137 | NAMES ${FIND_SFML_COMPONENT_NAME}-d 138 | PATH_SUFFIXES lib64 lib 139 | PATHS ${FIND_SFML_PATHS}) 140 | else() 141 | # static release library 142 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE 143 | NAMES ${FIND_SFML_COMPONENT_NAME}-s 144 | PATH_SUFFIXES lib64 lib 145 | PATHS ${FIND_SFML_PATHS}) 146 | 147 | # static debug library 148 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG 149 | NAMES ${FIND_SFML_COMPONENT_NAME}-s-d 150 | PATH_SUFFIXES lib64 lib 151 | PATHS ${FIND_SFML_PATHS}) 152 | 153 | # dynamic release library 154 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE 155 | NAMES ${FIND_SFML_COMPONENT_NAME} 156 | PATH_SUFFIXES lib64 lib 157 | PATHS ${FIND_SFML_PATHS}) 158 | 159 | # dynamic debug library 160 | find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG 161 | NAMES ${FIND_SFML_COMPONENT_NAME}-d 162 | PATH_SUFFIXES lib64 lib 163 | PATHS ${FIND_SFML_PATHS}) 164 | 165 | # choose the entries that fit the requested link type 166 | if(SFML_STATIC_LIBRARIES) 167 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE) 168 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE}) 169 | endif() 170 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG) 171 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG}) 172 | endif() 173 | else() 174 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE) 175 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE}) 176 | endif() 177 | if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG) 178 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG}) 179 | endif() 180 | endif() 181 | endif() 182 | 183 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG OR SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) 184 | # library found 185 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND TRUE) 186 | 187 | # if both are found, set SFML_XXX_LIBRARY to contain both 188 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) 189 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY debug ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG} 190 | optimized ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) 191 | endif() 192 | 193 | # if only one debug/release variant is found, set the other to be equal to the found one 194 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) 195 | # debug and not release 196 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}) 197 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}) 198 | endif() 199 | if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG) 200 | # release and not debug 201 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) 202 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) 203 | endif() 204 | else() 205 | # library not found 206 | set(SFML_FOUND FALSE) 207 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND FALSE) 208 | set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY "") 209 | set(FIND_SFML_MISSING "${FIND_SFML_MISSING} SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY") 210 | endif() 211 | 212 | # mark as advanced 213 | MARK_AS_ADVANCED(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY 214 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE 215 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG 216 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE 217 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG 218 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE 219 | SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG) 220 | 221 | # add to the global list of libraries 222 | set(SFML_LIBRARIES ${SFML_LIBRARIES} "${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY}") 223 | endforeach() 224 | 225 | # in case of static linking, we must also define the list of all the dependencies of SFML libraries 226 | if(SFML_STATIC_LIBRARIES) 227 | 228 | # detect the OS 229 | if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 230 | set(FIND_SFML_OS_WINDOWS 1) 231 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 232 | set(FIND_SFML_OS_LINUX 1) 233 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 234 | set(FIND_SFML_OS_FREEBSD 1) 235 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 236 | set(FIND_SFML_OS_MACOSX 1) 237 | endif() 238 | 239 | # start with an empty list 240 | set(SFML_DEPENDENCIES) 241 | set(FIND_SFML_DEPENDENCIES_NOTFOUND) 242 | 243 | # macro that searches for a 3rd-party library 244 | macro(find_sfml_dependency output friendlyname) 245 | # No lookup in environment variables (PATH on Windows), as they may contain wrong library versions 246 | find_library(${output} NAMES ${ARGN} PATHS ${FIND_SFML_PATHS} PATH_SUFFIXES lib NO_SYSTEM_ENVIRONMENT_PATH) 247 | if(${${output}} STREQUAL "${output}-NOTFOUND") 248 | unset(output) 249 | set(FIND_SFML_DEPENDENCIES_NOTFOUND "${FIND_SFML_DEPENDENCIES_NOTFOUND} ${friendlyname}") 250 | endif() 251 | endmacro() 252 | 253 | # sfml-system 254 | list(FIND SFML_FIND_COMPONENTS "system" FIND_SFML_SYSTEM_COMPONENT) 255 | if(NOT ${FIND_SFML_SYSTEM_COMPONENT} EQUAL -1) 256 | 257 | # update the list -- these are only system libraries, no need to find them 258 | if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD OR FIND_SFML_OS_MACOSX) 259 | set(SFML_SYSTEM_DEPENDENCIES "pthread") 260 | endif() 261 | if(FIND_SFML_OS_LINUX) 262 | set(SFML_SYSTEM_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} "rt") 263 | endif() 264 | if(FIND_SFML_OS_WINDOWS) 265 | set(SFML_SYSTEM_DEPENDENCIES "winmm") 266 | endif() 267 | set(SFML_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} ${SFML_DEPENDENCIES}) 268 | endif() 269 | 270 | # sfml-network 271 | list(FIND SFML_FIND_COMPONENTS "network" FIND_SFML_NETWORK_COMPONENT) 272 | if(NOT ${FIND_SFML_NETWORK_COMPONENT} EQUAL -1) 273 | 274 | # update the list -- these are only system libraries, no need to find them 275 | if(FIND_SFML_OS_WINDOWS) 276 | set(SFML_NETWORK_DEPENDENCIES "ws2_32") 277 | endif() 278 | set(SFML_DEPENDENCIES ${SFML_NETWORK_DEPENDENCIES} ${SFML_DEPENDENCIES}) 279 | endif() 280 | 281 | # sfml-window 282 | list(FIND SFML_FIND_COMPONENTS "window" FIND_SFML_WINDOW_COMPONENT) 283 | if(NOT ${FIND_SFML_WINDOW_COMPONENT} EQUAL -1) 284 | 285 | # find libraries 286 | if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD) 287 | find_sfml_dependency(X11_LIBRARY "X11" X11) 288 | find_sfml_dependency(XRANDR_LIBRARY "Xrandr" Xrandr) 289 | endif() 290 | 291 | if(FIND_SFML_OS_LINUX) 292 | find_sfml_dependency(UDEV_LIBRARIES "UDev" udev libudev) 293 | endif() 294 | 295 | # update the list 296 | if(FIND_SFML_OS_WINDOWS) 297 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "opengl32" "winmm" "gdi32") 298 | elseif(FIND_SFML_OS_LINUX) 299 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} ${UDEV_LIBRARIES}) 300 | elseif(FIND_SFML_OS_FREEBSD) 301 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} "usbhid") 302 | elseif(FIND_SFML_OS_MACOSX) 303 | set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "-framework OpenGL -framework Foundation -framework AppKit -framework IOKit -framework Carbon") 304 | endif() 305 | set(SFML_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} ${SFML_DEPENDENCIES}) 306 | endif() 307 | 308 | # sfml-graphics 309 | list(FIND SFML_FIND_COMPONENTS "graphics" FIND_SFML_GRAPHICS_COMPONENT) 310 | if(NOT ${FIND_SFML_GRAPHICS_COMPONENT} EQUAL -1) 311 | 312 | # find libraries 313 | find_sfml_dependency(FREETYPE_LIBRARY "FreeType" freetype) 314 | find_sfml_dependency(JPEG_LIBRARY "libjpeg" jpeg) 315 | 316 | # update the list 317 | set(SFML_GRAPHICS_DEPENDENCIES ${FREETYPE_LIBRARY} ${JPEG_LIBRARY}) 318 | set(SFML_DEPENDENCIES ${SFML_GRAPHICS_DEPENDENCIES} ${SFML_DEPENDENCIES}) 319 | endif() 320 | 321 | # sfml-audio 322 | list(FIND SFML_FIND_COMPONENTS "audio" FIND_SFML_AUDIO_COMPONENT) 323 | if(NOT ${FIND_SFML_AUDIO_COMPONENT} EQUAL -1) 324 | 325 | # find libraries 326 | find_sfml_dependency(OPENAL_LIBRARY "OpenAL" openal openal32) 327 | find_sfml_dependency(OGG_LIBRARY "Ogg" ogg) 328 | find_sfml_dependency(VORBIS_LIBRARY "Vorbis" vorbis) 329 | find_sfml_dependency(VORBISFILE_LIBRARY "VorbisFile" vorbisfile) 330 | find_sfml_dependency(VORBISENC_LIBRARY "VorbisEnc" vorbisenc) 331 | find_sfml_dependency(FLAC_LIBRARY "FLAC" FLAC) 332 | 333 | # update the list 334 | set(SFML_AUDIO_DEPENDENCIES ${OPENAL_LIBRARY} ${FLAC_LIBRARY} ${VORBISENC_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) 335 | set(SFML_DEPENDENCIES ${SFML_DEPENDENCIES} ${SFML_AUDIO_DEPENDENCIES}) 336 | endif() 337 | 338 | endif() 339 | 340 | # handle errors 341 | if(NOT SFML_VERSION_OK) 342 | # SFML version not ok 343 | set(FIND_SFML_ERROR "SFML found but version too low (requested: ${SFML_FIND_VERSION}, found: ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH})") 344 | set(SFML_FOUND FALSE) 345 | elseif(SFML_STATIC_LIBRARIES AND FIND_SFML_DEPENDENCIES_NOTFOUND) 346 | set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})") 347 | set(SFML_FOUND FALSE) 348 | elseif(NOT SFML_FOUND) 349 | # include directory or library not found 350 | set(FIND_SFML_ERROR "Could NOT find SFML (missing: ${FIND_SFML_MISSING})") 351 | endif() 352 | if (NOT SFML_FOUND) 353 | if(SFML_FIND_REQUIRED) 354 | # fatal error 355 | message(FATAL_ERROR ${FIND_SFML_ERROR}) 356 | elseif(NOT SFML_FIND_QUIETLY) 357 | # error but continue 358 | message("${FIND_SFML_ERROR}") 359 | endif() 360 | endif() 361 | 362 | # handle success 363 | if(SFML_FOUND AND NOT SFML_FIND_QUIETLY) 364 | message(STATUS "Found SFML ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH} in ${SFML_INCLUDE_DIR}") 365 | endif() 366 | --------------------------------------------------------------------------------