31 Commits
v0.7 ... v0.8

Author SHA1 Message Date
a6950b9de7 add more related repos to the README 2022-04-26 15:44:00 +02:00
5023a658e7 allow changing particle buffer size 2022-04-26 15:27:15 +02:00
5cc6aa8c74 fix liteparticles2d being named hdr_bloom 2022-04-26 01:01:21 +02:00
563dd80a52 fix liteparticles shaders not compiling on windows firefox webgl d3d 2022-04-25 23:53:47 +02:00
1ae43457ed lite_particles2d 2022-04-18 17:24:01 +02:00
5f066faa28 fix tracy for emscripten 2022-04-16 20:33:35 +02:00
bbbbc655e7 update tracy to v0.8 2022-04-15 18:19:45 +02:00
410b373fda update spdlog to v1.10.0 2022-04-15 18:09:53 +02:00
72add20751 update EnTT to v3.10.0 2022-04-15 16:37:53 +02:00
4dd5a69dee port to EnTT v3.10.0 2022-04-15 16:37:53 +02:00
e0f503728d add glsl noise lib 2022-04-14 18:05:15 +02:00
b520811e72 add missing 4 comp hash variants 2022-04-13 23:35:56 +02:00
69a04cbd7e refactor ogl buffer 2022-04-13 01:23:39 +02:00
ab169927fe improve resouce iteration 2022-04-12 16:44:28 +02:00
a98098af91 add dither noise to comp 2022-04-05 22:48:01 +02:00
631b9433c2 adding David Hoskins's hasing functions to shader builtins
https://www.shadertoy.com/view/4djSRW (hash without sine)
2022-04-02 16:12:20 +02:00
15ab73909f fix scene tools 2022-03-30 23:01:39 +02:00
3dc66de3bc change bloom render texture format, and make gles/webgl work properly 2022-03-29 15:32:33 +02:00
5e58a61c93 new bloom render tasks, setup util and hdr bloom code example 2022-03-28 14:22:46 +02:00
8f44e09f32 add trim 2022-03-10 19:10:29 +01:00
0408f771b7 fix builtins test vertex shader 2022-02-22 01:46:39 +01:00
4029dcd50c add builtin shaders 2022-02-21 00:42:18 +01:00
338fbf70d0 add gles build to github 2022-02-19 00:59:07 +01:00
e39b23ac72 better fbo building error printing 2022-02-19 00:56:16 +01:00
c48ae81238 disable ms for gles 2022-02-18 17:21:53 +01:00
7a461111e1 change blur to allow output to a different buffer then input 2022-02-18 00:41:34 +01:00
9ff0b17a5e [tilemap] add bias to verts to avoid cracks 2022-02-17 18:54:11 +01:00
60aca24ec6 add multisampled fbo support + multisampled fixes 2022-02-17 16:28:54 +01:00
4304701e5e fix camera, broken since f5eb0e0dfa 2022-02-17 00:53:21 +01:00
601e1ca99d add multisampled textures 2022-02-16 19:22:25 +01:00
f5eb0e0dfa add camera view dir helper 2022-02-15 17:21:45 +01:00
66 changed files with 3456 additions and 260 deletions

View File

@ -41,6 +41,27 @@ jobs:
## See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
#run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure
linux_gles:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
# TODO: cache
- name: Install Dependencies
run: sudo apt update && sudo apt -y install libsdl2-dev
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DMM_OPENGL_3_GLES=ON
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4
macos:
timeout-minutes: 10

View File

