improved base classes
This commit is contained in:
parent
49b2ba0ba7
commit
2811c525d3
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
cmake-wsl-build-debug
|
||||
cmake-build-debug/
|
||||
.idea/
|
||||
build/
|
||||
|
@ -163,4 +163,5 @@ message(STATUS ${summary})
|
||||
#add_subdirectory(docs)
|
||||
|
||||
# Additional applications
|
||||
add_subdirectory(source/applications)
|
||||
add_subdirectory(source/applications)
|
||||
add_subdirectory(source/creator)
|
6
cmake/external/glad.cmake
vendored
6
cmake/external/glad.cmake
vendored
@ -3,7 +3,13 @@ include(${CMAKE_SOURCE_DIR}/cmake/tools/CPM.cmake)
|
||||
CPMAddPackage(
|
||||
NAME glad
|
||||
GIT_REPOSITORY https://github.com/Dav1dde/glad.git
|
||||
#VERSION 2.0.2
|
||||
VERSION 0.1.36
|
||||
GIT_PROGRESS TRUE
|
||||
OPTIONS "GLAD_EXPORT ON" "GLAD_INSTALL ON"
|
||||
)
|
||||
|
||||
#if(glad_ADDED)
|
||||
# add_subdirectory("${glad_SOURCE_DIR}/cmake" glad_cmake)
|
||||
# glad_add_library(glad REPRODUCIBLE API gl:core=3.3)
|
||||
#endif()
|
@ -1,3 +0,0 @@
|
||||
add_subdirectory(hpr)
|
||||
#add_subdirectory(creator)
|
||||
#add_subdirectory(applications)
|
@ -1,6 +1,6 @@
|
||||
|
||||
project(
|
||||
hyporo
|
||||
hyporo-creator
|
||||
VERSION 0.1.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
@ -13,40 +13,49 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
creator.cpp
|
||||
#add_executable(${PROJECT_NAME}
|
||||
# creator.cpp
|
||||
#)
|
||||
|
||||
../hpr/window_system/window_system.cpp
|
||||
../hpr/window_system/monitor.cpp
|
||||
../hpr/window_system/window.cpp
|
||||
../hpr/window_system/glfw/window_system.cpp
|
||||
../hpr/window_system/glfw/monitor.cpp
|
||||
../hpr/window_system/glfw/window.cpp
|
||||
)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/external/imgui.cmake)
|
||||
#message(STATUS "project name: ${PROJECT_NAME}")
|
||||
#target_link_libraries(${PROJECT_NAME}
|
||||
# hpr::hpr
|
||||
# imgui
|
||||
#)
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/imgui.cmake)
|
||||
message(STATUS "project name: ${PROJECT_NAME}")
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
hpr::hpr
|
||||
imgui
|
||||
)
|
||||
#target_include_directories(${PROJECT_NAME}
|
||||
# PRIVATE
|
||||
# ../
|
||||
#)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
../
|
||||
)
|
||||
#set(CMAKE_CXX_STANDARD 20)
|
||||
#add_executable(testi
|
||||
# test.cpp
|
||||
#)
|
||||
|
||||
#target_include_directories(testi
|
||||
# PRIVATE
|
||||
# ../
|
||||
#)
|
||||
|
||||
#target_link_libraries(testi
|
||||
# hpr::hpr
|
||||
# imgui
|
||||
#)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
add_executable(testi
|
||||
test.cpp
|
||||
)
|
||||
|
||||
target_include_directories(testi
|
||||
PRIVATE
|
||||
../
|
||||
)
|
||||
add_executable(hyporo-creator
|
||||
test2.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(testi
|
||||
hpr::hpr
|
||||
imgui
|
||||
)
|
||||
target_include_directories(hyporo-creator
|
||||
PRIVATE
|
||||
../
|
||||
)
|
||||
|
||||
target_link_libraries(hyporo-creator
|
||||
hpr::gpu
|
||||
hpr::window-system
|
||||
imgui
|
||||
)
|
||||
|
@ -25,7 +25,24 @@ const char *fragmentShaderSource = "#version 330 core\n"
|
||||
"{\n"
|
||||
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
|
||||
"}\n\0";
|
||||
|
||||
const GLchar* vertexSource = R"glsl(
|
||||
#version 150 core
|
||||
in vec3 position;
|
||||
in vec3 color;
|
||||
in vec2 texcoord;
|
||||
out vec3 Color;
|
||||
out vec2 Texcoord;
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 proj;
|
||||
uniform vec3 overrideColor;
|
||||
void main()
|
||||
{
|
||||
Color = overrideColor * color;
|
||||
Texcoord = texcoord;
|
||||
gl_Position = proj * view * model * vec4(position, 1.0);
|
||||
}
|
||||
)glsl";
|
||||
int main()
|
||||
{
|
||||
using namespace hpr;
|
||||
|
133
source/creator/test2.cpp
Normal file
133
source/creator/test2.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
|
||||
#include "hpr/gpu.hpp"
|
||||
#include "hpr/window_system/window_system.hpp"
|
||||
#include "hpr/window_system/glfw/window_system.hpp"
|
||||
#include "hpr/window_system/glfw/window.hpp"
|
||||
#include "hpr/math.hpp"
|
||||
#include "hpr/mesh.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <iostream>
|
||||
|
||||
const char *vertexShaderSource = "#version 330 core\n"
|
||||
"layout (location = 0) in vec3 aPos;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
|
||||
"}\0";
|
||||
const char *fragmentShaderSource = "#version 330 core\n"
|
||||
"out vec4 FragColor;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
|
||||
"}\n\0";
|
||||
|
||||
int main()
|
||||
{
|
||||
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 (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
|
||||
throw std::runtime_error("Cannot initialize gl context");
|
||||
|
||||
gpu::Shader vshader {gpu::Shader::Type::Vertex, vertexShaderSource};
|
||||
gpu::Shader fshader {gpu::Shader::Type::Fragment, fragmentShaderSource};
|
||||
gpu::ShaderProgram sprogram {};
|
||||
|
||||
vshader.create();
|
||||
fshader.create();
|
||||
sprogram.create();
|
||||
sprogram.attach(vshader);
|
||||
sprogram.attach(fshader);
|
||||
sprogram.link();
|
||||
for (auto& sh : sprogram.shaders())
|
||||
std::cout << sh.index() << std::endl;
|
||||
|
||||
|
||||
darray<float> vertices {
|
||||
0.5f, 0.5f, 0.0f, // top right
|
||||
0.5f, -0.5f, 0.0f, // bottom right
|
||||
-0.5f, -0.5f, 0.0f, // bottom left
|
||||
-0.5f, 0.5f, 0.0f // top left
|
||||
};
|
||||
darray<unsigned int> indices2 { // note that we start from 0!
|
||||
0, 1, 3, // first Triangle
|
||||
1, 2, 3 // second Triangle
|
||||
};
|
||||
|
||||
gpu::ArrayObject vao {};
|
||||
gpu::BufferObject vbo {gpu::BufferObject::Type::Vertex};
|
||||
gpu::BufferObject ebo {gpu::BufferObject::Type::Index};
|
||||
|
||||
vao.create();
|
||||
vao.bind();
|
||||
vbo.create<float>(vertices);
|
||||
ebo.create<unsigned int>(indices2);
|
||||
vao.attribPointer(vbo, 0, 3);
|
||||
vao.unbind();
|
||||
|
||||
gpu::Viewport viewport {{0, 0}, {600, 400}};
|
||||
gpu::ColorBuffer colorBuffer;
|
||||
gpu::DepthBuffer depthBuffer {true};
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplGlfw_InitForOpenGL(dynamic_cast<gpu::glfw::Window*>(w)->instance(), true);
|
||||
ImGui_ImplOpenGL3_Init("#version 420");
|
||||
|
||||
while (w->isOpen())
|
||||
{
|
||||
viewport.set();
|
||||
|
||||
colorBuffer.clear({0.8f, 0.2f, 0.2f, 1.0f});
|
||||
depthBuffer.clear();
|
||||
|
||||
sprogram.bind();
|
||||
vao.bind();
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
bool yes = true;
|
||||
ImGui::ShowDemoWindow(&yes);
|
||||
|
||||
ImGui::Begin("Hello, world!");
|
||||
{
|
||||
if (ImGui::Button("Exit"))
|
||||
w->state(gpu::Window::State::Closed);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
dynamic_cast<gpu::glfw::Window*>(w)->swapBuffers();
|
||||
dynamic_cast<gpu::glfw::Window*>(w)->pollEvents();
|
||||
}
|
||||
|
||||
sprogram.destroy();
|
||||
vshader.destroy();
|
||||
fshader.destroy();
|
||||
vbo.destroy();
|
||||
ebo.destroy();
|
||||
vao.destroy();
|
||||
|
||||
ws->destroyWindow(w);
|
||||
gpu::WindowSystem::destroy(ws);
|
||||
|
||||
return 0;
|
||||
}
|
@ -5,6 +5,8 @@ add_subdirectory(containers)
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(mesh)
|
||||
add_subdirectory(geometry)
|
||||
|
||||
if(WITH_CSG)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/external/occt.cmake)
|
||||
add_subdirectory(csg)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <limits>
|
||||
#include <functional>
|
||||
#include <concepts>
|
||||
|
||||
|
||||
namespace hpr
|
||||
@ -170,7 +171,7 @@ public:
|
||||
return const_iterator(p_end);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual
|
||||
[[nodiscard]] virtual constexpr
|
||||
size_type size() const
|
||||
{
|
||||
return size_type(p_end - p_start);
|
||||
|
@ -364,7 +364,7 @@ public:
|
||||
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));
|
||||
transform.SetRotation(gp_Ax1({pos[0], pos[1], pos[2]}, {axis[0], axis[1], axis[2]}), rad(angle));
|
||||
BRepBuilderAPI_Transform builder {p_shape, transform, true};
|
||||
|
||||
return builder.Shape();
|
||||
|
5
source/hpr/geometry.hpp
Normal file
5
source/hpr/geometry.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "geometry/polytope.hpp"
|
||||
#include "geometry/triangle.hpp"
|
||||
#include "geometry/tetrahedron.hpp"
|
57
source/hpr/geometry/CMakeLists.txt
Normal file
57
source/hpr/geometry/CMakeLists.txt
Normal file
@ -0,0 +1,57 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(geometry
|
||||
VERSION "${HPR_PROJECT_VERSION}"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
add_library(${CMAKE_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
add_module(${PROJECT_NAME})
|
||||
|
||||
file(GLOB ${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS
|
||||
"../geometry.hpp" "*.hpp"
|
||||
)
|
||||
|
||||
foreach(_header_path ${${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS})
|
||||
list(APPEND ${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS_INTERFACE "$<BUILD_INTERFACE:${_header_path}>")
|
||||
endforeach()
|
||||
|
||||
target_sources(${PROJECT_NAME}
|
||||
INTERFACE
|
||||
${${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS_INTERFACE}
|
||||
$<INSTALL_INTERFACE:include/${CMAKE_PROJECT_NAME}/${PROJECT_NAME}>
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}Targets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}
|
||||
NAMELINK_SKIP
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
install(
|
||||
EXPORT ${PROJECT_NAME}Targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CMAKE_PROJECT_NAME}-${HPR_PROJECT_VERSION}
|
||||
NAMESPACE ${CMAKE_PROJECT_NAME}::
|
||||
)
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}
|
||||
NAMELINK_ONLY
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
install(
|
||||
DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}
|
||||
COMPONENT devel
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
PATTERN "tests" EXCLUDE
|
||||
)
|
||||
install(
|
||||
FILES ../${PROJECT_NAME}.hpp
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}
|
||||
COMPONENT devel
|
||||
)
|
48
source/hpr/geometry/polytope.hpp
Normal file
48
source/hpr/geometry/polytope.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "../containers.hpp"
|
||||
#include "../math.hpp"
|
||||
|
||||
|
||||
namespace hpr::geometry
|
||||
{
|
||||
|
||||
template <int Dim, int Space>
|
||||
class Polytope
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Nullitope = -1,
|
||||
Monon,
|
||||
Dion,
|
||||
Polygon,
|
||||
Polyhedron,
|
||||
Polychoron,
|
||||
Unknown
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
const int p_dimension;
|
||||
const int p_space;
|
||||
Type p_type;
|
||||
darray<VectorSpace<scalar, Space>> p_points;
|
||||
|
||||
public:
|
||||
|
||||
Polytope() :
|
||||
p_dimension {Dim},
|
||||
p_space {Space},
|
||||
p_type {Type::Unknown},
|
||||
p_points {}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Polytope() = default;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "gpu/context.hpp"
|
||||
|
||||
#include "gpu/array_object.hpp"
|
||||
#include "gpu/buffer_object.hpp"
|
||||
#include "gpu/color_buffer.hpp"
|
||||
#include "gpu/cull_face.hpp"
|
||||
#include "gpu/depth_buffer.hpp"
|
||||
#include "gpu/framebuffer.hpp"
|
||||
#include "gpu/renderbuffer.hpp"
|
||||
#include "gpu/shader.hpp"
|
||||
#include "gpu/shader_program.hpp"
|
||||
#include "gpu/buffer.hpp"
|
||||
#include "gpu/device.hpp"
|
||||
#include "gpu/stencil_buffer.hpp"
|
||||
#include "gpu/texture.hpp"
|
||||
|
||||
#include "gpu/opengl/context.hpp"
|
||||
#include "gpu/opengl/shader.hpp"
|
||||
#include "gpu/opengl/shader_program.hpp"
|
||||
#include "gpu/opengl/buffer.hpp"
|
||||
#include "gpu/opengl/device.hpp"
|
||||
#include "gpu/opengl/texture.hpp"
|
||||
#include "gpu/viewport.hpp"
|
||||
|
@ -10,7 +10,7 @@ add_library(${CMAKE_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
add_module(${PROJECT_NAME})
|
||||
|
||||
file(GLOB ${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS
|
||||
"../gpu.hpp" "*.hpp" "opengl/*.hpp"
|
||||
"../gpu.hpp" "*.hpp"
|
||||
)
|
||||
|
||||
foreach(_header_path ${${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS})
|
||||
@ -18,7 +18,7 @@ foreach(_header_path ${${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS})
|
||||
endforeach()
|
||||
|
||||
file(GLOB ${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_SOURCES
|
||||
"*.cpp" "opengl/*.cpp"
|
||||
"*.cpp"
|
||||
)
|
||||
|
||||
target_sources(${PROJECT_NAME}
|
||||
|
2
source/hpr/gpu/array_object.cpp
Normal file
2
source/hpr/gpu/array_object.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include <glad/glad.h>
|
||||
#include "array_object.hpp"
|
104
source/hpr/gpu/array_object.hpp
Normal file
104
source/hpr/gpu/array_object.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include "buffer_object.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class ArrayObject
|
||||
{
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int p_index;
|
||||
int p_size;
|
||||
int p_stride;
|
||||
bool p_binded;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
ArrayObject() :
|
||||
p_index {0},
|
||||
p_size {0},
|
||||
p_stride {0},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~ArrayObject() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
int size() const
|
||||
{
|
||||
return p_size;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
void create()
|
||||
{
|
||||
glGenVertexArrays(1, &p_index);
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
glBindVertexArray(p_index);
|
||||
p_binded = true;
|
||||
}
|
||||
|
||||
void unbind()
|
||||
{
|
||||
glBindVertexArray(0);
|
||||
p_binded = false;
|
||||
}
|
||||
|
||||
bool binded() const
|
||||
{
|
||||
return p_binded;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
glDeleteVertexArrays(1, &p_index);
|
||||
}
|
||||
|
||||
void attribPointer(BufferObject& buffer, unsigned int location, unsigned int size)
|
||||
{
|
||||
if (buffer.type() == BufferObject::Type::Unknown)
|
||||
throw std::runtime_error("Unknown buffer type");
|
||||
if (!binded())
|
||||
throw std::runtime_error("ArrayObject is not binded");
|
||||
if (!buffer.valid())
|
||||
throw std::runtime_error("BufferObject is invalid");
|
||||
|
||||
buffer.bind();
|
||||
glVertexAttribPointer(location, size, GL_FLOAT, GL_FALSE, sizeof(float) * buffer.offset(), static_cast<void*>(nullptr));
|
||||
glEnableVertexAttribArray(location);
|
||||
buffer.unbind();
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
bool valid() const
|
||||
{
|
||||
return p_index > 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
|
||||
#include "buffer.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
Buffer::Buffer() :
|
||||
Context {DeviceAPI::Unknown},
|
||||
p_type {BufferType::Undefined},
|
||||
p_size {0},
|
||||
p_stride {0}
|
||||
{}
|
||||
|
||||
Buffer::Buffer(DeviceAPI api) :
|
||||
Context {api},
|
||||
p_type {BufferType::Undefined},
|
||||
p_size {0},
|
||||
p_stride {0}
|
||||
{}
|
||||
|
||||
Buffer::~Buffer() = default;
|
||||
|
||||
// Member functions
|
||||
|
||||
int Buffer::size() const
|
||||
{
|
||||
return p_size;
|
||||
}
|
||||
|
||||
Buffer::BufferType Buffer::type() const
|
||||
{
|
||||
return p_type;
|
||||
}
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Buffer : public Context
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
public:
|
||||
|
||||
enum class BufferType
|
||||
{
|
||||
Undefined,
|
||||
Vertex,
|
||||
Index,
|
||||
Uniform,
|
||||
BufferTypeCount
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
BufferType p_type;
|
||||
int p_size;
|
||||
int p_stride;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Buffer();
|
||||
|
||||
explicit
|
||||
Buffer(DeviceAPI api);
|
||||
|
||||
~Buffer() override;
|
||||
|
||||
// Member functions
|
||||
|
||||
[[nodiscard]]
|
||||
int size() const;
|
||||
|
||||
[[nodiscard]]
|
||||
BufferType type() const;
|
||||
};
|
||||
|
||||
} // end namespace hpr::gpu
|
27
source/hpr/gpu/buffer_object.cpp
Normal file
27
source/hpr/gpu/buffer_object.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "buffer_object.hpp"
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
void BufferObject::bind()
|
||||
{
|
||||
glBindBuffer((GLenum)p_type, p_index);
|
||||
p_binded = true;
|
||||
}
|
||||
|
||||
void BufferObject::unbind()
|
||||
{
|
||||
glBindBuffer((GLenum)p_type, 0);
|
||||
p_binded = false;
|
||||
}
|
||||
|
||||
void BufferObject::destroy()
|
||||
{
|
||||
if (p_type == Type::Unknown)
|
||||
std::runtime_error("Unknown buffer type");
|
||||
|
||||
glDeleteBuffers(1, &p_index);
|
||||
}
|
||||
}
|
134
source/hpr/gpu/buffer_object.hpp
Normal file
134
source/hpr/gpu/buffer_object.hpp
Normal file
@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#include "../containers.hpp"
|
||||
|
||||
#include <string>
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class BufferObject
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Vertex = 0x8892, //GL_ARRAY_BUFFER,
|
||||
Index = 0x8893, //GL_ELEMENT_ARRAY_BUFFER,
|
||||
Uniform = 0x8A11, //GL_UNIFORM_BUFFER,
|
||||
Unknown = -1
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
Type p_type;
|
||||
unsigned int p_index;
|
||||
int p_size;
|
||||
int p_offset;
|
||||
bool p_binded;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
BufferObject() :
|
||||
p_type {Type::Unknown},
|
||||
p_index {0},
|
||||
p_size {0},
|
||||
p_offset {0},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
explicit inline
|
||||
BufferObject(Type type) :
|
||||
p_type {type},
|
||||
p_index {0},
|
||||
p_size {0},
|
||||
p_offset {0},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~BufferObject() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
int size() const
|
||||
{
|
||||
return p_size;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
Type type() const
|
||||
{
|
||||
return p_type;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int offset() const
|
||||
{
|
||||
return p_offset;
|
||||
}
|
||||
|
||||
void bind() ;
|
||||
|
||||
void unbind();
|
||||
|
||||
[[nodiscard]]
|
||||
bool binded() const
|
||||
{
|
||||
return p_binded;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void create(const darray<T>& data, unsigned int offset = 0)
|
||||
{
|
||||
if (p_type == Type::Unknown)
|
||||
std::runtime_error("Unknown buffer type");
|
||||
|
||||
unsigned int drawType;
|
||||
|
||||
if (p_type == Type::Uniform)
|
||||
drawType = GL_DYNAMIC_DRAW;
|
||||
else
|
||||
drawType = GL_STATIC_DRAW;
|
||||
|
||||
glGenBuffers(1, &p_index);
|
||||
bind();
|
||||
glBufferData((GLenum)p_type, sizeof(T) * data.size(), data.data(), drawType);
|
||||
unbind();
|
||||
|
||||
p_offset = offset;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void edit(const darray<T>& data, unsigned int offset = 0)
|
||||
{
|
||||
if (p_type == Type::Unknown)
|
||||
std::runtime_error("Unknown buffer type");
|
||||
|
||||
bind();
|
||||
glBufferSubData(p_type, offset, sizeof(T) * data.size(), data.data());
|
||||
unbind();
|
||||
}
|
||||
|
||||
void destroy();
|
||||
|
||||
[[nodiscard]]
|
||||
inline
|
||||
bool valid() const
|
||||
{
|
||||
return p_index > 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
40
source/hpr/gpu/camera.hpp
Normal file
40
source/hpr/gpu/camera.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "../math.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Camera
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
vec3 p_front;
|
||||
vec3 p_up;
|
||||
vec3 p_left;
|
||||
|
||||
scalar p_yaw;
|
||||
scalar p_pitch;
|
||||
scalar p_roll;
|
||||
|
||||
vec3 p_position;
|
||||
vec3 p_target;
|
||||
|
||||
scalar p_distance;
|
||||
|
||||
public:
|
||||
|
||||
Camera() :
|
||||
p_front {0., 0., -1.},
|
||||
p_up {0., 0., 1.},
|
||||
p_left {1., 0., 0.}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Camera() = default;
|
||||
|
||||
};
|
||||
|
||||
}
|
68
source/hpr/gpu/color_buffer.hpp
Normal file
68
source/hpr/gpu/color_buffer.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "../math/vector.hpp"
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class ColorBuffer
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
bool p_enabledRed;
|
||||
bool p_enabledGreen;
|
||||
bool p_enabledBlue;
|
||||
bool p_enabledAlpha;
|
||||
vec4 p_color;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
ColorBuffer() :
|
||||
p_enabledRed {true},
|
||||
p_enabledGreen {true},
|
||||
p_enabledBlue {true},
|
||||
p_enabledAlpha {true},
|
||||
p_color {}
|
||||
{}
|
||||
|
||||
inline
|
||||
ColorBuffer(bool red, bool green, bool blue, bool alpha) :
|
||||
p_enabledRed {red},
|
||||
p_enabledGreen {green},
|
||||
p_enabledBlue {blue},
|
||||
p_enabledAlpha {alpha},
|
||||
p_color {}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~ColorBuffer() = default;
|
||||
|
||||
void mask(bool red, bool green, bool blue, bool alpha)
|
||||
{
|
||||
glColorMask(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
inline
|
||||
void clear(const vec4& color)
|
||||
{
|
||||
p_color = color;
|
||||
glClearColor(color[0], color[1], color[2], color[3]);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
inline
|
||||
void clear()
|
||||
{
|
||||
clear(p_color);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
4
source/hpr/gpu/common.hpp
Normal file
4
source/hpr/gpu/common.hpp
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
@ -1,23 +0,0 @@
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
Context::Context() :
|
||||
p_api {DeviceAPI::Unknown}
|
||||
{}
|
||||
|
||||
Context::Context(DeviceAPI api) :
|
||||
p_api {api}
|
||||
{}
|
||||
|
||||
Context::~Context() = default;
|
||||
|
||||
bool Context::checkCompability(const Context* ctx) const
|
||||
{
|
||||
return (ctx != nullptr) ? ctx->p_api == p_api : true;
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Context
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
enum class DeviceAPI
|
||||
{
|
||||
Unknown,
|
||||
OpenGL,
|
||||
DeviceAPICount
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
DeviceAPI p_api;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Context();
|
||||
|
||||
explicit
|
||||
Context(DeviceAPI api);
|
||||
|
||||
virtual
|
||||
~Context();
|
||||
|
||||
// Member functions
|
||||
|
||||
bool checkCompability(const Context* ctx) const;
|
||||
};
|
||||
|
||||
}
|
76
source/hpr/gpu/cull_face.hpp
Normal file
76
source/hpr/gpu/cull_face.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class CullFace
|
||||
{
|
||||
|
||||
enum class Mode
|
||||
{
|
||||
Front = GL_FRONT,
|
||||
Back = GL_BACK,
|
||||
FrontAndBack = GL_FRONT_AND_BACK,
|
||||
None = GL_NONE
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
bool p_binded;
|
||||
Mode p_mode;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
CullFace() :
|
||||
p_binded {false},
|
||||
p_mode {Mode::FrontAndBack}
|
||||
{}
|
||||
|
||||
inline
|
||||
CullFace(Mode mode) :
|
||||
p_binded {false},
|
||||
p_mode {mode}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~CullFace() = default;
|
||||
|
||||
inline
|
||||
void bind()
|
||||
{
|
||||
p_binded = true;
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
inline
|
||||
void unbind()
|
||||
{
|
||||
p_binded = false;
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
bool binded() const
|
||||
{
|
||||
return p_binded;
|
||||
}
|
||||
|
||||
inline
|
||||
void set(Mode mode)
|
||||
{
|
||||
p_mode = mode;
|
||||
glCullFace(static_cast<GLenum>(mode));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
84
source/hpr/gpu/depth_buffer.hpp
Normal file
84
source/hpr/gpu/depth_buffer.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class DepthBuffer
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
bool p_enabled;
|
||||
bool p_binded;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
DepthBuffer() :
|
||||
p_enabled {true},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
inline
|
||||
DepthBuffer(bool enabled) :
|
||||
p_enabled {enabled},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~DepthBuffer() = default;
|
||||
|
||||
inline
|
||||
void bind()
|
||||
{
|
||||
p_binded = true;
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
inline
|
||||
void unbind()
|
||||
{
|
||||
p_binded = false;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
inline
|
||||
bool binded() const
|
||||
{
|
||||
return p_binded;
|
||||
}
|
||||
|
||||
inline
|
||||
void enable()
|
||||
{
|
||||
p_enabled = true;
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
inline
|
||||
void disable()
|
||||
{
|
||||
p_enabled = false;
|
||||
glDepthMask(GL_FALSE);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
inline
|
||||
bool enabled() const
|
||||
{
|
||||
return p_enabled;
|
||||
}
|
||||
|
||||
inline
|
||||
void clear() const
|
||||
{
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,245 +0,0 @@
|
||||
|
||||
#include "device.hpp"
|
||||
#include "opengl/device.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
Device::Device() :
|
||||
Context {DeviceAPI::Unknown},
|
||||
p_currentVertexBuffer {},
|
||||
p_currentIndexBuffer {},
|
||||
p_currentUniformBuffer {},
|
||||
p_currentShaderProgram {}
|
||||
{}
|
||||
|
||||
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
|
||||
|
||||
void Device::create(Device** device, DeviceAPI api)
|
||||
{
|
||||
if (device == nullptr)
|
||||
throw std::invalid_argument("Invalid parameter 'nullptr'");
|
||||
if (api == DeviceAPI::Unknown)
|
||||
throw std::invalid_argument("Cannot create device for 'Unknown'");
|
||||
|
||||
*device = nullptr;
|
||||
|
||||
if (api == DeviceAPI::OpenGL)
|
||||
*device = new opengl::Device;
|
||||
else
|
||||
throw std::invalid_argument("Unsupported device");
|
||||
}
|
||||
|
||||
// Render targets
|
||||
|
||||
void Device::moveRenderTarget(RenderTarget* target, int x, int y)
|
||||
{
|
||||
if (target == nullptr)
|
||||
throw std::invalid_argument("Invalid parameter");
|
||||
target->p_posX = x;
|
||||
target->p_posY = y;
|
||||
}
|
||||
|
||||
void Device::scaleRenderTarget(RenderTarget* target, int width, int height)
|
||||
{
|
||||
if (target == nullptr)
|
||||
throw std::invalid_argument("Invalid parameter");
|
||||
|
||||
target->p_width = width;
|
||||
target->p_height = height;
|
||||
}
|
||||
|
||||
void Device::destroyRenderTarget(RenderTarget*& target)
|
||||
{
|
||||
if (!target)
|
||||
throw std::runtime_error("Invalid parameter");
|
||||
|
||||
for (auto iter = p_renderTargets.begin(); iter != p_renderTargets.end(); ++iter)
|
||||
if (*iter == target)
|
||||
{
|
||||
delete target;
|
||||
target = nullptr;
|
||||
p_renderTargets.remove(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Buffers
|
||||
|
||||
void Device::useVertexBuffer(Buffer* buffer, int stride, int offset)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
if (buffer->p_type == Buffer::BufferType::Vertex)
|
||||
{
|
||||
p_currentVertexBuffer = buffer;
|
||||
p_currentVertexBuffer->p_stride = stride;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Incompatible buffer");
|
||||
}
|
||||
else
|
||||
p_currentVertexBuffer = nullptr;
|
||||
}
|
||||
|
||||
void Device::useIndexBuffer(Buffer* buffer, int offset)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
if (buffer->p_type == Buffer::BufferType::Index)
|
||||
{
|
||||
p_currentIndexBuffer = buffer;
|
||||
}
|
||||
else
|
||||
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 std::runtime_error("Invalid parameter");
|
||||
|
||||
for (auto iter = p_buffers.begin(); iter != p_buffers.end(); ++iter)
|
||||
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)
|
||||
{
|
||||
if (shader == nullptr)
|
||||
throw std::runtime_error("Invalid parameter");
|
||||
|
||||
for (auto iter = p_shaders.begin(); iter != p_shaders.end(); ++iter)
|
||||
if (*iter == shader)
|
||||
{
|
||||
delete shader;
|
||||
shader = nullptr;
|
||||
p_shaders.remove(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Shader programs
|
||||
|
||||
void Device::attachShader(ShaderProgram *program, Shader *shader)
|
||||
{
|
||||
if (program == nullptr || shader == nullptr)
|
||||
throw std::runtime_error("Invalid parameter");
|
||||
if (program->p_isLinked)
|
||||
throw std::runtime_error("Shader program already linked");
|
||||
|
||||
program->p_slots[(int)shader->p_type] = shader;
|
||||
}
|
||||
|
||||
void Device::linkProgram(ShaderProgram *program)
|
||||
{
|
||||
if (program == nullptr)
|
||||
throw std::runtime_error("Invalid parameter");
|
||||
if (program->p_isLinked)
|
||||
throw std::runtime_error("Shader program already linked");
|
||||
|
||||
program->p_isLinked = true;
|
||||
}
|
||||
|
||||
void Device::useShaderProgram(ShaderProgram *program)
|
||||
{
|
||||
if (program != nullptr)
|
||||
if (!program->p_isLinked)
|
||||
throw std::runtime_error("Shader program is not linked");
|
||||
|
||||
p_currentShaderProgram = program;
|
||||
}
|
||||
|
||||
void Device::destroyShaderProgram(ShaderProgram *&program, bool withShaders)
|
||||
{
|
||||
if (program == p_currentShaderProgram)
|
||||
useShaderProgram(nullptr);
|
||||
|
||||
if (withShaders)
|
||||
for (size_t n = 0; n < (size_t)Shader::ShaderType::ShaderTypeCount; n++)
|
||||
destroyShader(program->p_slots[n]);
|
||||
|
||||
for (auto iter = p_shaderPrograms.begin(); iter != p_shaderPrograms.end(); ++iter)
|
||||
if (*iter == program)
|
||||
{
|
||||
delete program;
|
||||
program = nullptr;
|
||||
p_shaderPrograms.remove(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Textures
|
||||
|
||||
void Device::destroyTexture(Texture *&texture)
|
||||
{
|
||||
for (auto iter = p_textures.begin(); iter != p_textures.end(); ++iter)
|
||||
if (*iter == texture)
|
||||
{
|
||||
delete texture;
|
||||
texture = nullptr;
|
||||
p_textures.remove(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "shader_program.hpp"
|
||||
#include "texture.hpp"
|
||||
#include "render_target.hpp"
|
||||
|
||||
#include "../containers/array.hpp"
|
||||
|
||||
#include "../window_system/window_system.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Device : public Context
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
enum class CullMode
|
||||
{
|
||||
Front,
|
||||
Back,
|
||||
FrontAndBack,
|
||||
None
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
darray<Buffer*> p_buffers;
|
||||
darray<Shader*> p_shaders;
|
||||
darray<ShaderProgram*> p_shaderPrograms;
|
||||
darray<Texture*> p_textures;
|
||||
|
||||
Buffer* p_currentVertexBuffer;
|
||||
Buffer* p_currentIndexBuffer;
|
||||
Buffer* p_currentUniformBuffer;
|
||||
ShaderProgram* p_currentShaderProgram;
|
||||
|
||||
darray<RenderTarget*> p_renderTargets;
|
||||
protected:
|
||||
|
||||
// Constructors
|
||||
|
||||
Device();
|
||||
|
||||
explicit
|
||||
Device(DeviceAPI api);
|
||||
|
||||
~Device() override;
|
||||
|
||||
public:
|
||||
|
||||
// Global functions
|
||||
|
||||
static
|
||||
void create(Device** device, DeviceAPI api);
|
||||
|
||||
// Member functions
|
||||
|
||||
// Setup
|
||||
|
||||
virtual
|
||||
bool initialize() = 0;
|
||||
virtual
|
||||
bool destroy() = 0;
|
||||
|
||||
// State
|
||||
|
||||
virtual
|
||||
void faceCulling(bool enableFaceCulling, CullMode faceCullingMode) = 0;
|
||||
|
||||
// Render targets
|
||||
|
||||
virtual
|
||||
void createScreenRenderTarget(RenderTarget** target, Window* window) = 0;
|
||||
virtual
|
||||
void createFramebufferRenderTarget(RenderTarget** target, int width, int height) = 0;
|
||||
virtual
|
||||
void createSubRenderTarget(RenderTarget** target, RenderTarget* parent, int x, int y, int width, int height) = 0;
|
||||
virtual
|
||||
void moveRenderTarget(RenderTarget* target, int x, int y);
|
||||
virtual
|
||||
void scaleRenderTarget(RenderTarget* target, int width, int height);
|
||||
virtual
|
||||
void destroyRenderTarget(RenderTarget*& target);
|
||||
|
||||
// Buffers
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Textures
|
||||
|
||||
virtual
|
||||
void createTexture(Texture** texture, const std::string& filename) = 0;
|
||||
virtual
|
||||
void useTexture(Texture* texture, int slot) = 0;
|
||||
virtual
|
||||
void destroyTexture(Texture*& texture);
|
||||
};
|
||||
|
||||
}
|
131
source/hpr/gpu/framebuffer.hpp
Normal file
131
source/hpr/gpu/framebuffer.hpp
Normal file
@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "texture.hpp"
|
||||
#include "renderbuffer.hpp"
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Framebuffer
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
Texture p_texture;
|
||||
Renderbuffer p_renderbuffer;
|
||||
unsigned int p_index;
|
||||
bool p_binded;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
Framebuffer() :
|
||||
p_index {0},
|
||||
p_texture {},
|
||||
p_renderbuffer {}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Framebuffer() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p_index);
|
||||
p_binded = true;
|
||||
}
|
||||
|
||||
void unbind()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
p_binded = false;
|
||||
}
|
||||
|
||||
bool binded() const
|
||||
{
|
||||
return p_binded;
|
||||
}
|
||||
|
||||
void attach(const Texture& texture)
|
||||
{
|
||||
if (!binded() && valid())
|
||||
std::runtime_error("Framebuffer not binded or invalid");
|
||||
if (!texture.valid())
|
||||
std::runtime_error("Texture is not valid");
|
||||
|
||||
p_texture = texture;
|
||||
p_texture.bind();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture.index(), 0);
|
||||
|
||||
p_texture.unbind();
|
||||
}
|
||||
|
||||
void attach(const Renderbuffer& renderbuffer)
|
||||
{
|
||||
if (!binded() && valid())
|
||||
std::runtime_error("Framebuffer not binded or invalid");
|
||||
if (!renderbuffer.valid())
|
||||
std::runtime_error("Renderbuffer is not valid");
|
||||
|
||||
p_renderbuffer = renderbuffer;
|
||||
p_renderbuffer.bind();
|
||||
p_renderbuffer.storage(p_texture.width(), p_texture.height());
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, p_renderbuffer.index());
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
throw std::runtime_error("Framebuffer is not complete");
|
||||
|
||||
p_renderbuffer.unbind();
|
||||
}
|
||||
|
||||
void create()
|
||||
{
|
||||
glGenFramebuffers(1, &p_index);
|
||||
bind();
|
||||
|
||||
if (!p_texture.valid())
|
||||
p_texture.create();
|
||||
attach(p_texture);
|
||||
|
||||
if (!p_renderbuffer.valid())
|
||||
p_renderbuffer.create();
|
||||
attach(p_renderbuffer);
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void rescale(int width, int height)
|
||||
{
|
||||
p_texture.bind();
|
||||
p_texture.rescale(width, height);
|
||||
|
||||
p_renderbuffer.bind();
|
||||
p_renderbuffer.storage(p_texture.width(), p_texture.height());
|
||||
|
||||
p_texture.unbind();
|
||||
p_renderbuffer.unbind();
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
p_texture.destroy();
|
||||
p_renderbuffer.destroy();
|
||||
glDeleteFramebuffers(1, &p_index);
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return p_index != 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
3
source/hpr/gpu/gpu.cpp
Normal file
3
source/hpr/gpu/gpu.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
//
|
||||
// Created by L-Nafaryus on 12/16/2022.
|
||||
//
|
@ -1,31 +0,0 @@
|
||||
|
||||
#include "buffer.hpp"
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
Buffer::Buffer() :
|
||||
gpu::Buffer(DeviceAPI::OpenGL),
|
||||
p_bufferIndex {0},
|
||||
p_vertexArrayIndex {0}
|
||||
{}
|
||||
|
||||
Buffer::~Buffer() = default;
|
||||
|
||||
int Buffer::target() const
|
||||
{
|
||||
switch (p_type)
|
||||
{
|
||||
case BufferType::Vertex:
|
||||
return GL_ARRAY_BUFFER;
|
||||
case BufferType::Index:
|
||||
return GL_ELEMENT_ARRAY_BUFFER;
|
||||
default:
|
||||
return GL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../buffer.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
class Buffer : public gpu::Buffer
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
protected:
|
||||
unsigned int p_bufferIndex;
|
||||
unsigned int p_vertexArrayIndex;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Buffer();
|
||||
|
||||
virtual ~Buffer();
|
||||
|
||||
// Member functions
|
||||
|
||||
int target() const;
|
||||
};
|
||||
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
//
|
||||
// Created by L-Nafaryus on 10/3/2022.
|
||||
//
|
||||
|
||||
#include "context.hpp"
|
@ -1,8 +0,0 @@
|
||||
//
|
||||
// Created by L-Nafaryus on 10/3/2022.
|
||||
//
|
||||
|
||||
#ifndef HYPORO_CONTEXT_HPP
|
||||
#define HYPORO_CONTEXT_HPP
|
||||
|
||||
#endif //HYPORO_CONTEXT_HPP
|
@ -1,597 +0,0 @@
|
||||
|
||||
#include "device.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "shader_program.hpp"
|
||||
#include "texture.hpp"
|
||||
#include "render_target.hpp"
|
||||
|
||||
#include "../../io/file.hpp"
|
||||
|
||||
#include <glad/glad.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
Device::Device() :
|
||||
gpu::Device {DeviceAPI::OpenGL},
|
||||
p_isInitialized {false}
|
||||
{}
|
||||
|
||||
Device::~Device() = default;
|
||||
|
||||
// Setup
|
||||
|
||||
bool Device::initialize()
|
||||
{
|
||||
return p_isInitialized = true;
|
||||
}
|
||||
|
||||
bool Device::destroy()
|
||||
{
|
||||
return p_isInitialized = false;
|
||||
}
|
||||
|
||||
bool Device::loadLoader()
|
||||
{
|
||||
return !gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||
}
|
||||
|
||||
// State
|
||||
|
||||
void Device::faceCulling(bool enableFaceCulling, CullMode faceCullingMode)
|
||||
{
|
||||
if (enableFaceCulling)
|
||||
glEnable(GL_CULL_FACE);
|
||||
else
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
switch (faceCullingMode)
|
||||
{
|
||||
case CullMode::Front:
|
||||
glCullFace(GL_FRONT);
|
||||
break;
|
||||
case CullMode::Back:
|
||||
glCullFace(GL_BACK);
|
||||
break;
|
||||
case CullMode::FrontAndBack:
|
||||
glCullFace(GL_FRONT_AND_BACK);
|
||||
break;
|
||||
case CullMode::None:
|
||||
glCullFace(GL_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Render targets
|
||||
|
||||
void Device::createScreenRenderTarget(gpu::RenderTarget** target, Window* window)
|
||||
{
|
||||
if (target == nullptr)
|
||||
throw std::invalid_argument("Invalid parameter");
|
||||
*target = nullptr;
|
||||
p_renderTargets.push(new opengl::RenderTarget());
|
||||
auto* newTarget = dynamic_cast<opengl::RenderTarget*>(p_renderTargets.back());
|
||||
|
||||
newTarget->p_type = RenderTarget::Type::Screen;
|
||||
newTarget->p_posX = 0;
|
||||
newTarget->p_posY = 0;
|
||||
newTarget->p_width = window->width();
|
||||
newTarget->p_height = window->height();
|
||||
|
||||
*target = newTarget;
|
||||
}
|
||||
|
||||
void Device::createFramebufferRenderTarget(gpu::RenderTarget** target, int width, int height)
|
||||
{
|
||||
if (target == nullptr)
|
||||
throw std::invalid_argument("Invalid parameter");
|
||||
unsigned int texture;
|
||||
glGenTextures(GL_TEXTURE_2D, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
unsigned int framebuffer;
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
|
||||
p_renderTargets.push(new opengl::RenderTarget());
|
||||
auto* newTarget = dynamic_cast<opengl::RenderTarget*>(p_renderTargets.back());
|
||||
|
||||
newTarget->p_type = RenderTarget::Type::Framebuffer;
|
||||
newTarget->p_posX = 0;
|
||||
newTarget->p_posY = 0;
|
||||
newTarget->p_width = width;
|
||||
newTarget->p_height = height;
|
||||
newTarget->p_frameBufferIndex = framebuffer;
|
||||
newTarget->p_textureIndex = texture;
|
||||
|
||||
*target = newTarget;
|
||||
}
|
||||
|
||||
void Device::createSubRenderTarget(gpu::RenderTarget** target, gpu::RenderTarget* parent, int x, int y, int width, int height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Device::moveRenderTarget(gpu::RenderTarget* target, int x, int y)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Device::scaleRenderTarget(gpu::RenderTarget* target, int width, int height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Device::destroyRenderTarget(gpu::RenderTarget*& target)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Buffers
|
||||
|
||||
|
||||
void Device::createVertexBuffer(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<opengl::Buffer*>(p_buffers.back());
|
||||
|
||||
newBuffer->p_type = Buffer::BufferType::Vertex;
|
||||
newBuffer->p_size = size;
|
||||
|
||||
glGenVertexArrays(1, &newBuffer->p_vertexArrayIndex);
|
||||
glBindVertexArray(newBuffer->p_vertexArrayIndex);
|
||||
|
||||
glGenBuffers(1, &newBuffer->p_bufferIndex);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, newBuffer->p_bufferIndex);
|
||||
glBufferData(GL_ARRAY_BUFFER, size, (void*)data, GL_STATIC_DRAW);
|
||||
|
||||
*buffer = static_cast<gpu::Buffer*>(newBuffer);
|
||||
}
|
||||
|
||||
void Device::createIndexBuffer(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<opengl::Buffer*>(p_buffers.back());
|
||||
|
||||
newBuffer->p_type = Buffer::BufferType::Index;
|
||||
newBuffer->p_size = size;
|
||||
|
||||
glGenVertexArrays(1, &newBuffer->p_vertexArrayIndex);
|
||||
glBindVertexArray(newBuffer->p_vertexArrayIndex);
|
||||
|
||||
glGenBuffers(1, &newBuffer->p_bufferIndex);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, newBuffer->p_bufferIndex);
|
||||
glBufferData(GL_UNIFORM_BUFFER, size, (void*)data, GL_STATIC_DRAW);
|
||||
|
||||
*buffer = static_cast<gpu::Buffer*>(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<opengl::Buffer*>(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<gpu::Buffer*>(newBuffer);
|
||||
}
|
||||
|
||||
void Device::useVertexBuffer(gpu::Buffer *buffer, int stride, int offset)
|
||||
{
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* curBuffer = dynamic_cast<opengl::Buffer*>(buffer);
|
||||
|
||||
if (curBuffer->p_type == Buffer::BufferType::Vertex && p_currentVertexBuffer != buffer)
|
||||
{
|
||||
glBindVertexArray(curBuffer->p_vertexArrayIndex);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, curBuffer->p_bufferIndex);
|
||||
|
||||
auto* curIndexBuffer = dynamic_cast<opengl::Buffer*>(p_currentIndexBuffer);
|
||||
|
||||
if (curIndexBuffer != nullptr)
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, curIndexBuffer->p_bufferIndex);
|
||||
}
|
||||
}
|
||||
gpu::Device::useVertexBuffer(buffer, stride, offset);
|
||||
}
|
||||
|
||||
void Device::useIndexBuffer(gpu::Buffer *buffer, int offset)
|
||||
{
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* curBuffer = dynamic_cast<opengl::Buffer*>(buffer);
|
||||
|
||||
if (curBuffer->p_type == Buffer::BufferType::Index && p_currentVertexBuffer != buffer)
|
||||
{
|
||||
glBindVertexArray(curBuffer->p_vertexArrayIndex);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, curBuffer->p_bufferIndex);
|
||||
}
|
||||
}
|
||||
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<opengl::Buffer*>(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<opengl::Buffer*>(activeBuffer(buffer->type()));
|
||||
|
||||
auto* buffer_ = dynamic_cast<opengl::Buffer*>(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");
|
||||
|
||||
auto* curBuffer = dynamic_cast<opengl::Buffer*>(buffer);
|
||||
glDeleteBuffers(1, &curBuffer->p_bufferIndex);
|
||||
|
||||
if (curBuffer->p_type == Buffer::BufferType::Vertex)
|
||||
glDeleteVertexArrays(1, &curBuffer->p_vertexArrayIndex);
|
||||
|
||||
gpu::Device::destroyBuffer(buffer);
|
||||
}
|
||||
|
||||
// Shaders
|
||||
|
||||
void Device::createVertexShader(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_VERTEX_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<opengl::Shader*>(p_shaders.back());
|
||||
newShader->p_type = Shader::ShaderType::Vertex;
|
||||
newShader->p_filename = filename;
|
||||
newShader->p_shaderIndex = shaderIndex;
|
||||
newShader->p_label = "VertexShader";
|
||||
|
||||
*shader = static_cast<Shader*>(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<opengl::Shader*>(p_shaders.back());
|
||||
newShader->p_type = Shader::ShaderType::Fragment;
|
||||
newShader->p_filename = filename;
|
||||
newShader->p_shaderIndex = shaderIndex;
|
||||
newShader->p_label = "FragmentShader";
|
||||
|
||||
*shader = static_cast<Shader*>(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<opengl::Shader*>(p_shaders.back());
|
||||
newShader->p_type = Shader::ShaderType::Geometry;
|
||||
newShader->p_filename = filename;
|
||||
newShader->p_shaderIndex = shaderIndex;
|
||||
newShader->p_label = "FragmentShader";
|
||||
|
||||
*shader = static_cast<gpu::Shader*>(newShader);
|
||||
}
|
||||
|
||||
void Device::destroyShader(gpu::Shader* shader)
|
||||
{
|
||||
if (shader == nullptr)
|
||||
throw std::invalid_argument("Invalid parameter");
|
||||
|
||||
auto* shader_ = dynamic_cast<opengl::Shader*>(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<opengl::ShaderProgram*>(p_shaderPrograms.back());
|
||||
newProgram->p_shaderProgramIndex = glCreateProgram();
|
||||
|
||||
*program = static_cast<gpu::ShaderProgram*>(newProgram);
|
||||
}
|
||||
|
||||
void Device::attachShader(gpu::ShaderProgram* program, gpu::Shader* shader)
|
||||
{
|
||||
gpu::Device::attachShader(program, shader);
|
||||
auto* program_ = dynamic_cast<opengl::ShaderProgram*>(program);
|
||||
auto* shader_ = dynamic_cast<opengl::Shader*>(shader);
|
||||
glAttachShader(program_->p_shaderProgramIndex, shader_->p_shaderIndex);
|
||||
}
|
||||
|
||||
|
||||
void Device::linkProgram(gpu::ShaderProgram* program)
|
||||
{
|
||||
gpu::Device::linkProgram(program);
|
||||
auto* program_ = dynamic_cast<opengl::ShaderProgram*>(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<opengl::ShaderProgram*>(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<opengl::ShaderProgram*>(program);
|
||||
|
||||
for (int n = 0; n < static_cast<int>(Shader::ShaderType::ShaderTypeCount); ++n)
|
||||
{
|
||||
auto* shader = dynamic_cast<opengl::Shader*>(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<opengl::Texture*>(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<gpu::Texture*>(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<opengl::Texture*>(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<opengl::Texture*>(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);
|
||||
}
|
||||
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../device.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
class Device : public gpu::Device
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
bool p_isInitialized;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Device();
|
||||
|
||||
~Device() override;
|
||||
|
||||
|
||||
// Member functions
|
||||
|
||||
// Setup
|
||||
|
||||
bool initialize() override;
|
||||
bool destroy() override;
|
||||
|
||||
static
|
||||
bool loadLoader();
|
||||
|
||||
// State
|
||||
|
||||
void faceCulling(bool enableFaceCulling, CullMode faceCullingMode) override;
|
||||
|
||||
// Render targets
|
||||
|
||||
void createScreenRenderTarget(RenderTarget** target, Window* window) override;
|
||||
void createFramebufferRenderTarget(RenderTarget** target, int width, int height) override;
|
||||
void createSubRenderTarget(RenderTarget** target, RenderTarget* parent, int x, int y, int width, int height) override;
|
||||
void moveRenderTarget(RenderTarget* target, int x, int y) override;
|
||||
void scaleRenderTarget(RenderTarget* target, int width, int height) override;
|
||||
void destroyRenderTarget(RenderTarget*& target) override;
|
||||
|
||||
// Buffers
|
||||
|
||||
virtual
|
||||
void createVertexBuffer(Buffer **buffer, int size, char* data);
|
||||
virtual
|
||||
void createIndexBuffer(Buffer **buffer, int size, char* data);
|
||||
virtual
|
||||
void createUniformBuffer(Buffer **buffer, int size, char* data);
|
||||
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);
|
||||
virtual
|
||||
void editBuffer(Buffer* buffer, char* data);
|
||||
virtual
|
||||
void destroyBuffer(Buffer*& buffer);
|
||||
|
||||
// Shaders
|
||||
|
||||
virtual
|
||||
void createVertexShader(Shader** shader, const std::string& filename, const std::string& label);
|
||||
virtual
|
||||
void createFragmentShader(Shader** shader, const std::string& filename, const std::string& label);
|
||||
virtual
|
||||
void createGeometryShader(Shader** shader, const std::string& filename, const std::string& label);
|
||||
virtual
|
||||
void destroyShader(Shader* shader);
|
||||
|
||||
// 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);
|
||||
|
||||
// Textures
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "render_target.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
RenderTarget::RenderTarget() :
|
||||
gpu::RenderTarget {DeviceAPI::OpenGL}
|
||||
{}
|
||||
|
||||
RenderTarget::~RenderTarget() = default;
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
//
|
||||
// Created by L-Nafaryus on 10/3/2022.
|
||||
//
|
||||
|
||||
#include "shader.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
Shader::Shader() :
|
||||
gpu::Shader(DeviceAPI::OpenGL),
|
||||
p_shaderIndex {0}
|
||||
{}
|
||||
|
||||
Shader::~Shader() = default;
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
|
||||
#include "../shader.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
class Shader : public gpu::Shader
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int p_shaderIndex;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Shader();
|
||||
|
||||
~Shader() override;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
|
||||
#include "shader_program.hpp"
|
||||
#include "shader.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
ShaderProgram::ShaderProgram() :
|
||||
gpu::ShaderProgram {DeviceAPI::OpenGL},
|
||||
p_shaderProgramIndex {0}
|
||||
{}
|
||||
|
||||
ShaderProgram::~ShaderProgram()
|
||||
{}
|
||||
|
||||
Shader* ShaderProgram::shader(gpu::Shader::ShaderType type)
|
||||
{
|
||||
return static_cast<opengl::Shader*>(p_slots[(size_t)type]);
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../shader_program.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
|
||||
class Shader;
|
||||
|
||||
//
|
||||
|
||||
class ShaderProgram : public gpu::ShaderProgram
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int p_shaderProgramIndex;
|
||||
|
||||
public:
|
||||
|
||||
ShaderProgram();
|
||||
|
||||
~ShaderProgram();
|
||||
|
||||
protected:
|
||||
|
||||
Shader* shader(gpu::Shader::ShaderType type);
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
|
||||
#include "texture.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
Texture::Texture() :
|
||||
gpu::Texture {DeviceAPI::OpenGL},
|
||||
p_textureIndex {0}
|
||||
{}
|
||||
|
||||
Texture::~Texture() = default;
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../texture.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu::opengl
|
||||
{
|
||||
|
||||
class Texture : public gpu::Texture
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int p_textureIndex;
|
||||
|
||||
public:
|
||||
|
||||
Texture();
|
||||
|
||||
~Texture() override;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#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;
|
||||
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
69
source/hpr/gpu/renderbuffer.hpp
Normal file
69
source/hpr/gpu/renderbuffer.hpp
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Renderbuffer
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int p_index;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
Renderbuffer() :
|
||||
p_index {0}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Renderbuffer() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, p_index);
|
||||
}
|
||||
|
||||
void unbind()
|
||||
{
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
void create()
|
||||
{
|
||||
glGenRenderbuffers(1, &p_index);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, p_index);
|
||||
}
|
||||
|
||||
void storage(int width, int height)
|
||||
{
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, p_index);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
glDeleteRenderbuffers(1, &p_index);
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return p_index != 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
8
source/hpr/gpu/scene.hpp
Normal file
8
source/hpr/gpu/scene.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by L-Nafaryus on 12/16/2022.
|
||||
//
|
||||
|
||||
#ifndef HPR_SCENE_HPP
|
||||
#define HPR_SCENE_HPP
|
||||
|
||||
#endif //HPR_SCENE_HPP
|
@ -1,39 +0,0 @@
|
||||
|
||||
#include "shader.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
Shader::Shader() :
|
||||
Context {DeviceAPI::Unknown},
|
||||
p_filename {},
|
||||
p_label {},
|
||||
p_type {ShaderType::Vertex}
|
||||
{}
|
||||
|
||||
Shader::Shader(DeviceAPI api) :
|
||||
Context {api},
|
||||
p_filename {},
|
||||
p_label {},
|
||||
p_type {ShaderType::Vertex}
|
||||
{}
|
||||
|
||||
Shader::~Shader() = default;
|
||||
|
||||
std::string Shader::filename() const
|
||||
{
|
||||
return p_filename;
|
||||
}
|
||||
|
||||
std::string Shader::label() const
|
||||
{
|
||||
return p_label;
|
||||
}
|
||||
|
||||
Shader::ShaderType Shader::type() const
|
||||
{
|
||||
return p_type;
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Shader : public Context
|
||||
class Shader
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
public:
|
||||
|
||||
enum class ShaderType
|
||||
enum class Type
|
||||
{
|
||||
Vertex,
|
||||
Geometry,
|
||||
Fragment,
|
||||
ShaderTypeCount
|
||||
Vertex = GL_VERTEX_SHADER,
|
||||
TessControl = GL_TESS_CONTROL_SHADER,
|
||||
TessEvaluation = GL_TESS_EVALUATION_SHADER,
|
||||
Geometry = GL_GEOMETRY_SHADER,
|
||||
Fragment = GL_FRAGMENT_SHADER,
|
||||
Compute = GL_COMPUTE_SHADER,
|
||||
Unknown = -1
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
std::string p_filename;
|
||||
std::string p_source;
|
||||
std::string p_label;
|
||||
ShaderType p_type;
|
||||
Type p_type;
|
||||
|
||||
unsigned int p_index;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
Shader();
|
||||
inline
|
||||
Shader() :
|
||||
p_filename {},
|
||||
p_source {},
|
||||
p_label {},
|
||||
p_type {Type::Unknown},
|
||||
p_index {0}
|
||||
{}
|
||||
|
||||
explicit
|
||||
Shader(DeviceAPI api);
|
||||
inline
|
||||
Shader(Type type) :
|
||||
p_filename {},
|
||||
p_source {},
|
||||
p_label {},
|
||||
p_type {type}
|
||||
{}
|
||||
|
||||
~Shader() override;
|
||||
inline
|
||||
Shader(Type type, const std::string& source) :
|
||||
p_filename {},
|
||||
p_source {source},
|
||||
p_label {},
|
||||
p_type {type},
|
||||
p_index {0}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Shader() = default;
|
||||
|
||||
// Member functions
|
||||
|
||||
[[nodiscard]]
|
||||
std::string filename() const;
|
||||
std::string filename() const
|
||||
{
|
||||
return p_filename;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::string label() const;
|
||||
std::string label() const
|
||||
{
|
||||
return p_label;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ShaderType type() const;
|
||||
Type type() const
|
||||
{
|
||||
return p_type;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
void create(const std::string& label = "")
|
||||
{
|
||||
if (p_type == Type::Unknown)
|
||||
throw std::runtime_error("Unknown shader type");
|
||||
|
||||
p_index = glCreateShader(static_cast<GLenum>(p_type));
|
||||
if (p_index == 0)
|
||||
throw std::runtime_error("Cannot create shader");
|
||||
|
||||
const char* shaderSource = p_source.c_str();
|
||||
glShaderSource(p_index, 1, &shaderSource, nullptr);
|
||||
GLenum result = glGetError();
|
||||
glCompileShader(p_index);
|
||||
|
||||
int shaderStatus;
|
||||
glGetShaderiv(p_index, GL_COMPILE_STATUS, &shaderStatus);
|
||||
if (!shaderStatus)
|
||||
{
|
||||
char error[2048 + 1];
|
||||
glGetShaderInfoLog(p_index, 2048, nullptr, error);
|
||||
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
p_label = label;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
glDeleteShader(p_index);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace hpr::gpu
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
|
||||
#include "shader_program.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
ShaderProgram::ShaderProgram() :
|
||||
Context {DeviceAPI::Unknown},
|
||||
p_isLinked {false}
|
||||
{}
|
||||
|
||||
ShaderProgram::ShaderProgram(DeviceAPI api) :
|
||||
Context {api},
|
||||
p_isLinked {false}
|
||||
{}
|
||||
|
||||
ShaderProgram::~ShaderProgram() = default;
|
||||
|
||||
}
|
@ -1,37 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "../containers.hpp"
|
||||
|
||||
#include "../containers/array.hpp"
|
||||
#include <string>
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class ShaderProgram : public Context
|
||||
{
|
||||
friend class Device;
|
||||
class ShaderProgram
|
||||
{
|
||||
|
||||
protected:
|
||||
protected:
|
||||
|
||||
sarray<Shader*, (size_t)Shader::ShaderType::ShaderTypeCount> p_slots;
|
||||
bool p_isLinked;
|
||||
unsigned int p_index;
|
||||
darray<Shader> p_shaders;
|
||||
|
||||
public:
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
// Constructors
|
||||
|
||||
ShaderProgram();
|
||||
inline
|
||||
ShaderProgram() :
|
||||
p_index {0}
|
||||
{}
|
||||
|
||||
explicit
|
||||
ShaderProgram(DeviceAPI api);
|
||||
virtual
|
||||
~ShaderProgram() = default;
|
||||
|
||||
~ShaderProgram() override;
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
// Member functions
|
||||
darray<Shader> shaders()
|
||||
{
|
||||
return p_shaders;
|
||||
}
|
||||
|
||||
const Shader* getShader(Shader::ShaderType type) const;
|
||||
};
|
||||
void create(const std::string& label = "")
|
||||
{
|
||||
p_index = glCreateProgram();
|
||||
}
|
||||
|
||||
}
|
||||
void attach(const Shader& shader)
|
||||
{
|
||||
glAttachShader(p_index, shader.index());
|
||||
p_shaders.push(shader);
|
||||
}
|
||||
|
||||
void detach(const Shader& shader)
|
||||
{
|
||||
// WARNING: segfault, destroy_at (char)
|
||||
p_shaders.remove([shader](const Shader& _shader)
|
||||
{
|
||||
return shader.index() == _shader.index();
|
||||
});
|
||||
|
||||
glDetachShader(p_index, shader.index());
|
||||
}
|
||||
|
||||
void link()
|
||||
{
|
||||
glLinkProgram(p_index);
|
||||
|
||||
GLint status;
|
||||
glGetProgramiv(p_index, GL_LINK_STATUS, &status);
|
||||
|
||||
if (status == GL_FALSE)
|
||||
throw std::runtime_error("Shader program link error");
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
//for (auto& shader : p_shaders)
|
||||
// detach(shader);
|
||||
glDeleteShader(p_index);
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
glUseProgram(p_index);
|
||||
}
|
||||
|
||||
void unbind()
|
||||
{
|
||||
glUseProgram(0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
84
source/hpr/gpu/stencil_buffer.hpp
Normal file
84
source/hpr/gpu/stencil_buffer.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class StencilBuffer
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
bool p_enabled;
|
||||
bool p_binded;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
StencilBuffer() :
|
||||
p_enabled {false},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
inline
|
||||
StencilBuffer(bool enabled) :
|
||||
p_enabled {enabled},
|
||||
p_binded {false}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~StencilBuffer() = default;
|
||||
|
||||
|
||||
inline
|
||||
void bind()
|
||||
{
|
||||
p_binded = true;
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
inline
|
||||
void unbind()
|
||||
{
|
||||
p_binded = false;
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
inline
|
||||
bool binded() const
|
||||
{
|
||||
return p_binded;
|
||||
}
|
||||
|
||||
inline
|
||||
void enable()
|
||||
{
|
||||
p_enabled = true;
|
||||
glStencilMask(GL_TRUE);
|
||||
}
|
||||
|
||||
inline
|
||||
void disable()
|
||||
{
|
||||
p_enabled = false;
|
||||
glStencilMask(GL_FALSE);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
inline
|
||||
bool enabled() const
|
||||
{
|
||||
return p_enabled;
|
||||
}
|
||||
|
||||
inline
|
||||
void clear() const
|
||||
{
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
|
||||
#include "texture.hpp"
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
Texture::Texture() :
|
||||
Context {DeviceAPI::Unknown},
|
||||
p_filename {},
|
||||
p_width {0},
|
||||
p_height {0}
|
||||
{}
|
||||
|
||||
|
||||
Texture::Texture(DeviceAPI api) :
|
||||
Context {api},
|
||||
p_filename {},
|
||||
p_width {0},
|
||||
p_height {0}
|
||||
{}
|
||||
|
||||
Texture::~Texture() = default;
|
||||
|
||||
std::string Texture::filename() const
|
||||
{
|
||||
return p_filename;
|
||||
}
|
||||
|
||||
int Texture::width() const
|
||||
{
|
||||
return p_width;
|
||||
}
|
||||
|
||||
int Texture::height() const
|
||||
{
|
||||
return p_height;
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +1,193 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
#include "../containers.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Texture : public Context
|
||||
{
|
||||
protected:
|
||||
class Texture
|
||||
{
|
||||
|
||||
std::string p_filename;
|
||||
int p_width;
|
||||
int p_height;
|
||||
public:
|
||||
|
||||
public:
|
||||
using ustring = std::basic_string<unsigned char>;
|
||||
|
||||
// Constructors
|
||||
public:
|
||||
|
||||
Texture();
|
||||
enum class Format
|
||||
{
|
||||
RGB = GL_RGB,
|
||||
RGBA = GL_RGBA
|
||||
};
|
||||
|
||||
explicit
|
||||
Texture(DeviceAPI api);
|
||||
protected:
|
||||
|
||||
~Texture() override;
|
||||
unsigned int p_index;
|
||||
std::string p_filename;
|
||||
ustring p_source;
|
||||
|
||||
// Member functions
|
||||
int p_width;
|
||||
int p_height;
|
||||
Format p_internalFormat;
|
||||
Format p_imageFormat;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string filename() const;
|
||||
public:
|
||||
|
||||
[[nodiscard]]
|
||||
int width() const;
|
||||
inline
|
||||
Texture() :
|
||||
p_index {0},
|
||||
p_filename {},
|
||||
p_source {},
|
||||
p_width {},
|
||||
p_height {},
|
||||
p_internalFormat {Format::RGBA},
|
||||
p_imageFormat {Format::RGBA}
|
||||
{}
|
||||
|
||||
[[nodiscard]]
|
||||
int height() const;
|
||||
};
|
||||
inline
|
||||
Texture(const std::string& filename) :
|
||||
p_index {0},
|
||||
p_filename {filename},
|
||||
p_source {},
|
||||
p_width {},
|
||||
p_height {},
|
||||
p_internalFormat {Format::RGB},
|
||||
p_imageFormat {Format::RGB}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Texture() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int index() const
|
||||
{
|
||||
return p_index;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int width() const
|
||||
{
|
||||
return p_width;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
unsigned int height() const
|
||||
{
|
||||
return p_height;
|
||||
}
|
||||
|
||||
void active()
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + p_index);
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, p_index);
|
||||
}
|
||||
|
||||
void unbind()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void alphaChannel(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
p_internalFormat = Format::RGBA;
|
||||
p_imageFormat = Format::RGBA;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_internalFormat = Format::RGB;
|
||||
p_imageFormat = Format::RGB;
|
||||
}
|
||||
}
|
||||
|
||||
void load(const std::string& filename)
|
||||
{
|
||||
auto filepath = std::filesystem::canonical(std::filesystem::path(filename)).string();
|
||||
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
int channelsCount;
|
||||
unsigned char* source = stbi_load(filepath.c_str(), &p_width, &p_height, &channelsCount, 0);
|
||||
|
||||
if (!source)
|
||||
throw std::runtime_error("Failed to load texture source");
|
||||
else
|
||||
create(source);
|
||||
|
||||
stbi_image_free(source);
|
||||
}
|
||||
|
||||
void load()
|
||||
{
|
||||
load(p_filename);
|
||||
}
|
||||
|
||||
void create()
|
||||
{
|
||||
glGenTextures(1, &p_index);
|
||||
bind();
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)p_internalFormat, p_width, p_height, 0, (GLint)p_imageFormat, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void create(const ustring& source)
|
||||
{
|
||||
glGenTextures(1, &p_index);
|
||||
bind();
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)p_internalFormat, p_width, p_height, 0, (GLint)p_imageFormat, GL_UNSIGNED_BYTE, source.data());
|
||||
|
||||
p_source = source;
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void rescale(int width, int height)
|
||||
{
|
||||
bind();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)p_internalFormat, p_width, p_height, 0, (GLint)p_imageFormat, GL_UNSIGNED_BYTE, !p_source.empty() ? p_source.data() : nullptr);
|
||||
unbind();
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
glDeleteTextures(1, &p_index);
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return p_index != 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} // end namespace hpr::gpu
|
8
source/hpr/gpu/viewer.hpp
Normal file
8
source/hpr/gpu/viewer.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by L-Nafaryus on 12/16/2022.
|
||||
//
|
||||
|
||||
#ifndef HPR_VIEWER_HPP
|
||||
#define HPR_VIEWER_HPP
|
||||
|
||||
#endif //HPR_VIEWER_HPP
|
45
source/hpr/gpu/viewport.hpp
Normal file
45
source/hpr/gpu/viewport.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "../math/vector.hpp"
|
||||
#ifndef __gl_h_
|
||||
#include <glad/glad.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace hpr::gpu
|
||||
{
|
||||
|
||||
class Viewport
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
vec2 p_pos;
|
||||
vec2 p_size;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
Viewport() :
|
||||
p_pos {0.f, 0.f},
|
||||
p_size {0.f, 0.f}
|
||||
{}
|
||||
|
||||
inline
|
||||
Viewport(const vec2& pos, const vec2& size) :
|
||||
p_pos {pos},
|
||||
p_size {size}
|
||||
{}
|
||||
|
||||
virtual
|
||||
~Viewport() = default;
|
||||
|
||||
inline
|
||||
void set()
|
||||
{
|
||||
glViewport(p_pos[0], p_pos[1], p_size[0], p_size[1]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -63,4 +63,6 @@ install(
|
||||
)
|
||||
|
||||
|
||||
|
||||
if(HPR_TEST)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
@ -1,35 +1,274 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "../containers/array.hpp"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
|
||||
namespace logging
|
||||
{
|
||||
enum Severity
|
||||
{
|
||||
Emergency,
|
||||
Alert,
|
||||
Critical,
|
||||
Error,
|
||||
Warning,
|
||||
Notice,
|
||||
Info,
|
||||
Debug
|
||||
};
|
||||
|
||||
class Sink
|
||||
{
|
||||
//friend class Logger;
|
||||
|
||||
protected:
|
||||
|
||||
Severity p_severity;
|
||||
std::string_view p_message;
|
||||
|
||||
public:
|
||||
|
||||
Sink() :
|
||||
p_severity {Emergency},
|
||||
p_message {}
|
||||
{}
|
||||
|
||||
Sink(Severity severity) :
|
||||
p_severity {severity},
|
||||
p_message {}
|
||||
{}
|
||||
|
||||
void addMessage(const std::string_view& message)
|
||||
{
|
||||
p_message = message;
|
||||
}
|
||||
|
||||
virtual
|
||||
void flush() = 0;
|
||||
|
||||
|
||||
virtual
|
||||
~Sink() = default;
|
||||
|
||||
};
|
||||
|
||||
class StandardOutput : public Sink
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
StandardOutput() :
|
||||
Sink()
|
||||
{}
|
||||
|
||||
explicit
|
||||
StandardOutput(Severity severity) :
|
||||
Sink(severity)
|
||||
{}
|
||||
|
||||
void flush() override
|
||||
{
|
||||
if (p_severity < Error)
|
||||
std::cerr << p_message << "\n";
|
||||
if (p_severity < Debug)
|
||||
std::cout << p_message << "\n";
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
|
||||
~StandardOutput() override = default;
|
||||
|
||||
};
|
||||
|
||||
enum class LoggerState
|
||||
{
|
||||
Endline,
|
||||
Flush,
|
||||
Exception,
|
||||
Exit
|
||||
};
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
enum Severity
|
||||
{
|
||||
None,
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
Debug
|
||||
};
|
||||
static Severity severity;
|
||||
static Logger g_instance;
|
||||
static darray<Sink*> g_sinks;
|
||||
|
||||
template <typename T>
|
||||
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;
|
||||
protected:
|
||||
|
||||
return l;
|
||||
Severity p_severity;
|
||||
std::ostringstream p_stream;
|
||||
int p_exitcode;
|
||||
sarray<std::string, 8> p_levelNames;
|
||||
darray<std::string> p_buffer;
|
||||
|
||||
protected:
|
||||
|
||||
Logger() :
|
||||
p_severity {Emergency},
|
||||
p_stream {},
|
||||
p_exitcode {-1},
|
||||
p_levelNames { "Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Info", "Debug"}
|
||||
{}
|
||||
|
||||
static Logger& instance()
|
||||
{
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static void destroy()
|
||||
{
|
||||
for (Sink* sink : g_sinks)
|
||||
delete sink;
|
||||
g_sinks.clear();
|
||||
}
|
||||
|
||||
static darray<Sink*>& sinks()
|
||||
{
|
||||
return g_sinks;
|
||||
}
|
||||
|
||||
static void addSink(Sink* sink)
|
||||
{
|
||||
if (sink != nullptr)
|
||||
g_sinks.push(sink);
|
||||
}
|
||||
|
||||
static Severity severity()
|
||||
{
|
||||
return g_instance.p_severity;
|
||||
}
|
||||
|
||||
static void severity(Severity severity)
|
||||
{
|
||||
g_instance.p_severity = severity;
|
||||
}
|
||||
|
||||
// begin functions
|
||||
friend
|
||||
std::ostringstream&& log(Severity severity);
|
||||
|
||||
|
||||
|
||||
// end functions
|
||||
friend
|
||||
LoggerState endl();
|
||||
|
||||
friend
|
||||
LoggerState flush();
|
||||
|
||||
friend
|
||||
LoggerState exception();
|
||||
|
||||
friend
|
||||
LoggerState exit(int code);
|
||||
|
||||
//
|
||||
friend
|
||||
std::ostream& operator<<(std::ostream& stream, const LoggerState& state);
|
||||
};
|
||||
Logger::Severity Logger::severity = Logger::Warning;
|
||||
Logger logger;
|
||||
|
||||
//
|
||||
Logger Logger::g_instance;
|
||||
darray<Sink*> Logger::g_sinks;
|
||||
|
||||
//
|
||||
std::ostringstream&& log(Severity severity)
|
||||
{
|
||||
Logger& instance = Logger::instance();
|
||||
instance.p_severity = severity;
|
||||
//std::ostringstream oss;
|
||||
//return instance.p_stream;
|
||||
return std::move(std::ostringstream());// oss;//std::forward(oss);
|
||||
}
|
||||
|
||||
std::ostringstream&& error()
|
||||
{
|
||||
return log(Error);
|
||||
}
|
||||
|
||||
//
|
||||
LoggerState endl()
|
||||
{
|
||||
return LoggerState::Endline;
|
||||
}
|
||||
|
||||
LoggerState flush()
|
||||
{
|
||||
return LoggerState::Flush;
|
||||
}
|
||||
|
||||
LoggerState exception()
|
||||
{
|
||||
return LoggerState::Exception;
|
||||
}
|
||||
|
||||
LoggerState exit(int code)
|
||||
{
|
||||
Logger& instance = Logger::instance();
|
||||
instance.p_exitcode = code;
|
||||
return LoggerState::Exit;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const LoggerState& state)
|
||||
{
|
||||
stream << std::endl;
|
||||
std::ostringstream oss;
|
||||
oss << stream.rdbuf();
|
||||
std::string test = oss.str();
|
||||
std::cout << test << std::endl;
|
||||
//static std::mutex mtx;
|
||||
//std::lock_guard<std::mutex> lock(mtx);
|
||||
Logger& instance = Logger::instance();
|
||||
|
||||
if (state >= LoggerState::Endline)
|
||||
{
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
//instance.p_stream << stream.rdbuf();
|
||||
|
||||
|
||||
if (state >= LoggerState::Flush)
|
||||
{
|
||||
// default sink
|
||||
if (Logger::sinks().is_empty())
|
||||
{
|
||||
std::unique_ptr<Sink> sink = std::make_unique<StandardOutput>(Logger::severity());
|
||||
sink->addMessage(oss.str());
|
||||
sink->flush();
|
||||
//delete sink;
|
||||
}
|
||||
|
||||
// global sinks
|
||||
for (Sink* sink : Logger::sinks())
|
||||
{
|
||||
sink->addMessage(oss.str());
|
||||
sink->flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (state == LoggerState::Exception)
|
||||
{
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
if (state == LoggerState::Exit)
|
||||
{
|
||||
std::exit(instance.p_exitcode);
|
||||
}
|
||||
|
||||
//instance.p_stream.flush();
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
source/hpr/io/tests/CMakeLists.txt
Normal file
14
source/hpr/io/tests/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
file(GLOB tests_cpp "*.cpp")
|
||||
|
||||
add_executable(${PROJECT_NAME}-tests
|
||||
${tests_cpp}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}-tests
|
||||
PUBLIC
|
||||
hpr::${PROJECT_NAME}
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
gtest_add_tests(TARGET ${PROJECT_NAME}-tests)
|
23
source/hpr/io/tests/io-test.cpp
Normal file
23
source/hpr/io/tests/io-test.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../logger.hpp"
|
||||
#include <thread>
|
||||
|
||||
void task1()
|
||||
{
|
||||
using namespace hpr;
|
||||
logging::error() << "Not error: thread " << std::this_thread::get_id() << logging::flush();
|
||||
//std::cout << (logging::error() << "Not error: thread " << std::this_thread::get_id()).str() << std::endl;// << logging::flush();
|
||||
//std::cout << "Not error: thread " << std::this_thread::get_id() << std::endl;
|
||||
}
|
||||
|
||||
TEST(io, Logger)
|
||||
{
|
||||
using namespace hpr;
|
||||
logging::error() << "Not error: main thread" << logging::flush();
|
||||
std::cout << "Main thread " << std::this_thread::get_id() << std::endl;
|
||||
/*std::thread t1 {task1};
|
||||
std::thread t2 {task1};
|
||||
t1.join();
|
||||
t2.join();*/
|
||||
}
|
@ -10,7 +10,7 @@ add_library(${CMAKE_PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
add_module(${PROJECT_NAME})
|
||||
|
||||
file(GLOB ${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS
|
||||
"../math.hpp" "*.hpp" "scalar/*.hpp" "vector/*.hpp" "matrix/*.hpp"
|
||||
"../math.hpp" "*.hpp" "scalar/*.hpp" "vector/*.hpp" "matrix/*.hpp" "quaternion/*.hpp"
|
||||
)
|
||||
|
||||
foreach(_header_path ${${CMAKE_PROJECT_NAME}_${PROJECT_NAME}_HEADERS})
|
||||
|
4
source/hpr/math/integer.hpp
Normal file
4
source/hpr/math/integer.hpp
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "integer/integer.hpp"
|
||||
#include "integer/size.hpp"
|
17
source/hpr/math/integer/integer.hpp
Normal file
17
source/hpr/math/integer/integer.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
// type traits
|
||||
|
||||
template <typename Type>
|
||||
struct is_integer : public std::is_integral<Type> {};
|
||||
|
||||
// concepts
|
||||
|
||||
template <typename Type>
|
||||
concept IsInteger = is_integer<Type>::value;
|
||||
}
|
22
source/hpr/math/integer/size.hpp
Normal file
22
source/hpr/math/integer/size.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
using Size = std::size_t;
|
||||
|
||||
// type traits
|
||||
|
||||
template <typename Type>
|
||||
struct is_size : public std::integral_constant<bool, std::is_integral<Type>::value && std::is_unsigned<Type>::value> {};
|
||||
|
||||
// concepts
|
||||
|
||||
template <typename Type>
|
||||
concept IsSize = is_size<Type>::value || std::convertible_to<Type, Size>;
|
||||
|
||||
|
||||
//using Size = typename IsSize<std::size_t>::type;
|
||||
}
|
@ -6,11 +6,11 @@
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
MatrixSpace<Type, 4, 4> ortho(Type left, Type right, Type bottom, Type top)
|
||||
Matrix<T, 4, 4> ortho(T left, T right, T bottom, T top)
|
||||
{
|
||||
MatrixSpace<Type, 4, 4> ms;
|
||||
Matrix<T, 4, 4> ms;
|
||||
ms.fill(1);
|
||||
ms(0, 0) = 2 / (right - left);
|
||||
ms(1, 1) = 2 / (top - bottom);
|
||||
@ -20,12 +20,12 @@ MatrixSpace<Type, 4, 4> ortho(Type left, Type right, Type bottom, Type top)
|
||||
return ms;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
MatrixSpace<Type, 4, 4> ortho(Type left, Type right, Type bottom, Type top, Type zNear, Type zFar)
|
||||
Matrix<T, 4, 4> ortho(T left, T right, T bottom, T top, T zNear, T zFar)
|
||||
{
|
||||
MatrixSpace<Type, 4, 4> ms;
|
||||
ms.fill(1);
|
||||
Matrix<T, 4, 4> ms {1};
|
||||
//ms.fill(1);
|
||||
ms(0, 0) = 2 / (right - left);
|
||||
ms(1, 1) = 2 / (top - bottom);
|
||||
ms(2, 2) = 2 / (zFar - zNear);
|
||||
@ -35,13 +35,13 @@ MatrixSpace<Type, 4, 4> ortho(Type left, Type right, Type bottom, Type top, Type
|
||||
return ms;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
MatrixSpace<Type, 4, 4> perspective(Type fovy, Type aspect, Type zNear, Type zFar)
|
||||
Matrix<T, 4, 4> perspective(T fovy, T aspect, T zNear, T zFar)
|
||||
{
|
||||
assert(abs(aspect - std::numeric_limits<Type>::epsilon()) > 0);
|
||||
MatrixSpace<Type, 4, 4> ms;
|
||||
const Type halfFovyTan = tan(fovy / 2);
|
||||
assert(abs(aspect - std::numeric_limits<T>::epsilon()) > 0);
|
||||
Matrix<T, 4, 4> ms;
|
||||
const T halfFovyTan = tan(fovy / 2);
|
||||
ms(0, 0) = 1 / (aspect * halfFovyTan);
|
||||
ms(1, 1) = 1 / halfFovyTan;
|
||||
ms(2, 2) = (zFar + zNear) / (zFar - zNear);
|
||||
|
@ -1,23 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "../vector.hpp"
|
||||
#include "../integer.hpp"
|
||||
#include "../scalar.hpp"
|
||||
#include "../../containers/array/static_array.hpp"
|
||||
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
template <typename Type, size_t Rows, size_t Cols>
|
||||
class MatrixSpace : public VectorSpace<Type, Rows * Cols>
|
||||
{
|
||||
static_assert(Rows >= 1);
|
||||
static_assert(Cols >= 1);
|
||||
using base = VectorSpace<Type, Rows * Cols>;
|
||||
// forward declarations
|
||||
|
||||
template <IsReal Type, Size Rows, Size Cols> requires (Rows >= 0 && Cols >= 0)
|
||||
class Matrix;
|
||||
|
||||
template <IsReal Type, Size Rows, Size Cols>
|
||||
using SubMatrix = typename std::conditional<(Rows >= 2 && Cols >= 2), Matrix<Type, Rows - 1, Cols - 1>, Matrix<Type, 1, 1>>::type;
|
||||
|
||||
// type traits
|
||||
|
||||
template <typename T>
|
||||
struct is_matrix : public std::false_type {};
|
||||
|
||||
template <typename T, Size Rows, Size Cols>
|
||||
struct is_matrix<Matrix<T, Rows, Cols>> : public std::true_type {};
|
||||
|
||||
// concepts
|
||||
|
||||
template <typename T>
|
||||
concept IsMatrix = is_matrix<T>::value;
|
||||
|
||||
}
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
template <IsReal Type, Size Rows, Size Cols> requires (Rows >= 0 && Cols >= 0)
|
||||
class Matrix : public StaticArray<Type, Rows * Cols>
|
||||
{
|
||||
|
||||
using base = StaticArray<Type, Rows * Cols>;
|
||||
|
||||
using Minor = typename std::conditional<(Rows >= 2 && Cols >= 2), MatrixSpace<Type, Rows - 1, Cols - 1>, MatrixSpace<Type, 1, 1>>::type;
|
||||
public:
|
||||
|
||||
using value_type = Type;
|
||||
using size_type = size_t;
|
||||
using size_type = Size;
|
||||
using pointer = Type*;
|
||||
using reference = Type&;
|
||||
using iterator = Iterator<Type>;
|
||||
@ -26,82 +52,101 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
static
|
||||
const size_type p_mrows = Rows - 1;
|
||||
static
|
||||
const size_type p_mcols = Cols - 1;
|
||||
size_type p_rows;
|
||||
size_type p_cols;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
MatrixSpace() :
|
||||
Matrix() :
|
||||
base {},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
MatrixSpace(const MatrixSpace& ms) :
|
||||
Matrix(const Matrix& ms) :
|
||||
base {static_cast<base>(ms)},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline explicit
|
||||
MatrixSpace(const base& ms) :
|
||||
base {ms},
|
||||
inline
|
||||
Matrix(Matrix&& ms) noexcept:
|
||||
base {std::forward<base>(static_cast<base>(ms))},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
MatrixSpace(MatrixSpace&& ms) noexcept :
|
||||
base {std::move(static_cast<base>(ms))},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
MatrixSpace& operator=(const MatrixSpace& ms)
|
||||
Matrix& operator=(const Matrix& ms)
|
||||
{
|
||||
base::operator=(ms);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline explicit
|
||||
Matrix(const base& vs) :
|
||||
base {vs},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline explicit
|
||||
Matrix(base&& vs) noexcept:
|
||||
base {std::forward<base>(vs)},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
MatrixSpace(typename base::iterator start, typename base::iterator end) :
|
||||
Matrix(typename base::iterator start, typename base::iterator end) :
|
||||
base {start, end},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
MatrixSpace(typename base::const_iterator start, typename base::const_iterator end) :
|
||||
Matrix(typename base::const_iterator start, typename base::const_iterator end) :
|
||||
base {start, end},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
MatrixSpace(std::initializer_list<value_type> list) :
|
||||
Matrix(std::initializer_list<value_type> list) :
|
||||
base {list},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
template <std::convertible_to<value_type>... Args>
|
||||
template <IsReal... Args>
|
||||
inline
|
||||
MatrixSpace(value_type&& v, Args&& ...args) :
|
||||
Matrix(value_type&& v, Args&& ...args) requires (1 + sizeof...(args) == Rows * Cols):
|
||||
base {v, static_cast<value_type>(std::forward<Args>(args))...},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{}
|
||||
|
||||
inline
|
||||
Matrix(const value_type& v) :
|
||||
base {},
|
||||
p_rows {Rows},
|
||||
p_cols {Cols}
|
||||
{
|
||||
static_assert(1 + sizeof...(args) == Rows * Cols, "Number of arguments must be equal to rows * cols of matrix");
|
||||
for (Size n = 0; n < Rows * Cols; ++n)
|
||||
(*this)[n] = v;
|
||||
}
|
||||
|
||||
// Member functions
|
||||
inline
|
||||
Matrix& operator=(const value_type& v)
|
||||
{
|
||||
for (Size n = 0; n < Rows * Cols; ++n)
|
||||
(*this)[n] = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// access
|
||||
|
||||
inline
|
||||
reference operator()(size_type row, size_type col)
|
||||
@ -123,154 +168,56 @@ public:
|
||||
return (*this)[col + p_rows * row];
|
||||
}
|
||||
|
||||
VectorSpace<value_type, Cols> row(size_type row)
|
||||
Vector<value_type, Cols> row(size_type row)
|
||||
{
|
||||
VectorSpace<value_type, Cols> vs;
|
||||
Vector<value_type, Cols> vs;
|
||||
for (auto n = 0; n < Cols; ++n)
|
||||
vs[n] = (*this)(row, n);
|
||||
return vs;
|
||||
}
|
||||
|
||||
VectorSpace<value_type, Cols> row(size_type row) const
|
||||
Vector<value_type, Cols> row(size_type row) const
|
||||
{
|
||||
VectorSpace<value_type, Cols> vs;
|
||||
Vector<value_type, Cols> vs;
|
||||
for (auto n = 0; n < Cols; ++n)
|
||||
vs[n] = (*this)(row, n);
|
||||
return vs;
|
||||
}
|
||||
|
||||
void row(size_type row, const VectorSpace<value_type, Cols>& vs)
|
||||
void row(size_type row, const Vector<value_type, Cols>& vs)
|
||||
{
|
||||
for (auto n = 0; n < Cols; ++n)
|
||||
(*this)(n, row) = vs[n];
|
||||
}
|
||||
|
||||
VectorSpace<value_type, Rows> col(size_type col)
|
||||
Vector<value_type, Rows> col(size_type col)
|
||||
{
|
||||
VectorSpace<value_type, Rows> vs;
|
||||
Vector<value_type, Rows> vs;
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
vs[n] = (*this)(n, col);
|
||||
return vs;
|
||||
}
|
||||
|
||||
void col(size_type col, const VectorSpace<value_type, Rows>& vs)
|
||||
void col(size_type col, const Vector<value_type, Rows>& vs)
|
||||
{
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
(*this)(n, col) = vs[n];
|
||||
}
|
||||
|
||||
size_type rows() const { return p_rows; }
|
||||
size_type cols() const { return p_cols; }
|
||||
[[nodiscard]] constexpr size_type rows() const { return p_rows; }
|
||||
[[nodiscard]] constexpr size_type cols() const { return p_cols; }
|
||||
|
||||
// member functions
|
||||
|
||||
[[nodiscard]]
|
||||
inline
|
||||
constexpr
|
||||
bool is_square() const
|
||||
{
|
||||
return p_rows == p_cols;
|
||||
}
|
||||
|
||||
inline
|
||||
Minor minor(size_type row, size_type col)
|
||||
{
|
||||
if (this->size() < 4)
|
||||
throw std::runtime_error("Impossible to find minor for matrix with size less than 2x2");
|
||||
Minor minor;
|
||||
auto minor_iter = minor.begin();
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
for (auto k = 0; k < Cols; ++k)
|
||||
if (k != col && n != row)
|
||||
*(minor_iter++) = (*this)[k + p_rows * n];
|
||||
return minor;
|
||||
}
|
||||
|
||||
inline
|
||||
value_type det()
|
||||
{
|
||||
if (!is_square())
|
||||
throw std::runtime_error("Matrix must be square");
|
||||
if (this->size() == 1)
|
||||
return (*this)[0];
|
||||
else if (this->size() == 4)
|
||||
return (*this)(0, 0) * (*this)(1, 1) - (*this)(0, 1) * (*this)(1, 0);
|
||||
else {
|
||||
auto res = 0;
|
||||
for (auto m = 0; m < Cols; ++m)
|
||||
res += std::pow(-1, m) * (*this)(0, m) * minor(0, m).det();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
MatrixSpace transpose()
|
||||
{
|
||||
MatrixSpace ms;
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
for (auto k = 0; k < Cols; ++k)
|
||||
ms(k, n) = (*this)(n, k);
|
||||
return ms;
|
||||
}
|
||||
|
||||
inline
|
||||
MatrixSpace adj()
|
||||
{
|
||||
MatrixSpace ms;
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
for (auto k = 0; k < Cols; ++k)
|
||||
{
|
||||
ms(n, k) = std::pow(-1, n + k) * minor(n, k).det();
|
||||
}
|
||||
return ms.transpose();
|
||||
}
|
||||
|
||||
inline
|
||||
MatrixSpace inv()
|
||||
{
|
||||
return MatrixSpace(adj() / det());
|
||||
}
|
||||
|
||||
// Friend functions
|
||||
|
||||
friend inline
|
||||
bool operator==(const MatrixSpace& lhs, const MatrixSpace& rhs)
|
||||
{
|
||||
for (auto n = 0; n < lhs.size(); ++n)
|
||||
if (lhs[n] != rhs[n])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
friend inline
|
||||
bool operator!=(const MatrixSpace& lhs, const MatrixSpace& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <typename T, size_type S>
|
||||
friend inline
|
||||
bool operator==(const MatrixSpace& lhs, const VectorSpace<T, S>& rhs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace<Type, Cols> operator*(const VectorSpace<Type, Cols>& vs, const MatrixSpace& ms)
|
||||
{
|
||||
VectorSpace<Type, Cols> res;
|
||||
for (auto n = 0; n < Cols; ++n)
|
||||
res[0] = sum(ms.col(n) * vs);
|
||||
return res;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace<Type, Rows> operator*(const MatrixSpace& ms, const VectorSpace<Type, Rows>& vs)
|
||||
{
|
||||
VectorSpace<Type, Rows> res;
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
res[0] = sum(ms.row(n) * vs);
|
||||
return res;
|
||||
}
|
||||
|
||||
MatrixSpace& fill(value_type value)
|
||||
Matrix& fill(value_type value)
|
||||
{
|
||||
for (auto n = 0; n < this->size(); ++n)
|
||||
(*this)[n] = value;
|
||||
@ -280,9 +227,9 @@ public:
|
||||
// Global functions
|
||||
|
||||
static inline
|
||||
MatrixSpace identity()
|
||||
Matrix identity()
|
||||
{
|
||||
MatrixSpace ms;
|
||||
Matrix ms;
|
||||
for (auto n = 0; n < Rows; ++n)
|
||||
for (auto k = 0; k < Cols; ++k)
|
||||
ms(n, k) = 1;
|
||||
@ -290,13 +237,128 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// global operators
|
||||
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator+(const Matrix<T, R, C>& lhs) { Matrix<T, R, C> ms; for (Size n = 0; n < lhs.size(); ++n) ms[n] = lhs[n]; return ms; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator-(const Matrix<T, R, C>& lhs) { Matrix<T, R, C> ms; for (Size n = 0; n < lhs.size(); ++n) ms[n] = -lhs[n]; return ms; }
|
||||
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C>& operator+=(Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] += rhs[n]; return lhs; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C>& operator-=(Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] -= rhs[n]; return lhs; }
|
||||
template <IsReal T, Size R, Size C, Size R2, Size C2> requires (R == C2 && R2 == C) inline Matrix<T, R, C>& operator*=(Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { Matrix<T, R, C> temp {lhs}; for (Size n = 0; n < R; ++n) for (Size k = 0; k < C; ++k) lhs(n, k) = sum(temp.col(k) * rhs.row(n)); return lhs; }
|
||||
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator+(const Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { Matrix<T, R, C> ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] += rhs[n]; return ms; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator-(const Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { Matrix<T, R, C> ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] -= rhs[n]; return ms; }
|
||||
template <IsReal T, Size R, Size C, Size R2, Size C2> requires (R == C2 && R2 == C) inline Matrix<T, R, C> operator*(const Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { Matrix<T, R, C> ms; for (Size n = 0; n < R; ++n) for (Size k = 0; k < C; ++k) ms(n, k) = sum(lhs.col(k) * rhs.row(n)); return ms; }
|
||||
|
||||
template <IsReal T, Size R, Size C> inline bool operator==(const Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { for (Size n = 0; n < lhs.size(); ++n) if (lhs[n] != rhs[n]) return false; return true; }
|
||||
template <IsReal T, Size R, Size C> inline bool operator!=(const Matrix<T, R, C>& lhs, const Matrix<T, R, C>& rhs) { for (Size n = 0; n < lhs.size(); ++n) if (lhs[n] == rhs[n]) return false; return true; }
|
||||
|
||||
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C>& operator+=(Matrix<T, R, C>& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] += rhs; return lhs; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C>& operator-=(Matrix<T, R, C>& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] -= rhs; return lhs; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C>& operator*=(Matrix<T, R, C>& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] *= rhs; return lhs; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C>& operator/=(Matrix<T, R, C>& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] /= rhs; return lhs; }
|
||||
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator+(const Matrix<T, R, C>& lhs, const T& rhs) { Matrix<T, R, C> ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] += rhs; return ms; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator-(const Matrix<T, R, C>& lhs, const T& rhs) { Matrix<T, R, C> ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] -= rhs; return ms; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator*(const Matrix<T, R, C>& lhs, const T& rhs) { Matrix<T, R, C> ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] *= rhs; return ms; }
|
||||
template <IsReal T, Size R, Size C> inline Matrix<T, R, C> operator/(const Matrix<T, R, C>& lhs, const T& rhs) { Matrix<T, R, C> ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] /= rhs; return ms; }
|
||||
|
||||
|
||||
template <IsReal T, Size R, Size C> inline Vector<T, R> operator*(const Matrix<T, R, C>& ms, const Vector<T, R>& vs) { Vector<T, R> res; for (Size n = 0; n < R; ++n) res[0] = sum(ms.row(n) * vs); return res; }
|
||||
template <IsReal T, Size R, Size C> inline Vector<T, C> operator*(const Vector<T, R>& vs, const Matrix<T, R, C>& ms) { Vector<T, C> res; for (Size n = 0; n < C; ++n) res[0] = sum(ms.col(n) * vs); return res; }
|
||||
|
||||
template <IsReal T, Size R, Size C> inline bool operator==(const Matrix<T, R, C>& lhs, const Vector<T, R * C>& rhs) { return false; }
|
||||
template <IsReal T, Size R, Size C> inline bool operator!=(const Matrix<T, R, C>& lhs, const Vector<T, R * C>& rhs) { return true; }
|
||||
|
||||
// matrix operations
|
||||
|
||||
//! Transpose matrix
|
||||
template <IsReal T, Size R, Size C>
|
||||
inline
|
||||
Matrix<T, R, C> transpose(const Matrix<T, R, C>& ms)
|
||||
{
|
||||
Matrix<T, R, C> res;
|
||||
for (Size n = 0; n < R; ++n)
|
||||
for (Size k = 0; k < C; ++k)
|
||||
res(k, n) = ms(n, k);
|
||||
return res;
|
||||
}
|
||||
|
||||
//! Trace of a matrix
|
||||
template <IsReal T, Size R, Size C>
|
||||
inline
|
||||
T trace(const Matrix<T, R, C>& ms) requires (R == C)
|
||||
{
|
||||
T res;
|
||||
for (auto n = 0; n < R; ++n)
|
||||
res += ms(n, n);
|
||||
return res;
|
||||
}
|
||||
|
||||
//! Minor of a matrix
|
||||
template <IsReal T, Size R, Size C>
|
||||
inline
|
||||
SubMatrix<T, R, C> minor(const Matrix<T, R, C>& ms, Size row, Size col)
|
||||
{
|
||||
if (ms.size() < 4)
|
||||
throw std::runtime_error("Matrix should be greater 2x2");
|
||||
|
||||
SubMatrix<T, R, C> minor;
|
||||
auto minor_iter = minor.begin();
|
||||
for (auto n = 0; n < R; ++n)
|
||||
for (auto k = 0; k < C; ++k)
|
||||
if (k != col && n != row)
|
||||
*(minor_iter++) = ms[k + ms.rows() * n];
|
||||
return minor;
|
||||
}
|
||||
|
||||
//! Determinant of a matrix
|
||||
template <IsReal T, Size R, Size C>
|
||||
inline
|
||||
scalar det(const Matrix<T, R, C>& ms) requires (R == C)
|
||||
{
|
||||
if (ms.size() == 1)
|
||||
return ms[0];
|
||||
|
||||
else if (ms.size() == 4)
|
||||
return ms(0, 0) * ms(1, 1) - ms(0, 1) * ms(1, 0);
|
||||
|
||||
else {
|
||||
scalar res = 0;
|
||||
for (auto n = 0; n < ms.cols(); ++n)
|
||||
res += pow(-1, n) * ms(0, n) * det(minor(ms, 0, n));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
//! Adjoint matrix
|
||||
template <IsReal T, Size R, Size C>
|
||||
inline
|
||||
Matrix<T, R, C> adj(const Matrix<T, R, C>& ms)
|
||||
{
|
||||
Matrix<T, R, C> res;
|
||||
for (auto n = 0; n < R; ++n)
|
||||
for (auto k = 0; k < C; ++k)
|
||||
res(n, k) = pow(-1, n + k) * det(minor(ms, n, k));
|
||||
return transpose(res);
|
||||
}
|
||||
|
||||
//! Inverse matrix
|
||||
template <IsReal T, Size R, Size C>
|
||||
inline
|
||||
Matrix<T, R, C> inv(const Matrix<T, R, C>& ms)
|
||||
{
|
||||
return adj(ms) / det(ms);
|
||||
}
|
||||
|
||||
// Aliases
|
||||
|
||||
template <typename Type, size_t Row, size_t Col>
|
||||
using mat = MatrixSpace<Type, Row, Col>;
|
||||
using mat = Matrix<Type, Row, Col>;
|
||||
|
||||
using mat2 = MatrixSpace<scalar, 2, 2>;
|
||||
using mat3 = MatrixSpace<scalar, 3, 3>;
|
||||
using mat4 = MatrixSpace<scalar, 4, 4>;
|
||||
using mat2 = Matrix<scalar, 2, 2>;
|
||||
using mat3 = Matrix<scalar, 3, 3>;
|
||||
using mat4 = Matrix<scalar, 4, 4>;
|
||||
|
||||
}
|
@ -6,37 +6,37 @@
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
mat<Type, 4, 4> translate(const mat<Type, 4, 4>& ms, const vec<Type, 3>& vs)
|
||||
Matrix<T, 4, 4> translate(const Matrix<T, 4, 4>& ms, const Vector<T, 3>& vs)
|
||||
{
|
||||
mat<Type, 4, 4> res {ms};
|
||||
Matrix<T, 4, 4> res {ms};
|
||||
res.col(3, ms.row(0) * vs[0] + ms.row(1) * vs[1] + ms.row(2) * vs[2] + ms.row(3));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
vec<Type, 3> translate(const vec<Type, 3>& vs1, const vec<Type, 3>& vs2)
|
||||
Vector<T, 3> translate(const Vector<T, 3>& vs1, const Vector<T, 3>& vs2)
|
||||
{
|
||||
mat<Type, 4, 4> res = mat<Type, 4, 4>::identity();
|
||||
res.row(3, vec<Type, 4>(vs1, 0.));
|
||||
Matrix<T, 4, 4> res = Matrix<T, 4, 4>::identity();
|
||||
res.row(3, Vector<T, 4>(vs1, 0.));
|
||||
res = translate(res, vs2);
|
||||
|
||||
return vec<Type, 3>(res.row(3));
|
||||
return Vector<T, 3>(res.row(3));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
mat<Type, 4, 4> rotate(const mat<Type, 4, 4>& ms, const vec<Type, 3>& vs, Type angle)
|
||||
Matrix<T, 4, 4> rotate(const Matrix<T, 4, 4>& ms, const Vector<T, 3>& vs, T angle)
|
||||
{
|
||||
const Type cosv = cos(angle);
|
||||
const Type sinv = sin(angle);
|
||||
vec<Type, 3> axis {normalize(vs)};
|
||||
vec<Type, 3> temp {(1. - cosv) * axis};
|
||||
const T cosv = cos(angle);
|
||||
const T sinv = sin(angle);
|
||||
Vector<T, 3> axis {normalize(vs)};
|
||||
Vector<T, 3> temp {(1. - cosv) * axis};
|
||||
|
||||
mat<Type, 4, 4> rot;
|
||||
Matrix<T, 4, 4> rot;
|
||||
rot(0, 0) = cosv + temp[0] * axis[0];
|
||||
rot(0, 1) = temp[0] * axis[1] + sinv * axis[2];
|
||||
rot(0, 2) = temp[0] * axis[2] - sinv * axis[1];
|
||||
@ -47,7 +47,7 @@ mat<Type, 4, 4> rotate(const mat<Type, 4, 4>& ms, const vec<Type, 3>& vs, Type a
|
||||
rot(2, 1) = temp[2] * axis[1] - sinv * axis[0];
|
||||
rot(2, 2) = cosv + temp[2] * axis[2];
|
||||
|
||||
mat<Type, 4, 4> res {ms};
|
||||
Matrix<T, 4, 4> res {ms};
|
||||
res.row(0, ms.row(0) * rot(0, 0) + ms.row(1) * rot(0, 1) + ms.row(2) * rot(0, 2));
|
||||
res.row(1, ms.row(0) * rot(1, 0) + ms.row(1) * rot(1, 1) + ms.row(2) * rot(1, 2));
|
||||
res.row(2, ms.row(0) * rot(2, 0) + ms.row(1) * rot(2, 1) + ms.row(2) * rot(2, 2));
|
||||
@ -56,22 +56,22 @@ mat<Type, 4, 4> rotate(const mat<Type, 4, 4>& ms, const vec<Type, 3>& vs, Type a
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
vec<Type, 3> rotate(const vec<Type, 3>& vs1, const vec<Type, 3>& vs2, Type angle)
|
||||
Vector<T, 3> rotate(const Vector<T, 3>& vs1, const Vector<T, 3>& vs2, T angle)
|
||||
{
|
||||
mat<Type, 4, 4> res = mat<Type, 4, 4>::identity();
|
||||
res.row(3, vec<Type, 4>(vs1, 0.));
|
||||
Matrix<T, 4, 4> res = Matrix<T, 4, 4>::identity();
|
||||
res.row(3, Vector<T, 4>(vs1, 0.));
|
||||
res = rotate(res, vs2, angle);
|
||||
|
||||
return vec<Type, 3>(res.row(3));
|
||||
return Vector<T, 3>(res.row(3));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <IsReal T>
|
||||
inline
|
||||
mat<Type, 4, 4> scale(const mat<Type, 4, 4>& ms, const vec<Type, 3>& vs)
|
||||
Matrix<T, 4, 4> scale(const Matrix<T, 4, 4>& ms, const Vector<T, 3>& vs)
|
||||
{
|
||||
mat<Type, 4, 4> res;
|
||||
Matrix<T, 4, 4> res;
|
||||
res.row(0, ms.row(0) * vs[0]);
|
||||
res.row(1, ms.row(1) * vs[1]);
|
||||
res.row(2, ms.row(2) * vs[2]);
|
||||
@ -81,19 +81,19 @@ mat<Type, 4, 4> scale(const mat<Type, 4, 4>& ms, const vec<Type, 3>& vs)
|
||||
}
|
||||
|
||||
|
||||
template <typename Type>
|
||||
template <typename T>
|
||||
inline
|
||||
mat<Type, 4, 4> lookAt(const mat<Type, 4, 4>& ms, const vec<Type, 3>& eye, const vec<Type, 3>& center, const vec<Type, 3>& up)
|
||||
Matrix<T, 4, 4> lookAt(const Matrix<T, 4, 4>& ms, const Vector<T, 3>& eye, const Vector<T, 3>& center, const Vector<T, 3>& up)
|
||||
{
|
||||
const vec<Type, 3> forward {normalize(center - eye)};
|
||||
const vec<Type, 3> left {normalize(cross(up, forward))};
|
||||
const vec<Type, 3> nup {cross(forward, left)};
|
||||
const Vector<T, 3> forward {normalize(center - eye)};
|
||||
const Vector<T, 3> left {normalize(cross(up, forward))};
|
||||
const Vector<T, 3> nup {cross(forward, left)};
|
||||
|
||||
mat<Type, 4, 4> res;
|
||||
res.col(0, vec<Type, 4>(left, 0));
|
||||
res.col(1, vec<Type, 4>(nup, 0));
|
||||
res.col(2, vec<Type, 4>(forward, 0));
|
||||
res.row(3, -vec<Type, 4>(dot(left, eye), dot(nup, eye), dot(forward, eye), -1.));
|
||||
Matrix<T, 4, 4> res;
|
||||
res.col(0, Vector<T, 4>(left, 0));
|
||||
res.col(1, Vector<T, 4>(nup, 0));
|
||||
res.col(2, Vector<T, 4>(forward, 0));
|
||||
res.row(3, -Vector<T, 4>(dot(left, eye), dot(nup, eye), dot(forward, eye), -1.));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
3
source/hpr/math/quaternion.hpp
Normal file
3
source/hpr/math/quaternion.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "quaternion/quaternion.hpp"
|
259
source/hpr/math/quaternion/quaternion.hpp
Normal file
259
source/hpr/math/quaternion/quaternion.hpp
Normal file
@ -0,0 +1,259 @@
|
||||
#pragma once
|
||||
|
||||
#include "../vector.hpp"
|
||||
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
// Forward declaration
|
||||
|
||||
class Quaternion;
|
||||
|
||||
inline
|
||||
Quaternion inverse(const Quaternion& q);
|
||||
|
||||
inline
|
||||
Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs);
|
||||
|
||||
// Class declaration
|
||||
class Quaternion
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
enum RotationSequence
|
||||
{
|
||||
ZYX, ZYZ, ZXY, ZXZ, YXZ, YXY, YZX, YZY, XYZ, XYX, XZY, XZX
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
scalar p_real;
|
||||
vec3 p_imag;
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
Quaternion() :
|
||||
p_real{},
|
||||
p_imag{}
|
||||
{}
|
||||
|
||||
inline
|
||||
Quaternion(const scalar real, const vec3& imag) :
|
||||
p_real {real},
|
||||
p_imag {imag}
|
||||
{}
|
||||
|
||||
inline explicit
|
||||
Quaternion(const scalar real) :
|
||||
p_real {real},
|
||||
p_imag {}
|
||||
{}
|
||||
|
||||
inline explicit
|
||||
Quaternion(const vec3& imag) :
|
||||
p_real {},
|
||||
p_imag {imag}
|
||||
{}
|
||||
|
||||
inline
|
||||
Quaternion(const vec3& vs, const scalar& theta) :
|
||||
p_real {cos(0.5 * theta)},
|
||||
p_imag {sin(0.5 * theta) * vs / mag(vs)}
|
||||
{}
|
||||
|
||||
static inline
|
||||
Quaternion unit(const vec3& vs)
|
||||
{
|
||||
return Quaternion(sqrt(1 - norm(vs)), vs);
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion(const RotationSequence rs, const vec3& angles)
|
||||
{
|
||||
switch (rs)
|
||||
{
|
||||
case XYZ:
|
||||
*this = Quaternion(vec3(0, 1, 0), angles[0]) *
|
||||
Quaternion(vec3(0, 1, 0), angles[1]) *
|
||||
Quaternion(vec3(0, 0, 1), angles[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unknown rotation sequence");
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
scalar real() const
|
||||
{
|
||||
return p_real;
|
||||
}
|
||||
|
||||
inline
|
||||
scalar& real()
|
||||
{
|
||||
return p_real;
|
||||
}
|
||||
|
||||
inline
|
||||
vec3 imag() const
|
||||
{
|
||||
return p_imag;
|
||||
}
|
||||
|
||||
inline
|
||||
vec3& imag()
|
||||
{
|
||||
return p_imag;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator+=(const Quaternion& q)
|
||||
{
|
||||
p_real += q.p_real;
|
||||
p_imag += q.p_imag;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator-=(const Quaternion& q)
|
||||
{
|
||||
p_real -= q.p_real;
|
||||
p_imag -= q.p_imag;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator*=(const Quaternion& q)
|
||||
{
|
||||
scalar temp = p_real;
|
||||
p_real = p_real * q.p_real - dot(p_imag, q.p_imag);
|
||||
p_imag = temp * q.p_imag + q.p_real * p_imag + cross(p_imag, q.p_imag);
|
||||
}
|
||||
|
||||
inline
|
||||
void operator/=(const Quaternion& q)
|
||||
{
|
||||
operator*=(inverse(q));
|
||||
}
|
||||
|
||||
inline
|
||||
void operator*=(const scalar s)
|
||||
{
|
||||
p_real *= s;
|
||||
p_imag *= s;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator/=(const scalar s)
|
||||
{
|
||||
p_real /= s;
|
||||
p_imag /= s;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
bool equal(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return lhs.real() == rhs.real() && lhs.imag() == rhs.imag();
|
||||
}
|
||||
|
||||
inline
|
||||
bool operator==(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return equal(lhs, rhs);
|
||||
}
|
||||
|
||||
inline
|
||||
bool operator!=(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return !equal(lhs, rhs);
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator+(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return {lhs.real() + rhs.real(), lhs.imag() + rhs.imag()};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator-(const Quaternion& q)
|
||||
{
|
||||
return {q.real(), q.imag()};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator-(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return {lhs.real() - rhs.real(), lhs.imag() - rhs.imag()};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return {lhs.real() * rhs.real() - dot(lhs.imag(), rhs.imag()),
|
||||
lhs.real() * rhs.imag() + rhs.real() * lhs.imag() + cross(lhs.imag(), rhs.imag())};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator/(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return lhs * inverse(rhs);
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator*(const scalar s, const Quaternion& q)
|
||||
{
|
||||
return {s * q.real(), s * q.imag()};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator*(const Quaternion& q, const scalar s)
|
||||
{
|
||||
return {q.real() * s, q.imag() * s};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion operator/(const Quaternion& q, const scalar s)
|
||||
{
|
||||
return {q.real() / s, q.imag() / s};
|
||||
}
|
||||
|
||||
inline
|
||||
scalar norm(const Quaternion& q)
|
||||
{
|
||||
return sqrt(pow(q.real(), 2) + dot(q.imag(), q.imag()));
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion conjugate(const Quaternion& q)
|
||||
{
|
||||
return {q.real(), -q.imag()};
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion inverse(const Quaternion& q)
|
||||
{
|
||||
return conjugate(q) / pow(norm(q), 2);
|
||||
}
|
||||
|
||||
inline
|
||||
Quaternion normalize(const Quaternion& q)
|
||||
{
|
||||
return q / norm(q);
|
||||
}
|
||||
|
||||
inline
|
||||
vec3 rotate(const vec3& point, const vec3& axis, const scalar& angle)
|
||||
{
|
||||
Quaternion p {point};
|
||||
Quaternion q {normalize(axis), angle};
|
||||
return (q * p * inverse(q)).imag();
|
||||
}
|
||||
|
||||
// Aliases
|
||||
|
||||
using quat = Quaternion;
|
||||
|
||||
}
|
@ -3,93 +3,306 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
// type traits
|
||||
|
||||
template <typename Type>
|
||||
struct is_scalar : public std::is_floating_point<Type> {};
|
||||
|
||||
// concepts
|
||||
|
||||
template <typename Type>
|
||||
concept IsScalar = is_scalar<Type>::value;
|
||||
|
||||
template <typename Type>
|
||||
concept IsReal = is_integer<Type>::value || is_scalar<Type>::value;
|
||||
|
||||
// forward declaration
|
||||
|
||||
template <IsScalar T>
|
||||
class Scalar;
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class T> struct is_floating_point<hpr::Scalar<T>> : true_type {};
|
||||
}
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
#define PRECISION_DOUBLE
|
||||
|
||||
#if defined(PRECISION_FLOAT)
|
||||
// class declaration
|
||||
|
||||
using scalar = float;
|
||||
template <IsScalar T>
|
||||
class Scalar
|
||||
{
|
||||
|
||||
#elif defined(PRECISION_DOUBLE)
|
||||
public:
|
||||
|
||||
using scalar = double;
|
||||
using type = Scalar<T>;
|
||||
using value_type = T;
|
||||
|
||||
#elif defined(PRECISION_LONGDOUBLE)
|
||||
protected:
|
||||
|
||||
using scalar = long double;
|
||||
value_type p_value;
|
||||
|
||||
public:
|
||||
|
||||
// constructors
|
||||
|
||||
constexpr Scalar() : p_value {} {}
|
||||
|
||||
template <IsScalar X> constexpr Scalar(const Scalar<X>& value) : p_value {static_cast<value_type>(value.p_value)} {}
|
||||
|
||||
template <IsReal X> constexpr Scalar(const X& value) : p_value {static_cast<value_type>(value)} {}
|
||||
|
||||
template <IsScalar X> constexpr type& operator=(const Scalar<X>& value) { p_value = static_cast<value_type>(value.p_value); return *this; }
|
||||
|
||||
template <IsReal X> constexpr type& operator=(const X& value) { p_value = static_cast<value_type>(value); return *this; }
|
||||
|
||||
virtual constexpr ~Scalar() = default;
|
||||
|
||||
// conversion
|
||||
|
||||
constexpr operator double() const { return static_cast<double>(p_value); }
|
||||
|
||||
constexpr operator float() const { return static_cast<float>(p_value); }
|
||||
|
||||
constexpr operator long double() const { return static_cast<long double>(p_value); }
|
||||
|
||||
constexpr operator bool() const { return static_cast<bool>(p_value); }
|
||||
|
||||
// access
|
||||
|
||||
[[nodiscard]] constexpr value_type value() const { return p_value; }
|
||||
|
||||
constexpr value_type& value() { return p_value; }
|
||||
|
||||
// properties
|
||||
|
||||
protected:
|
||||
|
||||
static value_type s_precision;
|
||||
|
||||
public:
|
||||
|
||||
static constexpr Scalar<T> precision() { return s_precision; }
|
||||
|
||||
static constexpr void precision(const value_type& precision) { s_precision = precision; }
|
||||
|
||||
static constexpr Scalar<T> inf() { return std::numeric_limits<value_type>::infinity(); }
|
||||
|
||||
static constexpr Scalar<T> epsilon() { return std::numeric_limits<value_type>::epsilon(); }
|
||||
};
|
||||
|
||||
// specialization type
|
||||
|
||||
#if defined(HPR_SCALAR_LONGDOUBLE)
|
||||
using scalar = Scalar<long double>;
|
||||
#elif defined(HPR_SCALAR_DOUBLE)
|
||||
using scalar = Scalar<double>;
|
||||
#elif defined(HPR_SCALAR_FLOAT)
|
||||
using scalar = Scalar<float>;
|
||||
#else
|
||||
|
||||
using scalar = float;
|
||||
|
||||
using scalar = Scalar<double>;
|
||||
#endif
|
||||
|
||||
static
|
||||
const scalar small = std::numeric_limits<scalar>::epsilon();
|
||||
static
|
||||
const scalar great = static_cast<scalar>(1.0) / small;
|
||||
static
|
||||
const scalar valueSmall = std::numeric_limits<scalar>::min();
|
||||
static
|
||||
const scalar valueGreat = std::numeric_limits<scalar>::max() * 0.1;
|
||||
static
|
||||
const scalar NaN = std::numeric_limits<scalar>::signaling_NaN();
|
||||
//
|
||||
|
||||
template<> scalar::value_type scalar::s_precision = static_cast<scalar::value_type>(1e-15);
|
||||
|
||||
// global operators
|
||||
|
||||
//- Return 1 if s is positive or 0 otherwise -1
|
||||
inline
|
||||
int sign(const scalar s)
|
||||
/// scalar vs scalar
|
||||
|
||||
constexpr scalar operator+(const scalar& s) { return s; }
|
||||
constexpr scalar operator-(const scalar& s) { return -s.value(); }
|
||||
constexpr bool operator!(const scalar& s) { return !static_cast<bool>(s.value()); }
|
||||
|
||||
constexpr scalar& operator+=(scalar& lhs, const scalar& rhs) { lhs.value() += static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
constexpr scalar& operator-=(scalar& lhs, const scalar& rhs) { lhs.value() -= static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
constexpr scalar& operator*=(scalar& lhs, const scalar& rhs) { lhs.value() *= static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
constexpr scalar& operator/=(scalar& lhs, const scalar& rhs) { lhs.value() /= static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
|
||||
constexpr scalar operator+(const scalar& lhs, const scalar& rhs) { return lhs.value() + rhs.value(); }
|
||||
constexpr scalar operator-(const scalar& lhs, const scalar& rhs) { return lhs.value() - rhs.value(); }
|
||||
constexpr scalar operator*(const scalar& lhs, const scalar& rhs) { return lhs.value() * rhs.value(); }
|
||||
constexpr scalar operator/(const scalar& lhs, const scalar& rhs) { return lhs.value() / rhs.value(); }
|
||||
|
||||
constexpr bool operator==(const scalar& lhs, const scalar& rhs) { return lhs.value() == rhs.value(); }
|
||||
constexpr bool operator!=(const scalar& lhs, const scalar& rhs) { return lhs.value() != rhs.value(); }
|
||||
constexpr bool operator&&(const scalar& lhs, const scalar& rhs) { return static_cast<bool>(lhs) && static_cast<bool>(rhs); }
|
||||
constexpr bool operator||(const scalar& lhs, const scalar& rhs) { return static_cast<bool>(lhs) || static_cast<bool>(rhs); }
|
||||
constexpr bool operator>(const scalar& lhs, const scalar& rhs) { return lhs.value() > rhs.value(); }
|
||||
constexpr bool operator<(const scalar& lhs, const scalar& rhs) { return lhs.value() < rhs.value(); }
|
||||
constexpr bool operator>=(const scalar& lhs, const scalar& rhs) { return lhs.value() >= rhs.value(); }
|
||||
constexpr bool operator<=(const scalar& lhs, const scalar& rhs) { return lhs.value() <= rhs.value(); }
|
||||
|
||||
/// scalar vs Scalar<T>
|
||||
|
||||
template <IsScalar T> constexpr scalar& operator+=(scalar& lhs, const Scalar<T>& rhs) { lhs.value() += static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
template <IsScalar T> constexpr scalar& operator-=(scalar& lhs, const Scalar<T>& rhs) { lhs.value() -= static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
template <IsScalar T> constexpr scalar& operator*=(scalar& lhs, const Scalar<T>& rhs) { lhs.value() *= static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
template <IsScalar T> constexpr scalar& operator/=(scalar& lhs, const Scalar<T>& rhs) { lhs.value() /= static_cast<scalar::value_type>(rhs.value()); return lhs; }
|
||||
|
||||
template <IsScalar T> constexpr scalar operator+(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() + static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr scalar operator+(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) + rhs.value(); }
|
||||
template <IsScalar T> constexpr scalar operator-(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() - static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr scalar operator-(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) - rhs.value(); }
|
||||
template <IsScalar T> constexpr scalar operator*(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() * static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr scalar operator*(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) * rhs.value(); }
|
||||
template <IsScalar T> constexpr scalar operator/(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() / static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr scalar operator/(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) / rhs.value(); }
|
||||
|
||||
template <IsScalar T> constexpr bool operator==(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() == static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr bool operator==(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) == rhs.value(); }
|
||||
template <IsScalar T> constexpr bool operator!=(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() != static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr bool operator!=(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) != rhs.value(); }
|
||||
template <IsScalar T> constexpr bool operator&&(const scalar& lhs, const Scalar<T>& rhs) { return static_cast<bool>(lhs) && static_cast<bool>(rhs); }
|
||||
template <IsScalar T> constexpr bool operator&&(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<bool>(lhs) && static_cast<bool>(rhs); }
|
||||
template <IsScalar T> constexpr bool operator||(const scalar& lhs, const Scalar<T>& rhs) { return static_cast<bool>(lhs) || static_cast<bool>(rhs); }
|
||||
template <IsScalar T> constexpr bool operator||(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<bool>(lhs) || static_cast<bool>(rhs); }
|
||||
template <IsScalar T> constexpr bool operator>(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() > static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr bool operator>(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) > rhs.value(); }
|
||||
template <IsScalar T> constexpr bool operator<(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() < static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr bool operator<(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) < rhs.value(); }
|
||||
template <IsScalar T> constexpr bool operator>=(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() >= static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr bool operator>=(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) >= rhs.value(); }
|
||||
template <IsScalar T> constexpr bool operator<=(const scalar& lhs, const Scalar<T>& rhs) { return lhs.value() <= static_cast<scalar::value_type>(rhs.value()); }
|
||||
template <IsScalar T> constexpr bool operator<=(const Scalar<T>& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs.value()) <= rhs.value(); }
|
||||
|
||||
template <IsScalar T> std::ostream& operator<<(std::ostream& stream, const Scalar<T>& s) { return stream << s.value(); }
|
||||
template <IsScalar T> std::istream& operator>>(std::istream& stream, Scalar<T>& s) { return stream >> s.value(); }
|
||||
|
||||
/// scalar vs real
|
||||
|
||||
template <IsReal T> constexpr scalar& operator+=(scalar& lhs, const T& rhs) { lhs.value() += static_cast<scalar::value_type>(rhs); return lhs; }
|
||||
template <IsReal T> constexpr scalar& operator-=(scalar& lhs, const T& rhs) { lhs.value() -= static_cast<scalar::value_type>(rhs); return lhs; }
|
||||
template <IsReal T> constexpr scalar& operator*=(scalar& lhs, const T& rhs) { lhs.value() *= static_cast<scalar::value_type>(rhs); return lhs; }
|
||||
template <IsReal T> constexpr scalar& operator/=(scalar& lhs, const T& rhs) { lhs.value() /= static_cast<scalar::value_type>(rhs); return lhs; }
|
||||
|
||||
template <IsReal T> constexpr T& operator+=(T& lhs, const scalar& rhs) { lhs += static_cast<T>(rhs); return lhs; }
|
||||
template <IsReal T> constexpr T& operator-=(T& lhs, const scalar& rhs) { lhs -= static_cast<T>(rhs); return lhs; }
|
||||
template <IsReal T> constexpr T& operator*=(T& lhs, const scalar& rhs) { lhs *= static_cast<T>(rhs); return lhs; }
|
||||
template <IsReal T> constexpr T& operator/=(T& lhs, const scalar& rhs) { lhs /= static_cast<T>(rhs); return lhs; }
|
||||
|
||||
template <IsReal T> constexpr scalar operator+(const scalar& lhs, const T& rhs) { return lhs.value() + static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr scalar operator+(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) + rhs.value(); }
|
||||
template <IsReal T> constexpr scalar operator-(const scalar& lhs, const T& rhs) { return lhs.value() - static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr scalar operator-(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) - rhs.value(); }
|
||||
template <IsReal T> constexpr scalar operator*(const scalar& lhs, const T& rhs) { return lhs.value() * static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr scalar operator*(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) * rhs.value(); }
|
||||
template <IsReal T> constexpr scalar operator/(const scalar& lhs, const T& rhs) { return lhs.value() / static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr scalar operator/(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) / rhs.value(); }
|
||||
|
||||
template <IsReal T> constexpr bool operator==(const scalar& lhs, const T& rhs) { return lhs.value() == static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr bool operator==(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) == rhs.value(); }
|
||||
template <IsReal T> constexpr bool operator!=(const scalar& lhs, const T& rhs) { return lhs.value() != static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr bool operator!=(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) != rhs.value(); }
|
||||
template <IsReal T> constexpr bool operator&&(const scalar& lhs, const T& rhs) { return static_cast<bool>(lhs) && static_cast<bool>(rhs); }
|
||||
template <IsReal T> constexpr bool operator&&(const T& lhs, const scalar& rhs) { return static_cast<bool>(lhs) && static_cast<bool>(rhs); }
|
||||
template <IsReal T> constexpr bool operator||(const scalar& lhs, const T& rhs) { return static_cast<bool>(lhs) || static_cast<bool>(rhs); }
|
||||
template <IsReal T> constexpr bool operator||(const T& lhs, const scalar& rhs) { return static_cast<bool>(lhs) || static_cast<bool>(rhs); }
|
||||
template <IsReal T> constexpr bool operator>(const scalar& lhs, const T& rhs) { return lhs.value() > static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr bool operator>(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) > rhs.value(); }
|
||||
template <IsReal T> constexpr bool operator<(const scalar& lhs, const T& rhs) { return lhs.value() < static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr bool operator<(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) < rhs.value(); }
|
||||
template <IsReal T> constexpr bool operator>=(const scalar& lhs, const T& rhs) { return lhs.value() >= static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr bool operator>=(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) >= rhs.value(); }
|
||||
template <IsReal T> constexpr bool operator<=(const scalar& lhs, const T& rhs) { return lhs.value() <= static_cast<scalar::value_type>(rhs); }
|
||||
template <IsReal T> constexpr bool operator<=(const T& lhs, const scalar& rhs) { return static_cast<scalar::value_type>(lhs) <= rhs.value(); }
|
||||
|
||||
// transcendentals
|
||||
|
||||
template <IsReal T> constexpr scalar cos(const T& s) { return std::cos(static_cast<scalar::value_type>(s));}
|
||||
|
||||
template <IsReal T> constexpr scalar acos(const T& s) { return std::acos(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar cosh(const T& s) { return std::cosh(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar acosh(const T& s) { return std::acosh(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar sin(const T& s) { return std::sin(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar asin(const T& s) { return std::asin(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar sinh(const T& s) { return std::sinh(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar asinh(const T& s) { return std::asinh(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar tan(const T& s) { return std::tan(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar atan(const T& s) { return std::atan(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar tanh(const T& s) { return std::tanh(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar atanh(const T& s) { return std::atanh(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar exp(const T& s) { return std::exp(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar log(const T& s) { return std::log(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar log10(const T& s) { return std::log10(scalar(s).value()); }
|
||||
|
||||
template <IsReal T, IsReal X> constexpr scalar pow(const T& s, const X& d) { return std::pow(scalar(s).value(), scalar(d).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar sqrt(const T& s) { return std::sqrt(scalar(s).value()); }
|
||||
|
||||
template <IsReal T> constexpr scalar isqrt(const T& s) { return static_cast<T>(1) / sqrt(scalar(s).value()); }
|
||||
|
||||
// constants
|
||||
|
||||
constexpr inline scalar pi() { return std::numbers::pi_v<scalar::value_type>; }
|
||||
|
||||
constexpr inline scalar e() { return std::numbers::e_v<scalar::value_type>; }
|
||||
|
||||
// etc
|
||||
|
||||
constexpr scalar abs(const scalar& s) { return std::abs(s.value()); }
|
||||
|
||||
constexpr scalar mag(const scalar& s) { return std::abs(s.value()); }
|
||||
|
||||
constexpr bool equal(const scalar& lhs, const scalar& rhs, const scalar& precision = scalar::precision()) { return abs(lhs - rhs) < precision; }
|
||||
|
||||
//! Convert degrees to radians
|
||||
constexpr scalar rad(const scalar& s) { return s * pi() / static_cast<scalar::value_type>(180); }
|
||||
|
||||
//! Convert radians to degrees
|
||||
constexpr scalar deg(const scalar& s) { return s * static_cast<scalar::value_type>(180) / pi(); }
|
||||
|
||||
constexpr scalar min(const scalar& s1, const scalar& s2) { return std::min(s1.value(),s2.value());}
|
||||
|
||||
constexpr scalar max(const scalar& s1, const scalar& s2) { return std::max(s1.value(), s2.value()); }
|
||||
|
||||
constexpr scalar clip(const scalar& s, const scalar& sMin, const scalar& sMax) { return min(sMax, max(s, sMin)); }
|
||||
|
||||
}
|
||||
|
||||
/*namespace std
|
||||
{
|
||||
return (s >= 0) ? 1: -1;
|
||||
}
|
||||
// compatibility with std
|
||||
|
||||
inline
|
||||
scalar mag(const scalar s)
|
||||
{
|
||||
return std::fabs(s);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
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 <typename Type>
|
||||
Type inversesqrt( Type n )
|
||||
{
|
||||
return static_cast<Type>(1) / sqrt(n);
|
||||
}
|
||||
|
||||
// trigonometric
|
||||
|
||||
static
|
||||
const scalar PI = static_cast<scalar>(M_PIl);
|
||||
|
||||
template <typename Type>
|
||||
inline
|
||||
Type radians(Type degrees)
|
||||
{
|
||||
static_assert(std::numeric_limits<Type>::is_iec559);
|
||||
return degrees * PI / static_cast<Type>(180);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline
|
||||
Type degrees(Type radians)
|
||||
{
|
||||
static_assert(std::numeric_limits<Type>::is_iec559);
|
||||
return radians * static_cast<Type>(180) / PI;
|
||||
}
|
||||
|
||||
}
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> cos(const hpr::Scalar<T>& s) { return hpr::cos(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> acos(const hpr::Scalar<T>& s) { return hpr::acos(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> cosh(const hpr::Scalar<T>& s) { return hpr::cosh(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> acosh(const hpr::Scalar<T>& s) { return hpr::acosh(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> sin(const hpr::Scalar<T>& s) { return hpr::sin(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> asin(const hpr::Scalar<T>& s) { return hpr::asin(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> sinh(const hpr::Scalar<T>& s) { return hpr::sinh(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> asinh(const hpr::Scalar<T>& s) { return hpr::asinh(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> tan(const hpr::Scalar<T>& s) { return hpr::tan(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> atan(const hpr::Scalar<T>& s) { return hpr::atan(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> tanh(const hpr::Scalar<T>& s) { return hpr::tanh(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> atanh(const hpr::Scalar<T>& s) { return hpr::atanh(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> exp(const hpr::Scalar<T>& s) { return hpr::exp(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> log(const hpr::Scalar<T>& s) { return hpr::log(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> log10(const hpr::Scalar<T>& s) { return hpr::log10(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> sqrt(const hpr::Scalar<T>& s) { return hpr::sqrt(s); }
|
||||
template <hpr::IsScalar T> constexpr hpr::Scalar<T> abs(const hpr::Scalar<T>& s) { return hpr::abs(s); }
|
||||
template <hpr::IsScalar T, hpr::IsScalar X> constexpr hpr::Scalar<T> pow(const hpr::Scalar<T>& s, const hpr::Scalar<X>& d) { return hpr::pow(s, d); }
|
||||
template <hpr::IsScalar T, hpr::IsReal X> constexpr hpr::Scalar<T> pow(const hpr::Scalar<T>& s, const X& d) { return hpr::pow(s, d); }
|
||||
template <hpr::IsReal T, hpr::IsScalar X> constexpr hpr::Scalar<T> pow(const T& s, const hpr::Scalar<X>& d) { return hpr::pow(s, d); }
|
||||
}*/
|
||||
|
@ -2,14 +2,46 @@
|
||||
|
||||
#include "../vector.hpp"
|
||||
#include "../matrix.hpp"
|
||||
#include "../quaternion.hpp"
|
||||
#include <complex>
|
||||
|
||||
TEST(math, Scalar)
|
||||
{
|
||||
EXPECT_EQ(hpr::scalar(5) + hpr::scalar(7), hpr::scalar(12));
|
||||
EXPECT_TRUE(std::is_floating_point_v<hpr::Scalar<double>>);
|
||||
EXPECT_TRUE(std::is_arithmetic_v<hpr::Scalar<double>>);
|
||||
EXPECT_EQ(5.f, hpr::Scalar<double>(5));
|
||||
EXPECT_EQ(hpr::rad(180), hpr::pi());
|
||||
EXPECT_EQ(hpr::deg(hpr::pi()), 180);
|
||||
EXPECT_EQ(hpr::cos(0), 1);
|
||||
EXPECT_EQ(hpr::sin(0), 0);
|
||||
EXPECT_EQ(hpr::abs(hpr::scalar(-1)), 1);
|
||||
EXPECT_EQ(hpr::pow(2, 2), 4);
|
||||
|
||||
EXPECT_TRUE(typeid(static_cast<float>(hpr::scalar(5))) == typeid(float));
|
||||
EXPECT_FALSE(!hpr::scalar(-1.));
|
||||
|
||||
std::stringstream oss;
|
||||
oss << hpr::cos(0);
|
||||
EXPECT_EQ(oss.str(), "1");
|
||||
hpr::scalar s;
|
||||
oss >> s;
|
||||
EXPECT_EQ(s, 1);
|
||||
|
||||
EXPECT_TRUE(hpr::equal(5.5453535353535395818593, 5.5453535353535395817592, 1e-18));
|
||||
EXPECT_EQ(hpr::min(7., 5), 5);
|
||||
EXPECT_EQ(hpr::max(7., 5), 7);
|
||||
EXPECT_EQ(hpr::clip(7., 5, 10), 7);
|
||||
EXPECT_EQ(hpr::clip(1., 5, 10), 5);
|
||||
EXPECT_EQ(hpr::clip(72., 5, 10), 10);
|
||||
}
|
||||
|
||||
TEST(math, Vector)
|
||||
{
|
||||
hpr::vec3 v1 {1, 3, 2};
|
||||
hpr::vec3 v2 {5, 7, -1};
|
||||
hpr::vec2 v31 {13, -2};
|
||||
hpr::vec3 v32 {v31, 9};
|
||||
hpr::vec3 v32 (v31, 9);
|
||||
EXPECT_EQ((hpr::vec3(v2.begin(), v2.end())), v2);
|
||||
EXPECT_EQ(v32, hpr::vec3(13, -2, 9));
|
||||
EXPECT_EQ(-v1, hpr::vec3(-1, -3, -2));
|
||||
@ -17,7 +49,7 @@ TEST(math, Vector)
|
||||
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::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));
|
||||
}
|
||||
|
||||
@ -26,6 +58,7 @@ TEST(math, Matrix)
|
||||
hpr::mat2 m1;
|
||||
hpr::vec4 v1;
|
||||
EXPECT_FALSE(v1 == m1);
|
||||
EXPECT_FALSE(m1 == v1);
|
||||
hpr::mat2 m2 {3, 2, 7, 4};
|
||||
hpr::vec2 v2 {2, 4};
|
||||
EXPECT_EQ(m2.col(1), v2);
|
||||
@ -34,14 +67,24 @@ TEST(math, Matrix)
|
||||
EXPECT_EQ(m2.col(1), v3);
|
||||
hpr::mat3 m4 {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
hpr::mat2 m41 {5, 6, 8, 9};
|
||||
EXPECT_EQ(m41.minor(0, 0), 9);
|
||||
//EXPECT_EQ(minor(m41, 0, 0), 9);
|
||||
hpr::mat2 m5 {1, 2, 3, 4};
|
||||
|
||||
EXPECT_EQ((m4.det()), 0);
|
||||
EXPECT_EQ(hpr::mat3(-9, 23, 3, 5, 5, 6, 7, -3, 9).det(), -786);
|
||||
EXPECT_EQ(det(m4), 0);
|
||||
EXPECT_EQ(hpr::det(m4), 0);
|
||||
EXPECT_EQ(det(hpr::mat3(-9, 23, 3, 5, 5, 6, 7, -3, 9)), -786);
|
||||
|
||||
hpr::mat2 m6 {2, 1, 7, 4};
|
||||
EXPECT_EQ(m6.inv(), hpr::mat2(4, -1, -7, 2));
|
||||
EXPECT_EQ(hpr::mat3(1, 0, 0, 0, 1, 0, 0, 0, 1).det(), 1.);
|
||||
EXPECT_EQ(det(m6), 1);
|
||||
EXPECT_EQ(adj(m6), hpr::mat2(4, -1, -7, 2));
|
||||
EXPECT_EQ(inv(m6), hpr::mat2(4, -1, -7, 2));
|
||||
EXPECT_EQ(det(hpr::mat3(1, 0, 0, 0, 1, 0, 0, 0, 1)), 1.);
|
||||
//EXPECT_EQ(m4.det(), 0);
|
||||
}
|
||||
|
||||
TEST(math, Quaternion)
|
||||
{
|
||||
hpr::quat q;
|
||||
hpr::vec3 np = hpr::rotate(hpr::vec3(0, 1, 0), {1, 0, 0}, hpr::pi() * 0.5);
|
||||
EXPECT_TRUE(hpr::all(hpr::equal(np, hpr::vec3(0, 0, 1))));
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../integer.hpp"
|
||||
#include "../scalar.hpp"
|
||||
#include "../../containers/array/static_array.hpp"
|
||||
|
||||
@ -7,18 +8,42 @@
|
||||
namespace hpr
|
||||
{
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
class VectorSpace : public StaticArray<Type, Size>
|
||||
// forward declarations
|
||||
|
||||
template <IsReal T, Size S> requires (S >= 0)
|
||||
class Vector;
|
||||
|
||||
template <IsReal T, Size S>
|
||||
using SubVector = typename std::conditional<S >= 2, Vector<T, S - 1>, Vector<T, 1>>::type;
|
||||
|
||||
// type traits
|
||||
|
||||
template <typename T>
|
||||
struct is_vector : public std::false_type {};
|
||||
|
||||
template <typename T, Size S>
|
||||
struct is_vector<Vector<T, S>> : public std::true_type {};
|
||||
|
||||
// concepts
|
||||
|
||||
template <typename T>
|
||||
concept IsVector = is_vector<T>::value;
|
||||
|
||||
}
|
||||
|
||||
namespace hpr
|
||||
{
|
||||
static_assert(std::is_arithmetic<Type>::value, "Type must be numeric");
|
||||
|
||||
using base = StaticArray<Type, Size>;
|
||||
template <IsReal Type, Size S> requires (S >= 0)
|
||||
class Vector : public StaticArray<Type, S>
|
||||
{
|
||||
|
||||
using base = StaticArray<Type, S>;
|
||||
|
||||
using SubVector = typename std::conditional<Size >= 2, VectorSpace<Type, Size - 1>, VectorSpace<Type, 1>>::type;
|
||||
public:
|
||||
|
||||
using value_type = Type;
|
||||
using size_type = size_t;
|
||||
using size_type = Size;
|
||||
using pointer = Type*;
|
||||
using reference = Type&;
|
||||
using iterator = Iterator<Type>;
|
||||
@ -26,361 +51,156 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
inline
|
||||
VectorSpace() :
|
||||
//! null constructor
|
||||
constexpr
|
||||
Vector() :
|
||||
base {}
|
||||
{}
|
||||
|
||||
inline
|
||||
VectorSpace(const VectorSpace& vs) :
|
||||
//! copy constructor
|
||||
constexpr
|
||||
Vector(const Vector& vs) :
|
||||
base {static_cast<base>(vs)}
|
||||
{}
|
||||
|
||||
inline
|
||||
VectorSpace(VectorSpace&& vs) noexcept :
|
||||
//! move constructor
|
||||
constexpr
|
||||
Vector(Vector&& vs) noexcept :
|
||||
base {std::forward<base>(static_cast<base>(vs))}
|
||||
{}
|
||||
|
||||
inline
|
||||
VectorSpace& operator=(const VectorSpace& vs)
|
||||
//! copy assignment operator
|
||||
constexpr
|
||||
Vector& operator=(const Vector& vs)
|
||||
{
|
||||
base::operator=(vs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
VectorSpace& operator=(VectorSpace&& vs) noexcept
|
||||
//! move assignment operator
|
||||
constexpr
|
||||
Vector& operator=(Vector&& vs) noexcept
|
||||
{
|
||||
swap(*this, vs);//std::forward<base>(static_cast<base>(*this)), std::forward<base>(static_cast<base>(vs)));
|
||||
//std::swap(*this, vs);
|
||||
swap(*this, vs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! destructor
|
||||
virtual
|
||||
~VectorSpace() = default;
|
||||
~Vector() = default;
|
||||
|
||||
inline
|
||||
VectorSpace(typename base::iterator start, typename base::iterator end) :
|
||||
//! copy constructor from base
|
||||
constexpr
|
||||
Vector(const base& arr) :
|
||||
base {arr}
|
||||
{}
|
||||
|
||||
//! move constructor from base
|
||||
constexpr
|
||||
Vector(base&& arr) :
|
||||
base {std::forward<base>(arr)}
|
||||
{}
|
||||
|
||||
//! construct from iterators
|
||||
constexpr
|
||||
Vector(typename base::iterator start, typename base::iterator end) :
|
||||
base {start, end}
|
||||
{}
|
||||
|
||||
inline
|
||||
VectorSpace(typename base::const_iterator start, typename base::const_iterator end) :
|
||||
//! construct from constant iterators
|
||||
constexpr
|
||||
Vector(typename base::const_iterator start, typename base::const_iterator end) :
|
||||
base {start, end}
|
||||
{}
|
||||
|
||||
inline
|
||||
VectorSpace(std::initializer_list<value_type> list) :
|
||||
//! construct from initializer list
|
||||
constexpr
|
||||
Vector(std::initializer_list<value_type> list) :
|
||||
base {list}
|
||||
{}
|
||||
|
||||
template <std::convertible_to<value_type>... Args>
|
||||
inline
|
||||
VectorSpace(const value_type& v, const Args& ...args) :
|
||||
//! copy constructor with variadic args
|
||||
template <IsReal... Args>
|
||||
constexpr
|
||||
Vector(const value_type& v, const Args& ...args) requires (S == 1 + sizeof...(args)):
|
||||
base {v, static_cast<value_type>(args)...}
|
||||
{
|
||||
static_assert(1 + sizeof...(args) == Size, "Number of arguments must be equal to size of vector");
|
||||
}
|
||||
{}
|
||||
|
||||
template <std::convertible_to<value_type>... Args>
|
||||
inline
|
||||
VectorSpace(value_type&& v, Args&& ...args) :
|
||||
//! move constructor with variadic args
|
||||
template <IsReal... Args>
|
||||
constexpr
|
||||
Vector(value_type&& v, Args&& ...args) requires (S == 1 + sizeof...(args)):
|
||||
base {v, static_cast<value_type>(std::forward<Args>(args))...}
|
||||
{
|
||||
static_assert(1 + sizeof...(args) == Size, "Number of arguments must be equal to size of vector");
|
||||
}
|
||||
{}
|
||||
|
||||
/*template <size_type SubSize, std::convertible_to<value_type>... Args>
|
||||
inline
|
||||
VectorSpace(const VectorSpace<value_type, SubSize>& subvec, const value_type& v, const Args& ...args) :
|
||||
base {static_cast<StaticArray<value_type, SubSize>>(subvec), v,
|
||||
static_cast<value_type>(std::forward<Args>(args))...}
|
||||
{}*/
|
||||
|
||||
inline
|
||||
VectorSpace(const SubVector& subvs, const value_type& v) :
|
||||
//! copy constructor with sub vector and value
|
||||
constexpr
|
||||
Vector(const SubVector<Type, S>& svs, const value_type& v) requires (S >= 1):
|
||||
base {}
|
||||
{
|
||||
for (auto n = 0; n < subvs.size(); ++n)
|
||||
(*this)[n] = subvs[n];
|
||||
(*this)[subvs.size()] = v;
|
||||
}
|
||||
|
||||
template <size_type BiggerSize>
|
||||
inline
|
||||
VectorSpace(const VectorSpace<Type, BiggerSize>& vs) :
|
||||
base {vs.begin(), vs.begin() + Size}
|
||||
{
|
||||
static_assert(BiggerSize > Size, "Size should be bigger");
|
||||
}
|
||||
|
||||
// Member functions
|
||||
|
||||
// vector versus scalar (per element operations)
|
||||
|
||||
friend inline
|
||||
VectorSpace operator-(const VectorSpace& rhs)
|
||||
{
|
||||
VectorSpace vs {rhs};
|
||||
for (value_type& v : vs)
|
||||
v = -v;
|
||||
return vs;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator*=(const value_type& val)
|
||||
{
|
||||
//for (value_type& v : *this)
|
||||
// v *= val;
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] *= val;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator+=(const value_type& val)
|
||||
{
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] += val;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator-=(const value_type& val)
|
||||
{
|
||||
//for (value_type& v : *this)
|
||||
// v -= val;
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] -= val;
|
||||
}
|
||||
|
||||
inline
|
||||
void operator/=(const value_type& val)
|
||||
{
|
||||
for (value_type& v : *this)
|
||||
v /= val;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator+(const VectorSpace& lhs, const value_type& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs += rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator+(const value_type& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
return operator+(rhs, lhs);
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator*(const VectorSpace& lhs, const value_type& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs *= rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator*(const value_type& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
return operator*(rhs, lhs);
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator/(const VectorSpace& lhs, const value_type& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs /= rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator/(const value_type& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
VectorSpace vs;
|
||||
for (auto n = 0; n < vs.size(); ++n)
|
||||
vs[n] = lhs / rhs[n];
|
||||
return vs;
|
||||
}
|
||||
|
||||
// vector versus vector (per element operations)
|
||||
|
||||
inline
|
||||
void operator*=(const VectorSpace& vs)
|
||||
{
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] *= vs[n];
|
||||
}
|
||||
|
||||
inline
|
||||
void operator+=(const VectorSpace& vs)
|
||||
{
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] += vs[n];
|
||||
}
|
||||
|
||||
inline
|
||||
void operator-=(const VectorSpace& vs)
|
||||
{
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] -= vs[n];
|
||||
}
|
||||
|
||||
inline
|
||||
void operator/=(const VectorSpace& vs)
|
||||
{
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
(*this)[n] /= vs[n];
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator+(const VectorSpace& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs += rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator-(const VectorSpace& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs -= rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator*(const VectorSpace& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs *= rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
VectorSpace operator/(const VectorSpace& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
VectorSpace vs {lhs};
|
||||
vs /= rhs;
|
||||
return vs;
|
||||
}
|
||||
|
||||
friend inline
|
||||
bool operator==(const VectorSpace& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
if (lhs[n] != rhs[n])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
friend inline
|
||||
bool operator!=(const VectorSpace& lhs, const VectorSpace& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
for (auto n = 0; n < svs.size(); ++n)
|
||||
(*this)[n] = svs[n];
|
||||
(*this)[svs.size()] = v;
|
||||
}
|
||||
|
||||
//! copy constructor from greater vector
|
||||
template <Size GS> requires (GS > S)
|
||||
constexpr explicit
|
||||
Vector(const Vector<Type, GS>& vs) :
|
||||
base {vs.begin(), vs.begin() + S}
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
// global operators
|
||||
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator+(const Vector<T, S>& lhs) { Vector<T, S> vs; for (Size n = 0; n < S; ++n) vs[n] = lhs[n]; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator-(const Vector<T, S>& lhs) { Vector<T, S> vs; for (Size n = 0; n < S; ++n) vs[n] = -lhs[n]; return vs; }
|
||||
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator+=(Vector<T, S>& lhs, const Vector<T, S>& rhs) { for (Size n = 0; n < S; ++n) lhs[n] += rhs[n]; return lhs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator-=(Vector<T, S>& lhs, const Vector<T, S>& rhs) { for (Size n = 0; n < S; ++n) lhs[n] -= rhs[n]; return lhs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator*=(Vector<T, S>& lhs, const Vector<T, S>& rhs) { for (Size n = 0; n < S; ++n) lhs[n] *= rhs[n]; return lhs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator/=(Vector<T, S>& lhs, const Vector<T, S>& rhs) { for (Size n = 0; n < S; ++n) lhs[n] /= rhs[n]; return lhs; }
|
||||
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator+(const Vector<T, S>& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] += rhs[n]; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator-(const Vector<T, S>& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] -= rhs[n]; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator*(const Vector<T, S>& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] *= rhs[n]; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator/(const Vector<T, S>& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] /= rhs[n]; return vs; }
|
||||
|
||||
template <IsReal T, Size S> inline bool operator==(const Vector<T, S>& lhs, const Vector<T, S>& rhs) { for (Size n = 0; n < S; ++n) if (lhs[n] != rhs[n]) return false; return true; }
|
||||
template <IsReal T, Size S> inline bool operator!=(const Vector<T, S>& lhs, const Vector<T, S>& rhs) { for (Size n = 0; n < S; ++n) if (lhs[n] == rhs[n]) return false; return true; }
|
||||
|
||||
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator+=(Vector<T, S>& lhs, const T& rhs) { for (Size n = 0; n < S; ++n) lhs[n] += rhs; return lhs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator-=(Vector<T, S>& lhs, const T& rhs) { for (Size n = 0; n < S; ++n) lhs[n] -= rhs; return lhs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator*=(Vector<T, S>& lhs, const T& rhs) { for (Size n = 0; n < S; ++n) lhs[n] *= rhs; return lhs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S>& operator/=(Vector<T, S>& lhs, const T& rhs) { for (Size n = 0; n < S; ++n) lhs[n] /= rhs; return lhs; }
|
||||
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator+(const Vector<T, S>& lhs, const T& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] += rhs; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator-(const Vector<T, S>& lhs, const T& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] -= rhs; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator*(const Vector<T, S>& lhs, const T& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] *= rhs; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator/(const Vector<T, S>& lhs, const T& rhs) { Vector<T, S> vs {lhs}; for (Size n = 0; n < S; ++n) vs[n] /= rhs; return vs; }
|
||||
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator+(const T& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {rhs}; for (Size n = 0; n < S; ++n) vs[n] += lhs; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator-(const T& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {rhs}; for (Size n = 0; n < S; ++n) vs[n] -= lhs; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator*(const T& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {rhs}; for (Size n = 0; n < S; ++n) vs[n] *= lhs; return vs; }
|
||||
template <IsReal T, Size S> inline Vector<T, S> operator/(const T& lhs, const Vector<T, S>& rhs) { Vector<T, S> vs {rhs}; for (Size n = 0; n < S; ++n) vs[n] /= lhs; return vs; }
|
||||
|
||||
// boolean operations
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
VectorSpace<bool, Size> equal(const VectorSpace<Type, Size>& lhs, const VectorSpace<Type, Size>& rhs, scalar precision = 1e-5)
|
||||
Vector<bool, S> equal(const Vector<Type, S>& lhs, const Vector<Type, S>& rhs, const scalar& precision = scalar::precision())
|
||||
{
|
||||
VectorSpace<bool, Size> res;
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
res[n] = equal(lhs[n], rhs[n], precision);
|
||||
return res;
|
||||
Vector<bool, S> vs;
|
||||
for (auto n = 0; n < S; ++n)
|
||||
vs[n] = equal(lhs[n], rhs[n], precision);
|
||||
return vs;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
template <Size S>
|
||||
inline
|
||||
Type sum(const VectorSpace<Type, Size>& vs)
|
||||
{
|
||||
Type sum {};
|
||||
for (const Type& v : vs)
|
||||
sum += v;
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
constexpr
|
||||
Type dot(const VectorSpace<Type, Size>& lhs, const VectorSpace<Type, Size>& rhs)
|
||||
{
|
||||
return sum(lhs * rhs);
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
inline
|
||||
Type length(const VectorSpace<Type, Size>& vs)
|
||||
{
|
||||
return sqrt(dot(vs, vs));
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
inline
|
||||
Type distance(const VectorSpace<Type, Size>& point1, const VectorSpace<Type, Size>& point2)
|
||||
{
|
||||
return length(point1 - point2);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
constexpr
|
||||
VectorSpace<Type, 3> cross(const VectorSpace<Type, 3>& lhs, const VectorSpace<Type, 3>& rhs)
|
||||
{
|
||||
return VectorSpace<Type, 3>(
|
||||
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 <typename Type, size_t Size>
|
||||
constexpr
|
||||
VectorSpace<Type, Size> pow(const VectorSpace<Type, Size>& vs, scalar degree)
|
||||
{
|
||||
VectorSpace<Type, Size> res;
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
res[n] = std::pow(vs[n], degree);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
constexpr
|
||||
VectorSpace<Type, Size> abs(const VectorSpace<Type, Size>& vs)
|
||||
{
|
||||
VectorSpace<Type, Size> res;
|
||||
for (auto n = 0; n < Size; ++n)
|
||||
res[n] = std::abs(vs[n]);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
constexpr
|
||||
Type norm(const VectorSpace<Type, Size>& vs)
|
||||
{
|
||||
return sqrt(sum(pow(abs(vs), 2)));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
constexpr
|
||||
Type angle(const VectorSpace<Type, 3>& lhs, const VectorSpace<Type, 3>& rhs)
|
||||
{
|
||||
scalar cos = dot(lhs, rhs) / (norm(lhs) * norm(rhs));
|
||||
return acos(cos); //clip(cos, -1., 1.));
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
inline
|
||||
VectorSpace<Type, Size> normalize(const VectorSpace<Type, Size>& vs)
|
||||
{
|
||||
return vs * inversesqrt(dot(vs, vs));
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
constexpr
|
||||
bool any(const VectorSpace<bool, Size>& vs)
|
||||
bool any(const Vector<bool, S>& vs)
|
||||
{
|
||||
bool res = false;
|
||||
for (auto e : vs)
|
||||
@ -388,9 +208,9 @@ bool any(const VectorSpace<bool, Size>& vs)
|
||||
return res;
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
constexpr
|
||||
bool all(const VectorSpace<bool, Size>& vs)
|
||||
template <Size S>
|
||||
inline
|
||||
bool all(const Vector<bool, S>& vs)
|
||||
{
|
||||
bool res = true;
|
||||
for (auto e : vs)
|
||||
@ -398,13 +218,111 @@ bool all(const VectorSpace<bool, Size>& vs)
|
||||
return res;
|
||||
}
|
||||
|
||||
// Aliases
|
||||
// per element operations
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
using vec = VectorSpace<Type, Size>;
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Vector<Type, S> abs(const Vector<Type, S>& vs)
|
||||
{
|
||||
Vector<Type, S> res;
|
||||
for (auto n = 0; n < S; ++n)
|
||||
res[n] = abs(vs[n]);
|
||||
return res;
|
||||
}
|
||||
|
||||
using vec2 = VectorSpace<scalar, 2>;
|
||||
using vec3 = VectorSpace<scalar, 3>;
|
||||
using vec4 = VectorSpace<scalar, 4>;
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Type sum(const Vector<Type, S>& vs)
|
||||
{
|
||||
Type sum {};
|
||||
for (const Type& v : vs)
|
||||
sum += v;
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Vector<Type, S> pow(const Vector<Type, S>& vs, scalar degree)
|
||||
{
|
||||
Vector<Type, S> res;
|
||||
for (auto n = 0; n < S; ++n)
|
||||
res[n] = pow(vs[n], degree);
|
||||
return res;
|
||||
}
|
||||
|
||||
// vector operations
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Type norm(const Vector<Type, S>& vs)
|
||||
{
|
||||
return sqrt(sum(pow(abs(vs), 2)));
|
||||
}
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Type dot(const Vector<Type, S>& lhs, const Vector<Type, S>& rhs)
|
||||
{
|
||||
return sum(lhs * rhs);
|
||||
}
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Type length(const Vector<Type, S>& vs)
|
||||
{
|
||||
return sqrt(dot(vs, vs));
|
||||
}
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Type mag(const Vector<Type, S>& vs)
|
||||
{
|
||||
return length(vs);
|
||||
}
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Type distance(const Vector<Type, S>& vs1, const Vector<Type, S>& vs2)
|
||||
{
|
||||
return length(vs1 - vs2);
|
||||
}
|
||||
|
||||
template <typename Type, Size S>
|
||||
inline
|
||||
Vector<Type, S> normalize(const Vector<Type, S>& vs)
|
||||
{
|
||||
return vs * isqrt(dot(vs, vs));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline
|
||||
Type angle(const Vector<Type, 3>& lhs, const Vector<Type, 3>& rhs)
|
||||
{
|
||||
scalar cos = dot(lhs, rhs) / (norm(lhs) * norm(rhs));
|
||||
return acos(cos); //clip(cos, -1., 1.));
|
||||
}
|
||||
|
||||
// vector 3 operations
|
||||
|
||||
template <typename Type>
|
||||
inline
|
||||
Vector<Type, 3> cross(const Vector<Type, 3>& lhs, const Vector<Type, 3>& rhs)
|
||||
{
|
||||
return Vector<Type, 3>(
|
||||
lhs[1] * rhs[2] - lhs[2] * rhs[1],
|
||||
lhs[2] * rhs[0] - lhs[0] * rhs[2],
|
||||
lhs[0] * rhs[1] - lhs[1] * rhs[0]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// aliases
|
||||
|
||||
template <typename Type, Size S>
|
||||
using vec = Vector<Type, S>;
|
||||
|
||||
using vec2 = Vector<scalar, 2>;
|
||||
using vec3 = Vector<scalar, 3>;
|
||||
using vec4 = Vector<scalar, 4>;
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user