diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index cdc2f30..0000000 --- a/.dir-locals.el +++ /dev/null @@ -1,10 +0,0 @@ -((nil . ( - (projectile-project-name . "hyporo") - (projectile-project-compilation-dir . "") - (projectile-enable-caching . t) - (projectile-project-configure-cmd . "cmake -S . -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=DEBUG") - (projectile-project-compilation-cmd . "cmake --build build ") - (projectile-project-test-cmd . "ctest --test-dir build") - (projectile-project-run-cmd . "cd build/source/creator && ./hyporo") - (cmake-ide-build-dir . "build") -))) diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ba179c7..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "files.associations": { - "any": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "compare": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "forward_list": "cpp", - "list": "cpp", - "map": "cpp", - "set": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "optional": "cpp", - "random": "cpp", - "ratio": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "fstream": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "numbers": "cpp", - "ostream": "cpp", - "semaphore": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "stop_token": "cpp", - "streambuf": "cpp", - "thread": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp", - "variant": "cpp" - } -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 933ebc4..2a3db0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,15 @@ cmake_minimum_required (VERSION 3.16) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +set(USE_SYSTEM_OCCT ON CACHE INTERNAL "") include(${CMAKE_SOURCE_DIR}/cmake/glad.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/stb.cmake) include(${CMAKE_SOURCE_DIR}/cmake/imgui.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/occt.cmake) + project( hyporo @@ -23,7 +30,7 @@ endif() set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") -add_definitions(-DPRECISION_FLOAT) +add_definitions(-DPRECISION_DOUBLE) add_subdirectory(source) diff --git a/cmake/occt.cmake b/cmake/occt.cmake new file mode 100644 index 0000000..f9f2eec --- /dev/null +++ b/cmake/occt.cmake @@ -0,0 +1,73 @@ + +if(USE_SYSTEM_OCCT) + find_package(OpenCASCADE REQUIRED) + + if(OpenCASCADE_FOUND) + message(STATUS "Found OCCT") + endif() +else() + include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake) + + CPMAddPackage( + NAME occt + GIT_REPOSITORY https://github.com/Open-Cascade-SAS/OCCT.git + GIT_TAG V7_6_2 + DOWNLOAD_ONLY YES + ) + + if(occt_ADDED) + # Freaks are using CMAKE_SOURCE_DIR and CMAKE_BINARY_DIR for the project root, fix it + file(READ ${occt_SOURCE_DIR}/CMakeLists.txt filedata_) + string(FIND "${filedata_}" "CMAKE_SOURCE_DIR" need_patch) + + if(NOT ${need_patch} EQUAL -1) + string(REPLACE "CMAKE_SOURCE_DIR" "OCCT_SOURCE_DIR" filedata_ "${filedata_}") + string(REPLACE "CMAKE_BINARY_DIR" "OCCT_BINARY_DIR" filedata_ "${filedata_}") + string(REPLACE "project (OCCT)" "" filedata_ "${filedata_}") + string(PREPEND filedata_ "project(OCCT)\nset(OCCT_BINARY_DIR $\{_OCCT_BINARY_DIR\})\n") + endif() + file(WRITE ${occt_SOURCE_DIR}/CMakeLists.txt "${filedata_}") + + file(GLOB_RECURSE files_to_patch ${occt_SOURCE_DIR}/adm/cmake "occt_*") + + foreach(file_path ${files_to_patch}) + file(READ ${file_path} filedata_) + string(REPLACE "CMAKE_SOURCE_DIR" "OCCT_SOURCE_DIR" filedata_ "${filedata_}") + string(REPLACE "CMAKE_BINARY_DIR" "OCCT_BINARY_DIR" filedata_ "${filedata_}") + file(WRITE ${file_path} "${filedata_}") + endforeach() + + project(OCCT) + # should be better way to pass build directory + set(_OCCT_BINARY_DIR ${occt_BINARY_DIR}) + set(INSTALL_DIR ${occt_BINARY_DIR} CACHE BOOL "" FORCE) + + set(USE_TK OFF CACHE BOOL "" FORCE) + set(USE_FREETYPE OFF CACHE BOOL "" FORCE) + set(USE_TCL OFF CACHE INTERNAL "" FORCE) + + set(BUILD_MODULE_Visualization OFF CACHE BOOL "" FORCE) + set(BUILD_MODULE_ApplicationFramework OFF CACHE BOOL "" FORCE) + set(BUILD_MODULE_Draw OFF CACHE BOOL "" FORCE) + + add_subdirectory(${occt_SOURCE_DIR}) + endif() +endif() + +set(OCCT_LIBRARIES + TKernel + TKService + TKV3d + TKOpenGl + TKBRep + TKBool + TKFillet + TKGeomBase + TKGeomAlgo + TKG3d + TKG2d + TKTopAlgo + TKPrim + TKSTEP + ) +set(OCCT_INCLUDE_DIRS ${OpenCASCADE_INCLUDE_DIR}) diff --git a/cmake/stb.cmake b/cmake/stb.cmake new file mode 100644 index 0000000..1308ff8 --- /dev/null +++ b/cmake/stb.cmake @@ -0,0 +1,49 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake) + +CPMAddPackage( + NAME stb_external + GIT_REPOSITORY https://github.com/nothings/stb.git + GIT_TAG af1a5bc352164740c1cc1354942b1c6b72eacb8a + DOWNLOAD_ONLY TRUE +) + +if(stb_external_ADDED) + project(stb) + + add_library(${PROJECT_NAME} INTERFACE) + add_library(stb::stb ALIAS ${PROJECT_NAME}) + + target_include_directories(${PROJECT_NAME} + INTERFACE + $ + ) + + set_target_properties(${PROJECT_NAME} + PROPERTIES + OUTPUT_NAME stb + ) + + include(GNUInstallDirs) + + install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME} + ) + + install( + EXPORT ${PROJECT_NAME}Targets + FILE ${PROJECT_NAME}Targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) + + install( + DIRECTORY ${stb_external_SOURCE_DIR} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} + COMPONENT devel + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.hpp" + ) +endif() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index bed84bb..4086f48 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(hyporo) add_subdirectory(creator) +add_subdirectory(applications) \ No newline at end of file diff --git a/source/applications/CMakeLists.txt b/source/applications/CMakeLists.txt new file mode 100644 index 0000000..07465bc --- /dev/null +++ b/source/applications/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(periodic) \ No newline at end of file diff --git a/source/applications/periodic/CMakeLists.txt b/source/applications/periodic/CMakeLists.txt new file mode 100644 index 0000000..0181954 --- /dev/null +++ b/source/applications/periodic/CMakeLists.txt @@ -0,0 +1,10 @@ + +add_executable(periodic + periodic.cpp +) + +target_link_libraries(periodic + hyporo-hyplib + hyporo-csg +) + diff --git a/source/applications/periodic/periodic.cpp b/source/applications/periodic/periodic.cpp new file mode 100644 index 0000000..800d7b2 --- /dev/null +++ b/source/applications/periodic/periodic.cpp @@ -0,0 +1,239 @@ +#include "../../hyporo/hyplib/scalar/scalar.hpp" +#include "../../hyporo/hyplib/vector/vector.hpp" +#include "../../hyporo/hyplib/array/array.hpp" +#include "../../hyporo/csg/csg.hpp" + +#include +#include + +using namespace hpr; + +void print(vec3 vs) +{ + for (auto& v : vs) + std::cout << v << " "; + std::cout << std::endl; +} + +class Periodic +{ +protected: + scalar p_alpha; + scalar p_initialRadius; + scalar p_sideLength; + scalar p_filletScale; + vec3 p_direction; + +public: + Periodic() : + p_alpha {0.1}, + p_initialRadius {1}, + p_filletScale {0.8}, + p_direction {} + {} + + Periodic(scalar alpha, scalar initialRadius, scalar filletScale, const vec3& direction) : + p_alpha {alpha}, + p_initialRadius {initialRadius}, + p_filletScale {filletScale}, + p_direction {direction} + {} + + virtual + ~Periodic() = default; + + scalar& alpha() + { + return p_alpha; + } + + scalar& initialRadius() + { + return p_initialRadius; + } + + virtual + scalar sideLength() = 0; + + virtual + scalar gamma() = 0; + + scalar& filletScale() + { + return p_filletScale; + } + + scalar radius() const + { + return p_initialRadius / (1. - p_alpha); + } + + scalar filletRadius() + { + scalar analytical = p_initialRadius * sqrt(2) / sqrt(1 - cos(gamma())) - radius(); + return analytical * p_filletScale; + } + + vec3& direction() + { + return p_direction; + } + + virtual + void build() = 0; +}; + +class Simple : public Periodic, public csg::Shape +{ +public: + + Simple() : + csg::Shape {}, + Periodic {0.01, 1, 0.8, vec3(1., 0., 0.)} + {} + + Simple(scalar alpha, const vec3& direction, scalar filletScale = 0.8) : + Simple {} + { + p_alpha = alpha; + p_direction = direction; + p_filletScale = filletScale; + } + + Simple(scalar alpha, scalar initialRadius, scalar filletScale, const vec3& direction) : + Periodic {alpha, initialRadius, filletScale, direction} + {} + + ~Simple() override = default; + + scalar sideLength() override + { + return 2 * initialRadius(); + } + + scalar gamma() override + { + return hpr::PI - 2 * 0.5 * 0.5 * hpr::PI; + } + + csg::Shape lattice() + { + csg::Shape lattice; + darray spheres; + + for (int zn = 0; zn < 3; ++zn) + { + scalar z = zn * sideLength(); + for (int yn = 0; yn < 3; ++yn) + { + scalar y = yn * sideLength(); + for (int xn = 0; xn < 3; ++xn) + { + scalar x = xn * sideLength(); + spheres.push(csg::sphere(vec3(x, y, z), radius())); + } + } + } + + lattice = csg::fuse({spheres.front()}, spheres.slice(spheres.begin() + 1, spheres.end())); + + if (filletScale() > 0) + { + lattice = lattice.scale({0, 0, 0}, 1e+2); + lattice = lattice.fillet(lattice.edges(), filletRadius() * 1e+2); + lattice = lattice.scale({0, 0, 0}, 1e-2); + } + + return lattice; + } + + csg::Shape boxCell() + { + scalar length = sideLength() * sqrt(2); + scalar width = sideLength() * sqrt(2); + scalar height = sideLength(); + scalar xl = sqrt(pow(length, 2) * 0.5); + scalar yw = xl; + scalar zh = height; + darray edges { + csg::Edge({xl, 0, 0}, {0, yw, 0}), + csg::Edge({0, yw, 0}, {0, yw, zh}), + csg::Edge({0, yw, zh}, {xl, 0, zh}), + csg::Edge({xl, 0, zh}, {xl, 0, 0}) + }; + csg::Face plgm {edges}; + + vec3 localX {csg::Surface(plgm).normal(0, 0)}; + vec3 localZ = vec3(0, 0, 1); + vec3 localY = cross(localX, localZ); + csg::Shape cell = plgm.extrude(localX, width); + + scalar angle; + hpr::vec3 normal; + + for (auto& face : cell.faces()) + { + normal = csg::Surface(csg::Face(face)).normal(0, 0); + angle = hpr::angle(localX, normal); + + if (face.tshape().Orientation() == TopAbs_FORWARD) + { + normal = -normal; + angle = hpr::angle(localX, normal); + } + + if (equal(angle, 0.)) + face.label("periodic-south"); + else if (equal(angle, hpr::PI)) + face.label("periodic-north"); + + angle = hpr::angle(localY, normal); + if (equal(angle, 0.)) + face.label("periodic-east"); + else if (equal(angle, hpr::PI)) + face.label("periodic-west"); + + angle = hpr::angle(localZ, normal); + if (equal(angle, hpr::PI)) + face.label("periodic-down"); + else if (equal(angle, 0.)) + face.label("periodic-up"); + } + + return cell; + } + + csg::Shape hexagonalPrismCell() + { + return csg::Shape(); + } + + void build() override + { + if (direction() == vec3(1., 0., 0.) || direction() == vec3(1., 0., 0.) || direction() == vec3(0., 0., 1.)) + p_shape = csg::cut(boxCell(), lattice()).tshape(); + else if (direction() == vec3(1., 1., 1.)) + p_shape = csg::cut(hexagonalPrismCell(), lattice()).tshape(); + else + throw std::runtime_error("Undefined cell for passed direction"); + + + p_shape = this->translate(-this->center()).tshape(); + p_shape = this->rotate(this->center(), {0, 0, 1}, 45).tshape(); + + for (auto& face : faces()) + if (face.label() == "default") + face.label("wall"); + } + +}; + + +int main(int argc, char** argv) +{ + Simple simple {0.01, {1., 0., 0.}}; + simple.build(); + + //simple.dump("simpleTest.step", csg::Shape::Format::STEP); + return 0; +} \ No newline at end of file diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 6e46b49..8f80dda 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1,9 +1,9 @@ include_directories( - ../hyporo/hyplib/vector - ../hyporo/hyplib/scalar - ../hyporo/hyplib/integer - ../hyporo/hyplib/matrix + ../hyporo/hyplib + ../hyporo/window_system + ../hyporo/gpu + ../hyporo/hmesh ) add_executable(hyporo @@ -14,5 +14,9 @@ add_executable(hyporo target_link_libraries(hyporo PUBLIC + hyporo-hyplib + hyporo-window-system hyporo-gpu - imgui) + hyporo-mesh + imgui +) diff --git a/source/creator/creator.cpp b/source/creator/creator.cpp index 42255ea..7c1e5dd 100644 --- a/source/creator/creator.cpp +++ b/source/creator/creator.cpp @@ -1,14 +1,142 @@ -#include "../hyporo/gpu/window_system.hpp" -#include "../hyporo/gpu/glfw/window.hpp" +#include "../window_system/window_system.hpp" +#include "../window_system/glfw/window.hpp" +#include "../gpu/device.hpp" +#include "../gpu/opengl/device.hpp" +#include "../hmesh/mesh.hpp" +#include "../hyplib/matrix/matrix.hpp" +#include "../hyplib/vector/vector.hpp" #include #include #include +#include -int main(void) +struct ConstantBuffer { + hpr::mat4 World; + hpr::mat4 View; + hpr::mat4 Projection; + + hpr::vec4 Eye; + + float Color[4]; + float Offset[4]; + + float Scale; + int AllWhiteLight; + + int Pad[2]; +}; + +struct ObjectBuffer { + hpr::mat4 ObjectTransform; + + hpr::vec4 DiffuseColor; + + hpr::vec4 FalloffColor; + float FalloffPower; + + int UseDiffuseMap; + int UseSpecularMap; + int UseNormalMap; + int UseFalloff; + int UseAlpha; + int UseReflectMap; + + float Specular; + float SpecularPower; + float ReflectPower; + + float Ambient; + + int Lit; + int LightExclusion[3]; + + int Pad[1]; +}; + +int main() { - hpr::gpu::WindowSystem* ws = hpr::gpu::WindowSystem::create(hpr::gpu::WindowContext::Provider::GLFW); - hpr::gpu::Window* w = ws->newWindow(); - w->init("test", hpr::gpu::Window::Style::Windowed, 0, 0, 600, 400, nullptr, nullptr); + using namespace hpr; + + gpu::WindowSystem* ws = gpu::WindowSystem::create(gpu::WindowContext::Provider::GLFW); + gpu::Window* w = ws->newWindow(); + w->init("test", gpu::Window::Style::Windowed, 0, 0, 600, 400, nullptr, nullptr); + + if (gpu::opengl::Device::loadLoader()) + std::cerr << "Load gl loader error" << std::endl; + + gpu::Device* device; + gpu::Device::create(&device, gpu::Device::DeviceAPI::OpenGL); + device->initialize(); + gpu::Shader* vertexShader; + device->createVertexShader(&vertexShader, "shaders/base.vert.glsl", "VS"); + gpu::Shader* fragmentShader; + device->createFragmentShader(&fragmentShader, "shaders/base.frag.glsl", "FS"); + gpu::ShaderProgram* shaderProgram; + device->createShaderProgram(&shaderProgram); + device->attachShader(shaderProgram, vertexShader); + device->attachShader(shaderProgram, fragmentShader); + device->linkProgram(shaderProgram); + + mesh::Mesh mesh; + mesh.addVertex(-1, 1, 0.5); + mesh.addVertex(1, 1, 0.5); + mesh.addVertex(1, -1, 0.5); + mesh.addVertex(-1, -1, 0.5); + mesh.addEdge(mesh.vertex(0), mesh.vertex(1)); + mesh.addEdge(mesh.vertex(1), mesh.vertex(2)); + mesh.addEdge(mesh.vertex(2), mesh.vertex(3)); + mesh.addEdge(mesh.vertex(3), mesh.vertex(0)); + mesh.addEdge(mesh.vertex(0), mesh.vertex(2)); + mesh.addFace(mesh.edge(0), mesh.edge(1), mesh.edge(4)); + mesh.addFace(mesh.edge(2), mesh.edge(3), mesh.edge(4)); + + darray data (3 * 6, 0.f); + auto arr = mesh.face(0)->vertices() + mesh.face(1)->vertices(); + for (auto n = 0; n < arr.size(); ++n) + for (auto k = 0; k < 3; ++k) + data[k + 3 * n] = *(arr[n]->data() + k); + darray indices (6, 0); + for (auto n = 0; n < arr.size(); ++n) + indices[n] = mesh.indexOf(arr[n]); + + gpu::Buffer* vertexBuffer; + device->createVertexBuffer(&vertexBuffer, sizeof(float) * data.size(), (char*)data.data()); + gpu::Buffer* indexBuffer; + device->createIndexBuffer(&indexBuffer, sizeof(unsigned short) * indices.size(), (char*)indices.data()); + + gpu::Buffer* constantBuffer; + device->createUniformBuffer(&constantBuffer, sizeof(ConstantBuffer), nullptr); + gpu::Buffer* objectBuffer; + device->createUniformBuffer(&objectBuffer, sizeof(ObjectBuffer), nullptr); + + ConstantBuffer constantData; + constantData.Color[0] = 1; + constantData.Color[1] = 0; + constantData.Color[2] = 0; + constantData.Color[3] = 1; + constantData.Offset[0] = 0; + constantData.Offset[1] = 0; + constantData.Offset[2] = 0; + constantData.Offset[3] = 1; + constantData.Scale = 1; + ObjectBuffer objectData; + objectData.Lit = 1; + objectData.DiffuseColor = vec4(1, 1, 1, 1); + objectData.Specular = 0.25; + objectData.SpecularPower = 1; + objectData.UseDiffuseMap = 0; + objectData.UseSpecularMap = 0; + objectData.UseNormalMap = 0; + objectData.UseFalloff = 0; + objectData.FalloffColor = vec4(1, 1, 1, 1); + objectData.FalloffPower = 1; + objectData.Ambient = 1; + objectData.UseAlpha = 0; + objectData.UseReflectMap = 0; + + device->useShaderProgram(shaderProgram); + device->useUniformBuffer(constantBuffer, 0); + device->useUniformBuffer(objectBuffer, 1); IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -19,13 +147,16 @@ int main(void) ImGui::StyleColorsDark(); - ImGui_ImplGlfw_InitForOpenGL(dynamic_cast(w)->instance(), true); - ImGui_ImplOpenGL3_Init("#version 330"); + ImGui_ImplGlfw_InitForOpenGL(dynamic_cast(w)->instance(), true); + ImGui_ImplOpenGL3_Init("#version 420"); while (w->isOpen()) { - dynamic_cast(w)->pollEvents(); - + dynamic_cast(w)->pollEvents(); + device->useShaderProgram(shaderProgram); + device->useVertexBuffer(vertexBuffer, 0, 0); + device->useIndexBuffer(indexBuffer, 0); + dynamic_cast(device)->Draw(2, 0, 0); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -33,24 +164,33 @@ int main(void) bool yes = true; ImGui::ShowDemoWindow(&yes); - ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + ImGui::Begin("Hello, world!"); { - if (ImGui::Button("Exit")) // Buttons return true when clicked (most widgets return true when edited/activated) - w->state(hpr::gpu::Window::State::Closed); + if (ImGui::Button("Exit")) + w->state(gpu::Window::State::Closed); ImGui::End(); } ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - dynamic_cast(w)->swapBuffers(); + dynamic_cast(w)->swapBuffers(); } ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); + device->destroyShaderProgram(shaderProgram, false); + device->destroyShader(vertexShader); + device->destroyShader(fragmentShader); + device->destroyBuffer(vertexBuffer); + device->destroyBuffer(indexBuffer); + device->destroyBuffer(constantBuffer); + device->destroyBuffer(objectBuffer); + delete dynamic_cast(device); + ws->destroyWindow(w); - hpr::gpu::WindowSystem::destroy(ws); + gpu::WindowSystem::destroy(ws); return 0; } diff --git a/source/hyporo/csg/CMakeLists.txt b/source/hyporo/csg/CMakeLists.txt index e69de29..632cc01 100644 --- a/source/hyporo/csg/CMakeLists.txt +++ b/source/hyporo/csg/CMakeLists.txt @@ -0,0 +1,44 @@ + +include_directories( + . + ../hyplib/integer + ../hyplib/scalar + ../hyplib/vector +) + +add_library(hyporo-csg STATIC + + # Header files + shape.hpp + + # Source files + shape.cpp +) + +target_link_libraries(hyporo-csg + hyporo-hyplib + ${OCCT_LIBRARIES} + ) + +target_include_directories(hyporo-csg + PUBLIC + ${OCCT_INCLUDE_DIRS} + ) + +if(WITH_GTESTS) + add_executable(hyporo-csg-test + tests/csg-test.cpp + shape.cpp + ) + + target_link_libraries(hyporo-csg-test + GTest::gtest_main + ${OCCT_LIBRARIES} + ) + + target_include_directories(hyporo-csg-test + PUBLIC + ${OCCT_INCLUDE_DIRS} + ) + gtest_add_tests(TARGET hyporo-csg-test) +endif() diff --git a/source/hyporo/csg/compound.hpp b/source/hyporo/csg/compound.hpp new file mode 100644 index 0000000..4e083eb --- /dev/null +++ b/source/hyporo/csg/compound.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "shape.hpp" +#include "solid.hpp" + + +namespace hpr::csg +{ + + class Compound : public Shape + { + + public: + + Compound() = default; + + ~Compound() override = default; + + explicit + Compound(const Shape& shape) : + Shape {shape.type() == Type::Compound ? shape : throw std::runtime_error("")} + {} + + explicit + Compound(const darray& shapes) : + Shape {} + { + BRep_Builder builder; + TopoDS_Compound compound; + builder.MakeCompound(compound); + + for (auto& shape : shapes) + builder.Add(compound, shape.tshape()); + + p_shape = compound; + } + + [[nodiscard]] + TopoDS_Compound tcast() const + { + return TopoDS::Compound(p_shape); + } + }; + +} diff --git a/source/hyporo/csg/csg.hpp b/source/hyporo/csg/csg.hpp new file mode 100644 index 0000000..613c95a --- /dev/null +++ b/source/hyporo/csg/csg.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "shape.hpp" +#include "vertex.hpp" +#include "edge.hpp" +#include "wire.hpp" +#include "face.hpp" +#include "shell.hpp" +#include "solid.hpp" +#include "compound.hpp" +#include "geometry.hpp" +#include "surface.hpp" + + +namespace hpr::csg +{} \ No newline at end of file diff --git a/source/hyporo/csg/edge.hpp b/source/hyporo/csg/edge.hpp new file mode 100644 index 0000000..c04f316 --- /dev/null +++ b/source/hyporo/csg/edge.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "shape.hpp" +#include "vertex.hpp" + + +namespace hpr::csg +{ + +class Edge : public Shape +{ + +public: + + Edge() = default; + + ~Edge() override = default; + + explicit + Edge(const Shape& shape) : + Shape {shape.type() == Type::Edge ? shape : throw std::runtime_error("")} + {} + + Edge(const vec3& p1, const vec3& p2) : + Shape {BRepBuilderAPI_MakeEdge(gp_Pnt(p1[0], p1[1], p1[2]), gp_Pnt(p2[0], p2[1], p2[2])).Shape()} + {} + + Edge(const Vertex& v1, const Vertex& v2) : + Shape {BRepBuilderAPI_MakeEdge(v1.tcast(), v2.tcast()).Shape()} + {} + + [[nodiscard]] + TopoDS_Edge tcast() const + { + return TopoDS::Edge(p_shape); + } +}; + +} diff --git a/source/hyporo/csg/face.hpp b/source/hyporo/csg/face.hpp new file mode 100644 index 0000000..d68199d --- /dev/null +++ b/source/hyporo/csg/face.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "shape.hpp" +#include "wire.hpp" + + +namespace hpr::csg +{ + +class Face : public Shape +{ + +public: + + Face() = default; + + ~Face() override = default; + + explicit + Face(const Shape& shape) : + Shape {shape.type() == Type::Face ? shape : throw std::runtime_error("")} + {} + + explicit + Face(const Wire& wire) : + Shape {BRepBuilderAPI_MakeFace(wire.tcast()).Shape()} + {} + + explicit + Face(const darray& edges) : + Face {Wire(edges)} + {} + + [[nodiscard]] + TopoDS_Face tcast() const + { + return TopoDS::Face(p_shape); + } +}; + +} + diff --git a/source/hyporo/csg/geometry.hpp b/source/hyporo/csg/geometry.hpp new file mode 100644 index 0000000..4d5f0ca --- /dev/null +++ b/source/hyporo/csg/geometry.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "shape.hpp" + + +namespace hpr::csg +{ + +class Geometry +{ + +public: + + Geometry() = default; + + virtual + ~Geometry() = default; + +}; + +} \ No newline at end of file diff --git a/source/hyporo/csg/shape.cpp b/source/hyporo/csg/shape.cpp new file mode 100644 index 0000000..5f06224 --- /dev/null +++ b/source/hyporo/csg/shape.cpp @@ -0,0 +1,27 @@ +#include "shape.hpp" + + +bool std::less::operator()(const hpr::csg::Shape& s1, const hpr::csg::Shape& s2) const +{ + return s1.tshape().HashCode(std::numeric_limits::max()) < + s2.tshape().HashCode(std::numeric_limits::max()); +} +namespace hpr::csg +{ + +std::map Shape::metadata; + + +Shape sphere(vec3 center, double radius) +{ + BRepPrimAPI_MakeSphere prim {gp_Pnt(center[0], center[1], center[2]), radius}; + return Shape {prim.Shape()}; +} + +Shape box(vec3 corner, double dx, double dy, double dz) +{ + BRepPrimAPI_MakeBox prim {gp_Pnt(corner[0], corner[1], corner[2]), dx, dy, dz}; + return Shape {prim.Shape()}; +} + +} diff --git a/source/hyporo/csg/shape.hpp b/source/hyporo/csg/shape.hpp new file mode 100644 index 0000000..30ed396 --- /dev/null +++ b/source/hyporo/csg/shape.hpp @@ -0,0 +1,489 @@ +#pragma once + +#include "../hyplib/scalar/scalar.hpp" +#include "../hyplib/vector/vector.hpp" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + + +namespace hpr::csg +{ + class Shape; +} + +namespace std +{ + template<> + struct less + { + bool operator() (const hpr::csg::Shape& s1, const hpr::csg::Shape& s2) const; + }; +} + +namespace hpr::csg +{ + +// Forward declaration of friend functions + +double distance(const Shape& lhs, const Shape& rhs); + +Shape fuse(const Shape& lhs, const Shape& rhs); + +Shape fuse(const darray& args, const darray& tools); + +Shape common(const Shape& lhs, const Shape& rhs); + +Shape cut(const Shape& lhs, const Shape& rhs); + + +// Class declaration + +class Shape +{ + +public: + + enum class Type + { + Compound, + Compsolid, + Solid, + Shell, + Face, + Wire, + Edge, + Vertex, + Shape, + Unknown + }; + + enum class Format + { + Unknown, + STEP + }; + + class Metadata + { + public: + std::string label; + public: + Metadata() : + label {"default"} + {} + void merge(const Metadata& data) + { + if (label == "default" && data.label != "default") + label = data.label; + } + }; + +public: + + // TODO: clean up map + static + std::map metadata; + +protected: + + TopoDS_Shape p_shape; + +public: + + [[nodiscard]] + TopoDS_Shape tshape() const { return p_shape; } + + Shape(const TopoDS_Shape& s) : + p_shape {s} + {} + + Shape(TopoDS_Shape&& s) noexcept: + p_shape {std::forward(s)} + {} + +public: + + Shape() : + p_shape {} + {} + + virtual + ~Shape() = default; + //{ + //if (metadata.contains(*this)) + // metadata.erase(*this); + //} + + [[nodiscard]] + Type type() const + { + switch (p_shape.ShapeType()) + { + case TopAbs_VERTEX: + return Type::Vertex; + case TopAbs_EDGE: + return Type::Edge; + case TopAbs_FACE: + return Type::Face; + case TopAbs_WIRE: + return Type::Wire; + case TopAbs_SHELL: + return Type::Shell; + case TopAbs_SOLID: + return Type::Solid; + case TopAbs_COMPOUND: + return Type::Compound; + case TopAbs_COMPSOLID: + return Type::Compsolid; + case TopAbs_SHAPE: + return Type::Shape; + default: + return Type::Unknown; + } + } + + [[nodiscard]] + vec3 center() const + { + GProp_GProps props; + + switch (type()) + { + case Type::Solid: + case Type::Compsolid: + case Type::Compound: + BRepGProp::VolumeProperties(p_shape, props); + case Type::Shell: + case Type::Face: + BRepGProp::SurfaceProperties(p_shape, props); + default: + BRepGProp::LinearProperties(p_shape, props); + } + + gp_Pnt center {props.CentreOfMass()}; + + return vec3 {center.X(), center.Y(), center.Z()}; + } + + [[nodiscard]] + double length() const + { + GProp_GProps props; + + switch (type()) + { + case Type::Vertex: + return 0; + default: + BRepGProp::LinearProperties(p_shape, props); + return props.Mass(); + } + } + + [[nodiscard]] + double area() const + { + GProp_GProps props; + + switch (type()) + { + case Type::Vertex: + case Type::Edge: + case Type::Wire: + return 0; + default: + BRepGProp::SurfaceProperties(p_shape, props); + return props.Mass(); + } + } + + [[nodiscard]] + double volume() const + { + GProp_GProps props; + + switch (type()) + { + case Type::Compsolid: + case Type::Solid: + BRepGProp::VolumeProperties(p_shape, props); + return props.Mass(); + + default: + return 0; + } + } + + void label(const std::string& label) const + { + metadata[*this].label = label; + } + + [[nodiscard]] + std::string label() const + { + return metadata[*this].label; + } + + void dump(const std::string& filename, Format format) const + { + if (p_shape.IsNull()) + throw std::runtime_error("Trying to export null shape"); + + switch (format) + { + case Format::STEP: + { + STEPControl_Writer writer; + Interface_Static::SetCVal("xstep.cascade.unit", "MM"); + Interface_Static::SetCVal("write.step.unit", "MM"); + Interface_Static::SetIVal("write.step.nonmanifold", 1); + + writer.Transfer(p_shape, STEPControl_AsIs); + writer.Write(filename.c_str()); + break; + } + case Format::Unknown: + default: + throw std::invalid_argument("Unknown export format"); + } + } + + // + + [[nodiscard]] + sarray boundingBox() const + { + Bnd_Box bbox; + BRepBndLib::Add(p_shape, bbox, true); + gp_Pnt p1 {bbox.CornerMin()}; + gp_Pnt p2 {bbox.CornerMax()}; + + return sarray {{p1.X(), p1.Y(), p1.Z()}, {p2.X(), p2.Y(), p2.Z()}}; + } + + void incrementalMesh(double deflection) + { + BRepTools::Clean(p_shape); + BRepMesh_IncrementalMesh(p_shape, deflection, true); + } + + darray subShapes(Type type) const + { + darray subshapes; + for (TopExp_Explorer exp(p_shape, static_cast(type)); exp.More(); exp.Next()) + subshapes.push(Shape(exp.Current())); + return subshapes; + } + + darray edges() const + { + return subShapes(Type::Edge); + } + + darray faces() const + { + return subShapes(Type::Face); + } + + darray shells() const + { + return subShapes(Type::Shell); + } + + // Member functions: transformations + + Shape translate(const vec3& dir) + { + gp_Trsf transform; + transform.SetTranslation(gp_Vec(dir[0], dir[1], dir[2])); + BRepBuilderAPI_Transform builder {p_shape, transform, true}; + + return builder.Shape(); + } + + Shape rotate(const vec3& pos, const vec3& axis, double angle) + { + gp_Trsf transform; + transform.SetRotation(gp_Ax1({pos[0], pos[1], pos[2]}, {axis[0], axis[1], axis[2]}), radians(angle)); + BRepBuilderAPI_Transform builder {p_shape, transform, true}; + + return builder.Shape(); + } + + Shape scale(const vec3& center, double scale) + { + gp_Trsf transform; + transform.SetScale(gp_Pnt(center[0], center[1], center[2]), scale); + BRepBuilderAPI_Transform builder {p_shape, transform, true}; + + return builder.Shape(); + } + + Shape& scaled(const vec3& center, double scale) + { + p_shape = this->scale(center, scale).p_shape; + return *this; + } + + Shape extrude(const vec3& dir, double length) + { + BRepPrimAPI_MakePrism builder {p_shape, length * gp_Vec(dir[0], dir[1], dir[2]), true}; + + for (auto& type : { Type::Solid, Type::Face, Type::Edge, Type::Vertex }) + for (TopExp_Explorer exp {p_shape, static_cast(type)}; exp.More(); exp.Next()) + { + auto data = metadata[Shape(exp.Current())]; + + for (auto& mod : builder.Generated(exp.Current())) + metadata[Shape(mod)].merge(data); + } + + return builder.Shape(); + } + + Shape fillet(darray edges, double radius) + { + BRepFilletAPI_MakeFillet fillet {p_shape}; + + for (auto& e : edges) + fillet.Add(radius, TopoDS::Edge(e.p_shape)); + fillet.Build(); + + return fillet.Shape(); + } + + // Friend functions + + friend + double distance(const Shape& lhs, const Shape& rhs) + { + return BRepExtrema_DistShapeShape(lhs.tshape(), rhs.tshape()).Value(); + } + + friend + Shape fuse(const Shape& lhs, const Shape& rhs) + { + BRepAlgoAPI_Fuse builder {lhs.p_shape, rhs.p_shape}; + builder.Build(); + return Shape {builder.Shape()}; + } + + friend + Shape fuse(const darray& args, const darray& tools) + { + BRepAlgoAPI_Fuse builder; + NCollection_List args_, tools_; + for (auto& arg : args) + args_.Append(arg.tshape()); + for (auto& tool : tools) + tools_.Append(tool.tshape()); + builder.SetArguments(args_); + builder.SetTools(tools_); + builder.Build(); + return Shape {builder.Shape()}; + } + + friend + Shape common(const Shape& lhs, const Shape& rhs) + { + BRepAlgoAPI_Common builder {lhs.p_shape, rhs.p_shape}; + builder.Build(); + return Shape {builder.Shape()}; + } + + friend + Shape cut(const Shape& lhs, const Shape& rhs) + { + BRepAlgoAPI_Cut builder {lhs.p_shape, rhs.p_shape}; + builder.Build(); + for (auto& type : { Type::Solid, Type::Face, Type::Edge, Type::Vertex }) + for (TopExp_Explorer exp {lhs.p_shape, static_cast(type)}; exp.More(); exp.Next()) + { + auto data = metadata[Shape(exp.Current())]; + + for (auto& mod : builder.Modified(exp.Current())) + metadata[Shape(mod)].merge(data); + } + for (auto& type : { Type::Solid, Type::Face, Type::Edge, Type::Vertex }) + for (TopExp_Explorer exp {rhs.p_shape, static_cast(type)}; exp.More(); exp.Next()) + { + auto data = metadata[Shape(exp.Current())]; + + for (auto& mod : builder.Modified(exp.Current())) + metadata[Shape(mod)].merge(data); + } + return Shape {builder.Shape()}; + } + +}; + +// Global functions: primitives + +Shape sphere(vec3 center, double radius); + +Shape box(vec3 corner, double dx, double dy, double dz); + +} + + + + diff --git a/source/hyporo/csg/shell.hpp b/source/hyporo/csg/shell.hpp new file mode 100644 index 0000000..9044425 --- /dev/null +++ b/source/hyporo/csg/shell.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "shape.hpp" +#include "face.hpp" + + +namespace hpr::csg +{ + +class Shell : public Shape +{ + +public: + + Shell() = default; + + ~Shell() override = default; + + explicit + Shell(const Shape& shape) : + Shape {shape.type() == Type::Shell ? shape : throw std::runtime_error("")} + {} + + explicit + Shell(const darray& faces) : + Shape {} + { + BRep_Builder builder; + TopoDS_Shell shell; + builder.MakeShell(shell); + + for (auto& shape : faces) + switch (shape.type()) + { + case Type::Face: + builder.Add(shell, Face(shape).tcast()); + break; + default: + throw std::runtime_error(""); + } + + p_shape = shell; + } + + [[nodiscard]] + TopoDS_Shell tcast() const + { + return TopoDS::Shell(p_shape); + } +}; + +} diff --git a/source/hyporo/csg/solid.hpp b/source/hyporo/csg/solid.hpp new file mode 100644 index 0000000..88320a1 --- /dev/null +++ b/source/hyporo/csg/solid.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "shape.hpp" +#include "shell.hpp" + + +namespace hpr::csg +{ + + class Solid : public Shape + { + + public: + + Solid() = default; + + ~Solid() override = default; + + explicit + Solid(const Shape& shape) : + Shape {shape.type() == Type::Solid ? shape : throw std::runtime_error("")} + {} + + explicit + Solid(const Shell& shell) : + Shape {} + { + BRep_Builder builder; + TopoDS_Solid solid; + + builder.MakeSolid(solid); + builder.Add(solid, shell.tcast()); + + p_shape = solid; + } + + [[nodiscard]] + TopoDS_Solid tcast() const + { + return TopoDS::Solid(p_shape); + } + }; + +} + diff --git a/source/hyporo/csg/surface.hpp b/source/hyporo/csg/surface.hpp new file mode 100644 index 0000000..cf95fb5 --- /dev/null +++ b/source/hyporo/csg/surface.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "geometry.hpp" +#include "face.hpp" + + +namespace hpr::csg +{ + + class Surface : public Geometry + { + + protected: + + Handle(Geom_Surface) p_surface; + + public: + + Surface() = default; + + ~Surface() override = default; + + explicit + Surface(const Face& face) : + Geometry {}, + p_surface {BRep_Tool::Surface(face.tcast())} + {} + + [[nodiscard]] + Handle(Geom_Surface) tcast() const + { + return p_surface; + } + + [[nodiscard]] + vec3 value(double u, double v) const + { + gp_Pnt p {p_surface->Value(u, v)}; + + return vec3 {p.X(), p.Y(), p.Z()}; + } + + [[nodiscard]] + vec3 normal(double u, double v) const + { + GeomLProp_SLProps props {p_surface, u, v, 1, 1e-8}; + gp_Dir dir {props.Normal()}; + + return vec3 {dir.X(), dir.Y(), dir.Z()}; + } + + vec3 normal() + { + gp_Vec du, dv; + gp_Pnt p; + p_surface->D1(0, 0, p, du, dv); + gp_Vec dir {du ^ dv}; + return vec3 {dir.X(), dir.Y(), dir.Z()}; + } + }; + +} + + diff --git a/source/hyporo/csg/tests/csg-test.cpp b/source/hyporo/csg/tests/csg-test.cpp new file mode 100644 index 0000000..289e12c --- /dev/null +++ b/source/hyporo/csg/tests/csg-test.cpp @@ -0,0 +1,24 @@ +#include +#include "../csg.hpp" + +TEST(csgTest, Shape) +{ + using namespace hpr; + double radius = 1.; + double volume = 4. / 3. * PI; + auto sphere = csg::sphere({0, 0, 0}, radius); + EXPECT_TRUE(equal(sphere.volume(), volume, 1e-6)); + auto box = csg::box({0, 0, 0}, 1, 1, 1); + EXPECT_TRUE(equal(box.volume(), 1)); + auto edge = csg::Edge(); + int n = 0; + for (auto& face : box.subShapes(csg::Shape::Type::Face)) + { + std::stringstream name; + name << "face" << n; + csg::Face(face).label(name.str()); + ++n; + } + box.scale(box.center(), 5); + EXPECT_EQ(box.subShapes(csg::Shape::Type::Face)[2].label(), "face2"); +} \ No newline at end of file diff --git a/source/hyporo/csg/vertex.hpp b/source/hyporo/csg/vertex.hpp new file mode 100644 index 0000000..031a2ba --- /dev/null +++ b/source/hyporo/csg/vertex.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "shape.hpp" + + +namespace hpr::csg +{ + +class Vertex : public Shape +{ + +public: + + Vertex() = default; + + ~Vertex() override = default; + + explicit + Vertex(const Shape& shape) : + Shape {shape.type() == Type::Vertex ? shape : throw std::runtime_error("")} + {} + + explicit + Vertex(const vec3& point) : + Shape {BRepBuilderAPI_MakeVertex(gp_Pnt(point[0], point[1], point[2])).Shape()} + {} + + [[nodiscard]] + TopoDS_Vertex tcast() const + { + return TopoDS::Vertex(p_shape); + } + + [[nodiscard]] + vec3 cast() const + { + gp_Pnt point = BRep_Tool::Pnt(tcast()); + return vec3 {point.X(), point.Y(), point.Z()}; + } +}; + +} + diff --git a/source/hyporo/csg/wire.hpp b/source/hyporo/csg/wire.hpp new file mode 100644 index 0000000..104d8ee --- /dev/null +++ b/source/hyporo/csg/wire.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "shape.hpp" +#include "edge.hpp" + + +namespace hpr::csg +{ + +class Wire : public Shape +{ + +public: + + Wire() = default; + + ~Wire() override = default; + + explicit + Wire(const Shape& shape) : + Shape {shape.type() == Type::Wire ? shape : throw std::runtime_error("")} + {} + + explicit + Wire(const darray& edges) : + Shape {} + { + BRepBuilderAPI_MakeWire builder; + + for (auto& shape : edges) + switch (shape.type()) + { + case Type::Edge: + builder.Add(Edge(shape).tcast()); + break; + case Type::Wire: + builder.Add(Wire(shape).tcast()); + break; + default: + throw std::runtime_error(""); + } + + p_shape = builder.Shape(); + } + + [[nodiscard]] + TopoDS_Wire tcast() const + { + return TopoDS::Wire(p_shape); + } +}; + +} diff --git a/source/hyporo/gpu/CMakeLists.txt b/source/hyporo/gpu/CMakeLists.txt index b743b21..3a866b3 100644 --- a/source/hyporo/gpu/CMakeLists.txt +++ b/source/hyporo/gpu/CMakeLists.txt @@ -36,5 +36,6 @@ add_library(hyporo-gpu STATIC target_link_libraries(hyporo-gpu glad + stb hyporo-hyplib ) \ No newline at end of file diff --git a/source/hyporo/gpu/buffer.cpp b/source/hyporo/gpu/buffer.cpp index b9b1c27..933ff08 100644 --- a/source/hyporo/gpu/buffer.cpp +++ b/source/hyporo/gpu/buffer.cpp @@ -19,8 +19,7 @@ Buffer::Buffer(DeviceAPI api) : p_stride {0} {} -Buffer::~Buffer() -{} +Buffer::~Buffer() = default; // Member functions diff --git a/source/hyporo/gpu/buffer.hpp b/source/hyporo/gpu/buffer.hpp index 3c65388..fad4063 100644 --- a/source/hyporo/gpu/buffer.hpp +++ b/source/hyporo/gpu/buffer.hpp @@ -19,6 +19,7 @@ public: Undefined, Vertex, Index, + Uniform, BufferTypeCount }; @@ -34,14 +35,17 @@ public: Buffer(); + explicit Buffer(DeviceAPI api); - virtual ~Buffer(); + ~Buffer() override; // Member functions + [[nodiscard]] int size() const; + [[nodiscard]] BufferType type() const; }; diff --git a/source/hyporo/gpu/context.cpp b/source/hyporo/gpu/context.cpp index d831153..d3b46e9 100644 --- a/source/hyporo/gpu/context.cpp +++ b/source/hyporo/gpu/context.cpp @@ -13,8 +13,7 @@ Context::Context(DeviceAPI api) : p_api {api} {} -Context::~Context() -{} +Context::~Context() = default; bool Context::checkCompability(const Context* ctx) const { diff --git a/source/hyporo/gpu/context.hpp b/source/hyporo/gpu/context.hpp index 487636d..440ad6d 100644 --- a/source/hyporo/gpu/context.hpp +++ b/source/hyporo/gpu/context.hpp @@ -26,6 +26,7 @@ public: Context(); + explicit Context(DeviceAPI api); virtual diff --git a/source/hyporo/gpu/device.cpp b/source/hyporo/gpu/device.cpp index 694dfb5..b483b72 100644 --- a/source/hyporo/gpu/device.cpp +++ b/source/hyporo/gpu/device.cpp @@ -10,6 +10,7 @@ Device::Device() : Context {DeviceAPI::Unknown}, p_currentVertexBuffer {}, p_currentIndexBuffer {}, + p_currentUniformBuffer {}, p_currentShaderProgram {} {} @@ -17,11 +18,21 @@ Device::Device(DeviceAPI api) : Context {api}, p_currentVertexBuffer {}, p_currentIndexBuffer {}, + p_currentUniformBuffer {}, p_currentShaderProgram {} {} Device::~Device() -{} +{ + for (auto& shader : p_shaders) + delete shader; + for (auto& buffer : p_buffers) + delete buffer; + for (auto& shaderProgram : p_shaderPrograms) + delete shaderProgram; + for (auto& texture : p_textures) + delete texture; +} // Global functions @@ -52,7 +63,7 @@ void Device::useVertexBuffer(Buffer* buffer, int stride, int offset) p_currentVertexBuffer->p_stride = stride; } else - throw "Incompatible buffer"; + throw std::runtime_error("Incompatible buffer"); } else p_currentVertexBuffer = nullptr; @@ -67,34 +78,70 @@ void Device::useIndexBuffer(Buffer* buffer, int offset) p_currentIndexBuffer = buffer; } else - throw "Incompatible buffer"; + throw std::runtime_error("Incompatible buffer"); } else p_currentIndexBuffer = nullptr; } +void Device::useUniformBuffer(Buffer* buffer, int slot) +{ + if (buffer) + { + if (buffer->p_type == Buffer::BufferType::Uniform) + p_currentUniformBuffer = buffer; + else + throw std::runtime_error("Incompatible buffer"); + } + else + p_currentUniformBuffer = nullptr; +} + void Device::destroyBuffer(Buffer*& buffer) { if (!buffer) - throw "Invalid parameter"; + throw std::runtime_error("Invalid parameter"); for (auto iter = p_buffers.begin(); iter != p_buffers.end(); ++iter) - if (&*iter == &*buffer) - p_buffers.erase(iter); - buffer = nullptr; + if (*iter == buffer) + { + delete buffer; + buffer = nullptr; + p_buffers.remove(iter); + break; + } +} + +Buffer* Device::activeBuffer(Buffer::BufferType type) +{ + switch (type) + { + case Buffer::BufferType::Vertex: + return p_currentVertexBuffer; + case Buffer::BufferType::Index: + return p_currentIndexBuffer; + case Buffer::BufferType::Uniform: + return p_currentUniformBuffer; + default: + return nullptr; + } } // Shaders -void Device::destroyShader(Shader *&shader) +void Device::destroyShader(Shader* shader) { if (shader == nullptr) - throw "Invalid parameter"; + throw std::runtime_error("Invalid parameter"); for (auto iter = p_shaders.begin(); iter != p_shaders.end(); ++iter) - if (&*iter == &*shader) - p_shaders.erase(iter); - shader = nullptr; + if (*iter == shader) + { + delete shader; + shader = nullptr; + p_shaders.remove(iter); + break; + } } // Shader programs @@ -102,9 +149,9 @@ void Device::destroyShader(Shader *&shader) void Device::attachShader(ShaderProgram *program, Shader *shader) { if (program == nullptr || shader == nullptr) - throw "Invalid parameter"; + throw std::runtime_error("Invalid parameter"); if (program->p_isLinked) - throw "Shader program already linked"; + throw std::runtime_error("Shader program already linked"); program->p_slots[(int)shader->p_type] = shader; } @@ -112,9 +159,9 @@ void Device::attachShader(ShaderProgram *program, Shader *shader) void Device::linkProgram(ShaderProgram *program) { if (program == nullptr) - throw "Invalid parameter"; + throw std::runtime_error("Invalid parameter"); if (program->p_isLinked) - throw "Shader program already linked"; + throw std::runtime_error("Shader program already linked"); program->p_isLinked = true; } @@ -123,7 +170,7 @@ void Device::useShaderProgram(ShaderProgram *program) { if (program != nullptr) if (!program->p_isLinked) - throw "Shader program is not linked"; + throw std::runtime_error("Shader program is not linked"); p_currentShaderProgram = program; } @@ -136,7 +183,15 @@ void Device::destroyShaderProgram(ShaderProgram *&program, bool withShaders) if (withShaders) for (size_t n = 0; n < (size_t)Shader::ShaderType::ShaderTypeCount; n++) destroyShader(program->p_slots[n]); - program = nullptr; + + for (auto iter = p_shaderPrograms.begin(); iter != p_shaderPrograms.end(); ++iter) + if (*iter == program) + { + delete program; + program = nullptr; + p_shaderPrograms.remove(iter); + break; + } } // Textures @@ -144,9 +199,13 @@ void Device::destroyShaderProgram(ShaderProgram *&program, bool withShaders) void Device::destroyTexture(Texture *&texture) { for (auto iter = p_textures.begin(); iter != p_textures.end(); ++iter) - if (&*iter == &*texture) - p_textures.erase(iter); - texture = nullptr; + if (*iter == texture) + { + delete texture; + texture = nullptr; + p_textures.remove(iter); + break; + } } } \ No newline at end of file diff --git a/source/hyporo/gpu/device.hpp b/source/hyporo/gpu/device.hpp index 375ed9c..ce51ad0 100644 --- a/source/hyporo/gpu/device.hpp +++ b/source/hyporo/gpu/device.hpp @@ -5,6 +5,7 @@ #include "shader.hpp" #include "shader_program.hpp" #include "texture.hpp" +#include "render_target.hpp" #include "../hyplib/array/array.hpp" @@ -27,13 +28,14 @@ public: protected: - darray p_buffers; - darray p_shaders; - darray p_shaderPrograms; - darray p_textures; + darray p_buffers; + darray p_shaders; + darray p_shaderPrograms; + darray p_textures; Buffer* p_currentVertexBuffer; Buffer* p_currentIndexBuffer; + Buffer* p_currentUniformBuffer; ShaderProgram* p_currentShaderProgram; protected: @@ -42,55 +44,87 @@ protected: Device(); + explicit Device(DeviceAPI api); - virtual ~Device(); + ~Device() override; public: // Global functions - static void create(Device** device, DeviceAPI api); + static + void create(Device** device, DeviceAPI api); // Member functions // Setup - virtual bool initialize() = 0; - virtual bool destroy() = 0; + virtual + bool initialize() = 0; + virtual + bool destroy() = 0; // State - virtual void faceCulling(bool enableFaceCulling, CullMode faceCullingMode = CullMode::None) = 0; + virtual + void faceCulling(bool enableFaceCulling, CullMode faceCullingMode) = 0; // Buffers - virtual void createVertexBuffer(Buffer **buffer, int size, char* data) = 0; - virtual void createIndexBuffer(Buffer **buffer, int size, char* data) = 0; - virtual void useVertexBuffer(Buffer* buffer, int stride, int offset); - virtual void useIndexBuffer(Buffer* buffer, int offset); - virtual void destroyBuffer(Buffer*& buffer); + virtual + void createVertexBuffer(Buffer **buffer, int size, char* data) = 0; + virtual + void createIndexBuffer(Buffer **buffer, int size, char* data) = 0; + virtual + void createUniformBuffer(Buffer **buffer, int size, char* data) = 0; + virtual + void useVertexBuffer(Buffer* buffer, int stride, int offset); + virtual + void useIndexBuffer(Buffer* buffer, int offset); + virtual + void useUniformBuffer(Buffer* buffer, int slot); + virtual + void editBuffer(Buffer* buffer, char* data, int size, int offset) = 0; + virtual + void editBuffer(Buffer* buffer, char* data) = 0; + virtual + void destroyBuffer(Buffer*& buffer); + Buffer* activeBuffer(Buffer::BufferType type); + // Shaders - virtual void createVertexShader(Shader** shader, const std::string& filename, const std::string& label) = 0; - virtual void createFragmentShader(Shader** shader, const std::string& filename, const std::string& label) = 0; - virtual void createGeometryShader(Shader** shader, const std::string& filename, const std::string& label) = 0; - virtual void destroyShader(Shader*& shader); + virtual + void createVertexShader(Shader** shader, const std::string& filename, const std::string& label) = 0; + virtual + void createFragmentShader(Shader** shader, const std::string& filename, const std::string& label) = 0; + virtual + void createGeometryShader(Shader** shader, const std::string& filename, const std::string& label) = 0; + virtual + void destroyShader(Shader* shader); // Shader programs - virtual void createShaderProgram(ShaderProgram** program) = 0; - virtual void attachShader(ShaderProgram* program, Shader* shader); - virtual void linkProgram(ShaderProgram* program); - virtual void useShaderProgram(ShaderProgram* program); - virtual void destroyShaderProgram(ShaderProgram*& program, bool withShaders = false); + virtual + void createShaderProgram(ShaderProgram** program) = 0; + virtual + void attachShader(ShaderProgram* program, Shader* shader); + virtual + void linkProgram(ShaderProgram* program); + virtual + void useShaderProgram(ShaderProgram* program); + virtual + void destroyShaderProgram(ShaderProgram*& program, bool withShaders); // Textures - virtual void createTexture(Texture** texture, const std::string& filename) = 0; - virtual void useTexture(Texture* texture, int slot) = 0; - virtual void destroyTexture(Texture*& texture); + virtual + void createTexture(Texture** texture, const std::string& filename) = 0; + virtual + void useTexture(Texture* texture, int slot) = 0; + virtual + void destroyTexture(Texture*& texture); }; } diff --git a/source/hyporo/gpu/opengl/buffer.cpp b/source/hyporo/gpu/opengl/buffer.cpp index a296f53..3167a30 100644 --- a/source/hyporo/gpu/opengl/buffer.cpp +++ b/source/hyporo/gpu/opengl/buffer.cpp @@ -13,8 +13,7 @@ Buffer::Buffer() : p_vertexArrayIndex {0} {} -Buffer::~Buffer() -{} +Buffer::~Buffer() = default; int Buffer::target() const { diff --git a/source/hyporo/gpu/opengl/device.cpp b/source/hyporo/gpu/opengl/device.cpp index e6c0f00..0e0f11d 100644 --- a/source/hyporo/gpu/opengl/device.cpp +++ b/source/hyporo/gpu/opengl/device.cpp @@ -4,12 +4,17 @@ #include "shader.hpp" #include "shader_program.hpp" #include "texture.hpp" +#include "render_target.hpp" #include "io/io.hpp" #include +#define STB_IMAGE_IMPLEMENTATION +#include #include +#include + namespace hpr::gpu::opengl { @@ -19,8 +24,7 @@ Device::Device() : p_isInitialized {false} {} -Device::~Device() -{} +Device::~Device() = default; // Setup @@ -34,6 +38,11 @@ bool Device::destroy() return p_isInitialized = false; } +bool Device::loadLoader() +{ + return !gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); +} + // State void Device::faceCulling(bool enableFaceCulling, CullMode faceCullingMode) @@ -60,14 +69,19 @@ void Device::faceCulling(bool enableFaceCulling, CullMode faceCullingMode) } } +// Render targets + +// Buffers + + void Device::createVertexBuffer(gpu::Buffer **buffer, int size, char *data) { if (buffer == nullptr) throw std::invalid_argument("Invalid parameter"); *buffer = nullptr; - p_buffers.emplace_back(opengl::Buffer()); - opengl::Buffer* newBuffer = dynamic_cast(&p_buffers.back()); + p_buffers.push(new opengl::Buffer()); + auto* newBuffer = dynamic_cast(p_buffers.back()); newBuffer->p_type = Buffer::BufferType::Vertex; newBuffer->p_size = size; @@ -88,10 +102,10 @@ void Device::createIndexBuffer(gpu::Buffer **buffer, int size, char *data) throw std::invalid_argument("Invalid parameter"); *buffer = nullptr; - p_buffers.emplace_back(opengl::Buffer()); - opengl::Buffer* newBuffer = dynamic_cast(&p_buffers.back()); + p_buffers.push(new opengl::Buffer()); + auto* newBuffer = dynamic_cast(p_buffers.back()); - newBuffer->p_type = Buffer::BufferType::Vertex; + newBuffer->p_type = Buffer::BufferType::Index; newBuffer->p_size = size; glGenVertexArrays(1, &newBuffer->p_vertexArrayIndex); @@ -104,6 +118,25 @@ void Device::createIndexBuffer(gpu::Buffer **buffer, int size, char *data) *buffer = static_cast(newBuffer); } +void Device::createUniformBuffer(gpu::Buffer** buffer, int size, char* data) +{ + if (buffer == nullptr) + throw std::invalid_argument("Invalid parameter"); + + *buffer = nullptr; + p_buffers.push(new opengl::Buffer()); + auto* newBuffer = dynamic_cast(p_buffers.back()); + + newBuffer->p_type = Buffer::BufferType::Uniform; + newBuffer->p_size = size; + + glGenBuffers(1, &newBuffer->p_bufferIndex); + glBindBuffer(GL_UNIFORM_BUFFER, newBuffer->p_bufferIndex); + glBufferData(GL_UNIFORM_BUFFER, size, (void*)data, GL_DYNAMIC_DRAW); + + *buffer = static_cast(newBuffer); +} + void Device::useVertexBuffer(gpu::Buffer *buffer, int stride, int offset) { if (buffer == nullptr) @@ -113,14 +146,14 @@ void Device::useVertexBuffer(gpu::Buffer *buffer, int stride, int offset) } else { - opengl::Buffer* curBuffer = dynamic_cast(buffer); + auto* curBuffer = dynamic_cast(buffer); if (curBuffer->p_type == Buffer::BufferType::Vertex && p_currentVertexBuffer != buffer) { glBindVertexArray(curBuffer->p_vertexArrayIndex); glBindBuffer(GL_ARRAY_BUFFER, curBuffer->p_bufferIndex); - opengl::Buffer* curIndexBuffer = dynamic_cast(p_currentIndexBuffer); + auto* curIndexBuffer = dynamic_cast(p_currentIndexBuffer); if (curIndexBuffer != nullptr) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, curIndexBuffer->p_bufferIndex); @@ -138,7 +171,7 @@ void Device::useIndexBuffer(gpu::Buffer *buffer, int offset) } else { - opengl::Buffer* curBuffer = dynamic_cast(buffer); + auto* curBuffer = dynamic_cast(buffer); if (curBuffer->p_type == Buffer::BufferType::Index && p_currentVertexBuffer != buffer) { @@ -149,12 +182,51 @@ void Device::useIndexBuffer(gpu::Buffer *buffer, int offset) gpu::Device::useIndexBuffer(buffer, offset); } +void Device::useUniformBuffer(gpu::Buffer* buffer, int slot) +{ + if (buffer == nullptr) + { + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + else + { + auto* curBuffer = dynamic_cast(buffer); + glBindBufferRange(GL_UNIFORM_BUFFER, slot, curBuffer->p_bufferIndex, 0, buffer->size()); + } + gpu::Device::useUniformBuffer(buffer, slot); +} + +void Device::editBuffer(gpu::Buffer* buffer, char* data, int size, int offset) +{ + if (!checkCompability(buffer)) + throw std::runtime_error("Incompatible platform"); + if (buffer == nullptr || data == nullptr) + throw std::invalid_argument("Invalid argument"); + if (size + offset > buffer->size() || size < 0 || offset < 0) + throw std::out_of_range("Out of bounds"); + + auto* prevBuffer = dynamic_cast(activeBuffer(buffer->type())); + + auto* buffer_ = dynamic_cast(buffer); + glBindBuffer(buffer_->target(), buffer_->p_bufferIndex); + glBufferSubData(buffer_->target(), offset, size, data); + + if (prevBuffer != buffer_) + glBindBuffer(buffer_->target(), prevBuffer ? prevBuffer->p_bufferIndex : 0); + +} + +void Device::editBuffer(gpu::Buffer* buffer, char* data) +{ + editBuffer(buffer, data, buffer->size(), 0); +} + void Device::destroyBuffer(gpu::Buffer *&buffer) { if (buffer == nullptr) throw std::invalid_argument("Invalid parameter"); - opengl::Buffer* curBuffer = dynamic_cast(buffer); + auto* curBuffer = dynamic_cast(buffer); glDeleteBuffers(1, &curBuffer->p_bufferIndex); if (curBuffer->p_type == Buffer::BufferType::Vertex) @@ -195,8 +267,8 @@ void Device::createVertexShader(gpu::Shader **shader, const std::string &filenam throw std::runtime_error(error); } - p_shaders.emplace_back(opengl::Shader()); - opengl::Shader* newShader = dynamic_cast(&p_shaders.back()); + p_shaders.push(new opengl::Shader()); + auto* newShader = dynamic_cast(p_shaders.back()); newShader->p_type = Shader::ShaderType::Vertex; newShader->p_filename = filename; newShader->p_shaderIndex = shaderIndex; @@ -205,4 +277,251 @@ void Device::createVertexShader(gpu::Shader **shader, const std::string &filenam *shader = static_cast(newShader); } + +void Device::createFragmentShader(gpu::Shader **shader, const std::string &filename, const std::string &label) +{ + if (shader == nullptr) + throw std::invalid_argument("Invalid parameter"); + + *shader = nullptr; + + unsigned int shaderIndex = glCreateShader(GL_FRAGMENT_SHADER); + if (shaderIndex == 0) + throw std::runtime_error("Could not create shader"); + + File file; + file.open(filename, File::Binary | File::Read); + std::string content = file.read().str(); + const char* shaderSource = content.c_str(); + + glShaderSource(shaderIndex, 1, &shaderSource, nullptr); + GLenum result = glGetError(); + glCompileShader(shaderIndex); + + int shaderStatus; + glGetShaderiv(shaderIndex, GL_COMPILE_STATUS, &shaderStatus); + if (!shaderStatus) + { + char error[2048 + 1]; + glGetShaderInfoLog(shaderIndex, 2048, nullptr, error); + + throw std::runtime_error(error); + } + + p_shaders.push(new opengl::Shader()); + auto* newShader = dynamic_cast(p_shaders.back()); + newShader->p_type = Shader::ShaderType::Fragment; + newShader->p_filename = filename; + newShader->p_shaderIndex = shaderIndex; + newShader->p_label = "FragmentShader"; + + *shader = static_cast(newShader); +} + + +void Device::createGeometryShader(gpu::Shader **shader, const std::string &filename, const std::string &label) +{ + if (shader == nullptr) + throw std::invalid_argument("Invalid parameter"); + + *shader = nullptr; + + unsigned int shaderIndex = glCreateShader(GL_GEOMETRY_SHADER); + if (shaderIndex == 0) + throw std::runtime_error("Could not create shader"); + + File file; + file.open(filename, File::Binary | File::Read); + std::string content = file.read().str(); + const char* shaderSource = content.c_str(); + + glShaderSource(shaderIndex, 1, &shaderSource, nullptr); + GLenum result = glGetError(); + glCompileShader(shaderIndex); + + int shaderStatus; + glGetShaderiv(shaderIndex, GL_COMPILE_STATUS, &shaderStatus); + if (!shaderStatus) + { + char error[2048 + 1]; + glGetShaderInfoLog(shaderIndex, 2048, nullptr, error); + + throw std::runtime_error(error); + } + + p_shaders.push(new opengl::Shader()); + auto* newShader = dynamic_cast(p_shaders.back()); + newShader->p_type = Shader::ShaderType::Geometry; + newShader->p_filename = filename; + newShader->p_shaderIndex = shaderIndex; + newShader->p_label = "FragmentShader"; + + *shader = static_cast(newShader); +} + +void Device::destroyShader(gpu::Shader* shader) +{ + if (shader == nullptr) + throw std::invalid_argument("Invalid parameter"); + + auto* shader_ = dynamic_cast(shader); + glDeleteShader(shader_->p_shaderIndex); + + gpu::Device::destroyShader(shader); +} + +void Device::createShaderProgram(gpu::ShaderProgram** program) +{ + if (program == nullptr) + throw std::invalid_argument("Invalid parameter"); + + *program = nullptr; + + p_shaderPrograms.push(new opengl::ShaderProgram()); + auto* newProgram = dynamic_cast(p_shaderPrograms.back()); + newProgram->p_shaderProgramIndex = glCreateProgram(); + + *program = static_cast(newProgram); +} + +void Device::attachShader(gpu::ShaderProgram* program, gpu::Shader* shader) +{ + gpu::Device::attachShader(program, shader); + auto* program_ = dynamic_cast(program); + auto* shader_ = dynamic_cast(shader); + glAttachShader(program_->p_shaderProgramIndex, shader_->p_shaderIndex); +} + + +void Device::linkProgram(gpu::ShaderProgram* program) +{ + gpu::Device::linkProgram(program); + auto* program_ = dynamic_cast(program); + glLinkProgram(program_->p_shaderProgramIndex); + + GLint status; + glGetProgramiv(program_->p_shaderProgramIndex, GL_LINK_STATUS, &status); + + if (status == GL_FALSE) + throw std::runtime_error("Shader program link error"); +} + +void Device::useShaderProgram(gpu::ShaderProgram* program) +{ + gpu::ShaderProgram* currentProgram = p_currentShaderProgram; + gpu::Device::useShaderProgram(program); + + if (currentProgram == program) + return; + if (program == nullptr) + glUseProgram(0); + else + { + auto* program_ = dynamic_cast(program); + glUseProgram(program_->p_shaderProgramIndex); + } +} + +void Device::destroyShaderProgram(gpu::ShaderProgram*& program, bool withShaders) +{ + if (program == nullptr) + throw std::invalid_argument("Invalid parameter"); + + auto* program_ = dynamic_cast(program); + + for (int n = 0; n < static_cast(Shader::ShaderType::ShaderTypeCount); ++n) + { + auto* shader = dynamic_cast(program_->p_slots[n]); + if (shader != nullptr) + glDetachShader(program_->p_shaderProgramIndex, shader->p_shaderIndex); + } + glDeleteProgram(program_->p_shaderProgramIndex); + gpu::Device::destroyShaderProgram(program, withShaders); +} + +void Device::createTexture(gpu::Texture** texture, const std::string& filename) +{ + if (texture == nullptr) + throw std::invalid_argument("Invalid parameter"); + if (filename.empty()) + throw std::invalid_argument("Invalid parameter"); + + *texture = nullptr; + bool alphaChannel = true; + int internalFormat = GL_RGBA; + int imageFormat = GL_RGBA; + int width; + int height; + int channelsCount; + + stbi_set_flip_vertically_on_load(true); + unsigned char* source = stbi_load(filename.c_str(), &width, &height, &channelsCount, 0); + + if (!source) + throw std::runtime_error("Failed to load texture source"); + else + { + p_textures.push(new opengl::Texture()); + auto* texture_ = dynamic_cast(p_textures.back()); + texture_->p_filename = filename; + texture_->p_width = width; + texture_->p_height = height; + + glGenTextures(1, &texture_->p_textureIndex); + glBindTexture(GL_TEXTURE_2D, texture_->p_textureIndex); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + if (alphaChannel) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + else + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, imageFormat, GL_UNSIGNED_BYTE, source); + glGenerateMipmap(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + *texture = static_cast(texture_); + } + + stbi_image_free(source); +} + +void Device::useTexture(gpu::Texture* texture, int slot) +{ + //gpu::Device::useTexture(texture, slot); + + if (texture == nullptr) + { + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, 0); + } + else + { + auto* texture_ = dynamic_cast(texture); + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, texture_->p_textureIndex); + } +} + +void Device::destroyTexture(gpu::Texture*& texture) +{ + if (texture == nullptr) + throw std::invalid_argument("Invalid parameter"); + + auto* texture_ = dynamic_cast(texture); + + glDeleteTextures(1, &texture_->p_textureIndex); + gpu::Device::destroyTexture(texture); +} + + +void Device::Draw(int numFaces, int indexOffset, int vertexOffset) +{ + if (p_currentVertexBuffer != nullptr) + glDrawElementsBaseVertex(GL_TRIANGLES, numFaces * 3, GL_UNSIGNED_SHORT, (void*)(indexOffset * 2), vertexOffset); +} + } \ No newline at end of file diff --git a/source/hyporo/gpu/opengl/device.hpp b/source/hyporo/gpu/opengl/device.hpp index 24dc557..6385004 100644 --- a/source/hyporo/gpu/opengl/device.hpp +++ b/source/hyporo/gpu/opengl/device.hpp @@ -2,6 +2,8 @@ #include "../device.hpp" +#include + namespace hpr::gpu::opengl { @@ -29,16 +31,28 @@ public: bool initialize() override; bool destroy() override; + static + bool loadLoader(); + // State - void faceCulling(bool enableFaceCulling, CullMode faceCullingMode = CullMode::None) override; + void faceCulling(bool enableFaceCulling, CullMode faceCullingMode) override; + + // Render targets + + virtual + void createScreenRenderTarget(RenderTarget** target); // Buffers void createVertexBuffer(Buffer **buffer, int size, char* data) override; void createIndexBuffer(Buffer **buffer, int size, char* data) override; + void createUniformBuffer(Buffer **buffer, int size, char* data) override; void useVertexBuffer(Buffer* buffer, int stride, int offset) override; void useIndexBuffer(Buffer* buffer, int offset) override; + void useUniformBuffer(Buffer* buffer, int slot) override; + void editBuffer(Buffer* buffer, char* data, int size, int offset) override; + void editBuffer(Buffer* buffer, char* data) override; void destroyBuffer(Buffer*& buffer) override; // Shaders @@ -46,21 +60,25 @@ public: void createVertexShader(Shader** shader, const std::string& filename, const std::string& label) override; void createFragmentShader(Shader** shader, const std::string& filename, const std::string& label) override; void createGeometryShader(Shader** shader, const std::string& filename, const std::string& label) override; - void destroyShader(Shader*& shader) override; + void destroyShader(Shader* shader) override; // Shader programs - virtual void createShaderProgram(ShaderProgram** program); - virtual void attachShader(ShaderProgram* program, Shader* shader); - virtual void linkProgram(ShaderProgram* program); - virtual void useShaderProgram(ShaderProgram* program); - virtual void destroyShaderProgram(ShaderProgram*& program, bool withShaders = false); + void createShaderProgram(ShaderProgram** program) override; + void attachShader(ShaderProgram* program, Shader* shader) override; + void linkProgram(ShaderProgram* program) override; + void useShaderProgram(ShaderProgram* program) override; + void destroyShaderProgram(ShaderProgram*& program, bool withShaders) override; // Textures - virtual void createTexture(Texture** texture, const std::string& filename); - virtual void useTexture(Texture* texture, int slot); - virtual void destroyTexture(Texture*& texture); + void createTexture(Texture** texture, const std::string& filename) override; + void useTexture(Texture* texture, int slot) override; + void destroyTexture(Texture*& texture) override; + + // + + void Draw(int numFaces, int indexOffset, int vertexOffset); }; } \ No newline at end of file diff --git a/source/hyporo/gpu/opengl/render_target.cpp b/source/hyporo/gpu/opengl/render_target.cpp new file mode 100644 index 0000000..a6f26ee --- /dev/null +++ b/source/hyporo/gpu/opengl/render_target.cpp @@ -0,0 +1,15 @@ +#pragma once + +#include "render_target.hpp" + + +namespace hpr::gpu::opengl +{ + +RenderTarget::RenderTarget() : + gpu::RenderTarget {DeviceAPI::OpenGL} +{} + +RenderTarget::~RenderTarget() = default; + +} \ No newline at end of file diff --git a/source/hyporo/gpu/opengl/render_target.hpp b/source/hyporo/gpu/opengl/render_target.hpp new file mode 100644 index 0000000..f4a6164 --- /dev/null +++ b/source/hyporo/gpu/opengl/render_target.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "../render_target.hpp" + + +namespace hpr::gpu::opengl +{ + +class RenderTarget : public gpu::RenderTarget +{ + friend class Device; + +protected: + + unsigned int p_frameBufferIndex; + unsigned int p_depthBufferIndex; + unsigned int p_textureIndex; + +public: + + RenderTarget(); + + ~RenderTarget() override; + + unsigned int frameBuffer() const + { + return p_frameBufferIndex; + } + + unsigned int depthBuffer() const + { + return p_depthBufferIndex; + } + + unsigned int texture() const + { + return p_textureIndex; + } + +}; + +} \ No newline at end of file diff --git a/source/hyporo/gpu/opengl/shader.cpp b/source/hyporo/gpu/opengl/shader.cpp index 1259230..606e01d 100644 --- a/source/hyporo/gpu/opengl/shader.cpp +++ b/source/hyporo/gpu/opengl/shader.cpp @@ -13,7 +13,6 @@ Shader::Shader() : p_shaderIndex {0} {} -Shader::~Shader() -{} +Shader::~Shader() = default; } \ No newline at end of file diff --git a/source/hyporo/gpu/opengl/shader.hpp b/source/hyporo/gpu/opengl/shader.hpp index 9c393a7..8018d97 100644 --- a/source/hyporo/gpu/opengl/shader.hpp +++ b/source/hyporo/gpu/opengl/shader.hpp @@ -19,7 +19,7 @@ public: Shader(); - virtual ~Shader(); + ~Shader() override; }; diff --git a/source/hyporo/gpu/opengl/texture.cpp b/source/hyporo/gpu/opengl/texture.cpp index 592df4b..b69f531 100644 --- a/source/hyporo/gpu/opengl/texture.cpp +++ b/source/hyporo/gpu/opengl/texture.cpp @@ -10,7 +10,6 @@ Texture::Texture() : p_textureIndex {0} {} -Texture::~Texture() -{} +Texture::~Texture() = default; } \ No newline at end of file diff --git a/source/hyporo/gpu/opengl/texture.hpp b/source/hyporo/gpu/opengl/texture.hpp index 6687e15..4ad3550 100644 --- a/source/hyporo/gpu/opengl/texture.hpp +++ b/source/hyporo/gpu/opengl/texture.hpp @@ -18,7 +18,7 @@ public: Texture(); - ~Texture(); + ~Texture() override; }; diff --git a/source/hyporo/gpu/render_target.cpp b/source/hyporo/gpu/render_target.cpp new file mode 100644 index 0000000..b3d2738 --- /dev/null +++ b/source/hyporo/gpu/render_target.cpp @@ -0,0 +1,35 @@ +#include "render_target.hpp" + + +namespace hpr::gpu +{ + +RenderTarget::RenderTarget() : + Context {DeviceAPI::Unknown}, + p_posX {}, + p_posY {}, + p_width {}, + p_height {}, + p_type {Type::Unknown}, + p_hasColorData {true}, + p_hasDepthBuffer {false}, + p_depthTestEnabled {false}, + p_parent {nullptr} +{} + +RenderTarget::RenderTarget(DeviceAPI api) : + Context {api}, + p_posX {}, + p_posY {}, + p_width {}, + p_height {}, + p_type {Type::Unknown}, + p_hasColorData {true}, + p_hasDepthBuffer {false}, + p_depthTestEnabled {false}, + p_parent {nullptr} +{} + +RenderTarget::~RenderTarget() = default; + +} diff --git a/source/hyporo/gpu/render_target.hpp b/source/hyporo/gpu/render_target.hpp new file mode 100644 index 0000000..66758ec --- /dev/null +++ b/source/hyporo/gpu/render_target.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include "context.hpp" + + +namespace hpr::gpu +{ + +class RenderTarget : public Context +{ + friend class Device; + +public: + + enum class Type + { + Unknown, + Screen, + Framebuffer + }; + +protected: + + int p_posX; + int p_posY; + int p_width; + int p_height; + + Type p_type; + + bool p_hasColorData; + bool p_hasDepthBuffer; + bool p_depthTestEnabled; + + RenderTarget* p_parent; + +public: + + RenderTarget(); + + RenderTarget(DeviceAPI api); + + virtual + ~RenderTarget(); + + int posX() const + { + return p_posX; + } + + int posY() const + { + return p_posY; + } + + int width() const + { + return p_width; + } + + int height() const + { + return p_height; + } + + Type type() const + { + return p_type; + } + + bool hasColorData() const + { + return p_hasColorData; + } + + bool hasDepthBuffer() const + { + return p_hasDepthBuffer; + } + + bool isDepthTestEnabled() const + { + return p_depthTestEnabled; + } + + void depthTest(bool enable) + { + p_depthTestEnabled = enable; + } + + RenderTarget* parent() + { + return p_parent; + } + +}; + +} \ No newline at end of file diff --git a/source/hyporo/gpu/shader.cpp b/source/hyporo/gpu/shader.cpp index 9bef2cd..6204790 100644 --- a/source/hyporo/gpu/shader.cpp +++ b/source/hyporo/gpu/shader.cpp @@ -7,32 +7,31 @@ namespace hpr::gpu Shader::Shader() : Context {DeviceAPI::Unknown}, - p_filename {"\0"}, - p_label {"\0"}, + p_filename {}, + p_label {}, p_type {ShaderType::Vertex} {} Shader::Shader(DeviceAPI api) : Context {api}, - p_filename {"\0"}, - p_label {"\0"}, + p_filename {}, + p_label {}, p_type {ShaderType::Vertex} {} -Shader::~Shader() -{} +Shader::~Shader() = default; -const std::string Shader::filename() const +std::string Shader::filename() const { return p_filename; } -const std::string Shader::label() const +std::string Shader::label() const { return p_label; } -const Shader::ShaderType Shader::type() const +Shader::ShaderType Shader::type() const { return p_type; } diff --git a/source/hyporo/gpu/shader.hpp b/source/hyporo/gpu/shader.hpp index 34c83d2..a780fbe 100644 --- a/source/hyporo/gpu/shader.hpp +++ b/source/hyporo/gpu/shader.hpp @@ -34,17 +34,21 @@ public: Shader(); + explicit Shader(DeviceAPI api); - virtual ~Shader(); + ~Shader() override; // Member functions - const std::string filename() const; + [[nodiscard]] + std::string filename() const; - const std::string label() const; + [[nodiscard]] + std::string label() const; - const ShaderType type() const; + [[nodiscard]] + ShaderType type() const; }; } // end namespace hpr::gpu \ No newline at end of file diff --git a/source/hyporo/gpu/shader_program.cpp b/source/hyporo/gpu/shader_program.cpp index a3da829..87f5b64 100644 --- a/source/hyporo/gpu/shader_program.cpp +++ b/source/hyporo/gpu/shader_program.cpp @@ -15,7 +15,6 @@ ShaderProgram::ShaderProgram(DeviceAPI api) : p_isLinked {false} {} -ShaderProgram::~ShaderProgram() -{} +ShaderProgram::~ShaderProgram() = default; } \ No newline at end of file diff --git a/source/hyporo/gpu/shader_program.hpp b/source/hyporo/gpu/shader_program.hpp index 85f2155..0952d46 100644 --- a/source/hyporo/gpu/shader_program.hpp +++ b/source/hyporo/gpu/shader_program.hpp @@ -24,9 +24,10 @@ public: ShaderProgram(); + explicit ShaderProgram(DeviceAPI api); - virtual ~ShaderProgram(); + ~ShaderProgram() override; // Member functions diff --git a/source/hyporo/gpu/shaders/base.frag.glsl b/source/hyporo/gpu/shaders/base.frag.glsl new file mode 100644 index 0000000..991933a --- /dev/null +++ b/source/hyporo/gpu/shaders/base.frag.glsl @@ -0,0 +1,204 @@ +#version 420 + +layout(binding = 0) uniform sampler2D diffuseTex; + +out vec4 out_Color; + +in vec4 ex_Pos; +in vec2 ex_Tex; +in vec3 ex_Normal; + +layout (binding = 0) uniform ScreenVariables { + mat4 CameraView; + mat4 Projection; + vec4 CameraEye; + + vec4 FogColor; + float FogNear; + float FogFar; +}; + +layout (binding = 1) uniform ObjectVariables { + mat4 Transform; + vec2 TexOffset; + vec2 TexScale; + vec4 Scale; + + vec4 BaseColor; + vec4 Emission; + float SpecularMix; + float DiffuseMix; + float Metallic; + float DiffuseRoughness; + float SpecularPower; + float IncidentSpecular; + + int ColorReplace; + int Lit; +}; + +struct Light { + vec4 Position; + vec4 Color; + vec4 Direction; + float Attenuation0; + float Attenuation1; + int FalloffEnabled; + int Active; +}; + +layout (binding = 3) uniform Lighting { + Light Lights[32]; + vec4 AmbientLighting; +}; + +float pow5(float v) { + return (v * v) * (v * v) * v; +} + +float f_diffuse(vec3 i, vec3 o, vec3 h, vec3 normal, float power, float roughness) { + float h_dot_i = dot(h, i); + float h_dot_i_2 = h_dot_i * h_dot_i; + float f_d90 = 0.5 + 2 * h_dot_i_2 * roughness; + + float cos_theta_i = dot(i, normal); + float cos_theta_o = dot(o, normal); + + float f_d = (1 + (f_d90 - 1) * pow5(1 - cos_theta_i)) * (1 + (f_d90 - 1) * pow5(1 - cos_theta_o)); + return clamp(f_d * power * cos_theta_i, 0.0, 1.0); +} + +float f_specular(vec3 i, vec3 o, vec3 h, vec3 normal, float F0, float power, float specularPower) { + vec3 reflected = -reflect(i, normal); + float intensity = dot(reflected, o); + + if (intensity < 0) return 0; + + // Fresnel approximation + float F0_scaled = 0.08 * F0; + float o_dot_h = dot(o, h); + float s = pow5(1 - o_dot_h); + float F = F0_scaled + s * (1 - F0_scaled); + + return clamp(pow(intensity, specularPower) * F * power, 0.0, 1.0); +} + +float f_specular_ambient(vec3 o, vec3 normal, float F0, float power) { + // Fresnel approximation + float F0_scaled = 0.08 * F0; + float o_dot_n = dot(o, normal); + float s = pow5(1 - o_dot_n); + float F = F0_scaled + s * (1 - F0_scaled); + + return clamp(F * power, 0.0, 1.0); +} + +float linearToSrgb(float u) { + const float MinSrgbPower = 0.0031308; + + if (u < MinSrgbPower) { + return 12.92 * u; + } + else { + return 1.055 * pow(u, 1 / 2.4) - 0.055; + } +} + +float srgbToLinear(float u) { + const float MinSrgbPower = 0.04045; + + if (u < MinSrgbPower) { + return u / 12.92; + } + else { + return pow((u + 0.055) / 1.055, 2.4); + } +} + +vec3 linearToSrgb(vec3 v) { + return vec3(linearToSrgb(v.r), linearToSrgb(v.g), linearToSrgb(v.b)); +} + +vec3 srgbToLinear(vec3 v) { + return vec3(srgbToLinear(v.r), srgbToLinear(v.g), srgbToLinear(v.b)); +} + +void main(void) { + const float FullSpecular = 1 / 0.08; + + vec3 totalLighting = vec3(1.0, 1.0, 1.0); + vec3 normal = normalize(ex_Normal.xyz); + + vec4 baseColor; + float roughness = 0.5; + float power = 1.0; + + if (ColorReplace == 0) { + vec4 diffuse = texture(diffuseTex, ex_Tex).rgba; + baseColor = vec4(srgbToLinear(diffuse.rgb), diffuse.a) * BaseColor; + } + else { + baseColor = BaseColor; + } + + totalLighting = baseColor.rgb; + + if (Lit == 1) { + vec3 o = normalize(CameraEye.xyz - ex_Pos.xyz); + float cos_theta_o = dot(o, normal); + + vec3 ambientSpecular = f_specular_ambient(o, normal, IncidentSpecular, SpecularMix) * AmbientLighting.rgb; + vec3 ambientDiffuse = f_diffuse(o, o, o, normal, DiffuseMix, DiffuseRoughness) * AmbientLighting.rgb * baseColor.rgb; + vec3 ambientMetallic = f_specular_ambient(o, normal, FullSpecular, 1.0) * AmbientLighting.rgb * baseColor.rgb; + + totalLighting = mix(ambientSpecular + ambientDiffuse, ambientMetallic, Metallic); + totalLighting += Emission.rgb; + + for (int li = 0; li < 32; ++li) { + if (Lights[li].Active == 0) continue; + + vec3 i = Lights[li].Position.xyz - ex_Pos.xyz; + float inv_dist = 1.0 / length(i); + i *= inv_dist; + + float cos_theta_i = dot(i, normal); + + if (cos_theta_i < 0) continue; + if (cos_theta_o < 0) continue; + + vec3 h = normalize(i + o); + vec3 diffuse = f_diffuse(i, o, h, normal, DiffuseMix, DiffuseRoughness) * baseColor.rgb * Lights[li].Color.rgb; + vec3 specular = f_specular(i, o, h, normal, IncidentSpecular, SpecularMix, SpecularPower) * Lights[li].Color.rgb; + vec3 metallic = vec3(0.0, 0.0, 0.0); + + if (Metallic > 0) { + metallic = f_specular(i, o, h, normal, FullSpecular, 1, SpecularPower) * Lights[li].Color.rgb * baseColor.rgb; + } + + // Spotlight calculation + float spotCoherence = -dot(i, Lights[li].Direction.xyz); + float spotAttenuation = 1.0; + if (spotCoherence > Lights[li].Attenuation0) spotAttenuation = 1.0; + else if (spotCoherence < Lights[li].Attenuation1) spotAttenuation = 0.0; + else { + float t = Lights[li].Attenuation0 - Lights[li].Attenuation1; + if (t == 0) spotAttenuation = 1.0; + else spotAttenuation = (spotCoherence - Lights[li].Attenuation1) / t; + } + + float falloff = 1.0; + if (Lights[li].FalloffEnabled == 1) { + falloff = (inv_dist * inv_dist); + } + + vec3 bsdf = mix(diffuse + specular, metallic, Metallic); + + totalLighting += falloff * bsdf * spotAttenuation * spotAttenuation * spotAttenuation; + } + } + + const float distanceToCamera = length(CameraEye.xyz - ex_Pos.xyz); + const float fogAttenuation = (clamp(distanceToCamera, FogNear, FogFar) - FogNear) / (FogFar - FogNear); + + out_Color = vec4(linearToSrgb(mix(totalLighting.rgb, FogColor.rgb, fogAttenuation)), baseColor.a); +} diff --git a/source/hyporo/gpu/shaders/base.vert.glsl b/source/hyporo/gpu/shaders/base.vert.glsl new file mode 100644 index 0000000..4ef2d61 --- /dev/null +++ b/source/hyporo/gpu/shaders/base.vert.glsl @@ -0,0 +1,57 @@ +#version 420 + +layout(location=0) in vec4 in_Position; +layout(location=1) in vec2 in_Tex; +layout(location=2) in vec4 in_Normal; + +out vec4 ex_Pos; +out vec2 ex_Tex; +out vec3 ex_Normal; + +layout (binding = 0) uniform ScreenVariables { + mat4 CameraView; + mat4 Projection; + vec4 CameraEye; + + vec4 FogColor; + float FogNear; + float FogFar; +}; + +layout (binding = 1) uniform ObjectVariables { + mat4 Transform; + vec2 TexOffset; + vec2 TexScale; + vec4 Scale; + + vec4 BaseColor; + vec4 Emission; + float SpecularMix; + float DiffuseMix; + float Metallic; + float DiffuseRoughness; + float SpecularPower; + float IncidentSpecular; + + int ColorReplace; + int Lit; +}; + +void main(void) { + vec4 inputPos = vec4(in_Position.xyz, 1.0); + + inputPos.xyz *= Scale.xyz; + + inputPos = inputPos * Transform; + ex_Pos = inputPos; + + inputPos = inputPos * CameraView; + inputPos = inputPos * Projection; + + vec4 finalNormal = vec4(in_Normal.xyz, 0.0); + ex_Normal = vec3(finalNormal * Transform); + + gl_Position = vec4(inputPos.xyzw); + + ex_Tex = in_Tex; +} diff --git a/source/hyporo/gpu/texture.cpp b/source/hyporo/gpu/texture.cpp index 4eaf9b7..8f6aeb6 100644 --- a/source/hyporo/gpu/texture.cpp +++ b/source/hyporo/gpu/texture.cpp @@ -20,8 +20,7 @@ Texture::Texture(DeviceAPI api) : p_height {0} {} -Texture::~Texture() -{} +Texture::~Texture() = default; std::string Texture::filename() const { diff --git a/source/hyporo/gpu/texture.hpp b/source/hyporo/gpu/texture.hpp index e91e0bf..68ddb16 100644 --- a/source/hyporo/gpu/texture.hpp +++ b/source/hyporo/gpu/texture.hpp @@ -22,16 +22,20 @@ public: Texture(); + explicit Texture(DeviceAPI api); - virtual ~Texture(); + ~Texture() override; // Member functions + [[nodiscard]] std::string filename() const; + [[nodiscard]] int width() const; + [[nodiscard]] int height() const; }; diff --git a/source/hyporo/hmesh/CMakeLists.txt b/source/hyporo/hmesh/CMakeLists.txt index b76a33f..36dbe41 100644 --- a/source/hyporo/hmesh/CMakeLists.txt +++ b/source/hyporo/hmesh/CMakeLists.txt @@ -6,10 +6,11 @@ include_directories( ../hyplib/vector ) -add_library(hyporo-hmesh STATIC +add_library(hyporo-mesh STATIC # Header files mesh.hpp + vertex.hpp # Source files mesh.cpp diff --git a/source/hyporo/hmesh/Cell.hpp b/source/hyporo/hmesh/Cell.hpp deleted file mode 100644 index 188af34..0000000 --- a/source/hyporo/hmesh/Cell.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "Face.hpp" - -namespace hyporo -{ - - -} // end namespace hyporo diff --git a/source/hyporo/hmesh/Edge.hpp b/source/hyporo/hmesh/Edge.hpp deleted file mode 100644 index 10c9c16..0000000 --- a/source/hyporo/hmesh/Edge.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "Vertex.hpp" - -namespace hyporo -{ - - -} // end namespace hyporo diff --git a/source/hyporo/hmesh/Face.hpp b/source/hyporo/hmesh/Face.hpp deleted file mode 100644 index 3a3c05b..0000000 --- a/source/hyporo/hmesh/Face.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "Edge.hpp" - -namespace hyporo -{ - - -} // end namespace hyporo diff --git a/source/hyporo/hmesh/Mesh.cpp b/source/hyporo/hmesh/Mesh.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/source/hyporo/hmesh/Mesh.hpp b/source/hyporo/hmesh/Mesh.hpp deleted file mode 100644 index 9c717c5..0000000 --- a/source/hyporo/hmesh/Mesh.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Cell.hpp" - -namespace hyporo -{ - -class Mesh -{ - - mutable list> vertices_; - mutable list> edges_; - mutable list> faces_; - mutable list> cells_; - -public: - - // Constructors - - //- Desctuctor - virtual ~Mesh(); - - // Mesh size parameters - - inline sizet nPoints() const; - inline sizet nEdges() const; - inline sizet nFaces() const; - inline sizet nCells() const; -}; - -} // end namespace hyporo diff --git a/source/hyporo/hmesh/Vertex.hpp b/source/hyporo/hmesh/Vertex.hpp deleted file mode 100644 index 4630ded..0000000 --- a/source/hyporo/hmesh/Vertex.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "vector.hpp" -#include -#include - -namespace hyporo -{ - -template -using list = std::vector; - -using std::shared_ptr; -using std::weak_ptr; - -// Forward declaration - -class Edge; - -class Mesh; - -// Class declaration - -class Vertex : public VectorSpace -{ - - //- List of weak pointers to edges that use this vertex - mutable list> edges_; - -public: - - // Constructors - - inline Vertex(Mesh& mesh, const scalar& x, const scalar& y, const scalar& z); - - // Member functions - - inline const scalar& x() const; - - inline const scalar& y() const; - - inline const scalar& z() const; -}; - -} // end namespace hyporo - -#include "Vertex.hxx" diff --git a/source/hyporo/hmesh/Vertex.hxx b/source/hyporo/hmesh/Vertex.hxx deleted file mode 100644 index 68479ca..0000000 --- a/source/hyporo/hmesh/Vertex.hxx +++ /dev/null @@ -1,27 +0,0 @@ - -namespace hyporo -{ - -Vertex::Point(const scalar& x, const scalar& y, const scalar& z) -{ - row[0] = x; - row[1] = y; - row[2] = z; -} - -const scalar& Vertex::x() const -{ - return row[0]; -} - -const scalar& Vertex::y() const; -{ - return row[1]; -} - -const scalar& Vertex::z() const; -{ - return row[2]; -} - -} // end namespace hyporo diff --git a/source/hyporo/hmesh/cell.hpp b/source/hyporo/hmesh/cell.hpp new file mode 100644 index 0000000..bb05754 --- /dev/null +++ b/source/hyporo/hmesh/cell.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "../hyplib/scalar/scalar.hpp" +#include "../hyplib/array/array.hpp" +#include "../hyplib/vector/vector.hpp" + + +namespace hpr::mesh +{ + +class Face; + +class Cell : public darray +{ + + using face_pointer = Face*; + using base = darray; + +public: + + Cell() : + base {} + {} + + Cell(std::initializer_list faces) : + base{faces} + {} + + ~Cell() override = default; + + darray& faces() + { + return *this; + } + +}; + +} \ No newline at end of file diff --git a/source/hyporo/hmesh/edge.hpp b/source/hyporo/hmesh/edge.hpp new file mode 100644 index 0000000..ab8e088 --- /dev/null +++ b/source/hyporo/hmesh/edge.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "../hyplib/scalar/scalar.hpp" +#include "../hyplib/array/array.hpp" +#include "../hyplib/vector/vector.hpp" + + +namespace hpr::mesh +{ + +class Vertex; +class Face; + +class Edge : public sarray +{ + friend class Mesh; + + using vertex_pointer = Vertex*; + using base = sarray; + +protected: + darray p_refFaces; + +public: + Edge() : + base{} + {} + + Edge(vertex_pointer v1, vertex_pointer v2) : + base{v1, v2} + {} + + ~Edge() override + { + for (auto& f: p_refFaces) + f = nullptr; + } + + darray& refFaces() + { + return p_refFaces; + } + + void addRefFace(Face* face) + { + p_refFaces.push(face); + } + + sarray& vertices() + { + return *this; + } + + vertex_pointer vertex(size_type n) + { + return (*this)[n]; + } + + bool isValid() + { + return *front() != *back(); + } +}; + +} \ No newline at end of file diff --git a/source/hyporo/hmesh/face.hpp b/source/hyporo/hmesh/face.hpp new file mode 100644 index 0000000..2466ded --- /dev/null +++ b/source/hyporo/hmesh/face.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "../hyplib/scalar/scalar.hpp" +#include "../hyplib/array/array.hpp" +#include "../hyplib/vector/vector.hpp" + + +namespace hpr::mesh +{ + +class Vertex; +class Edge; +class Cell; + +class Face : public darray +{ + friend class Mesh; + + using edge_pointer = Edge*; + using vertex_pointer = Vertex*; + using base = darray; + +protected: + darray p_refCells; + +public: + Face() : + base{} + {} + + Face(std::initializer_list edges) : + base{edges} + {} + + ~Face() override + { + for (auto& c: p_refCells) + c = nullptr; + } + + darray& refCells() + { + return p_refCells; + } + + void addRefCell(Cell* cell) + { + p_refCells.push(cell); + } + + darray& edges() + { + return *this; + } + + edge_pointer edge(size_type n) + { + return (*this)[n]; + } + + darray vertices() + { + darray vertices_ {size(), nullptr}; + for (auto n = 0; n < size(); ++n) + vertices_[n] = edge(n)->vertex(0); + return vertices_; + } + +}; + +} \ No newline at end of file diff --git a/source/hyporo/hmesh/mesh.hpp b/source/hyporo/hmesh/mesh.hpp index bc43827..79e5cbf 100644 --- a/source/hyporo/hmesh/mesh.hpp +++ b/source/hyporo/hmesh/mesh.hpp @@ -4,154 +4,37 @@ #include "../hyplib/array/array.hpp" #include "../hyplib/vector/vector.hpp" -#include +#include "vertex.hpp" +#include "edge.hpp" +#include "face.hpp" +#include "cell.hpp" + -#include namespace hpr::mesh { -class Edge; -class Vertex : public vec -{ - friend class Mesh; - using base = vec; -protected: - darray p_refEdges; -public: - Vertex() : - base {} - {} - Vertex(const scalar& x, const scalar& y, const scalar& z) : - base {x, y, z} - {} - virtual - ~Vertex() - { - for (auto& e : p_refEdges) - delete e; - } - darray& refEdges() - { - return p_refEdges; - } - void addRefEdge(Edge* edge) - { - p_refEdges.push(edge); - } -}; -class Face; -class Edge : public sarray -{ - friend class Mesh; - using vertex_pointer = Vertex*; - using base = sarray; -protected: - darray p_refFaces; -public: - Edge() : - base {} - {} - Edge(vertex_pointer v1, vertex_pointer v2) : - base {v1, v2} - {} - virtual - ~Edge() - { - for (auto& f : p_refFaces) - delete f; - } - darray& refFaces() - { - return p_refFaces; - } - void addRefFace(Face* face) - { - p_refFaces.push(face); - } - sarray& vertices() - { - return *this; - } - vertex_pointer vertex(size_type n) - { - return (*this)[n]; - } -}; -class Cell; -class Face : public darray -{ - friend class Mesh; - using edge_pointer = Edge*; - using vertex_pointer = Vertex*; - using base = darray; -protected: - darray p_refCells; -public: - Face() : - base {} - {} - Face(std::initializer_list edges) : - base {edges} - {} - virtual - ~Face() - { - for (auto& c : p_refCells) - delete c; - } - darray& refCells() - { - return p_refCells; - } - void addRefCell(Cell* cell) - { - p_refCells.push(cell); - } - darray& edges() - { - return *this; - } - edge_pointer edge(size_type n) - { - return (*this)[n]; - } - darray vertices() - { - darray vertices_ {size(), nullptr}; - for (auto n = 0; n < size(); ++n) - vertices_[n] = edge(n)->vertex(0); - return vertices_; - } -}; - -class Cell : public darray -{ - using face_pointer = Face*; - using base = darray; -public: - Cell(std::initializer_list faces) : - base {faces} - {} -}; - class Mesh { -#include "vertex.hpp" + public: using size_type = std::size_t; using vertex_pointer = Vertex*; using edge_pointer = Edge*; using face_pointer = Face*; using cell_pointer = Cell*; + protected: darray p_vertices; darray p_edges; darray p_faces; darray p_cells; + public: Mesh() = default; + Mesh(const Mesh&) = default; + ~Mesh() { for (auto& v : p_vertices) @@ -261,10 +144,13 @@ public: removeFace(indexOf(refFace), false); removeNullFaces(); } + for (auto& vertex : edge(n)->vertices()) + vertex->refEdges().remove([this, n](edge_pointer e){ return e == edge(n); }); delete edge(n); if (erase) edges().remove(n); } + darray& faces() { return p_faces; @@ -297,6 +183,8 @@ public: removeCell(indexOf(refCell), false); removeNullFaces(); } + for (auto& edge : face(n)->edges()) + edge->refFaces().remove([this, n](face_pointer f){ return f == face(n); }); delete face(n); if (erase) faces().remove(n); @@ -313,6 +201,14 @@ public: return p_cells[n]; } + template ... Faces> + void addCell(const Faces& ...faces) + { + cells().push(new Cell {static_cast(faces)...}); + for (auto& face : *cells().back()) + face->addRefCell(cells().back()); + } + void removeNullCells() { cells().remove([](cell_pointer cell){ return cell == nullptr; }); @@ -321,10 +217,13 @@ public: void removeCell(size_type n, bool erase = true, bool cascade = true) { static_cast(cascade); + for (auto& face : cell(n)->faces()) + face->refCells().remove([this, n](cell_pointer c){ return c == cell(n); }); delete cell(n); if (erase) cells().remove(n); } + }; } \ No newline at end of file diff --git a/source/hyporo/hmesh/tests/hmesh-test.cpp b/source/hyporo/hmesh/tests/hmesh-test.cpp index 6c4d0c0..4afaf49 100644 --- a/source/hyporo/hmesh/tests/hmesh-test.cpp +++ b/source/hyporo/hmesh/tests/hmesh-test.cpp @@ -4,7 +4,18 @@ TEST(hmeshTest, Mesh) { hpr::mesh::Mesh mesh; - mesh.addVertex(1, 2, 3); - mesh.addVertex(1, 3, 3); + mesh.addVertex(0, 0, 0); + mesh.addVertex(1, 1, 1); + mesh.addVertex(10, 0, 0); + mesh.addVertex(0, 0, 0); mesh.addEdge(mesh.vertex(0), mesh.vertex(1)); + mesh.addEdge(mesh.vertex(1), mesh.vertex(2)); + mesh.addEdge(mesh.vertex(2), mesh.vertex(0)); + mesh.addEdge(mesh.vertex(0), mesh.vertex(3)); + mesh.addFace(mesh.edge(0), mesh.edge(1), mesh.edge(2)); + EXPECT_EQ(mesh.vertices().size(), 4); + EXPECT_EQ(mesh.edges().size(), 4); + EXPECT_EQ(mesh.faces().size(), 1); + EXPECT_EQ(mesh.vertex(1)->refEdges().size(), 2); + EXPECT_FALSE(mesh.edges().back()->isValid()); } diff --git a/source/hyporo/hmesh/vertex.hpp b/source/hyporo/hmesh/vertex.hpp new file mode 100644 index 0000000..b45cb5c --- /dev/null +++ b/source/hyporo/hmesh/vertex.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "../hyplib/scalar/scalar.hpp" +#include "../hyplib/array/array.hpp" +#include "../hyplib/vector/vector.hpp" + + +namespace hpr::mesh +{ + +class Edge; + +class Vertex : public vec +{ + friend class Mesh; + + using base = vec; + +protected: + darray p_refEdges; + +public: + Vertex() : + base{} + {} + + Vertex(const scalar& x, const scalar& y, const scalar& z) : + base{x, y, z} + {} + + ~Vertex() override + { + for (auto& e: p_refEdges) + e = nullptr; + } + + darray& refEdges() + { + return p_refEdges; + } + + void addRefEdge(Edge* edge) + { + p_refEdges.push(edge); + } + +}; + +} \ No newline at end of file diff --git a/source/hyporo/hyplib/array/dynamic_array.hpp b/source/hyporo/hyplib/array/dynamic_array.hpp index 90918e3..8a8eef0 100644 --- a/source/hyporo/hyplib/array/dynamic_array.hpp +++ b/source/hyporo/hyplib/array/dynamic_array.hpp @@ -23,7 +23,7 @@ public: using iterator = Iterator; using const_pointer = Type const*; using const_reference = Type const&; - using const_iterator = Iterator const; + using const_iterator = Iterator; protected: @@ -63,6 +63,28 @@ public: swap(*this, arr); } + inline + DynamicArray(const_iterator start, const_iterator end) : + p_size {size_type(end.operator->() - start.operator->())}, + p_capacity {p_size}, + p_start {new value_type[p_size]}, + p_end {p_start + p_size}, + p_storage_end {p_start + p_capacity} + { + std::copy(start, end, p_start); + } + + inline + DynamicArray(iterator start, iterator end) : + p_size {size_type(end.operator->() - start.operator->())}, + p_capacity {p_size}, + p_start {new value_type[p_size]}, + p_end {p_start + p_size}, + p_storage_end {p_start + p_capacity} + { + std::copy(start, end, p_start); + } + inline DynamicArray(std::initializer_list list) : p_size {list.size()}, @@ -76,14 +98,14 @@ public: inline DynamicArray(size_type size, value_type value) : - p_size {size}, + p_size {0}, p_capacity {size}, p_start {new value_type[p_capacity]}, p_end {p_start + p_size}, p_storage_end {p_start + p_capacity} { - for (auto n = 0; n < p_size; ++n) - *(p_start + n) = value; + for (auto n = 0; n < size; ++n) + push(value); } inline @@ -161,12 +183,16 @@ public: virtual reference operator[](size_type n) { + if (n >= size()) + throw std::out_of_range("Index out of bounds"); return *(p_start + n); } virtual const_reference operator[](size_type n) const { + if (n >= size()) + throw std::out_of_range("Index out of bounds"); return *(p_start + n); } @@ -353,6 +379,11 @@ public: p_storage_end = p_end + p_capacity; } + DynamicArray slice(iterator start, iterator end) + { + return DynamicArray {start, end}; + } + // Friend functions friend @@ -373,6 +404,18 @@ public: return false; return true; } + + friend + DynamicArray operator+(const DynamicArray& lhs, const DynamicArray& rhs) + { + DynamicArray arr {rhs.size() + lhs.size(), {}}; + for (auto n = 0; n < rhs.size(); ++n) + { + arr[n] = rhs[n]; + arr[n + rhs.size()] = lhs[n]; + } + return arr; + } }; } \ No newline at end of file diff --git a/source/hyporo/hyplib/array/static_array.hpp b/source/hyporo/hyplib/array/static_array.hpp index 914378a..65bc1fa 100644 --- a/source/hyporo/hyplib/array/static_array.hpp +++ b/source/hyporo/hyplib/array/static_array.hpp @@ -124,7 +124,7 @@ public: } inline - StaticArray& operator=(const StaticArray& vs) noexcept + StaticArray& operator=(const StaticArray& vs) { std::copy(vs.begin(), vs.end(), begin()); return *this; @@ -185,12 +185,16 @@ public: virtual reference operator[](size_type n) { + if (n >= size()) + throw std::out_of_range("Index out of bounds"); return *(p_start + n); } virtual const_reference operator[](size_type n) const { + if (n >= size()) + throw std::out_of_range("Index out of bounds"); return *(p_start + n); } @@ -227,6 +231,13 @@ public: std::swap(lhs.p_end, rhs.p_end); } + friend + void swap(StaticArray&& lhs, StaticArray&& rhs) + { + std::swap(lhs.p_start, rhs.p_start); + std::swap(lhs.p_end, rhs.p_end); + } + friend bool operator==(const StaticArray& lhs, const StaticArray& rhs) { diff --git a/source/hyporo/hyplib/logger/logger.hpp b/source/hyporo/hyplib/logger/logger.hpp new file mode 100644 index 0000000..fb37385 --- /dev/null +++ b/source/hyporo/hyplib/logger/logger.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + + +namespace hpr +{ + class Logger + { + public: + enum Severity + { + None, + Error, + Warning, + Info, + Debug + }; + static Severity severity; + + template + friend + Logger& operator<<(Logger& l, const T& data) + { + if (severity == Info) + std::cout << data << std::endl; + else if (severity == Error) + std::cerr << data << std::endl; + + return l; + } + }; + Logger::Severity Logger::severity = Logger::Warning; + Logger logger; +} diff --git a/source/hyporo/hyplib/matrix/matrix_space.cpp b/source/hyporo/hyplib/matrix/matrix_space.cpp new file mode 100644 index 0000000..54f7288 --- /dev/null +++ b/source/hyporo/hyplib/matrix/matrix_space.cpp @@ -0,0 +1,9 @@ +#include "matrix_space.hpp" +#include + +namespace hpr +{ + + + +} diff --git a/source/hyporo/hyplib/matrix/matrix_space.hpp b/source/hyporo/hyplib/matrix/matrix_space.hpp index 984df7c..3cdacb3 100644 --- a/source/hyporo/hyplib/matrix/matrix_space.hpp +++ b/source/hyporo/hyplib/matrix/matrix_space.hpp @@ -168,7 +168,7 @@ public: else { auto res = 0; for (auto m = 0; m < Cols; ++m) - res += pow(-1, m) * (*this)(0, m) * minor(0, m).det(); + res += std::pow(-1, m) * (*this)(0, m) * minor(0, m).det(); return res; } } @@ -190,7 +190,7 @@ public: for (auto n = 0; n < Rows; ++n) for (auto k = 0; k < Cols; ++k) { - ms(n, k) = pow(-1, n + k) * minor(n, k).det(); + ms(n, k) = std::pow(-1, n + k) * minor(n, k).det(); } return ms.transpose(); } diff --git a/source/hyporo/hyplib/scalar/scalar.hpp b/source/hyporo/hyplib/scalar/scalar.hpp index 5948fcf..6a4f053 100644 --- a/source/hyporo/hyplib/scalar/scalar.hpp +++ b/source/hyporo/hyplib/scalar/scalar.hpp @@ -25,29 +25,58 @@ using scalar = float; #endif -static const scalar small = std::numeric_limits::epsilon(); -static const scalar great = static_cast(1.0) / small; -static const scalar valueSmall = std::numeric_limits::min(); -static const scalar valueGreat = std::numeric_limits::max() * 0.1; -static const scalar NaN = std::numeric_limits::signaling_NaN(); +static +const scalar small = std::numeric_limits::epsilon(); +static +const scalar great = static_cast(1.0) / small; +static +const scalar valueSmall = std::numeric_limits::min(); +static +const scalar valueGreat = std::numeric_limits::max() * 0.1; +static +const scalar NaN = std::numeric_limits::signaling_NaN(); + //- Return 1 if s is positive or 0 otherwise -1 -inline int sign(const scalar s) +inline +int sign(const scalar s) { return (s >= 0) ? 1: -1; } -inline scalar mag(const scalar s) +inline +scalar mag(const scalar s) { return std::fabs(s); } +template +inline +bool equal(Type lhs, Type rhs, Type precision = 1e-5) +{ + return mag(lhs - rhs) < precision; +} + +inline +scalar clip(scalar value, scalar valueMin, scalar valueMax) +{ + return std::min(valueMax, std::max(value, valueMin)); +} + +template +Type inversesqrt( Type n ) +{ + return static_cast(1) / sqrt(n); +} + // trigonometric -static const scalar PI = static_cast(M_PIl); +static +const scalar PI = static_cast(M_PIl); + template -constexpr +inline Type radians(Type degrees) { static_assert(std::numeric_limits::is_iec559); @@ -55,7 +84,7 @@ Type radians(Type degrees) } template -constexpr +inline Type degrees(Type radians) { static_assert(std::numeric_limits::is_iec559); diff --git a/source/hyporo/hyplib/tests/hyplib-test.cpp b/source/hyporo/hyplib/tests/hyplib-test.cpp index 1d27e94..c74fb6a 100644 --- a/source/hyporo/hyplib/tests/hyplib-test.cpp +++ b/source/hyporo/hyplib/tests/hyplib-test.cpp @@ -5,7 +5,7 @@ #include "matrix.hpp" -TEST(hyplib, Array) +TEST(hyplib, StaticArray) { hpr::StaticArray arr {1, 3, 2}; hpr::StaticArray sarr {arr, 5}; @@ -29,6 +29,13 @@ TEST(hyplib, DynamicArray) arr3.remove([](float num) { return num == 0; }); EXPECT_EQ(arr3, hpr::darray({1, 3, 2, 9, 5})); EXPECT_EQ(arr3.size(), 5); + + hpr::DynamicArray arr4; + arr4.push(new float(5)); + arr4.push(new float(7)); + arr4.push(new float(9)); + EXPECT_EQ(*arr4[0], 5.f); + EXPECT_EQ(*arr4[2], 9.f); } TEST(hyplib, Vector) @@ -43,6 +50,9 @@ TEST(hyplib, Vector) EXPECT_EQ(v1 + v2, hpr::vec3(6, 10, 1)); EXPECT_EQ(v1 - v2, hpr::vec3(-4, -4, 3)); EXPECT_EQ((hpr::dot(v1, v2) ), 24); + EXPECT_EQ((hpr::cross(hpr::vec3(1, 0, 0), hpr::vec3(0, 1, 0))), hpr::vec3(0, 0, 1)); + EXPECT_EQ((hpr::angle(hpr::vec3(1, 0, 0), hpr::vec3(0, 0, 1))), hpr::PI * 0.5); + EXPECT_EQ((hpr::normalize(hpr::vec3(1, 1, 1))), hpr::vec3(0.5773502691896258, 0.5773502691896258, 0.5773502691896258)); } TEST(hyplib, Matrix) diff --git a/source/hyporo/hyplib/tests/mytest.cpp b/source/hyporo/hyplib/tests/mytest.cpp new file mode 100644 index 0000000..c28d7a2 --- /dev/null +++ b/source/hyporo/hyplib/tests/mytest.cpp @@ -0,0 +1,26 @@ +#include +#include "../vector/vector_space.hpp" + +template +using vs = hpr::VectorSpace; + +using std::cout, std::endl; + +template +void print(const vs& vec) +{ + cout << "Vector: "; + for (auto n = vec.begin(); n != vec.end(); ++n) + cout << *n << " "; + cout << endl; +} +int main(void) +{ + + vs v1 {3, 4, 7, 8}; + v1 += 3; + print(v1); + + + return 0; +} diff --git a/source/hyporo/hyplib/vector/vector.hpp b/source/hyporo/hyplib/vector/vector.hpp index 468689b..bcfd27f 100644 --- a/source/hyporo/hyplib/vector/vector.hpp +++ b/source/hyporo/hyplib/vector/vector.hpp @@ -1,6 +1,6 @@ #pragma once -#include "scalar.hpp" +#include "../scalar/scalar.hpp" #include "vector_space.hpp" diff --git a/source/hyporo/hyplib/vector/vector_space.hpp b/source/hyporo/hyplib/vector/vector_space.hpp index 65c6f8c..de1a0e6 100644 --- a/source/hyporo/hyplib/vector/vector_space.hpp +++ b/source/hyporo/hyplib/vector/vector_space.hpp @@ -1,7 +1,9 @@ #pragma once +#include "../scalar/scalar.hpp" #include "../array/array.hpp" + namespace hpr { @@ -40,12 +42,17 @@ public: {} inline - VectorSpace& operator=(const VectorSpace& vs) noexcept = default; + VectorSpace& operator=(const VectorSpace& vs) + { + base::operator=(vs); + return *this; + } inline VectorSpace& operator=(VectorSpace&& vs) noexcept { - std::swap(*this, vs); + swap(*this, vs);//std::forward(static_cast(*this)), std::forward(static_cast(vs))); + //std::swap(*this, vs); return *this; } @@ -67,6 +74,14 @@ public: base {list} {} + template ... Args> + inline + VectorSpace(const value_type& v, const Args& ...args) : + base {v, static_cast(args)...} + { + static_assert(1 + sizeof...(args) == Size, "Number of arguments must be equal to size of vector"); + } + template ... Args> inline VectorSpace(value_type&& v, Args&& ...args) : @@ -261,11 +276,21 @@ public: }; +template +inline +VectorSpace equal(const VectorSpace& lhs, const VectorSpace& rhs, scalar precision = 1e-5) +{ + VectorSpace res; + for (auto n = 0; n < Size; ++n) + res[n] = equal(lhs[n], rhs[n], precision); + return res; +} + template inline Type sum(const VectorSpace& vs) { - Type sum {0}; + Type sum {}; for (const Type& v : vs) sum += v; return sum; @@ -297,12 +322,47 @@ constexpr VectorSpace cross(const VectorSpace& lhs, const VectorSpace& rhs) { return VectorSpace( - lhs[2] * rhs[3] - lhs[3] * rhs[2], - lhs[3] * rhs[1] - lhs[1] * rhs[3], - lhs[1] * rhs[2] - lhs[2] * rhs[1] + lhs[1] * rhs[2] - lhs[2] * rhs[1], + lhs[2] * rhs[0] - lhs[0] * rhs[2], + lhs[0] * rhs[1] - lhs[1] * rhs[0] ); } +template +constexpr +VectorSpace pow(const VectorSpace& vs, scalar degree) +{ + VectorSpace res; + for (auto n = 0; n < Size; ++n) + res[n] = std::pow(vs[n], degree); + return res; +} + +template +constexpr +VectorSpace abs(const VectorSpace& vs) +{ + VectorSpace res; + for (auto n = 0; n < Size; ++n) + res[n] = std::abs(vs[n]); + return res; +} + +template +constexpr +Type norm(const VectorSpace& vs) +{ + return sqrt(sum(pow(abs(vs), 2))); +} + +template +constexpr +Type angle(const VectorSpace& lhs, const VectorSpace& rhs) +{ + scalar cos = dot(lhs, rhs) / (norm(lhs) * norm(rhs)); + return acos(cos); //clip(cos, -1., 1.)); +} + template inline VectorSpace normalize(const VectorSpace& vs) @@ -310,16 +370,6 @@ VectorSpace normalize(const VectorSpace& vs) return vs * inversesqrt(dot(vs, vs)); } -template -constexpr -VectorSpace equal(const VectorSpace& lhs, const VectorSpace& rhs) -{ - VectorSpace vs; - for (auto n = 0; n < Size; ++n) - vs[n] = lhs[n] == rhs[n]; - return vs; -} - template constexpr bool any(const VectorSpace& vs) diff --git a/source/hyporo/window_system/glfw/window_system.cpp b/source/hyporo/window_system/glfw/window_system.cpp index b30b08e..3c1c3bf 100644 --- a/source/hyporo/window_system/glfw/window_system.cpp +++ b/source/hyporo/window_system/glfw/window_system.cpp @@ -2,6 +2,8 @@ #include "window.hpp" #include "window_system.hpp" +#include + namespace hpr::gpu::glfw { @@ -30,9 +32,4 @@ gpu::Window* WindowSystem::newWindow() return static_cast(p_windows.back()); } -std::function WindowSystem::deviceProcAddress() const -{ - return std::function(glfwGetProcAddress); -} - } \ No newline at end of file diff --git a/source/hyporo/window_system/glfw/window_system.hpp b/source/hyporo/window_system/glfw/window_system.hpp index 636e4ec..c4bfdf5 100644 --- a/source/hyporo/window_system/glfw/window_system.hpp +++ b/source/hyporo/window_system/glfw/window_system.hpp @@ -2,9 +2,6 @@ #include "../window_system.hpp" -#include -#include - namespace hpr::gpu::glfw { @@ -20,7 +17,6 @@ public: gpu::Window* newWindow() override; - std::function deviceProcAddress() const; }; } \ No newline at end of file diff --git a/source/hyporo/window_system/window_system.cpp b/source/hyporo/window_system/window_system.cpp index 00730ff..73281fd 100644 --- a/source/hyporo/window_system/window_system.cpp +++ b/source/hyporo/window_system/window_system.cpp @@ -44,7 +44,9 @@ void WindowSystem::closeWindow(Window* window) void WindowSystem::destroyWindow(Window* window) { - p_windows.remove(window); + for (auto n = 0; n < p_windows.size(); ++n) + if (p_windows[n] == window) + p_windows.remove(n); window->close(); window = nullptr; } @@ -56,6 +58,8 @@ Monitor* WindowSystem::monitor(int index) void WindowSystem::destroyMonitor(Monitor* monitor) { - p_monitors.remove(monitor); + for (auto n = 0; n < p_monitors.size(); ++n) + if (p_monitors[n] == monitor) + p_monitors.remove(n); } }