improved base classes

This commit is contained in:
L-Nafaryus 2023-01-11 14:46:49 +05:00
parent 49b2ba0ba7
commit 2811c525d3
75 changed files with 3003 additions and 2486 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
cmake-wsl-build-debug
cmake-build-debug/
.idea/
build/

View File

@ -164,3 +164,4 @@ message(STATUS ${summary})
# Additional applications
add_subdirectory(source/applications)
add_subdirectory(source/creator)

View File

@ -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()

View File

@ -1,3 +0,0 @@
add_subdirectory(hpr)
#add_subdirectory(creator)
#add_subdirectory(applications)

View File

@ -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
add_executable(hyporo-creator
test2.cpp
)
target_include_directories(testi
target_include_directories(hyporo-creator
PRIVATE
../
)
target_link_libraries(testi
hpr::hpr
target_link_libraries(hyporo-creator
hpr::gpu
hpr::window-system
imgui
)

View File

@ -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
View 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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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
View File

@ -0,0 +1,5 @@
#pragma once
#include "geometry/polytope.hpp"
#include "geometry/triangle.hpp"
#include "geometry/tetrahedron.hpp"

View 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
)

View 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;
};
}

View File

@ -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"

View File

@ -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}

View File

@ -0,0 +1,2 @@
#include <glad/glad.h>
#include "array_object.hpp"

View 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;
}
};
}

View File

@ -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;
}
}

View File

@ -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

View 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);
}
}

View 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
View 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;
};
}

View 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);
}
};
}

View File

@ -0,0 +1,4 @@
#pragma once
#include <glad/glad.h>

View File

@ -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;
}
}

View File

@ -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;
};
}

View 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));
}
};
}

View 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);
}
};
}

View File

@ -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;
}
}
}

View File

@ -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);
};
}

View 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
View File

@ -0,0 +1,3 @@
//
// Created by L-Nafaryus on 12/16/2022.
//

View File

@ -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;
}
}
}

View File

@ -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;
};
}

View File

@ -1,5 +0,0 @@
//
// Created by L-Nafaryus on 10/3/2022.
//
#include "context.hpp"

View File

@ -1,8 +0,0 @@
//
// Created by L-Nafaryus on 10/3/2022.
//
#ifndef HYPORO_CONTEXT_HPP
#define HYPORO_CONTEXT_HPP
#endif //HYPORO_CONTEXT_HPP

View File

@ -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);
}
}

View File

@ -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);
};
}

View File

@ -1,15 +0,0 @@
#pragma once
#include "render_target.hpp"
namespace hpr::gpu::opengl
{
RenderTarget::RenderTarget() :
gpu::RenderTarget {DeviceAPI::OpenGL}
{}
RenderTarget::~RenderTarget() = default;
}

View File

@ -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;
}
};
}

View File

@ -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;
}

View File

@ -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;
};
}

View File

@ -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]);
}
}

View File

@ -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);
};
}

View File

@ -1,15 +0,0 @@
#include "texture.hpp"
namespace hpr::gpu::opengl
{
Texture::Texture() :
gpu::Texture {DeviceAPI::OpenGL},
p_textureIndex {0}
{}
Texture::~Texture() = default;
}

View File

@ -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;
};
}

View File

@ -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;
}

View File

@ -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;
}
};
}

View 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
View 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

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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
class ShaderProgram
{
friend class Device;
protected:
sarray<Shader*, (size_t)Shader::ShaderType::ShaderTypeCount> p_slots;
bool p_isLinked;
unsigned int p_index;
darray<Shader> p_shaders;
public:
// 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);
}
};
}

View 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);
}
};
}

View File

@ -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;
}
}

View File

@ -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
class Texture
{
protected:
std::string p_filename;
int p_width;
int p_height;
public:
// Constructors
using ustring = std::basic_string<unsigned char>;
Texture();
public:
explicit
Texture(DeviceAPI api);
~Texture() override;
// Member functions
[[nodiscard]]
std::string filename() const;
[[nodiscard]]
int width() const;
[[nodiscard]]
int height() const;
enum class Format
{
RGB = GL_RGB,
RGBA = GL_RGBA
};
} // end namespace hpr::gpu
protected:
unsigned int p_index;
std::string p_filename;
ustring p_source;
int p_width;
int p_height;
Format p_internalFormat;
Format p_imageFormat;
public:
inline
Texture() :
p_index {0},
p_filename {},
p_source {},
p_width {},
p_height {},
p_internalFormat {Format::RGBA},
p_imageFormat {Format::RGBA}
{}
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;
}
};
}

View 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

View 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]);
}
};
}

View File

@ -63,4 +63,6 @@ install(
)
if(HPR_TEST)
add_subdirectory(tests)
endif()

View File

@ -1,35 +1,274 @@
#pragma once
#include <ostream>
#include "../containers/array.hpp"
#include <iostream>
#include <memory>
#include <sstream>
#include <vector>
namespace hpr
{
class Logger
namespace logging
{
public:
enum Severity
{
None,
Emergency,
Alert,
Critical,
Error,
Warning,
Notice,
Info,
Debug
};
static Severity severity;
template <typename T>
friend
Logger& operator<<(Logger& l, const T& data)
class Sink
{
if (severity == Info)
std::cout << data << std::endl;
else if (severity == Error)
std::cerr << data << std::endl;
//friend class Logger;
return l;
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;
};
Logger::Severity Logger::severity = Logger::Warning;
Logger logger;
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
{
static Logger g_instance;
static darray<Sink*> g_sinks;
protected:
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 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;
}
}
}

View 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)

View 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();*/
}

View File

@ -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})

View File

@ -0,0 +1,4 @@
#pragma once
#include "integer/integer.hpp"
#include "integer/size.hpp"

View 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;
}

View 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;
}

View File

@ -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);

View File

@ -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>;
}

View File

@ -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;
}

View File

@ -0,0 +1,3 @@
#pragma once
#include "quaternion/quaternion.hpp"

View 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;
}

View File

@ -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); }
}*/

View File

@ -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))));
}

View File

@ -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>;
}