@ -13,7 +13,15 @@ The private repo had over 900 commits, before the first public release.
- [Github](https://github.com/MadeOfJelly/MushMachine)
- [Setup](docs/setup.md)
- [Example Game Setup](docs/basic_game.md)
- [mm_template](https://github.com/MadeOfJelly/mm_template) (a GitHub Template for quick starting MM game dev)
- The [Docs Directory](docs/) contains Documentation loosely based on the Source structure.
- Engine Extensions:
- [Box2D integration](https://github.com/Green-Sky/mushmachine_box2d)
- [fx_draw (simple line drawing rendertask)](https://github.com/Green-Sky/fx_draw)
- Projects using the Engine:
- [miniTD](https://github.com/Green-Sky/miniTD)
- [LiteParticles2DDemo](https://github.com/MadeOfJelly/mm_lite_particles2d_demo)
- [mm_fireworks](https://github.com/Green-Sky/mm_fireworks)
## Platforms
- PC (Linux, Windows and maybe APPLE(compiles))

2
external/entt vendored

View File

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
if(NOT EMSCRIPTEN)
add_library(tracy_client
"${CMAKE_CURRENT_LIST_DIR}/tracy/Tracy.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyLua.hpp"
@ -9,14 +10,14 @@ add_library(tracy_client
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyClient.cpp"
)
target_compile_features(tracy_client PUBLIC cxx_std_17)
if(TRACY_ENABLE)
target_compile_definitions(tracy_client PUBLIC TRACY_ENABLE)
#target_compile_definitions(tracy_client PUBLIC TRACY_NO_SYSTEM_TRACING)
message("Enabled TRACY")
endif()
target_compile_features(tracy_client PUBLIC cxx_std_17)
target_include_directories(tracy_client PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
if(UNIX)
@ -26,4 +27,12 @@ endif()
if(WIN32)
target_link_libraries(tracy_client ws2_32 dbghelp)
endif()
else() # EMSCRIPTEN
add_library(tracy_client INTERFACE)
target_compile_features(tracy_client INTERFACE cxx_std_17)
target_include_directories(tracy_client INTERFACE "${CMAKE_CURRENT_LIST_DIR}")
endif()

View File

@ -85,8 +85,8 @@ private:
bool entityHasComponent(Registry& registry, EntityType& entity, ComponentTypeID type_id)
{
ComponentTypeID type[] = { type_id };
return registry.runtime_view(std::cbegin(type), std::cend(type)).contains(entity);
const auto storage_it = registry.storage(type_id);
return storage_it != registry.storage().end() && storage_it->second.contains(entity);
}
public:
@ -243,7 +243,16 @@ public:
}
});
} else {
auto view = registry.runtime_view(comp_list.begin(), comp_list.end());
entt::basic_runtime_view<entt::basic_sparse_set<EntityType>> view{};
for (const auto type : comp_list) {
auto storage_it = registry.storage(type);
if (storage_it != registry.storage().end()) {
view.iterate(registry.storage(type)->second);
}
}
// TODO: add support for exclude
ImGui::Text("%lu Entities Matching:", view.size_hint());
if (ImGui::BeginChild("entity list")) {
@ -297,7 +306,8 @@ public:
// MIT License
// Copyright (c) 2020 Erik Scholz, Gnik Droy
// Copyright (c) 2019-2022 Erik Scholz
// Copyright (c) 2020 Gnik Droy
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@ -16,20 +16,20 @@ void Camera3D(MM::Scene& scene) {
ImGui::Indent();
auto* camera = scene.try_ctx<MM::OpenGL::Camera3D>();
if (!camera) {
if (!scene.ctx().contains<MM::OpenGL::Camera3D>()) {
ImGui::TextUnformatted("NO CAMERA!");
return;
}
auto& camera = scene.ctx().at<MM::OpenGL::Camera3D>();
static bool follow_entity = false;
static MM::Entity tracking = entt::null;
ImGui::InputFloat("screenRatio", &camera->screenRatio);
ImGui::InputFloat("nearPlane", &camera->nearPlane);
ImGui::InputFloat("farPlane", &camera->farPlane);
ImGui::InputFloat("screenRatio", &camera.screenRatio);
ImGui::InputFloat("nearPlane", &camera.nearPlane);
ImGui::InputFloat("farPlane", &camera.farPlane);
if (camera->ortho) {
if (camera.ortho) {
ImGui::TextUnformatted("orthographic mode");
ImGui::Checkbox("follow entity", &follow_entity);
@ -39,39 +39,39 @@ void Camera3D(MM::Scene& scene) {
MM::ImGuiWidgets::Entity(tracking, scene);
if (scene.valid(tracking)) {
if (scene.all_of<MM::Components::Position2D>(tracking)) {
camera->translation = {scene.get<MM::Components::Position2D>(tracking).pos, 0.f};
camera.translation = {scene.get<MM::Components::Position2D>(tracking).pos, 0.f};
} else if (scene.all_of<MM::Components::Position3D>(tracking)) { // "3d" fallback
camera->translation = scene.get<MM::Components::Position3D>(tracking).pos;
camera.translation = scene.get<MM::Components::Position3D>(tracking).pos;
} else {
ImGui::TextUnformatted("error: Entity has neither Position2D nor Position3D");
}
}
} else {
ImGui::DragFloat2("translation", &camera->translation.x, 0.1f);
ImGui::DragFloat2("translation", &camera.translation.x, 0.1f);
}
ImGui::DragFloat("h_vp_size", &camera->horizontalViewPortSize, 0.1f);
ImGui::DragFloat("h_vp_size", &camera.horizontalViewPortSize, 0.1f);
// TODO: aspect ratio
// TODO: check for change
camera->setOrthographic();
camera.setOrthographic();
} else { // perspective
ImGui::TextUnformatted("perspective mode");
ImGui::DragFloat3("translation", &camera->translation.x, 0.1f);
ImGui::SliderFloat("fov", &camera->fov, 0.1f, glm::pi<float>());
ImGui::DragFloat3("translation", &camera.translation.x, 0.1f);
ImGui::SliderFloat("fov", &camera.fov, 0.1f, glm::pi<float>());
ImGui::SliderFloat("yaw", &camera->yaw, 0.0f, 2*glm::pi<float>());
ImGui::SliderFloat("pitch", &camera->pitch, -glm::pi<float>()/2, glm::pi<float>()/2);
ImGui::SliderFloat("yaw", &camera.yaw, 0.0f, 2*glm::pi<float>());
ImGui::SliderFloat("pitch", &camera.pitch, -glm::pi<float>()/2, glm::pi<float>()/2);
ImGui::InputFloat3("up", &camera->up.x);
ImGui::InputFloat3("up", &camera.up.x);
camera->setPerspective();
camera.setPerspective();
}
camera->updateView();
camera.updateView();
ImGui::Unindent();
}

View File

@ -1,5 +1,4 @@
#include "./scene_tools.hpp"
#include "mm/components/velocity2d_rotation.hpp"
#include <mm/engine.hpp>
@ -32,10 +31,11 @@
namespace MM::Services {
bool ImGuiSceneToolsService::enable(Engine& engine, std::vector<UpdateStrategies::TaskInfo>& task_array) {
if (!engine.tryService<MM::Services::SceneServiceInterface>()) {
LOGIGS("error: no SceneServiceInterface");
return false;
}
// enable anyway
//if (!engine.tryService<MM::Services::SceneServiceInterface>()) {
//LOGIGS("error: no SceneServiceInterface");
//return false;
//}
// setup entity editor defaults
{
@ -71,14 +71,13 @@ namespace MM::Services {
};
menu_bar.menu_tree["Scene"]["TimeCtx"] = [this](Engine& e) {
MM::Components::TimeDelta* td_ptr = nullptr;
bool have_ctx = false;
if (auto* ssi_ptr = e.tryService<MM::Services::SceneServiceInterface>()) {
auto& scene = ssi_ptr->getScene();
td_ptr = scene.try_ctx<MM::Components::TimeDelta>();
have_ctx = ssi_ptr->getScene().ctx().contains<MM::Components::TimeDelta>();
}
ImGui::MenuItem("TimeDelta Context", NULL, &_show_time_delta_ctx, td_ptr);
ImGui::MenuItem("TimeDelta Context", NULL, &_show_time_delta_ctx, have_ctx);
};
// add task
@ -102,6 +101,10 @@ namespace MM::Services {
}
void ImGuiSceneToolsService::renderImGui(Engine& engine) {
if (engine.tryService<MM::Services::SceneServiceInterface>() == nullptr) {
return; // no scene, nothing to see
}
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
if (_show_scene_metrics) {
@ -153,10 +156,13 @@ namespace MM::Services {
if (_show_time_delta_ctx) {
if (ImGui::Begin("Scene TimeDelta Context", &_show_time_delta_ctx)) {
auto* td_ptr = scene.try_ctx<MM::Components::TimeDelta>();
ImGui::Value("tickDelta", td_ptr->tickDelta);
ImGui::SliderFloat("deltaFactor", &td_ptr->deltaFactor, 0.f, 10.f, "%.5f", ImGuiSliderFlags_Logarithmic);
if (scene.ctx().contains<MM::Components::TimeDelta>()) {
auto& td = scene.ctx().at<MM::Components::TimeDelta>();
ImGui::Value("tickDelta", td.tickDelta);
ImGui::SliderFloat("deltaFactor", &td.deltaFactor, 0.f, 10.f, "%.5f", ImGuiSliderFlags_Logarithmic);
} else {
ImGui::TextUnformatted("No TimeDelta");
}
}
ImGui::End();
}

View File

@ -6,20 +6,28 @@
namespace MM::OpenGL {
Buffer::Buffer(const void* data, std::size_t size, GLenum usage) : _size(size) {
Buffer::Buffer(const void* data, std::size_t size, GLenum usage, GLenum target) : _size(size), _target(target) {
glGenBuffers(1, &_handle);
glBindBuffer(GL_ARRAY_BUFFER, _handle);
glBufferData(GL_ARRAY_BUFFER, size, data, usage);
glBindBuffer(_target, _handle);
glBufferData(_target, size, data, usage);
}
Buffer::~Buffer(void) {
glDeleteBuffers(1, &_handle);
}
void Buffer::bind(void) const {
glBindBuffer(_target, _handle);
}
void Buffer::bind(GLenum target) const {
glBindBuffer(target, _handle);
}
void Buffer::unbind(void) const {
glBindBuffer(_target, 0);
}
void Buffer::unbind(GLenum target) const {
glBindBuffer(target, 0);
}
@ -28,5 +36,9 @@ std::size_t Buffer::getSize(void) const {
return _size;
}
GLuint Buffer::getHandle(void) const {
return _handle;
}
} // MM::OpenGL

View File

@ -21,15 +21,20 @@ namespace MM::OpenGL {
private:
GLuint _handle = 0;
std::size_t _size = 0;
GLenum _target;
public:
Buffer(const void* data, std::size_t size, GLenum usage);
Buffer(const void* data, std::size_t size, GLenum usage, GLenum target = GL_ARRAY_BUFFER);
~Buffer(void);
void bind(void) const;
void bind(GLenum target) const;
void unbind(void) const;
void unbind(GLenum target) const;
std::size_t getSize(void) const;
GLuint getHandle(void) const;
};
} // MM::OpenGL

View File

@ -18,10 +18,30 @@ FBOBuilder FBOBuilder::start(void) {
}
std::shared_ptr<FrameBufferObject> FBOBuilder::finish(void) {
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
const auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE) {
return _fbo;
}
const char* error_str = "UNK";
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
error_str = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
#ifdef MM_OPENGL_3_GLES
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
error_str = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
break;
#endif
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
error_str = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
error_str = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
}
SPDLOG_ERROR("framebuffer status: {}", error_str);
return nullptr;
}
@ -46,8 +66,15 @@ FBOBuilder& FBOBuilder::attachTexture(std::shared_ptr<Texture> tex, GLuint attac
break;
}
//glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->getHandle(), 0);
if (tex->samples == 0u) {
glFramebufferTexture2D(target, attachment_type, GL_TEXTURE_2D, tex->getHandle(), 0);
} else {
#ifndef MM_OPENGL_3_GLES
glFramebufferTexture2D(target, attachment_type, GL_TEXTURE_2D_MULTISAMPLE, tex->getHandle(), 0);
#else
assert(false && "GLES has no multisampling support");
#endif
}
_fbo->_texAttachments.push_back(tex); // keep a ref at the fbo
return *this;

View File

@ -90,6 +90,7 @@ void Shader::setUniformMat3f(const std::string& name, const glm::mat3& matrix) {
}
// TODO: refactor this whole thing out
// FIXME: hangs if trailing whitespace
std::string Shader::parse(Engine& engine, const std::string& filePath) {
auto& fs = engine.getService<MM::Services::FilesystemService>();

View File

@ -16,28 +16,49 @@ uint32_t Texture::getHandle(void) const {
Texture::Texture(
uint32_t handle,
int32_t width_, int32_t height_,
int32_t internalFormat, int32_t format, int32_t type
int32_t internalFormat, int32_t format, int32_t type,
uint32_t samples_
) : _handle(handle), width(width_), height(height_),
_internalFormat(internalFormat), _format(format), _type(type) {}
samples(samples_),
_internalFormat(internalFormat), _format(format), _type(type) {
}
Texture::~Texture(void) {
glDeleteTextures(1, &_handle);
}
void Texture::unbind(void) const {
// TODO: do i need ms variant?
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::bind(uint32_t slot) const {
glActiveTexture(GL_TEXTURE0 + slot);
if (samples == 0) {
glBindTexture(GL_TEXTURE_2D, _handle);
} else {
#ifndef MM_OPENGL_3_GLES
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _handle);
#else
assert(false && "GLES has no multisampling support");
#endif
}
}
void Texture::resize(int32_t new_width, int32_t new_height) {
// if (glTexImage2D == true)
glBindTexture(GL_TEXTURE_2D, _handle);
if (samples == 0) {
glBindTexture(GL_TEXTURE_2D, _handle);
glTexImage2D(GL_TEXTURE_2D, 0, _internalFormat, new_width, new_height, 0, _format, _type, NULL);
} else {
#ifndef MM_OPENGL_3_GLES
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _handle);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, _internalFormat, new_width, new_height, 0);
#else
assert(false && "GLES has no multisampling support");
#endif
}
// HACK: super dirty
*(const_cast<int32_t*>(&width)) = new_width;
@ -57,5 +78,27 @@ Texture::handle_t Texture::createEmpty(int32_t internalFormat, int32_t width, in
return handle_t(new Texture(id, width, height, internalFormat, format, type));
}
Texture::handle_t Texture::createEmptyMultiSampled(int32_t internalFormat, int32_t width, int32_t height, uint32_t samples) {
#ifndef MM_OPENGL_3_GLES
uint32_t id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, id);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_TRUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
// HACK: format + type?
return handle_t(new Texture(id, width, height, internalFormat, 0, 0, samples));
#else
(void)internalFormat;
(void)width;
(void)height;
(void)samples;
assert(false && "GLES has no multisampling support");
#endif
}
} // MM::OpenGL

View File

@ -20,7 +20,8 @@ namespace MM::OpenGL {
Texture(
uint32_t handle,
int32_t width_, int32_t height_,
int32_t internalFormat, int32_t format, int32_t type
int32_t internalFormat, int32_t format, int32_t type,
uint32_t samples = 0u
);
public:
@ -29,6 +30,8 @@ namespace MM::OpenGL {
int32_t const width;
int32_t const height;
//int32_t const bpp; // bits per pixel
uint32_t const samples{0u}; // sample count, 0 == off
private:
int32_t const _internalFormat;
int32_t const _format;
@ -45,6 +48,7 @@ namespace MM::OpenGL {
void resize(int32_t new_width, int32_t new_height);
static handle_t createEmpty(int32_t internalFormat, int32_t width, int32_t height, int32_t format, int32_t type);
static handle_t createEmptyMultiSampled(int32_t internalFormat, int32_t width, int32_t height, uint32_t samples);
};
} // MM::OpenGL

View File

@ -31,5 +31,12 @@ namespace MM::OpenGL {
}
};
struct TextureLoaderEmptyMultiSampled final {
template<typename... Args>
std::shared_ptr<Texture> load(Args&& ... args) const {
return Texture::createEmptyMultiSampled(std::forward<Args>(args)...);
}
};
} // MM::OpenGL

View File

@ -9,6 +9,9 @@ add_library(opengl_renderer_s
src/mm/opengl/camera_3d.hpp
src/mm/opengl/camera_3d.cpp
src/mm/opengl/res/shaders_builtin.hpp
src/mm/opengl/res/shaders_builtin.cpp
)
target_include_directories(opengl_renderer_s PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
@ -176,6 +179,49 @@ target_link_libraries(blur_render_task
engine
)
############# bloom_extraction render task ###########
add_library(bloom_extraction_render_task
src/mm/opengl/render_tasks/bloom_extraction.hpp
src/mm/opengl/render_tasks/bloom_extraction.cpp
)
target_include_directories(bloom_extraction_render_task PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(bloom_extraction_render_task
opengl_renderer_s
engine
)
############# bloom_combine render task ###########
add_library(bloom_combine_render_task
src/mm/opengl/render_tasks/bloom_combine.hpp
src/mm/opengl/render_tasks/bloom_combine.cpp
)
target_include_directories(bloom_combine_render_task PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(bloom_combine_render_task
opengl_renderer_s
engine
)
############# composition render task ###########
# intendet for bloom compositing and tonemapping
add_library(composition_render_task
src/mm/opengl/render_tasks/composition.hpp
src/mm/opengl/render_tasks/composition.cpp
)
target_include_directories(composition_render_task PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(composition_render_task
opengl_renderer_s
engine
)
############# tilemap renderer ###########
add_library(tilemap_render_task
@ -207,6 +253,42 @@ target_link_libraries(fast_sky_render_task
engine
)
############# bloom ###########
add_library(bloom
src/mm/opengl/bloom.hpp
src/mm/opengl/bloom.cpp
)
target_include_directories(bloom PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(bloom
bloom_extraction_render_task
blur_render_task
bloom_combine_render_task
)
############# lite_particles2d ###########
add_library(lite_particles2d
src/mm/opengl/lite_particles2d_type.hpp
src/mm/opengl/lite_particles2d_type_loader.hpp
src/mm/opengl/lite_particles2d_type_loader.cpp
src/mm/opengl/components/lite_particles2d.hpp
src/mm/opengl/render_tasks/lite_particles2d.hpp
src/mm/opengl/render_tasks/lite_particles2d.cpp
)
target_include_directories(lite_particles2d PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(lite_particles2d
engine
opengl_renderer_s
common_components_serialize_json # glm serl
)
########################
if (BUILD_TESTING)

View File

@ -0,0 +1,160 @@
#include "./bloom.hpp"
#include <mm/opengl/render_tasks/bloom_extraction.hpp>
#include <mm/opengl/render_tasks/blur.hpp>
#include <mm/opengl/render_tasks/bloom_combine.hpp>
#include <mm/opengl/fbo_builder.hpp>
#include <mm/opengl/texture_loader.hpp>
namespace MM::OpenGL {
using namespace entt::literals;
void setup_bloom(
MM::Engine& engine,
const std::string color_src_tex,
const size_t bloom_phases,
const float bloom_in_scale,
const float bloom_phase_scale
) {
assert(bloom_phases > 1);
auto& rs = engine.getService<MM::Services::OpenGLRenderer>();
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto [w, h] = engine.getService<MM::Services::SDLService>().getWindowSize();
#ifdef MM_OPENGL_3_GLES
#if 0 // NOPE!!
// TODO: caps at 1, invest in half float?
const auto bloom_internal_format = GL_RGB565; // prolly fine. NOPE its not. it causes green pixely halos
const auto bloom_format_type = GL_UNSIGNED_BYTE;
#else
//const auto bloom_internal_format = GL_RGBA16F;
const auto bloom_internal_format = GL_R11F_G11F_B10F;
const auto bloom_format_type = GL_FLOAT;
#endif
#else
//const auto bloom_internal_format = GL_RGB16F;
const auto bloom_internal_format = GL_R11F_G11F_B10F;
const auto bloom_format_type = GL_FLOAT;
#endif
{ // bloom in (bloom extraction)
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"bloom_in",
bloom_internal_format,
w * bloom_in_scale, h * bloom_in_scale,
GL_RGB, bloom_format_type
);
{ // filter
rm_t.get("bloom_in"_hs)->bind(0);
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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
rs.targets["bloom_extraction"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("bloom_in"_hs), GL_COLOR_ATTACHMENT0)
.setResizeFactors(bloom_in_scale, bloom_in_scale)
.setResize(true)
.finish();
assert(rs.targets["bloom_extraction"]);
}
// blur textures and fbos
for (size_t i = 1; i <= bloom_phases; i++) {
// TODO: further dedup
std::string tex_out_name {"blur_out" + std::to_string(i)};
auto tex_out_id = entt::hashed_string::value(tex_out_name.c_str());
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
tex_out_id,
bloom_internal_format,
w * bloom_in_scale * glm::pow(bloom_phase_scale, i), h * bloom_in_scale * glm::pow(bloom_phase_scale, i),
GL_RGB, bloom_format_type
);
{ // filter
rm_t.get(tex_out_id)->bind(0);
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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
std::string tex_tmp_name {"blur_tmp" + std::to_string(i)};
auto tex_tmp_id = entt::hashed_string::value(tex_tmp_name.c_str());
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
tex_tmp_id,
bloom_internal_format,
w * bloom_in_scale * glm::pow(bloom_phase_scale, i), h * bloom_in_scale * glm::pow(bloom_phase_scale, i),
GL_RGB, bloom_format_type
);
{ // filter
rm_t.get(tex_tmp_id)->bind(0);
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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
rs.targets[tex_out_name] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get(tex_out_id), GL_COLOR_ATTACHMENT0)
.setResizeFactors(bloom_in_scale * glm::pow(bloom_phase_scale, i), bloom_in_scale * glm::pow(bloom_phase_scale, i))
.setResize(true)
.finish();
assert(rs.targets[tex_out_name]);
rs.targets[tex_tmp_name] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get(tex_tmp_id), GL_COLOR_ATTACHMENT0)
.setResizeFactors(bloom_in_scale * glm::pow(bloom_phase_scale, i), bloom_in_scale * glm::pow(bloom_phase_scale, i))
.setResize(true)
.finish();
assert(rs.targets[tex_tmp_name]);
}
{ // render tasks
auto& extraction = rs.addRenderTask<MM::OpenGL::RenderTasks::BloomExtraction>(engine);
extraction.src_tex = color_src_tex;
extraction.target_fbo = "bloom_extraction";
const glm::vec2 blur_factor {1.f, 1.f};
{ // blur rt
std::string prev_out_tex = "bloom_in";
for (size_t i = 1; i <= bloom_phases; i++) {
auto& blur = rs.addRenderTask<MM::OpenGL::RenderTasks::Blur>(engine);
// h
blur.in_tex = prev_out_tex;
blur.temp_fbo = "blur_tmp" + std::to_string(i);
// v
blur.temp_tex = "blur_tmp" + std::to_string(i);
blur.out_fbo = "blur_out" + std::to_string(i);
blur.out_tex = "blur_out" + std::to_string(i);
blur.tex_offset_factor = blur_factor * glm::vec2{2.f, 1.f}; // the input texture is double the size
// old blur:
//blur.tex_offset = {1.f/(w * bloom_in_scale * bloom_in_scale), 1.f/(h * bloom_in_scale * bloom_in_scale)};
prev_out_tex = blur.out_tex;
}
}
// combine passes
for (size_t i = bloom_phases; i > 1; i--) {
auto& combine = rs.addRenderTask<MM::OpenGL::RenderTasks::BloomCombine>(engine);
if (i == bloom_phases) {
combine.tex0 = "blur_out" + std::to_string(i);
} else {
combine.tex0 = "blur_tmp" + std::to_string(i);
}
combine.tex1 = "blur_out" + std::to_string(i-1);
combine.target_fbo = "blur_tmp" + std::to_string(i-1); // -> blur_tmpi-1
}
}
}
} // MM::OpenGL

View File

@ -0,0 +1,27 @@
#pragma once
#include <string>
#include "./render_task.hpp"
namespace MM::OpenGL {
// helper function to setup a bloom blur and recombine chain
// creates (texture) rendertarget, fbos and rendertasks
// outputs blur to "blur_tmp1" texture
//
// you still need to add the Composition rendertask (or eqv) your self, eg:
// auto& comp = rs.addRenderTask<MM::OpenGL::RenderTasks::Composition>(engine);
// comp.color_tex = "hdr_color";
// comp.bloom_tex = "blur_tmp1";
// comp.target_fbo = "display";
void setup_bloom(
MM::Engine& engine,
const std::string color_src_tex = "hdr_color", // texture to extract color from
const size_t bloom_phases = 5, // number of downsampled blurs (4 prob fine for 720, 5 for 1080)
const float bloom_in_scale = 0.5f, // scale of bloom extraction layer (1 - 0.5 best, lower for more perf)
const float bloom_phase_scale = 0.5f // ammount of scaling per downsampling
);
} // MM::OpenGL

View File

@ -51,20 +51,7 @@ void Camera3D::updateView(void) {
pitch = glm::clamp(pitch, -(glm::pi<float>()/2 - 0.00001f), glm::pi<float>()/2 - 0.00001f);
yaw = glm::mod(yaw, 2*glm::pi<float>());
glm::vec3 front {0,0,0};
{ // TODO: optimize
// if y up/down
front.x += up.y * glm::cos(pitch) * glm::cos(-yaw); // TODO: y is yaw inverted??
front.y += up.y * glm::sin(pitch);
front.z += up.y * glm::cos(pitch) * glm::sin(-yaw);
// if z up/down
front.x += up.z * glm::cos(pitch) * glm::cos(yaw);
front.y += up.z * glm::cos(pitch) * glm::sin(yaw);
front.z += up.z * glm::sin(pitch);
}
front = glm::normalize(front);
glm::vec3 front = getViewDir();
_view = glm::lookAt(translation, translation + front, up);
}
@ -82,6 +69,28 @@ glm::mat4 Camera3D::getProjection() const {
return _projection;
}
glm::vec3 Camera3D::getViewDir(void) const {
glm::vec3 front {0,0,0};
{ // TODO: optimize
if (up.y != 0.f) {
// if y up/down
front.x += up.y * glm::cos(pitch) * glm::cos(-yaw); // TODO: y is yaw inverted??
front.y += up.y * glm::sin(pitch);
front.z += up.y * glm::cos(pitch) * glm::sin(-yaw);
}
if (up.z != 0.f) {
// if z up/down
front.x += up.z * glm::cos(pitch) * glm::cos(yaw);
front.y += up.z * glm::cos(pitch) * glm::sin(yaw);
front.z += up.z * glm::sin(pitch);
}
}
front = glm::normalize(front);
return front;
}
std::array<glm::vec4, 6> Camera3D::getFrustumPlanes(void) const {
ZoneScopedN("Camera3D::getFrustumPlanes")

View File

@ -47,6 +47,8 @@ namespace MM::OpenGL {
glm::mat4 getView(void) const;
glm::mat4 getProjection(void) const;
glm::vec3 getViewDir(void) const;
// call updateView beforehand, does not cache
std::array<glm::vec4, 6> getFrustumPlanes(void) const;

View File

@ -0,0 +1,26 @@
#pragma once
#include <glm/vec2.hpp>
#include <queue> // tmp
namespace MM::Components {
// not intended to be a component
// see LiteParticles2DUploadQueue
struct LiteParticle2D {
uint32_t type_id {0u}; // entt::hashed_string, ResourceManager
glm::vec2 pos {0.f, 0.f};
glm::vec2 vel {0.f, 0.f};
float age {0.f};
};
struct LiteParticles2DUploadQueue {
// TODO: vector
std::queue<LiteParticle2D> queue;
};
} // MM::Components

View File

@ -0,0 +1,95 @@
#pragma once
#include <glm/vec4.hpp>
#include <nlohmann/json.hpp>
#include <mm/components/serialize/json_glm.hpp>
#include <vector>
namespace MM::OpenGL {
struct LiteParticles2DType {
struct Compute {
float age_delta;
glm::vec2 force_vec;
float turbulence;
float turbulence_noise_scale;
float turbulence_individuality;
float turbulence_time_scale;
float dampening;
static constexpr size_t _member_count = 8;
} compute;
struct Render {
glm::vec4 color_start;
glm::vec4 color_end;
float size_start;
float size_end;
static constexpr size_t _member_count = 10;
} render;
// naive, prob fine, not time critical
void upload(std::vector<float>& comp_vec, std::vector<float>& rend_vec) const {
{
comp_vec.push_back(compute.age_delta);
comp_vec.push_back(compute.force_vec.x);
comp_vec.push_back(compute.force_vec.y);
comp_vec.push_back(compute.turbulence);
comp_vec.push_back(compute.turbulence_noise_scale);
comp_vec.push_back(compute.turbulence_individuality);
comp_vec.push_back(compute.turbulence_time_scale);
comp_vec.push_back(compute.dampening);
}
{
rend_vec.push_back(render.color_start.r);
rend_vec.push_back(render.color_start.g);
rend_vec.push_back(render.color_start.b);
rend_vec.push_back(render.color_start.a);
rend_vec.push_back(render.color_end.r);
rend_vec.push_back(render.color_end.g);
rend_vec.push_back(render.color_end.b);
rend_vec.push_back(render.color_end.a);
rend_vec.push_back(render.size_start);
rend_vec.push_back(render.size_end);
}
}
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LiteParticles2DType::Compute,
age_delta,
force_vec,
turbulence,
turbulence_noise_scale,
turbulence_individuality,
turbulence_time_scale,
dampening
)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LiteParticles2DType::Render,
color_start,
color_end,
size_start,
size_end
)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LiteParticles2DType, compute, render)
} // MM::OpenGL

View File

@ -0,0 +1,39 @@
#include "./lite_particles2d_type_loader.hpp"
#include <mm/services/filesystem.hpp>
#include <mm/logger.hpp>
namespace MM::OpenGL {
std::shared_ptr<LiteParticles2DType> LiteParticles2DTypeLoaderJson::load(const nlohmann::json& j) const {
auto new_type = std::make_shared<LiteParticles2DType>();
try {
*new_type = j;
} catch (...) {
SPDLOG_ERROR("failed parsing particle type json:\n{}", j.dump(2));
return nullptr;
}
return new_type;
}
std::shared_ptr<LiteParticles2DType> LiteParticles2DTypeLoaderFile::load(MM::Engine& engine, const std::string& path) const {
auto* fs = engine.tryService<MM::Services::FilesystemService>();
if (fs == nullptr) {
// TODO: error
return nullptr;
}
auto new_particle = LiteParticles2DTypeLoaderJson{}.load(fs->readJson(path.c_str()));
if (!new_particle) {
SPDLOG_ERROR("particle type file: '{}'", path);
}
return new_particle;
}
} // MM::OpenGL

View File

@ -0,0 +1,18 @@
#pragma once
#include "./lite_particles2d_type.hpp"
#include <mm/engine_fwd.hpp>
namespace MM::OpenGL {
struct LiteParticles2DTypeLoaderJson final {
std::shared_ptr<LiteParticles2DType> load(const nlohmann::json& j) const;
};
struct LiteParticles2DTypeLoaderFile final {
std::shared_ptr<LiteParticles2DType> load(MM::Engine& engine, const std::string& path) const;
};
} // MM::OpenGL

View File

@ -30,5 +30,5 @@ namespace MM::OpenGL {
//virtual void reload(void) {} // TODO: remove
//virtual std::vector<const char*> getShaderPaths(void) {return {};} // TODO: remove
};
}
} // MM:OpenGL

View File

@ -27,9 +27,6 @@
namespace MM::OpenGL::RenderTasks {
BatchedSpriteSheet::BatchedSpriteSheet(Engine& engine) {
default_cam.setOrthographic();
default_cam.updateView();
float vertices[] = {
-0.5f, 0.5f,
-0.5f, -0.5f,
@ -70,6 +67,10 @@ void BatchedSpriteSheet::render(Services::OpenGLRenderer& rs, Engine& engine) {
auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
struct sp_data {
SpriteSheet sp;
struct instance_data {
@ -116,12 +117,9 @@ void BatchedSpriteSheet::render(Services::OpenGLRenderer& rs, Engine& engine) {
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto* cam = scene.try_ctx<Camera3D>();
if (!cam) {
cam = &default_cam;
}
auto& cam = scene.ctx().at<Camera3D>();
auto vp = cam->getViewProjection();
auto vp = cam.getViewProjection();
_shader->setUniformMat4f("_VP", vp);
for (auto& sp_ent : batch_map) {

View File

@ -37,8 +37,6 @@ namespace MM::OpenGL::RenderTasks {
public:
glm::vec4 default_color {1.f, 1.f, 1.f, 1.f};
OpenGL::Camera3D default_cam;
BatchedSpriteSheet(Engine& engine);
~BatchedSpriteSheet(void);

View File

@ -0,0 +1,161 @@
#include "./bloom_combine.hpp"
#include <mm/fs_const_archiver.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <mm/opengl/texture.hpp>
#include <tracy/Tracy.hpp>
namespace MM::OpenGL::RenderTasks {
BloomCombine::BloomCombine(Engine& engine) {
float vertices[] = {
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
};
_vertexBuffer = std::make_unique<Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<VertexArrayObject>();
_vao->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
setupShaderFiles();
_shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
BloomCombine::~BloomCombine(void) {
}
void BloomCombine::render(Services::OpenGLRenderer& rs, Engine& engine) {
ZoneScopedN("RenderTasks::BloomCombine::render");
rs.targets[target_fbo]->bind(FrameBufferObject::W);
{ // TODO: move to fbo
GLsizei width {0};
GLsizei height {0};
{ // get size of fb <.<
auto& fbo_tex_arr = rs.targets[target_fbo]->_texAttachments;
if (fbo_tex_arr.empty()) {
//GLint o_type {0};
//glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &o_type);
//if (o_type == GL_NONE) {
//// default framebuffer or error
//SPDLOG_INFO("gl none");
//}
// nah, just assume screen res
std::tie(width, height) = engine.getService<Services::SDLService>().getWindowSize();
} else {
auto& target_fbo_tex = rs.targets[target_fbo]->_texAttachments.front();
width = target_fbo_tex->width;
height = target_fbo_tex->height;
}
}
glViewport(0, 0, width, height);
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& rm = MM::ResourceManager<Texture>::ref();
auto tex_0 = rm.get(entt::hashed_string::value(tex0.c_str()));
tex_0->bind(0);
auto tex_1 = rm.get(entt::hashed_string::value(tex1.c_str()));
tex_1->bind(1);
// assign image units
_shader->setUniform1i("_tex0", 0);
_shader->setUniform1i("_tex1", 1);
glDrawArrays(GL_TRIANGLES, 0, 6);
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_shader->unbind();
}
void BloomCombine::reloadShaders(Engine& engine) {
auto tmp_shader = Shader::createF(engine, vertexPath, fragmentPath);
if (tmp_shader) {
_shader = tmp_shader;
}
}
void BloomCombine::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(vertexPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec2 _vertexPosition;
out vec2 _uv;
void main() {
gl_Position = vec4(_vertexPosition, 0, 1);
_uv = vec2(_vertexPosition.x * 0.5 + 0.5, _vertexPosition.y * 0.5 + 0.5);
})")
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
// tent sampling
#include "/shaders/builtin/sampling.glsl"
uniform sampler2D _tex0;
uniform sampler2D _tex1;
in vec2 _uv;
out vec3 _out_color;
vec3 tentSampling() {
// i hope the pipeline caches this
ivec2 tex0_size = textureSize(_tex0, 0); // TODO: lod
vec2 tex0_texel_step = vec2(1.0) / vec2(tex0_size);
return sampling2D_tent3x3_vec3(
_tex0,
_uv,
tex0_texel_step
);
}
vec3 simpleSampling() {
return texture(_tex0, _uv).rgb;
}
void main() {
#ifdef SIMPLE_SAMPLING
_out_color = texture(_tex1, _uv).rgb + simpleSampling();
#else
_out_color = texture(_tex1, _uv).rgb + tentSampling();
#endif
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,40 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
namespace MM::OpenGL::RenderTasks {
class BloomCombine : public RenderTask {
private:
std::shared_ptr<Shader> _shader;
std::unique_ptr<Buffer> _vertexBuffer;
std::unique_ptr<VertexArrayObject> _vao;
public:
BloomCombine(Engine& engine);
~BloomCombine(void);
const char* name(void) override { return "BloomCombine"; }
void render(Services::OpenGLRenderer& rs, Engine& engine) override;
public:
const char* vertexPath {"shader/combine_render_task/vert.glsl"};
const char* fragmentPath {"shader/combine_render_task/frag.glsl"};
std::string target_fbo {"display"};
std::string tex0 {"tex0"}; // lower res
std::string tex1 {"tex1"};
void reloadShaders(Engine& engine);
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,114 @@
#include "./bloom_extraction.hpp"
#include <mm/services/opengl_renderer.hpp>
#include <mm/fs_const_archiver.hpp>
#include <mm/opengl/texture.hpp>
#include <tracy/Tracy.hpp>
namespace MM::OpenGL::RenderTasks {
BloomExtraction::BloomExtraction(Engine& engine) {
float vertices[] = {
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
};
_vertexBuffer = std::make_unique<Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<VertexArrayObject>();
_vao->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
setupShaderFiles();
_shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
BloomExtraction::~BloomExtraction(void) {
}
void BloomExtraction::render(Services::OpenGLRenderer& rs, Engine&) {
ZoneScopedN("RenderTasks::BloomExtraction::render");
auto& target_fbo_ = rs.targets[target_fbo];
target_fbo_->bind(FrameBufferObject::W);
auto& target_fbo_tex = target_fbo_->_texAttachments.front();
glViewport(0, 0, target_fbo_tex->width, target_fbo_tex->height);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& rm = MM::ResourceManager<Texture>::ref();
auto tex = rm.get(entt::hashed_string::value(src_tex.c_str()));
tex->bind(0);
_shader->setUniform1i("color_tex", 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_shader->unbind();
}
void BloomExtraction::reloadShaders(Engine& engine) {
auto tmp_shader = Shader::createF(engine, vertexPath, fragmentPath);
if (tmp_shader) {
_shader = tmp_shader;
}
}
void BloomExtraction::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(vertexPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec2 _vertexPosition;
out vec2 _uv;
void main() {
gl_Position = vec4(_vertexPosition, 0, 1);
_uv = vec2(_vertexPosition.x * 0.5 + 0.5, _vertexPosition.y * 0.5 + 0.5);
})")
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D color_tex;
in vec2 _uv;
out vec3 _out_color;
void main() {
vec3 color = texture(color_tex, _uv).rgb;
// TODO: expose threshold
_out_color = max(vec3(0.0), color - vec3(1.0));
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,39 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
namespace MM::OpenGL::RenderTasks {
class BloomExtraction : public RenderTask {
private:
std::shared_ptr<Shader> _shader;
std::unique_ptr<Buffer> _vertexBuffer;
std::unique_ptr<VertexArrayObject> _vao;
public:
BloomExtraction(Engine& engine);
~BloomExtraction(void);
const char* name(void) override { return "BloomExtraction"; }
void render(Services::OpenGLRenderer& rs, Engine& engine) override;
public:
const char* vertexPath {"shader/bloom_extraction_render_task/vert.glsl"};
const char* fragmentPath {"shader/bloom_extraction_render_task/frag.glsl"};
std::string target_fbo {"display"};
std::string src_tex {"hdr_color"};
void reloadShaders(Engine& engine);
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -36,10 +36,8 @@ Blur::Blur(Engine& engine) {
_vao->unbind();
setupShaderFiles();
_hShader = Shader::createF(engine, vertexPath, fragmentHPath);
assert(_hShader != nullptr);
_vShader = Shader::createF(engine, vertexPath, fragmentVPath);
assert(_vShader != nullptr);
_shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
Blur::~Blur(void) {
@ -48,47 +46,44 @@ Blur::~Blur(void) {
void Blur::render(Services::OpenGLRenderer& rs, Engine&) {
ZoneScopedN("MM::OpenGL::RenderTasks::Blur::render");
rs.targets[io_fbo]->bind(FrameBufferObject::W);
//rs.targets[io_fbo]->bind(FrameBufferObject::W);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND); // TODO: test
glDisable(GL_BLEND);
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto tex_io = rm_t.get(entt::hashed_string::value(io_tex.c_str())); // TODO: perf problems
auto tex_in = rm_t.get(entt::hashed_string::value(in_tex.c_str())); // TODO: perf problems
auto tex_out = rm_t.get(entt::hashed_string::value(out_tex.c_str())); // TODO: perf problems
auto tex_temp = rm_t.get(entt::hashed_string::value(temp_tex.c_str())); // TODO: perf problems
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
_shader->setUniform2f("tex_offset_factor", tex_offset_factor);
{ // horizontal
rs.targets[temp_fbo]->bind(FrameBufferObject::W);
_hShader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
glViewport(0, 0, tex_temp->width, tex_temp->height);
tex_io->bind(0); // read
tex_in->bind(0); // read
_hShader->setUniform2f("tex_offset", tex_offset);
_shader->setUniform1i("horizontal", 1);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
{ // vertical
rs.targets[io_fbo]->bind(FrameBufferObject::W);
rs.targets[out_fbo]->bind(FrameBufferObject::W);
_vShader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
glViewport(0, 0, tex_io->width, tex_io->height);
glViewport(0, 0, tex_out->width, tex_out->height);
tex_temp->bind(0); // read
_vShader->setUniform2f("tex_offset", tex_offset);
_shader->setUniform1i("horizontal", 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vShader->unbind();
}
void Blur::setupShaderFiles(void) {
@ -107,9 +102,7 @@ void main() {
_tex_uv = _vertexPosition * 0.5 + 0.5;
})")
// TODO: deduplicate
FS_CONST_MOUNT_FILE(fragmentHPath,
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
@ -119,54 +112,19 @@ R"(
uniform sampler2D tex0;
in vec2 _tex_uv;
//uniform bool horizontal;
const bool horizontal = true;
uniform vec2 tex_offset;
uniform bool horizontal;
//const bool horizontal = true;
uniform vec2 tex_offset_factor;
const float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
out vec4 _out_color;
void main() {
//vec2 tex_offset = vec2(1.0) / vec2(textureSize(tex0, 0)); // gets size of single texel
vec3 result = texture(tex0, _tex_uv).rgb * weight[0]; // current fragment's contribution
if (horizontal) {
for (int i = 1; i < 5; i++) {
result += texture(tex0, _tex_uv + vec2(tex_offset.x * float(i), 0.0)).rgb * weight[i];
result += texture(tex0, _tex_uv - vec2(tex_offset.x * float(i), 0.0)).rgb * weight[i];
}
} else {
for (int i = 1; i < 5; i++) {
result += texture(tex0, _tex_uv + vec2(0.0, tex_offset.y * float(i))).rgb * weight[i];
result += texture(tex0, _tex_uv - vec2(0.0, tex_offset.y * float(i))).rgb * weight[i];
}
}
_out_color = vec4(result, 1.0);
})")
FS_CONST_MOUNT_FILE(fragmentVPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D tex0;
in vec2 _tex_uv;
//uniform bool horizontal;
const bool horizontal = false;
uniform vec2 tex_offset;
const float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
out vec4 _out_color;
void main() {
//vec2 tex_offset = vec2(1.0) / vec2(textureSize(tex0, 0)); // gets size of single texel
vec3 result = texture(tex0, _tex_uv).rgb * weight[0]; // current fragment's contribution
vec2 tex_offset = vec2(1.0) / vec2(textureSize(tex0, 0).xy); // gets size of single texel
tex_offset *= tex_offset_factor;
if (horizontal) {
for (int i = 1; i < 5; i++) {

View File

@ -17,8 +17,7 @@ namespace MM::OpenGL::RenderTasks {
// this task expects to read and write to textures
class Blur : public RenderTask {
private:
std::shared_ptr<Shader> _hShader;
std::shared_ptr<Shader> _vShader;
std::shared_ptr<Shader> _shader;
std::unique_ptr<Buffer> _vertexBuffer;
std::unique_ptr<VertexArrayObject> _vao;
@ -31,19 +30,19 @@ namespace MM::OpenGL::RenderTasks {
void render(Services::OpenGLRenderer& rs, Engine& engine) override;
public:
const char* vertexPath = "shader/blur_render_task/vert.glsl";
const char* fragmentHPath = "shader/blur_render_task/frag_h.glsl";
const char* fragmentVPath = "shader/blur_render_task/frag_v.glsl";
const char* vertexPath {"shader/blur_render_task/vert.glsl"};
const char* fragmentPath {"shader/blur_render_task/frag_h.glsl"};
std::string io_fbo = "blur_io";
std::string temp_fbo = "blur_temp";
std::string out_fbo {"blur_io"};
std::string temp_fbo {"blur_temp"};
// bc of it beeing a 2 pass, we need to flipflop
std::string io_tex = "blur_io";
std::string temp_tex = "blur_temp";
std::string in_tex {"blur_io"};
std::string out_tex {"blur_io"};
std::string temp_tex {"blur_temp"};
// determines the kernel lookup offset. "ideal" is 1/tex_size
glm::vec2 tex_offset {0.001f, 0.001f};
// kernel lookup offset factor
glm::vec2 tex_offset_factor {1.f, 1.f};
private:
void setupShaderFiles(void);

View File

@ -0,0 +1,168 @@
#include "./composition.hpp"
#include <mm/fs_const_archiver.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <mm/opengl/texture.hpp>
#include <tracy/Tracy.hpp>
namespace MM::OpenGL::RenderTasks {
Composition::Composition(MM::Engine& engine) {
float vertices[] = {
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
};
_vertexBuffer = std::make_unique<MM::OpenGL::Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<MM::OpenGL::VertexArrayObject>();
_vao->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
setupShaderFiles();
_shader = MM::OpenGL::Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
}
Composition::~Composition(void) {
}
void Composition::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
ZoneScopedN("RenderTasks::Composition::render");
rs.targets[target_fbo]->bind(MM::OpenGL::FrameBufferObject::W);
{ // TODO: move to fbo
GLsizei width {0};
GLsizei height {0};
{ // get size of fb <.<
auto& fbo_tex_arr = rs.targets[target_fbo]->_texAttachments;
if (fbo_tex_arr.empty()) {
//GLint o_type {0};
//glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &o_type);
//if (o_type == GL_NONE) {
//// default framebuffer or error
//SPDLOG_INFO("gl none");
//}
// nah, just assume screen res
std::tie(width, height) = engine.getService<MM::Services::SDLService>().getWindowSize();
} else {
auto& target_fbo_tex = rs.targets[target_fbo]->_texAttachments.front();
width = target_fbo_tex->width;
height = target_fbo_tex->height;
}
}
glViewport(0, 0, width, height);
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
_shader->bind();
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& rm = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto tex_a = rm.get(entt::hashed_string::value(color_tex.c_str()));
tex_a->bind(0);
auto tex_n = rm.get(entt::hashed_string::value(bloom_tex.c_str()));
tex_n->bind(1);
// assign image units
_shader->setUniform1i("color_tex", 0);
_shader->setUniform1i("bloom_tex", 1);
_shader->setUniform1f("bloom_factor", bloom_factor);
glDrawArrays(GL_TRIANGLES, 0, 6);
_vao->unbind();
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_shader->unbind();
}
void Composition::reloadShaders(MM::Engine& engine) {
auto tmp_shader = MM::OpenGL::Shader::createF(engine, vertexPath, fragmentPath);
if (tmp_shader) {
_shader = tmp_shader;
}
}
void Composition::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(vertexPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec2 _vertexPosition;
out vec2 _uv;
void main() {
gl_Position = vec4(_vertexPosition, 0, 1);
_uv = vec2(_vertexPosition.x * 0.5 + 0.5, _vertexPosition.y * 0.5 + 0.5);
})")
FS_CONST_MOUNT_FILE(fragmentPath,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
#include "/shaders/builtin/tonemapping.glsl"
#include "/shaders/builtin/hashing.glsl"
uniform sampler2D color_tex;
uniform sampler2D bloom_tex;
// high bc 32bit on cpu
uniform highp float bloom_factor;
in vec2 _uv;
out vec3 _out_color;
void main() {
vec3 color = texture(color_tex, _uv).rgb;
vec3 bloom = texture(bloom_tex, _uv).rgb;
vec3 comp = color + bloom * vec3(bloom_factor);
#if 0
const vec3 tint = vec3(1.5, 0.8, 1.1);
comp *= tint;
#endif
//// reinhart + gamma
//_out_color = pow(comp / (comp + vec3(1.0)), vec3(1.0 / 2.2));
// makes more sense pre bloom
//comp *= vec3(pow(2.0, -1.0)); // pre expose, -1 fstops
//_out_color = tonemapReinhard(comp);
_out_color = tonemapACESFilm(comp); // looks right
//_out_color = pow(tonemapACESFilm(pow(comp, vec3(2.2))), vec3(1.0/2.2)); // insane saturation o.O
//_out_color = pow(tonemapACESFilm(comp), vec3(1.0/2.2)); // looks just wrong
// noise dither for 8bit displays
_out_color += (hash32(gl_FragCoord.xy) * vec3(2.0) - vec3(1.0)) / vec3(255.0);
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,42 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
namespace MM::OpenGL::RenderTasks {
class Composition : public MM::OpenGL::RenderTask {
private:
std::shared_ptr<MM::OpenGL::Shader> _shader;
std::unique_ptr<MM::OpenGL::Buffer> _vertexBuffer;
std::unique_ptr<MM::OpenGL::VertexArrayObject> _vao;
public:
Composition(MM::Engine& engine);
~Composition(void);
const char* name(void) override { return "Composition"; }
void render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) override;
public:
const char* vertexPath {"shader/composition_render_task/vert.glsl"};
const char* fragmentPath {"shader/composition_render_task/frag.glsl"};
std::string target_fbo {"display"};
std::string color_tex {"hdr_color"};
std::string bloom_tex {"bloom"};
float bloom_factor {1.f};
void reloadShaders(MM::Engine& engine);
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -47,7 +47,6 @@ FastSky::FastSky(MM::Engine& engine) {
_vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind();
}
FastSky::~FastSky(void) {
@ -56,6 +55,17 @@ FastSky::~FastSky(void) {
void FastSky::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
ZoneScopedN("MM::OpenGL::RenderTasks::FastSky::render");
auto* ssi = engine.tryService<MM::Services::SceneServiceInterface>();
if (ssi == nullptr) {
return; // nothing to draw
}
auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
rs.targets[target_fbo]->bind(MM::OpenGL::FrameBufferObject::W);
glEnable(GL_DEPTH_TEST);
@ -67,9 +77,8 @@ void FastSky::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
{
auto& cam = scene.ctx<MM::OpenGL::Camera3D>();
auto& cam = scene.ctx().at<MM::OpenGL::Camera3D>();
MM::OpenGL::Camera3D tmp_cam = cam;
// create cam with y up, bc shader says so
tmp_cam.up = {0, 1, 0};
@ -80,9 +89,13 @@ void FastSky::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
}
{
auto* ctx_ptr = scene.try_ctx<FastSkyContext>();
if (!ctx_ptr) {
ctx_ptr = &_default_context;
//auto* ctx_ptr = scene.try_ctx<FastSkyContext>();
//if (!ctx_ptr) {
//ctx_ptr = &_default_context;
//}
auto* ctx_ptr = &_default_context;
if (scene.ctx().contains<FastSkyContext>()) {
ctx_ptr = &scene.ctx().at<FastSkyContext>();
}
_shader->setUniform1f("time", ctx_ptr->time);

View File

@ -0,0 +1,785 @@
#include "./lite_particles2d.hpp"
#include <mm/opengl/shader.hpp>
#include <mm/opengl/shader_builder.hpp>
#include <mm/opengl/buffer.hpp>
#include <mm/opengl/instance_buffer.hpp>
#include <mm/opengl/vertex_array_object.hpp>
#include <mm/fs_const_archiver.hpp>
#include <mm/services/scene_service_interface.hpp>
#include <entt/entity/registry.hpp>
#include <mm/components/time_delta.hpp>
#include <mm/opengl/camera_3d.hpp>
#include "../components/lite_particles2d.hpp"
#include <mm/resource_manager.hpp>
#include "../lite_particles2d_type.hpp"
#include <mm/opengl/texture.hpp>
#include <mm/opengl/texture_loader.hpp>
#include <glm/common.hpp>
#include <tracy/Tracy.hpp>
#ifndef MM_OPENGL_3_GLES
#include <tracy/TracyOpenGL.hpp>
#else
#define TracyGpuContext
#define TracyGpuCollect
#define TracyGpuZone(...)
#endif
#include <mm/logger.hpp>
#include <vector>
namespace MM::OpenGL::RenderTasks {
// this was glsl code
// TODO: refactor this
// packing helper
static uint16_t pack_float_16bit(const float a, const float pack_mult) {
uint32_t a_bits = uint32_t(abs(a*pack_mult));
return (a_bits & 0x7fffu) | (uint16_t(a>=0.0 ? 0u : 1u) << 15u);
}
const float pack_vel_multiplier = 1000.0;
static float pack_vel(const glm::vec2 vel) {
// ez just mult and save as shorts
return glm::uintBitsToFloat(
(pack_float_16bit(vel.x, pack_vel_multiplier) << 16u)
| pack_float_16bit(vel.y, pack_vel_multiplier)
);
}
const float pack_age_multiplier = 10000.0;
static float pack_age_type(const float age, const uint16_t type) {
return glm::uintBitsToFloat((pack_float_16bit(age, pack_age_multiplier) << 16u) | (type & 0xffffu));
}
LiteParticles2D::LiteParticles2D(Engine& engine) {
_particles_0_buffers[0] = std::make_unique<InstanceBuffer<glm::vec4>>();
_particles_0_buffers[1] = std::make_unique<InstanceBuffer<glm::vec4>>();
resetBuffers();
_tf_vao[0] = std::make_unique<VertexArrayObject>();
_tf_vao[1] = std::make_unique<VertexArrayObject>();
auto setup_tf_vao = [this](size_t i) {
_tf_vao[i]->bind();
_particles_0_buffers[i]->bind();
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
_particles_0_buffers[i]->unbind();
glEnableVertexAttribArray(0);
_tf_vao[i]->unbind();
};
setup_tf_vao(0);
setup_tf_vao(1);
// rendering
_render_vao[0] = std::make_unique<VertexArrayObject>();
_render_vao[1] = std::make_unique<VertexArrayObject>();
auto setup_rend_vao = [this](size_t i) {
size_t next_index = i == 0 ? 1 : 0;
_render_vao[i]->bind();
// bc we computed into the other one 0->1
_particles_0_buffers[next_index]->bind();
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
_particles_0_buffers[next_index]->unbind();
glEnableVertexAttribArray(0);
_render_vao[i]->unbind();
};
setup_rend_vao(0);
setup_rend_vao(1);
resetTypeBuffers();
setupShaderFiles();
_tf_shader = ShaderBuilder::start()
.addStageVertexF(engine, vertexPathTF)
.addTransformFeedbackVarying("_out_0")
.addStageFragmentF(engine, fragmentPathTF) // empty stage
.finish();
assert(static_cast<bool>(_tf_shader));
_points_shader = ShaderBuilder::start()
.addStageVertexF(engine, vertexPathPoints)
.addStageFragmentF(engine, fragmentPathPoints)
.finish();
assert(static_cast<bool>(_points_shader));
_last_time = clock::now();
}
LiteParticles2D::~LiteParticles2D(void) {
}
void LiteParticles2D::uploadParticles(Services::OpenGLRenderer&, Scene& scene) {
ZoneScopedN("MM::OpenGL::RenderTasks::LiteParticles2D::uploadParticles");
if (!scene.ctx().contains<Components::LiteParticles2DUploadQueue>()) {
// TODO: warn?
return; // nothing to upload
}
auto& queue = scene.ctx().at<Components::LiteParticles2DUploadQueue>();
while (!queue.queue.empty()) {
// get range
auto range_size = queue.queue.size();
size_t range_first = _particle_buffer_next_index;
if (_particle_buffer_next_index + (range_size - 1) >= _particle_buffer_size) { // wrap
range_size = _particle_buffer_size - range_first;
}
//std::cerr << "s:" << range_size << " f:" << range_first << "\n";
//assert(range_first <= range_last);
assert(range_size >= 1);
size_t curr_buff_index = first_particles_buffer ? 0 : 1;
#ifdef MM_OPENGL_3_GLES
// i think invalidating the whole buffer, instead of only the range results in undefined behaviour, but webgl wont let me otherwise and it seems to work
const auto gl_access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
#else
// turns out, amd desktop opengl drivers dont like invalidate buffer (everyone else seems to be ok)
const auto gl_access = GL_MAP_WRITE_BIT;
#endif
auto* data_0 = _particles_0_buffers[curr_buff_index]->mapRange(range_first, range_size, gl_access);
for (size_t i = 0; i < range_size; i++) {
auto& p = queue.queue.front();
uint16_t type = 0u;
if (_type_map.count(p.type_id)) {
type = _type_map.at(p.type_id);
} else {
SPDLOG_ERROR("unknown type {}, defaulting to zero.", p.type_id);
}
data_0[i] = glm::vec4(
p.pos.x, p.pos.y,
pack_vel(p.vel),
pack_age_type(p.age, type)
);
queue.queue.pop();
}
_particles_0_buffers[curr_buff_index]->unmap();
_particle_buffer_next_index = (range_first + range_size) % (_particle_buffer_size);
}
}
void LiteParticles2D::computeParticles(Services::OpenGLRenderer&, Scene& scene) {
ZoneScopedN("MM::OpenGL::RenderTasks::LiteParticles2D::computeParticles");
using namespace entt::literals;
_tf_shader->bind();
{ // time
auto newNow = clock::now();
std::chrono::duration<double, std::ratio<1, 1>> deltaTime = newNow - _last_time;
_last_time = newNow;
float time_delta = deltaTime.count() * scene.ctx().at<MM::Components::TimeDelta>().deltaFactor;
_time += time_delta;
_tf_shader->setUniform1f("_time_delta", time_delta);
_tf_shader->setUniform1f("_time", _time);
}
auto& rm_t = MM::ResourceManager<Texture>::ref();
rm_t.get("MM::LiteParticles2DTypes::Compute"_hs)->bind(0);
//_tf_shader->setUniform3f("_env_vec", env_vec * env_force);
//_tf_shader->setUniform1f("_noise_force", noise_force);
//_tf_shader->setUniform1f("_dampening", dampening);
const size_t curr_index = first_particles_buffer ? 0 : 1;
const size_t next_index = first_particles_buffer ? 1 : 0;
// bind in particles
_tf_vao[curr_index]->bind();
// bind out particles
// the order is the same as given to the ShaderBuilder
_particles_0_buffers[next_index]->bindBase(0);
glEnable(GL_RASTERIZER_DISCARD); // compute only rn
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, _particle_buffer_size);
glEndTransformFeedback();
glDisable(GL_RASTERIZER_DISCARD);
// TODO: move this
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
//glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, 0);
_tf_vao[curr_index]->unbind();
_tf_shader->unbind();
// TODO: do i need this??
glFlush();
first_particles_buffer = !first_particles_buffer;
}
void LiteParticles2D::renderParticles(Services::OpenGLRenderer& rs, Scene& scene) {
using namespace entt::literals;
ZoneScopedN("MM::OpenGL::RenderTasks::LiteParticles2D::renderParticles");
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
//glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); // dont write to depth
//glEnable(GL_BLEND);
#ifndef MM_OPENGL_3_GLES
glEnable(GL_PROGRAM_POINT_SIZE);
#endif
rs.targets[target_fbo]->bind(FrameBufferObject::RW);
_points_shader->bind();
auto& rm_t = MM::ResourceManager<Texture>::ref();
rm_t.get("MM::LiteParticles2DTypes::Render"_hs)->bind(0);
Camera3D& cam = scene.ctx().at<Camera3D>();
_points_shader->setUniformMat4f("_vp", cam.getViewProjection());
GLint view_port[4];
glGetIntegerv(GL_VIEWPORT, view_port);
// width
_points_shader->setUniform1f("_point_size", view_port[2]/cam.horizontalViewPortSize);
const size_t curr_index = first_particles_buffer; // 0->1 / 1->0 bc compute phase also switches
_render_vao[curr_index]->bind();
glDrawArrays(GL_POINTS, 0, _particle_buffer_size);
_render_vao[curr_index]->unbind();
_points_shader->unbind();
glDepthMask(GL_TRUE);
}
void LiteParticles2D::render(Services::OpenGLRenderer& rs, Engine& engine) {
ZoneScopedN("MM::OpenGL::RenderTasks::LiteParticles2D::render");
auto* scene_service = engine.tryService<MM::Services::SceneServiceInterface>();
if (scene_service == nullptr) {
return;
}
Scene& scene = scene_service->getScene();
uploadParticles(rs, scene);
computeParticles(rs, scene);
renderParticles(rs, scene);
}
void LiteParticles2D::setParticleBufferSize(uint32_t new_buffer_size) {
_particle_buffer_size = new_buffer_size;
resetBuffers();
}
void LiteParticles2D::resetBuffers(void) {
const auto gl_buffer_type = GL_DYNAMIC_COPY;
auto reset_buffer_0 = [this](size_t i) {
auto* data = _particles_0_buffers[i]->map(_particle_buffer_size, gl_buffer_type);
for (size_t x = 0; x < _particle_buffer_size; x++) {
data[x] = glm::vec4{
0.f, 0.f, // pos
0, // vel (pack, but 0 should mean 0)
pack_age_type(2.f, 0)
};
}
_particles_0_buffers[i]->unmap();
};
reset_buffer_0(0);
reset_buffer_0(1);
_particle_buffer_next_index = 0; // only matters if resize
}
void LiteParticles2D::resetTypeBuffers(void) {
using namespace entt::literals;
// clear
_type_map.clear();
// build data
std::vector<float> type_compute_upload_buffer {};
std::vector<float> type_render_upload_buffer {};
// TODO: replace with "default"
{ // 0
// TODO: propper 0 particle
LiteParticles2DType {
{
0.04f, //age_delta;
{0.f, 0.f}, //force_vec
5.f, //turbulence;
0.1f, //turbulence_noise_scale;
0.01f, //turbulence_individuality;
0.5f, //turbulence_time_scale;
1.0f, //dampening;
},
{
{1.8f, 3.f, 6.f, 1.f}, //color_start;
{2.3f, 1.2f, 0.5f, 1.f}, //color_end;
0.1f, //size_start;
0.02f //size_end;
}
}.upload(type_compute_upload_buffer, type_render_upload_buffer);
}
// defaults
_type_map[0u] = 0u;
_type_map[""_hs] = 0u;
_type_map["0"_hs] = 0u;
_type_map["default"_hs] = 0u;
auto& rm_lpt = MM::ResourceManager<LiteParticles2DType>::ref();
uint16_t curr_idx = 1; // 0 is reserved for now :P
rm_lpt.each([this, &curr_idx, &type_compute_upload_buffer, &type_render_upload_buffer](const auto type_id, const auto& res) {
_type_map[type_id] = curr_idx++;
res->upload(type_compute_upload_buffer, type_render_upload_buffer);
});
SPDLOG_INFO("have {} LiteParticles2D types.", curr_idx);
// upload / create textures
// HACK: refactor loader
auto& rm_t = MM::ResourceManager<Texture>::ref();
{ // compute
auto r = rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"MM::LiteParticles2DTypes::Compute",
GL_R32F,
LiteParticles2DType::Compute::_member_count, curr_idx,
GL_RED,
GL_FLOAT
);
assert(r);
// and re-"create"
rm_t.get("MM::LiteParticles2DTypes::Compute"_hs)->bind(0); // hack
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_R32F,
LiteParticles2DType::Compute::_member_count, curr_idx,
0,
GL_RED,
GL_FLOAT,
type_compute_upload_buffer.data()
);
// just in case
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
{ // render
auto r = rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"MM::LiteParticles2DTypes::Render",
GL_R32F,
LiteParticles2DType::Render::_member_count, curr_idx,
GL_RED,
GL_FLOAT
);
assert(r);
// and re-"create"
rm_t.get("MM::LiteParticles2DTypes::Render"_hs)->bind(0); // hack
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_R32F,
LiteParticles2DType::Render::_member_count, curr_idx,
0,
GL_RED,
GL_FLOAT,
type_render_upload_buffer.data()
);
// just in case
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}
void LiteParticles2D::setupShaderFiles(void) {
FS_CONST_MOUNT_FILE(commonPathTF,
R"(
#include "/shaders/builtin/noise.glsl"
// variants, based on offset, might be faster
vec2 var_noise22(vec2 x) {
return vec2(noise12(x), noise12(x + 13037.0));
}
vec2 var_noise23(vec3 x) {
return vec2(noise13(x), noise13(x + 13037.0));
}
vec2 var_noise24(vec4 x) {
return vec2(noise14(x), noise14(x + 13037.0));
}
// packing helper
uint pack_float_16bit(in float a, in float pack_mult) {
uint a_bits = uint(abs(a*pack_mult));
return (a_bits & 0x7fffu) | (uint(a>=0.0 ? 0u : 1u) << 15u);
}
float unpack_float_16bit(in uint a, in float pack_mult) {
return (float(a & 0x7fffu) / pack_mult) * ((a >> 15u) != 0u ? -1.0 : 1.0);
}
const float pack_vel_multiplier = 1000.0;
float pack_vel(in vec2 vel) {
return uintBitsToFloat(
(pack_float_16bit(vel.x, pack_vel_multiplier) << 16u)
| pack_float_16bit(vel.y, pack_vel_multiplier)
);
}
vec2 unpack_vel(in float pack) {
uint pack_bits = floatBitsToUint(pack);
return vec2(
unpack_float_16bit(pack_bits >> 16u, pack_vel_multiplier),
unpack_float_16bit(pack_bits & 0xffffu, pack_vel_multiplier)
);
}
const float pack_age_multiplier = 10000.0;
float pack_age_type(in float age, in uint type) {
return uintBitsToFloat(
(pack_float_16bit(age, pack_age_multiplier) << 16u)
| (type & 0xffffu)
);
}
)")
FS_CONST_MOUNT_FILE(vertexPathTF,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#include "./common.glsl"
uniform float _time_delta;
uniform float _time;
uniform sampler2D _type_buffer;
layout(location = 0) in vec4 _in_0;
out vec4 _out_0;
void set_out(in vec2 pos, in vec2 vel, in float age, in uint type) {
_out_0 = vec4(
pos.x, pos.y,
pack_vel(vel),
pack_age_type(age, type)
);
}
void get_in(out vec2 pos, out vec2 vel, out float age, out uint type) {
pos = _in_0.xy;
vel = unpack_vel(_in_0.z);
uint age_type_bits = floatBitsToUint(_in_0.w);
age = unpack_float_16bit(age_type_bits >> 16u, pack_age_multiplier);
type = age_type_bits & 0xffffu;
}
struct sim_config_type {
highp float age_delta;
//highp float gravity;
vec2 force_vec;
highp float turbulence;
highp float turbulence_noise_scale;
highp float turbulence_individuality;
highp float turbulence_time_scale;
highp float dampening;
};
vec2 fetch_vec2(in uint offset, in uint type) {
return vec2(
texelFetch(_type_buffer, ivec2(offset+0u, type), 0).r,
texelFetch(_type_buffer, ivec2(offset+1u, type), 0).r
);
}
sim_config_type fetch_type_config(uint type) {
return sim_config_type(
texelFetch(_type_buffer, ivec2(0u, type), 0).r,
fetch_vec2(1u, type),
texelFetch(_type_buffer, ivec2(3u, type), 0).r,
texelFetch(_type_buffer, ivec2(4u, type), 0).r,
texelFetch(_type_buffer, ivec2(5u, type), 0).r,
texelFetch(_type_buffer, ivec2(6u, type), 0).r,
texelFetch(_type_buffer, ivec2(7u, type), 0).r
);
}
vec2 calc_turbulence_vec(
in vec2 pos,
in float turbulence,
in float turbulence_noise_scale,
in float turbulence_individuality,
in float turbulence_time_scale
) {
vec2 turbulence_vec =
(
noise24(vec4(
pos * turbulence_noise_scale,
float(gl_VertexID & 0x0fff) * turbulence_individuality,
_time * turbulence_time_scale
))
- vec2(0.5)
)
* 2.0
* turbulence
;
return turbulence_vec;
}
void calc_particle_sim(
// inout (particle mem)
inout vec2 pos,
inout vec2 vel,
inout float age,
inout uint type,
// type specific config
in sim_config_type conf
) {
vec2 turbulence_vec = calc_turbulence_vec(
pos,
conf.turbulence,
conf.turbulence_noise_scale,
conf.turbulence_individuality,
conf.turbulence_time_scale
);
vel = (vel + (
turbulence_vec +
//vec2(0.0, -10.0) * conf.gravity
conf.force_vec
) * _time_delta);
// TODO: this does not behave correctly
// TODO: experiment with formula
vel = mix(vel, vec2(0.0), conf.dampening * _time_delta);
pos = pos + (vel * _time_delta);
age = min(age + (conf.age_delta * _time_delta), 2.0);
}
void main() {
vec2 pos;
vec2 vel;
float age;
uint type;
get_in(pos, vel, age, type);
sim_config_type conf = fetch_type_config(type);
calc_particle_sim(
pos,
vel,
age,
type,
conf
);
set_out(pos, vel, age, type);
})")
FS_CONST_MOUNT_FILE(fragmentPathTF,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
// i am never executed
out vec4 _out_color;
void main() {
_out_color = vec4(1.0);
})")
FS_CONST_MOUNT_FILE(vertexPathPoints,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#include "./common.glsl"
uniform mat4 _vp;
uniform float _point_size;
uniform sampler2D _type_buffer;
layout(location = 0) in vec4 _in_0;
void get_in(out vec2 pos, out vec2 vel, out float age, out uint type) {
pos = _in_0.xy;
vel = unpack_vel(_in_0.z);
uint age_type_bits = floatBitsToUint(_in_0.w);
age = unpack_float_16bit(age_type_bits >> 16u, pack_age_multiplier);
type = age_type_bits & 0xffffu;
}
out vec4 _frag_color;
flat out int _vertex_id;
struct config_type {
highp vec4 color_start;
highp vec4 color_end;
highp float size_start;
highp float size_end;
};
vec4 fetch_vec4(in uint offset, in uint type) {
return vec4(
texelFetch(_type_buffer, ivec2(offset+0u, type), 0).r,
texelFetch(_type_buffer, ivec2(offset+1u, type), 0).r,
texelFetch(_type_buffer, ivec2(offset+2u, type), 0).r,
texelFetch(_type_buffer, ivec2(offset+3u, type), 0).r
);
}
config_type fetch_config_type(uint type) {
return config_type(
fetch_vec4(0u, type),
fetch_vec4(4u, type),
texelFetch(_type_buffer, ivec2(8u, type), 0).r,
texelFetch(_type_buffer, ivec2(9u, type), 0).r
);
}
void main() {
vec2 in_pos;
vec2 in_vel;
float in_age;
uint in_type;
get_in(in_pos, in_vel, in_age, in_type);
// skip dead
if (in_age > 1.0) {
_frag_color = vec4(1.0, 0.0, 1.0, 0.0); // so it does not get rendered (alpha)
gl_Position.z = 100000000000.0; // clip (does not allways work <.<)
// gl_PointSize = 0.0; // ub
return;
}
config_type conf = fetch_config_type(in_type);
//_frag_color = mix(conf.color_start, conf.color_end, in_age);
_frag_color = mix(conf.color_start, conf.color_end, smoothstep(0.0, 1.0, in_age));
gl_Position = _vp * vec4(in_pos, 0.0, 1.0);
// TODO: fix before merge
gl_Position.z = -1.0; // hack for ortho ??
gl_PointSize = max(mix(conf.size_start, conf.size_end, in_age) * _point_size, 1.0);
_vertex_id = gl_VertexID;
})")
FS_CONST_MOUNT_FILE(fragmentPathPoints,
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
in vec4 _frag_color;
flat in int _vertex_id;
out vec4 _out_color;
bool should_discard(vec2 screen_pos, float alpha) {
const int dither[8 * 8] = int[8 * 8](
0, 32, 8, 40, 2, 34, 10, 42,
48, 16, 56, 24, 50, 18, 58, 26,
12, 44, 4, 36, 14, 46, 6, 38,
60, 28, 52, 20, 62, 30, 54, 22,
3, 35, 11, 43, 1, 33, 9, 41,
51, 19, 59, 27, 49, 17, 57, 25,
15, 47, 7, 39, 13, 45, 5, 37,
63, 31, 55, 23, 61, 29, 53, 21
);
int mat_value = dither[int(screen_pos.x)%8 + 8 * (int(screen_pos.y)%8)];
float n_mat_value = float(mat_value) * (1.0/64.0);
return n_mat_value < 1.0 - alpha;
}
void main() {
if (should_discard(vec2(gl_FragCoord.x + float(_vertex_id%8), gl_FragCoord.y + float(_vertex_id%7)), _frag_color.a)) {
discard;
}
_out_color = vec4(_frag_color.rgb, 1.0);
})")
}
} // MM::OpenGL::RenderTasks

View File

@ -0,0 +1,83 @@
#pragma once
#include <mm/opengl/render_task.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <glm/fwd.hpp>
// TODO: make the renderer provide a time delta
#include <chrono>
#include <string>
#include <unordered_map>
// fwd
namespace MM::OpenGL {
class Shader;
class Buffer;
class VertexArrayObject;
template<typename TInstance>
class InstanceBuffer;
}
namespace MM::OpenGL::RenderTasks {
class LiteParticles2D : public RenderTask {
private:
std::shared_ptr<Shader> _tf_shader;
std::shared_ptr<Shader> _points_shader;
std::unordered_map<uint32_t, uint16_t> _type_map {}; // maps id to "index" in type texture
// double buffered
bool first_particles_buffer {true};
std::unique_ptr<VertexArrayObject> _tf_vao[2];
std::unique_ptr<VertexArrayObject> _render_vao[2];
// vec2 pos (2 floats)
// half vec2 vel (1 float)
// fixedpoint age + uint16 type (1 float)
std::unique_ptr<MM::OpenGL::InstanceBuffer<glm::vec4>> _particles_0_buffers[2];
uint32_t _particle_buffer_size {10'000};
size_t _particle_buffer_next_index {0};
using clock = std::chrono::high_resolution_clock;
std::chrono::time_point<clock> _last_time;
float _time {0};
public:
// TODO: impl some environment controlled force
//glm::vec2 env_vec{0, 1};
LiteParticles2D(Engine& engine);
~LiteParticles2D(void);
const char* name(void) override { return "LiteParticles2D"; }
void uploadParticles(Services::OpenGLRenderer& rs, Scene& scene);
void computeParticles(Services::OpenGLRenderer& rs, Scene& scene);
void renderParticles(Services::OpenGLRenderer& rs, Scene& scene);
void render(Services::OpenGLRenderer& rs, Engine& engine) override;
void setParticleBufferSize(uint32_t new_buffer_size = 10'000);
void resetBuffers(void);
void resetTypeBuffers(void);
public:
const char* commonPathTF {"shader/lite_particles2d/common.glsl"};
const char* vertexPathTF {"shader/lite_particles2d/tf_vert.glsl"};
const char* fragmentPathTF {"shader/lite_particles2d/tf_frag.glsl"}; // "empty" bc of webgl (es)
const char* vertexPathPoints {"shader/lite_particles2d/point_vert.glsl"};
const char* fragmentPathPoints {"shader/lite_particles2d/point_frag.glsl"};
std::string target_fbo {"display"};
private:
void setupShaderFiles(void);
};
} // MM::OpenGL::RenderTasks

View File

@ -22,9 +22,6 @@
namespace MM::OpenGL::RenderTasks {
SimpleRect::SimpleRect(Engine& engine) {
default_cam.setOrthographic();
default_cam.updateView();
float vertices[] = {
-0.5f, 0.5f,
-0.5f, -0.5f,
@ -63,6 +60,10 @@ void SimpleRect::render(Services::OpenGLRenderer& rs, Engine& engine) {
auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
rs.targets[target_fbo]->bind(FrameBufferObject::RW);
glEnable(GL_DEPTH_TEST);
@ -71,12 +72,8 @@ void SimpleRect::render(Services::OpenGLRenderer& rs, Engine& engine) {
_shader->bind();
_vao->bind();
Camera3D* cam = scene.try_ctx<Camera3D>();
if (!cam) {
cam = &default_cam;
}
auto vp = cam->getViewProjection();
Camera3D& cam = scene.ctx().at<Camera3D>();
auto vp = cam.getViewProjection();
scene.view<const Components::Transform4x4>().each([this, &scene, &vp](entt::entity e, const auto& t) {
_shader->setUniformMat4f("_WVP", vp * t.trans);

View File

@ -5,7 +5,6 @@
#include <mm/opengl/camera_3d.hpp>
//#include <glm/fwd.hpp>
#include <glm/vec4.hpp>
// fwd
@ -26,8 +25,6 @@ namespace MM::OpenGL::RenderTasks {
public:
glm::vec4 default_color {1,1,1,1};
Camera3D default_cam;
SimpleRect(Engine& engine);
~SimpleRect(void);

View File

@ -28,9 +28,6 @@
namespace MM::OpenGL::RenderTasks {
SimpleSprite::SimpleSprite(Engine& engine) {
default_cam.setOrthographic();
default_cam.updateView();
float vertices[] = {
-0.5f, 0.5f,
-0.5f, -0.5f,
@ -69,6 +66,10 @@ void SimpleSprite::render(Services::OpenGLRenderer& rs, Engine& engine) {
auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
rs.targets[target_fbo]->bind(FrameBufferObject::W);
glEnable(GL_DEPTH_TEST);
@ -79,12 +80,8 @@ void SimpleSprite::render(Services::OpenGLRenderer& rs, Engine& engine) {
_vao->bind();
auto* cam = scene.try_ctx<Camera3D>();
if (!cam) {
cam = &default_cam;
}
auto vp = cam->getViewProjection();
Camera3D& cam = scene.ctx().at<Camera3D>();
auto vp = cam.getViewProjection();
scene.view<const Components::Transform4x4, Components::OpenGL::Texture>().each([this, &scene, &vp](entt::entity e, const auto& t, auto& tex) {
assert(tex.tex); // debug

View File

@ -26,8 +26,6 @@ namespace MM::OpenGL::RenderTasks {
public:
glm::vec4 default_color {1,1,1,1};
Camera3D default_cam;
SimpleSprite(Engine& engine);
~SimpleSprite(void);

View File

@ -30,9 +30,6 @@
namespace MM::OpenGL::RenderTasks {
SimpleSpriteSheet::SimpleSpriteSheet(Engine& engine) {
default_cam.setOrthographic();
default_cam.updateView();
float vertices[] = {
-0.5f, 0.5f,
-0.5f, -0.5f,
@ -56,11 +53,6 @@ SimpleSpriteSheet::SimpleSpriteSheet(Engine& engine) {
setupShaderFiles();
_shader = Shader::createF(engine, vertexPath, fragmentPath);
assert(_shader != nullptr);
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
if (!scene.try_ctx<Camera3D>()) {
LOG_SSSR("warn: scene has no Camera!");
}
}
SimpleSpriteSheet::~SimpleSpriteSheet(void) {
@ -76,6 +68,10 @@ void SimpleSpriteSheet::render(Services::OpenGLRenderer& rs, Engine& engine) {
auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
rs.targets[target_fbo]->bind(FrameBufferObject::W);
glEnable(GL_DEPTH_TEST);
@ -85,12 +81,8 @@ void SimpleSpriteSheet::render(Services::OpenGLRenderer& rs, Engine& engine) {
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto* cam = scene.try_ctx<Camera3D>();
if (!cam) {
cam = &default_cam;
}
auto vp = cam->getViewProjection();
Camera3D& cam = scene.ctx().at<Camera3D>();
auto vp = cam.getViewProjection();
scene.view<const Components::Transform4x4, SpriteSheetRenderable>().each([this, &scene, &vp](entt::entity e, const auto& t, auto& spr) {
assert(spr.sp.tex); // debug

View File

@ -26,8 +26,6 @@ namespace MM::OpenGL::RenderTasks {
public:
glm::vec4 default_color {1.f, 1.f, 1.f, 1.f};
Camera3D default_cam;
SimpleSpriteSheet(Engine& engine);
~SimpleSpriteSheet(void);

View File

@ -43,6 +43,12 @@ Tilemap::Tilemap(MM::Engine& engine) {
-0.5f, 0.5f,
};
// bias to avoid cracks
const float vert_offset_fact = 1.001f;
for (size_t i = 0; i < 2 * 6; i++) {
vertices[i] *= vert_offset_fact;
}
_vertexBuffer = std::make_unique<MM::OpenGL::Buffer>(vertices, 2 * 6 * sizeof(float), GL_STATIC_DRAW);
_vao = std::make_unique<MM::OpenGL::VertexArrayObject>();
_vao->bind();
@ -69,6 +75,17 @@ Tilemap::~Tilemap(void) {
void Tilemap::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
ZoneScopedN("MM::OpenGL::Renderers::TilemapRenderer::render");
auto* ssi = engine.tryService<MM::Services::SceneServiceInterface>();
if (ssi == nullptr) {
return; // nothing to draw
}
auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
rs.targets[target_fbo]->bind(MM::OpenGL::FrameBufferObject::W);
glEnable(GL_DEPTH_TEST);
@ -79,17 +96,13 @@ void Tilemap::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
_vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind();
auto& scene = engine.tryService<Services::SceneServiceInterface>()->getScene();
MM::OpenGL::Camera3D& cam = scene.ctx<MM::OpenGL::Camera3D>();
MM::OpenGL::Camera3D& cam = scene.ctx().at<MM::OpenGL::Camera3D>();
auto vp = cam.getViewProjection();
_shader->setUniform3f("_ambient_color", ambient_color);
scene.view<MM::Components::Transform4x4, OpenGL::TilemapRenderable>()
.each([&](auto, MM::Components::Transform4x4& t, OpenGL::TilemapRenderable& tilemap) {
//_shader->setUniformMat4f("_WVP", vp * t.getTransform4(tilemap.z + 500.f));
_shader->setUniformMat4f("_WVP", vp * t.trans);
// for each sprite layer

View File

@ -0,0 +1,396 @@
#include "./shaders_builtin.hpp"
#include <mm/fs_const_archiver.hpp>
namespace MM::OpenGL {
void load_builtin_shaders_fs(void) {
// ==================== sampling.glsl ==================== START
FS_CONST_MOUNT_FILE("/shaders/builtin/sampling.glsl",
R"(
#ifndef INCLUDE_BUILTIN_SAMPLING
#define INCLUDE_BUILTIN_SAMPLING
// kernel defined 9tap sampling
// samper, uv, 1.0/textureSize(), kernel, sum of kernel
vec3 sampling2D_kernel3x3_vec3(in sampler2D tex, in vec2 pos, in vec2 dir_step, in mat3x3 kernel, in float kernel_weight) {
vec3 color =
// upper row
texture(tex, pos + dir_step * vec2(-1.0, 1.0)).rgb * kernel[0][0]
+ texture(tex, pos + dir_step * vec2(0.0, 1.0)).rgb * kernel[0][1]
+ texture(tex, pos + dir_step * vec2(1.0, 1.0)).rgb * kernel[0][2]
// middle row
+ texture(tex, pos + dir_step * vec2(-1.0, 0.0)).rgb * kernel[1][0]
+ texture(tex, pos).rgb * kernel[1][1]
+ texture(tex, pos + dir_step * vec2(1.0, 0.0)).rgb * kernel[1][2]
// lower row
+ texture(tex, pos + dir_step * vec2(-1.0, -1.0)).rgb * kernel[2][0]
+ texture(tex, pos + dir_step * vec2(0.0, -1.0)).rgb * kernel[2][1]
+ texture(tex, pos + dir_step * vec2(1.0, -1.0)).rgb * kernel[2][2]
;
return color / vec3(kernel_weight);
}
// 3x3 9tap tent filter
// 1 2 1
// 2 4 2
// 1 2 1
vec3 sampling2D_tent3x3_vec3(in sampler2D tex, in vec2 pos, in vec2 dir_step) {
const mat3x3 tent_kernel = mat3x3(
vec3(1.0, 2.0, 1.0),
vec3(2.0, 4.0, 2.0),
vec3(1.0, 2.0, 1.0)
);
const float tent_kernel_weight = 16.0;
return sampling2D_kernel3x3_vec3(
tex,
pos,
dir_step,
tent_kernel,
tent_kernel_weight
);
}
#endif
)")
// ==================== sampling.glsl ==================== END
// ==================== tonemapping.glsl ==================== START
FS_CONST_MOUNT_FILE("/shaders/builtin/tonemapping.glsl",
R"(
#ifndef INCLUDE_BUILTIN_TONEMAPPING
#define INCLUDE_BUILTIN_TONEMAPPING
// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
vec3 tonemapACESFilm(vec3 x) {
const float a = 2.51;
const float b = 0.03;
const float c = 2.43;
const float d = 0.59;
const float e = 0.14;
return clamp((x*(a*x+b))/(x*(c*x+d)+e), vec3(0.0), vec3(1.0));
}
vec3 tonemapReinhard(vec3 x) {
return x / (x + vec3(1.0));
}
#endif
)")
// ==================== tonemapping.glsl ==================== END
// ==================== hashing.glsl ==================== START
FS_CONST_MOUNT_FILE("/shaders/builtin/hashing.glsl",
R"(
#ifndef INCLUDE_BUILTIN_HASHING
#define INCLUDE_BUILTIN_HASHING
// https://www.shadertoy.com/view/4djSRW
// Hash without Sine
// MIT License...
/* Copyright (c)2014 David Hoskins.
modified by Erik Scholz 2022
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
//----------------------------------------------------------------------------------------
// 1 out, 1 in...
float hash11(float p)
{
p = fract(p * .1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
//----------------------------------------------------------------------------------------
// 1 out, 2 in...
float hash12(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
//----------------------------------------------------------------------------------------
// 1 out, 3 in...
float hash13(vec3 p3)
{
p3 = fract(p3 * .1031);
p3 += dot(p3, p3.zyx + 31.32);
return fract((p3.x + p3.y) * p3.z);
}
//----------------------------------------------------------------------------------------
// TODO: test properly
// 1 out, 4 in...
float hash14(vec4 p4)
{
p4 = fract(p4 * .1031);
p4 += dot(p4, p4.zywx + 31.32);
return fract((p4.x + p4.y + p4.w) * p4.z);
}
//----------------------------------------------------------------------------------------
// 2 out, 1 in...
vec2 hash21(float p)
{
vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.xx+p3.yz)*p3.zy);
}
//----------------------------------------------------------------------------------------
// 2 out, 2 in...
vec2 hash22(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yzx+33.33);
return fract((p3.xx+p3.yz)*p3.zy);
}
//----------------------------------------------------------------------------------------
// 2 out, 3 in...
vec2 hash23(vec3 p3)
{
p3 = fract(p3 * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yzx+33.33);
return fract((p3.xx+p3.yz)*p3.zy);
}
//----------------------------------------------------------------------------------------
// TODO: test properly
// 2 out, 4 in...
vec2 hash24(vec4 p4)
{
p4 = fract(p4 * vec4(.1031, .1030, .0973, .1337));
p4 += dot(p4, p4.ywzx+33.33);
return fract((p4.wx+p4.yz)*p4.zy);
}
//----------------------------------------------------------------------------------------
// 3 out, 1 in...
vec3 hash31(float p)
{
vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yzx+33.33);
return fract((p3.xxy+p3.yzz)*p3.zyx);
}
//----------------------------------------------------------------------------------------
// 3 out, 2 in...
vec3 hash32(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yxz+33.33);
return fract((p3.xxy+p3.yzz)*p3.zyx);
}
//----------------------------------------------------------------------------------------
// 3 out, 3 in...
vec3 hash33(vec3 p3)
{
p3 = fract(p3 * vec3(.1031, .1030, .0973));
p3 += dot(p3, p3.yxz+33.33);
return fract((p3.xxy + p3.yxx)*p3.zyx);
}
//----------------------------------------------------------------------------------------
// 4 out, 1 in...
vec4 hash41(float p)
{
vec4 p4 = fract(vec4(p) * vec4(.1031, .1030, .0973, .1099));
p4 += dot(p4, p4.wzxy+33.33);
return fract((p4.xxyz+p4.yzzw)*p4.zywx);
}
//----------------------------------------------------------------------------------------
// 4 out, 2 in...
vec4 hash42(vec2 p)
{
vec4 p4 = fract(vec4(p.xyxy) * vec4(.1031, .1030, .0973, .1099));
p4 += dot(p4, p4.wzxy+33.33);
return fract((p4.xxyz+p4.yzzw)*p4.zywx);
}
//----------------------------------------------------------------------------------------
// 4 out, 3 in...
vec4 hash43(vec3 p)
{
vec4 p4 = fract(vec4(p.xyzx) * vec4(.1031, .1030, .0973, .1099));
p4 += dot(p4, p4.wzxy+33.33);
return fract((p4.xxyz+p4.yzzw)*p4.zywx);
}
//----------------------------------------------------------------------------------------
// 4 out, 4 in...
vec4 hash44(vec4 p4)
{
p4 = fract(p4 * vec4(.1031, .1030, .0973, .1099));
p4 += dot(p4, p4.wzxy+33.33);
return fract((p4.xxyz+p4.yzzw)*p4.zywx);
}
#endif
)")
// ==================== hashing.glsl ==================== END
// ==================== noise.glsl ==================== START
FS_CONST_MOUNT_FILE("/shaders/builtin/noise.glsl",
R"(
#ifndef INCLUDE_BUILTIN_NOISE
#define INCLUDE_BUILTIN_NOISE
#include "/shaders/builtin/hashing.glsl"
// value noise based on Inigo Quilez value noise shaders.
// https://www.youtube.com/c/InigoQuilez
// https://iquilezles.org/
// value noise 2d: https://www.shadertoy.com/view/lsf3WH
// value noise 3d: https://www.shadertoy.com/view/4sfGzS
float noise12(in vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix( hash12(i + vec2(0.0, 0.0)),
hash12(i + vec2(1.0, 0.0)), u.x),
mix(hash12(i + vec2(0.0, 1.0)),
hash12(i + vec2(1.0, 1.0)), u.x), u.y);
}
vec2 noise22(in vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix( hash22(i + vec2(0.0, 0.0)),
hash22(i + vec2(1.0, 0.0)), u.x),
mix(hash22(i + vec2(0.0, 1.0)),
hash22(i + vec2(1.0, 1.0)), u.x), u.y);
}
float noise13(in vec3 x) {
vec3 i = floor(x);
vec3 f = fract(x);
vec3 u = f * f * (3.0 - 2.0 * f);
return mix(mix(mix( hash13(i + vec3(0.0, 0.0, 0.0)),
hash13(i + vec3(1.0, 0.0, 0.0)), u.x),
mix(hash13(i + vec3(0.0, 1.0, 0.0)),
hash13(i + vec3(1.0, 1.0, 0.0)), u.x), u.y),
mix(mix(hash13(i + vec3(0.0, 0.0, 1.0)),
hash13(i + vec3(1.0, 0.0, 1.0)), u.x),
mix(hash13(i + vec3(0.0, 1.0, 1.0)),
hash13(i + vec3(1.0, 1.0, 1.0)), u.x), u.y), u.z);
}
float noise14(in vec4 x) {
vec4 i = floor(x);
vec4 f = fract(x);
vec4 u = f * f * (3.0 - 2.0 * f);
return
mix(mix(mix(mix(hash14(i + vec4(0.0, 0.0, 0.0, 0.0)),
hash14(i + vec4(1.0, 0.0, 0.0, 0.0)), u.x
),
mix(hash14(i + vec4(0.0, 1.0, 0.0, 0.0)),
hash14(i + vec4(1.0, 1.0, 0.0, 0.0)), u.x
), u.y
),
mix(mix(hash14(i + vec4(0.0, 0.0, 1.0, 0.0)),
hash14(i + vec4(1.0, 0.0, 1.0, 0.0)), u.x
),
mix(hash14(i + vec4(0.0, 1.0, 1.0, 0.0)),
hash14(i + vec4(1.0, 1.0, 1.0, 0.0)), u.x
), u.y
), u.z
),
mix(mix(mix(hash14(i + vec4(0.0, 0.0, 0.0, 1.0)),
hash14(i + vec4(1.0, 0.0, 0.0, 1.0)), u.x
),
mix(hash14(i + vec4(0.0, 1.0, 0.0, 1.0)),
hash14(i + vec4(1.0, 1.0, 0.0, 1.0)), u.x
), u.y
),
mix(mix(hash14(i + vec4(0.0, 0.0, 1.0, 1.0)),
hash14(i + vec4(1.0, 0.0, 1.0, 1.0)), u.x
),
mix(hash14(i + vec4(0.0, 1.0, 1.0, 1.0)),
hash14(i + vec4(1.0, 1.0, 1.0, 1.0)), u.x
), u.y
), u.z
), u.w
);
}
vec2 noise24(in vec4 x) {
vec4 i = floor(x);
vec4 f = fract(x);
vec4 u = f * f * (3.0 - 2.0 * f);
return
mix(mix(mix(mix(hash24(i + vec4(0.0, 0.0, 0.0, 0.0)),
hash24(i + vec4(1.0, 0.0, 0.0, 0.0)), u.x
),
mix(hash24(i + vec4(0.0, 1.0, 0.0, 0.0)),
hash24(i + vec4(1.0, 1.0, 0.0, 0.0)), u.x
), u.y
),
mix(mix(hash24(i + vec4(0.0, 0.0, 1.0, 0.0)),
hash24(i + vec4(1.0, 0.0, 1.0, 0.0)), u.x
),
mix(hash24(i + vec4(0.0, 1.0, 1.0, 0.0)),
hash24(i + vec4(1.0, 1.0, 1.0, 0.0)), u.x
), u.y
), u.z
),
mix(mix(mix(hash24(i + vec4(0.0, 0.0, 0.0, 1.0)),
hash24(i + vec4(1.0, 0.0, 0.0, 1.0)), u.x
),
mix(hash24(i + vec4(0.0, 1.0, 0.0, 1.0)),
hash24(i + vec4(1.0, 1.0, 0.0, 1.0)), u.x
), u.y
),
mix(mix(hash24(i + vec4(0.0, 0.0, 1.0, 1.0)),
hash24(i + vec4(1.0, 0.0, 1.0, 1.0)), u.x
),
mix(hash24(i + vec4(0.0, 1.0, 1.0, 1.0)),
hash24(i + vec4(1.0, 1.0, 1.0, 1.0)), u.x
), u.y
), u.z
), u.w
);
}
#endif
)")
// ==================== noise.glsl ==================== END
}
} // MM::OpenGL

View File

@ -0,0 +1,14 @@
#pragma once
namespace MM::OpenGL {
// loads glsl files into const fs at "/shaders/builtin/"
// file list:
// - sampling.glsl
// - tonemapping.glsl
// - hashing.glsl
// - noise.glsl
void load_builtin_shaders_fs(void);
} // MM::OpenGL

View File

@ -10,6 +10,7 @@
#include <mm/opengl/texture_loader.hpp>
#include "../opengl/res/default_texture.h" // data
#include "../opengl/res/errig_texture.h" // data
#include "../opengl/res/shaders_builtin.hpp" // data-ish
#include <tracy/Tracy.hpp>
#ifndef MM_OPENGL_3_GLES
@ -111,7 +112,7 @@ bool OpenGLRenderer::enable(Engine& engine, std::vector<UpdateStrategies::TaskIn
return false;
}
{ // default texures
{ // default textures
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
if (!rm_t.contains("default"_hs)) {
if (!rm_t.load<MM::OpenGL::TextureLoaderConstBuffer>("default", default_png, default_png_len)) {
@ -125,6 +126,10 @@ bool OpenGLRenderer::enable(Engine& engine, std::vector<UpdateStrategies::TaskIn
}
}
{ // builtin shaders
OpenGL::load_builtin_shaders_fs();
}
{ // add task
task_array.push_back(
UpdateStrategies::TaskInfo{"OpenGLRenderer::render"}

View File

@ -9,6 +9,17 @@ add_test(NAME opengl_renderer_s_test COMMAND opengl_renderer_s_test)
#################
add_executable(opengl_builtins_test ./builtins.cpp)
target_link_libraries(opengl_builtins_test
opengl_renderer_s
gtest_main
)
add_test(NAME opengl_builtins_test COMMAND opengl_builtins_test)
#################
add_executable(imgui_render_task_test imgui_render_task_test.cpp)
target_link_libraries(imgui_render_task_test
@ -149,3 +160,53 @@ target_link_libraries(fast_sky_render_task_test
add_test(NAME fast_sky_render_task_test COMMAND fast_sky_render_task_test)
################# hdr bloom example
add_executable(hdr_bloom_pipeline_example ./hdr_bloom_pipeline_example.cpp)
target_link_libraries(hdr_bloom_pipeline_example
opengl_renderer_s
organizer_scene
clear_render_task
simple_rect_render_task
#bloom_extraction_render_task
#blur_render_task
#bloom_combine_render_task
bloom
composition_render_task
simple_velocity_system
transform_system
random
gtest_main
)
add_test(NAME hdr_bloom_pipeline_example COMMAND hdr_bloom_pipeline_example)
################# lite_particles2d (the test uses bloom)
add_executable(lite_particles2d_test ./lite_particles2d.cpp)
target_link_libraries(lite_particles2d_test
opengl_renderer_s
organizer_scene
clear_render_task
#simple_rect_render_task
lite_particles2d # not only rendertask
bloom
composition_render_task
#simple_velocity_system
#transform_system
random
gtest_main
)
add_test(NAME lite_particles2d_test COMMAND lite_particles2d_test)

View File

@ -68,7 +68,7 @@ TEST(batched_spritesheet_render_task, it) {
auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>());
auto& cam = scene.set<MM::OpenGL::Camera3D>();
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.horizontalViewPortSize = 5;
cam.setOrthographic();
cam.updateView();
@ -87,8 +87,8 @@ TEST(batched_spritesheet_render_task, it) {
// setup systems
scene.set<float>(0.f); // accu
auto& org = scene.set<entt::organizer>();
scene.ctx().emplace<float>(0.f); // accu
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&update_spritesheet_animation>("update_spritesheet_animation");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -72,9 +72,10 @@ TEST(blur_render_task, it) {
bsrr_rend.target_fbo = "blur_io";
MM::OpenGL::RenderTasks::Blur& blur_rend = rs.addRenderTask<MM::OpenGL::RenderTasks::Blur>(engine);
blur_rend.io_tex = "blur_io";
blur_rend.in_tex = "blur_io";
blur_rend.out_tex = "blur_io";
blur_rend.temp_tex = "blur_temp";
blur_rend.io_fbo = "blur_io";
blur_rend.out_fbo = "blur_io";
blur_rend.temp_fbo = "blur_temp";
// render to display
@ -128,9 +129,12 @@ TEST(blur_render_task, it) {
scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Rotation2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>(); // in this example only rotation is touched
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.setOrthographic();
cam.updateView();
// setup v system
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<MM::Systems::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -0,0 +1,61 @@
#include <gtest/gtest.h>
#include <mm/engine.hpp>
#include <mm/services/filesystem.hpp>
#include <mm/services/sdl_service.hpp>
#include <mm/services/opengl_renderer.hpp>
//#include <mm/opengl/res/shaders_builtin.hpp>
#include <mm/opengl/shader.hpp>
#include <mm/opengl/shader_builder.hpp>
#include <mm/fs_const_archiver.hpp> // include only works on files rn
static const char* argv0 = "";
TEST(builtins, all) {
MM::Engine engine;
engine.addService<MM::Services::FilesystemService>(argv0);
ASSERT_TRUE(engine.enableService<MM::Services::FilesystemService>());
engine.addService<MM::Services::SDLService>();
ASSERT_TRUE(engine.enableService<MM::Services::SDLService>());
engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>()); // adds builtins
engine.update();
FS_CONST_MOUNT_FILE("/shaders/test_frag.glsl",
GLSL_VERSION_STRING
R"(
#ifdef GL_ES
precision mediump float;
#endif
#include "/shaders/builtin/sampling.glsl"
#include "/shaders/builtin/tonemapping.glsl"
#include "/shaders/builtin/hashing.glsl"
#include "/shaders/builtin/noise.glsl"
void main() {
}
)");
auto sb = MM::OpenGL::ShaderBuilder::start();
sb.addStageVertex("void main() { gl_Position = vec4(0.0); }");
sb.addStageFragmentF(engine, "/shaders/test_frag.glsl");
auto shader = sb.finish();
ASSERT_TRUE(shader);
}
int main(int argc, char** argv) {
argv0 = argv[0];
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -42,18 +42,18 @@ TEST(fast_sky_render_task, it) {
rs.addRenderTask<MM::OpenGL::RenderTasks::FastSky>(engine);
// setup systems
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&MM::Systems::fast_sky_sun>("fast_sky_sun");
// HACK: instead you would switch to this scene
engine.getService<MM::Services::OrganizerSceneService>().updateOrganizerVertices(scene);
auto& cam = scene.set<MM::OpenGL::Camera3D>();
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.setPerspective();
cam.updateView();
scene.set<MM::OpenGL::RenderTasks::FastSkyContext>();
scene.ctx().emplace<MM::OpenGL::RenderTasks::FastSkyContext>();
engine.run();
}

View File

@ -0,0 +1,254 @@
#include <gtest/gtest.h>
#include <mm/engine.hpp>
#include <mm/services/filesystem.hpp>
#include <mm/services/sdl_service.hpp>
#include <mm/services/organizer_scene.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/organizer.hpp>
#include <mm/opengl/render_tasks/clear.hpp>
#include <mm/opengl/render_tasks/blit_fb.hpp>
#include <mm/opengl/render_tasks/simple_rect.hpp>
//#include <mm/opengl/render_tasks/bloom_extraction.hpp>
//#include <mm/opengl/render_tasks/blur.hpp>
//#include <mm/opengl/render_tasks/bloom_combine.hpp>
#include <mm/opengl/bloom.hpp>
#include <mm/opengl/render_tasks/composition.hpp>
#include <mm/components/position2d.hpp>
#include <mm/components/position2d_zoffset.hpp>
#include <mm/components/scale2d.hpp>
#include <mm/components/rotation2d.hpp>
#include <mm/components/velocity2d_rotation.hpp>
#include <mm/components/position3d.hpp>
#include <mm/components/transform4x4.hpp>
#include <mm/components/color.hpp>
#include <mm/systems/simple_velocity_system2d.hpp>
#include <mm/systems/transform.hpp>
#include <mm/opengl/fbo_builder.hpp>
#include <mm/opengl/texture_loader.hpp>
#include <mm/random/srng.hpp>
using namespace entt::literals;
const char* argv0;
static void setup_textures(MM::Engine& engine) {
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto [w, h] = engine.getService<MM::Services::SDLService>().getWindowSize();
// we dont have a gbuffer in this example
{ // gbuffer
// depth
#ifdef MM_OPENGL_3_GLES
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"depth",
GL_DEPTH_COMPONENT24, // d16 is the onlyone for gles 2 (TODO: test for 3)
w, h,
GL_DEPTH_COMPONENT, GL_UNSIGNED_INT
);
#else
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"depth",
GL_DEPTH_COMPONENT32F, // TODO: stencil?
w, h,
GL_DEPTH_COMPONENT, GL_FLOAT
);
#endif
#if 0
// albedo
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"albedo",
#ifdef MM_OPENGL_3_GLES
GL_RGB565,
#else
GL_RGBA8, // waste of A
#endif
w, h,
GL_RGB, GL_UNSIGNED_BYTE
);
#endif
#if 0
// normal
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"normal",
// prolly fine, but need to remapp to -1,1
#ifdef MM_OPENGL_3_GLES
GL_RGB565,
#else
GL_RGBA8, // waste of A
#endif
w, h,
GL_RGB, GL_UNSIGNED_BYTE
);
#endif
}
const float render_scale = 1.f;
// hdr color gles3 / webgl2
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"hdr_color",
GL_RGBA16F,
w * render_scale, h * render_scale,
GL_RGBA,
GL_HALF_FLOAT
);
{ // filter
rm_t.get("hdr_color"_hs)->bind(0);
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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
static void setup_fbos(MM::Engine& engine) {
auto& rs = engine.getService<MM::Services::OpenGLRenderer>();
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
const float render_scale = 1.f;
rs.targets["game_view"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("depth"_hs), GL_DEPTH_ATTACHMENT)
.setResizeFactors(render_scale, render_scale)
.setResize(true)
.finish();
assert(rs.targets["game_view"]);
}
TEST(hdr_bloom_pipeline, it) {
MM::Engine engine;
auto& sdl_ss = engine.addService<MM::Services::SDLService>();
ASSERT_TRUE(engine.enableService<MM::Services::SDLService>());
sdl_ss.createGLWindow("hdr_bloom_pipeline_example", 1280, 720);
engine.addService<MM::Services::OrganizerSceneService>();
ASSERT_TRUE(engine.enableService<MM::Services::OrganizerSceneService>());
bool provide_ret = engine.provide<MM::Services::SceneServiceInterface, MM::Services::OrganizerSceneService>();
ASSERT_TRUE(provide_ret);
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
engine.addService<MM::Services::FilesystemService>(argv0, "hdr_bloom_pipeline_example");
ASSERT_TRUE(engine.enableService<MM::Services::FilesystemService>());
auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>());
{ // setup rendering
// TODO: split vertically
setup_textures(engine);
setup_fbos(engine);
// clear
auto& clear_opaque = rs.addRenderTask<MM::OpenGL::RenderTasks::Clear>(engine);
clear_opaque.target_fbo = "game_view";
// clears all color attachments
clear_opaque.r = 0.f;
clear_opaque.g = 0.f;
clear_opaque.b = 0.f;
clear_opaque.a = 1.f;
clear_opaque.mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
{ // render, whatever
MM::OpenGL::RenderTasks::SimpleRect& bsrr_rend = rs.addRenderTask<MM::OpenGL::RenderTasks::SimpleRect>(engine);
bsrr_rend.target_fbo = "game_view";
}
// rn does rt too
MM::OpenGL::setup_bloom(engine);
// not part of setup_bloom
auto& comp = rs.addRenderTask<MM::OpenGL::RenderTasks::Composition>(engine);
comp.color_tex = "hdr_color";
comp.bloom_tex = "blur_tmp1";
comp.target_fbo = "display";
}
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::Position2D_ZOffset>>();
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::Position3D>>();
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::Transform4x4>>();
scene.on_construct<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position2D_ZOffset>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position3D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Rotation2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>(); // in this example only rotation is touched
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.setOrthographic();
cam.updateView();
// setup v system
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<MM::Systems::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");
org.emplace<MM::Systems::transform3d_rotate2d>("transform3d_rotate2d");
org.emplace<MM::Systems::transform3d_scale2d>("transform3d_scale2d");
org.emplace<MM::Systems::transform_clear_dirty>("transform_clear_dirty");
// HACK: instead you would switch to this scene
engine.getService<MM::Services::OrganizerSceneService>().updateOrganizerVertices(scene);
MM::Random::SRNG rng{42};
for (int i = 0; i < 5; i++) {
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = 0.f;
p.pos.y = 25.f - i * 10.f;
auto& s = scene.emplace<MM::Components::Scale2D>(e);
s.scale = {50.f, i*0.2f + 0.1f};
auto& col = scene.emplace<MM::Components::Color>(e);
col.color = {3.f, 3.f, 3.f, 1.f};
}
for (int i = 0; i < 10; i++) {
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = i * 9.f - 40;
// zoffset is created by event
auto& s = scene.emplace<MM::Components::Scale2D>(e);
s.scale = {5,5};
scene.emplace<MM::Components::Rotation2D>(e);
auto& v = scene.emplace<MM::Components::Velocity2DRotation>(e);
v.rot_vel = i * 0.3f;
auto& col = scene.emplace<MM::Components::Color>(e);
col.color = {rng.zeroToOne()*2.f, rng.zeroToOne()*2.f, rng.zeroToOne()*2.f, 1.f};
}
engine.run();
}
int main(int argc, char** argv) {
argv0 = argv[0];
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,371 @@
#include <gtest/gtest.h>
#include <mm/engine.hpp>
#include <mm/services/filesystem.hpp>
#include <mm/services/sdl_service.hpp>
#include <mm/services/organizer_scene.hpp>
#include <mm/services/opengl_renderer.hpp>
#include <mm/fs_const_archiver.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/organizer.hpp>
#include <entt/core/hashed_string.hpp>
#include <mm/opengl/render_tasks/clear.hpp>
#include <mm/opengl/render_tasks/lite_particles2d.hpp>
#include <mm/opengl/bloom.hpp>
#include <mm/opengl/render_tasks/composition.hpp>
// ctx
#include <mm/opengl/components/lite_particles2d.hpp>
#include <mm/components/time_delta.hpp>
#include <mm/opengl/camera_3d.hpp>
// components
#include <mm/components/position2d.hpp>
#include <mm/resource_manager.hpp>
#include <mm/opengl/texture_loader.hpp>
#include <mm/opengl/lite_particles2d_type_loader.hpp>
#include <mm/opengl/fbo_builder.hpp>
#include <mm/random/srng.hpp>
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
#include <glm/common.hpp>
#include <glm/gtc/constants.hpp>
#include <glm/trigonometric.hpp>
#include <mm/scalar_range2.hpp>
#include <vector>
#include <string>
using namespace entt::literals;
namespace Components {
// or what ever
struct LiteParticles2DEmitter {
float age {0.f}; // this changes each tick, if >=1, spawn and reset to 0
float age_delta {1.f}; // times per sec
MM::ScalarRange2<uint16_t> particle_count {1u, 1u};
// particles data
std::string p_type {};
MM::ScalarRange2<float> p_pos_x {0.f, 0.f};
MM::ScalarRange2<float> p_pos_y {0.f, 0.f};
MM::ScalarRange2<float> p_dir {0.f, 0.f}; // 0-1, not rad
MM::ScalarRange2<float> p_dir_force {0.f, 0.f};
MM::ScalarRange2<float> p_age {0.f, 0.f};
};
} // Components
namespace Systems {
void lite_particles2d_emit(
entt::view<entt::get_t<
const MM::Components::Position2D,
Components::LiteParticles2DEmitter
>> view,
const MM::Components::TimeDelta& td,
MM::Components::LiteParticles2DUploadQueue& lp_uq,
MM::Random::SRNG& rng
) {
view.each([&lp_uq, &td, &rng](const auto& pos, Components::LiteParticles2DEmitter& lpe) {
lpe.age += lpe.age_delta * td.tickDelta;
if (lpe.age < 1.f) {
return;
}
lpe.age = 0.f; // TODO: dont discard ?
const auto type = entt::hashed_string::value(lpe.p_type.c_str());
const size_t count = rng.range(lpe.particle_count);
for (size_t i = 0; i < count; i++) {
float dir = rng.range(lpe.p_dir) * glm::two_pi<float>();
lp_uq.queue.push(MM::Components::LiteParticle2D{
type, // type
//{rng.negOneToOne() * 30.f, rng.negOneToOne() * 30.f}, // pos
pos.pos + /*lpe.pos_offset +*/ glm::vec2{rng.range(lpe.p_pos_x), rng.range(lpe.p_pos_y)}, // pos
glm::vec2{glm::cos(dir), glm::sin(dir)} * rng.range(lpe.p_dir_force), // vel
rng.range(lpe.p_age) // age
});
}
});
}
} // Systems
const char* argv0;
static void setup_textures(MM::Engine& engine) {
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
auto [w, h] = engine.getService<MM::Services::SDLService>().getWindowSize();
// we dont have a gbuffer in this example
{ // gbuffer
// depth
#ifdef MM_OPENGL_3_GLES
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"depth",
GL_DEPTH_COMPONENT24, // d16 is the onlyone for gles 2 (TODO: test for 3)
w, h,
GL_DEPTH_COMPONENT, GL_UNSIGNED_INT
);
#else
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"depth",
GL_DEPTH_COMPONENT32F, // TODO: stencil?
w, h,
GL_DEPTH_COMPONENT, GL_FLOAT
);
#endif
}
const float render_scale = 1.f;
// hdr color gles3 / webgl2
rm_t.reload<MM::OpenGL::TextureLoaderEmpty>(
"hdr_color",
GL_RGBA16F,
w * render_scale, h * render_scale,
GL_RGBA,
GL_HALF_FLOAT
);
{ // filter
rm_t.get("hdr_color"_hs)->bind(0);
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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
static void setup_fbos(MM::Engine& engine) {
auto& rs = engine.getService<MM::Services::OpenGLRenderer>();
auto& rm_t = MM::ResourceManager<MM::OpenGL::Texture>::ref();
const float render_scale = 1.f;
rs.targets["game_view"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("depth"_hs), GL_DEPTH_ATTACHMENT)
.setResizeFactors(render_scale, render_scale)
.setResize(true)
.finish();
assert(rs.targets["game_view"]);
}
void setup_particles_types(MM::Engine& engine) {
auto& lpt_rm = MM::ResourceManager<MM::OpenGL::LiteParticles2DType>::ref();
lpt_rm.load<MM::OpenGL::LiteParticles2DTypeLoaderJson>("MM::spark1",
R"({
"compute": {
"age_delta": 2.0,
"force_vec": { "x": 0.0, "y": -5.0 },
"turbulence": 1.0,
"turbulence_individuality": 1.0,
"turbulence_noise_scale": 1.0,
"turbulence_time_scale": 1.0,
"dampening": 1.0
},
"render": {
"color_start": {
"x": 6.0,
"y": 6.0,
"z": 1.8,
"w": 1.0
},
"color_end": {
"x": 1.5,
"y": 1.0,
"z": 0.3,
"w": 1.0
},
"size_start": 0.01,
"size_end": 0.002
}
})"_json);
// mount into vfx
FS_CONST_MOUNT_FILE("/particles/lite_particles2d/fire1.json",
R"({
"compute": {
"age_delta": 1.0,
"force_vec": { "x": 0.0, "y": 5.0 },
"turbulence": 5.0,
"turbulence_individuality": 1.0,
"turbulence_noise_scale": 1.0,
"turbulence_time_scale": 1.0,
"dampening": 0.0
},
"render": {
"color_start": {
"x": 3.0,
"y": 2.1,
"z": 1.5,
"w": 0.8
},
"color_end": {
"x": 3.0,
"y": 1.1,
"z": 1.0,
"w": 0.0
},
"size_start": 0.15,
"size_end": 0.4
}
})");
// and load it
lpt_rm.load<MM::OpenGL::LiteParticles2DTypeLoaderFile>("MM::fire1", engine, "/particles/lite_particles2d/fire1.json");
}
TEST(lite_particles2d, it) {
MM::Engine engine;
auto& sdl_ss = engine.addService<MM::Services::SDLService>();
ASSERT_TRUE(engine.enableService<MM::Services::SDLService>());
sdl_ss.createGLWindow("lite_particles2d", 1280, 720);
engine.addService<MM::Services::OrganizerSceneService>();
ASSERT_TRUE(engine.enableService<MM::Services::OrganizerSceneService>());
bool provide_ret = engine.provide<MM::Services::SceneServiceInterface, MM::Services::OrganizerSceneService>();
ASSERT_TRUE(provide_ret);
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
engine.addService<MM::Services::FilesystemService>(argv0, "lite_particles2d");
ASSERT_TRUE(engine.enableService<MM::Services::FilesystemService>());
auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>());
// load particle types
// before addRenderTask<LiteParticle2D>
// OR
// call LiteParticle2D::resetTypeBuffers()
setup_particles_types(engine);
{ // setup rendering
// TODO: split vertically
setup_textures(engine);
setup_fbos(engine);
// clear
auto& clear_opaque = rs.addRenderTask<MM::OpenGL::RenderTasks::Clear>(engine);
clear_opaque.target_fbo = "game_view";
// clears all color attachments
clear_opaque.r = 0.f;
clear_opaque.g = 0.f;
clear_opaque.b = 0.f;
clear_opaque.a = 1.f;
clear_opaque.mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
{ // render
//MM::OpenGL::RenderTasks::SimpleRect& bsrr_rend = rs.addRenderTask<MM::OpenGL::RenderTasks::SimpleRect>(engine);
//bsrr_rend.target_fbo = "game_view";
auto& lprt = rs.addRenderTask<MM::OpenGL::RenderTasks::LiteParticles2D>(engine);
lprt.target_fbo = "game_view";
}
// rn does rt too
MM::OpenGL::setup_bloom(engine);
// not part of setup_bloom
auto& comp = rs.addRenderTask<MM::OpenGL::RenderTasks::Composition>(engine);
comp.color_tex = "hdr_color";
comp.bloom_tex = "blur_tmp1";
comp.target_fbo = "display";
}
// setup scene
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.horizontalViewPortSize = 30.f;
cam.setOrthographic();
cam.updateView();
scene.ctx().emplace<MM::Components::LiteParticles2DUploadQueue>();
scene.ctx().emplace<MM::Random::SRNG>(42u);
// setup v system
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<Systems::lite_particles2d_emit>("lite_particles2d_emit");
// HACK: instead you would switch to this scene
engine.getService<MM::Services::OrganizerSceneService>().updateOrganizerVertices(scene);
{ // default
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = -10.f;
auto& lpe = scene.emplace<Components::LiteParticles2DEmitter>(e);
lpe.age_delta = 60.f;
lpe.particle_count = {1, 1};
lpe.p_type = "default";
lpe.p_dir = {-0.1f, +0.1f};
lpe.p_dir_force = {0.f, 8.f};
lpe.p_age = {0.f, 0.7f};
}
{ // sparks
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = 0.f;
auto& lpe = scene.emplace<Components::LiteParticles2DEmitter>(e);
lpe.age_delta = 1.f;
lpe.particle_count = {25, 40};
lpe.p_type = "MM::spark1";
lpe.p_dir = {0.f, 1.f};
lpe.p_dir_force = {0.f, 1.f}; // m/s
lpe.p_age = {0.f, 0.7f};
}
{ // fire
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = 10.f;
auto& lpe = scene.emplace<Components::LiteParticles2DEmitter>(e);
lpe.age_delta = 60.f;
lpe.particle_count = {1, 1};
lpe.p_type = "MM::fire1";
lpe.p_dir = {0.f, 1.f};
lpe.p_dir_force = {0.f, 1.f};
lpe.p_age = {0.f, 0.5f};
}
engine.run();
}
int main(int argc, char** argv) {
argv0 = argv[0];
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -62,8 +62,12 @@ TEST(simple_rect_render_task, it) {
scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Rotation2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>(); // in this example only rotation is touched
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.setOrthographic();
cam.updateView();
// setup v system
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<MM::Systems::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -65,9 +65,12 @@ TEST(simple_sprite_render_task, it) {
scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Rotation2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>(); // in this example only rotation is touched
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.setOrthographic();
cam.updateView();
// setup v system
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<MM::Systems::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -64,7 +64,7 @@ TEST(simple_spritesheet_render_task, it) {
auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<MM::Services::OpenGLRenderer>());
auto& cam = scene.set<MM::OpenGL::Camera3D>();
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.horizontalViewPortSize = 5;
cam.setOrthographic();
cam.updateView();
@ -82,8 +82,8 @@ TEST(simple_spritesheet_render_task, it) {
scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
// setup systems
scene.set<float>(0.f); // accu
auto& org = scene.set<entt::organizer>();
scene.ctx().emplace<float>(0.f); // accu
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&update_spritesheet_animation>("update_spritesheet_animation");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -39,7 +39,7 @@ TEST(tilemap_render_task_test, it) {
ASSERT_TRUE(provide_ret);
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene();
auto& cam = scene.set<MM::OpenGL::Camera3D>();
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.translation = {2.f, -2.f, 0.f};
cam.horizontalViewPortSize = 20.f;
cam.setOrthographic();
@ -63,7 +63,7 @@ TEST(tilemap_render_task_test, it) {
scene.on_update<MM::Components::Position2D_ZOffset>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
scene.on_update<MM::Components::Position3D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");
org.emplace<MM::Systems::transform_clear_dirty>("transform_clear_dirty");

View File

@ -52,7 +52,7 @@ bool OrganizerSceneService::enable(Engine& engine, std::vector<UpdateStrategies:
// default scene
if (!_scene) {
_scene = std::make_unique<Scene>();
_scene->set<MM::Engine&>(engine);
_scene->ctx().emplace<MM::Engine&>(engine);
updateOrganizerVertices(*_scene);
}
@ -74,14 +74,14 @@ void OrganizerSceneService::sceneFixedUpdate(Engine&) {
size_t continuous_counter = 0;
auto& time_ctx = _scene->ctx_or_set<MM::Components::TimeDelta>(f_delta, initial_delta_factor);
auto& time_ctx = _scene->ctx().emplace<MM::Components::TimeDelta>(f_delta, initial_delta_factor);
time_ctx.tickDelta = f_delta * time_ctx.deltaFactor;
while (_accumulator >= f_delta){
_accumulator -= f_delta;
continuous_counter++;
for (auto&& v : _scene->ctx<std::vector<entt::organizer::vertex>>()) {
for (auto&& v : _scene->ctx().at<std::vector<entt::organizer::vertex>>()) {
v.callback()(v.data(), *_scene);
}
@ -97,8 +97,8 @@ void OrganizerSceneService::changeSceneFixedUpdate(Engine& engine) {
if (_next_scene) {
LOG_OSS("changing scene...");
_scene = std::move(_next_scene);
if (!_scene->try_ctx<MM::Engine>()) {
_scene->set<MM::Engine&>(engine); // make engine accessible from scene
if (!_scene->ctx().contains<MM::Engine>()) {
_scene->ctx().emplace<MM::Engine&>(engine); // make engine accessible from scene
}
updateOrganizerVertices(*_scene);
}
@ -119,14 +119,14 @@ void OrganizerSceneService::changeSceneNow(std::unique_ptr<Scene>&& new_scene) {
}
void OrganizerSceneService::updateOrganizerVertices(Scene& scene) {
scene.ctx_or_set<std::vector<entt::organizer::vertex>>() =
scene.ctx_or_set<entt::organizer>().graph();
scene.ctx().emplace<std::vector<entt::organizer::vertex>>() =
scene.ctx().emplace<entt::organizer>().graph();
if (!scene.try_ctx<MM::Components::TimeDelta>()) {
scene.set<MM::Components::TimeDelta>();
if (!scene.ctx().contains<MM::Components::TimeDelta>()) {
scene.ctx().emplace<MM::Components::TimeDelta>();
}
SPDLOG_DEBUG("graph:\n{}", scene.ctx<std::vector<entt::organizer::vertex>>());
SPDLOG_DEBUG("graph:\n{}", scene.ctx().at<std::vector<entt::organizer::vertex>>());
}
void OrganizerSceneService::resetTime(void) {

View File

@ -85,7 +85,8 @@ class ResourceManager {
discard(entt::hashed_string{id}.value());
}
void each(std::function<void(res_id_type, std::shared_ptr<Resource>)>& fn) {
template<typename FN>
void each(FN&& fn) {
for (auto& it : _storage) {
fn(it.first, it.second);
}

View File

@ -1,9 +1,30 @@
#pragma once
#include <string_view>
#include <cctype>
namespace MM::std_utils {
inline std::string_view trim_prefix(std::string_view sv) {
while (!sv.empty() && std::isspace(sv.front())) {
sv.remove_prefix(1);
}
return sv;
}
inline std::string_view trim_suffix(std::string_view sv) {
while (!sv.empty() && std::isspace(sv.back())) {
sv.remove_suffix(1);
}
return sv;
}
inline std::string_view trim(std::string_view sv) {
return trim_suffix(trim_prefix(sv));
}
// src : https://marcoarena.wordpress.com/2017/01/03/string_view-odi-et-amo/
inline std::vector<std::string_view> split(std::string_view str, const char* delims) {
std::vector<std::string_view> ret;

View File

@ -120,11 +120,11 @@ void create_mm_logo(MM::Engine& engine, MM::Services::ScreenDirector::Screen& sc
auto new_scene = std::make_unique<MM::Scene>();
auto& scene = *new_scene;
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
scene.set<MM::Engine&>(_engine); // alias
scene.ctx().emplace<MM::Engine&>(_engine); // alias
scene.set<Components::screen_timer>(0.f, screen_duration, next_screen);
scene.ctx().emplace<Components::screen_timer>(0.f, screen_duration, next_screen);
org.emplace<Systems::screen_timer_system>("screen_timer_system");
@ -135,7 +135,7 @@ void create_mm_logo(MM::Engine& engine, MM::Services::ScreenDirector::Screen& sc
org.emplace<MM::Systems::transform3d_scale2d>("transform3d_scale2d");
//org.emplace<MM::Systems::transform_clear_dirty>("transform_clear_dirty");
auto& cam = scene.set<MM::OpenGL::Camera3D>();
auto& cam = scene.ctx().emplace<MM::OpenGL::Camera3D>();
cam.horizontalViewPortSize = 89.f;
cam.setOrthographic();
cam.updateView();

View File

@ -29,7 +29,7 @@ TEST(player_velocity, basic_run) {
auto& scene = engine.getService<MM::Services::SceneServiceInterface>().getScene();
// setup v system
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&MM::Systems::player_velocity2d>("player_velocity2d");
// HACK: instead you would switch to this scene

View File

@ -11,13 +11,13 @@ TEST(simple_velocity_2d, basic_run) {
MM::Scene scene;
// setup v system
auto& org = scene.set<entt::organizer>();
auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&MM::Systems::simple_positional_velocity>("simple_positional_velocity");
org.emplace<&MM::Systems::simple_rotational_velocity>("simple_rotational_velocity");
auto graph = org.graph();
// setup delta
auto& time_ctx = scene.ctx_or_set<MM::Components::TimeDelta>(1.f/60.f, 1.f);
auto& time_ctx = scene.ctx().emplace<MM::Components::TimeDelta>(1.f/60.f, 1.f);
time_ctx.tickDelta = 1.f/60.f * time_ctx.deltaFactor;
// setup test entity