18 Commits
v0.7.1 ... 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
48 changed files with 1999 additions and 202 deletions

View File

@ -13,7 +13,15 @@ The private repo had over 900 commits, before the first public release.
- [Github](https://github.com/MadeOfJelly/MushMachine) - [Github](https://github.com/MadeOfJelly/MushMachine)
- [Setup](docs/setup.md) - [Setup](docs/setup.md)
- [Example Game Setup](docs/basic_game.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. - 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 ## Platforms
- PC (Linux, Windows and maybe APPLE(compiles)) - PC (Linux, Windows and maybe APPLE(compiles))

2
external/entt vendored

View File

@ -1,29 +1,38 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR) cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
add_library(tracy_client if(NOT EMSCRIPTEN)
"${CMAKE_CURRENT_LIST_DIR}/tracy/Tracy.hpp" add_library(tracy_client
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyLua.hpp" "${CMAKE_CURRENT_LIST_DIR}/tracy/Tracy.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyVulkan.hpp" "${CMAKE_CURRENT_LIST_DIR}/tracy/TracyLua.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyOpenGL.hpp" "${CMAKE_CURRENT_LIST_DIR}/tracy/TracyVulkan.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyOpenGL.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyClient.cpp" "${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()
if(TRACY_ENABLE) target_compile_features(tracy_client PUBLIC cxx_std_17)
target_compile_definitions(tracy_client PUBLIC TRACY_ENABLE)
#target_compile_definitions(tracy_client PUBLIC TRACY_NO_SYSTEM_TRACING) target_include_directories(tracy_client PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
message("Enabled TRACY")
if(UNIX)
target_link_libraries(tracy_client dl)
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() endif()
target_include_directories(tracy_client PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
if(UNIX)
target_link_libraries(tracy_client dl)
endif()
if(WIN32)
target_link_libraries(tracy_client ws2_32 dbghelp)
endif()

View File

@ -85,8 +85,8 @@ private:
bool entityHasComponent(Registry& registry, EntityType& entity, ComponentTypeID type_id) bool entityHasComponent(Registry& registry, EntityType& entity, ComponentTypeID type_id)
{ {
ComponentTypeID type[] = { type_id }; const auto storage_it = registry.storage(type_id);
return registry.runtime_view(std::cbegin(type), std::cend(type)).contains(entity); return storage_it != registry.storage().end() && storage_it->second.contains(entity);
} }
public: public:
@ -243,7 +243,16 @@ public:
} }
}); });
} else { } 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()); ImGui::Text("%lu Entities Matching:", view.size_hint());
if (ImGui::BeginChild("entity list")) { if (ImGui::BeginChild("entity list")) {
@ -297,7 +306,8 @@ public:
// MIT License // 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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal

View File

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

View File

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

View File

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

View File

@ -21,15 +21,20 @@ namespace MM::OpenGL {
private: private:
GLuint _handle = 0; GLuint _handle = 0;
std::size_t _size = 0; std::size_t _size = 0;
GLenum _target;
public: 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); ~Buffer(void);
void bind(void) const;
void bind(GLenum target) const; void bind(GLenum target) const;
void unbind(void) const;
void unbind(GLenum target) const; void unbind(GLenum target) const;
std::size_t getSize(void) const; std::size_t getSize(void) const;
GLuint getHandle(void) const;
}; };
} // MM::OpenGL } // MM::OpenGL

View File

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

View File

@ -268,6 +268,27 @@ target_link_libraries(bloom
bloom_combine_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) if (BUILD_TESTING)

View File

@ -26,14 +26,16 @@ void setup_bloom(
#ifdef MM_OPENGL_3_GLES #ifdef MM_OPENGL_3_GLES
#if 0 // NOPE!! #if 0 // NOPE!!
// TODO: caps at 1, invest in half float? // TODO: caps at 1, invest in half float?
const auto bloom_internal_format = GL_RGB565; // prolly fine 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; const auto bloom_format_type = GL_UNSIGNED_BYTE;
#else #else
const auto bloom_internal_format = GL_RGB16F; //const auto bloom_internal_format = GL_RGBA16F;
const auto bloom_internal_format = GL_R11F_G11F_B10F;
const auto bloom_format_type = GL_FLOAT; const auto bloom_format_type = GL_FLOAT;
#endif #endif
#else #else
const auto bloom_internal_format = GL_RGB16F; //const auto bloom_internal_format = GL_RGB16F;
const auto bloom_internal_format = GL_R11F_G11F_B10F;
const auto bloom_format_type = GL_FLOAT; const auto bloom_format_type = GL_FLOAT;
#endif #endif

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

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

View File

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

View File

@ -125,6 +125,7 @@ R"(
#endif #endif
#include "/shaders/builtin/tonemapping.glsl" #include "/shaders/builtin/tonemapping.glsl"
#include "/shaders/builtin/hashing.glsl"
uniform sampler2D color_tex; uniform sampler2D color_tex;
uniform sampler2D bloom_tex; uniform sampler2D bloom_tex;
@ -157,6 +158,9 @@ void main() {
_out_color = tonemapACESFilm(comp); // looks right _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(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 //_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);
})") })")
} }

View File

@ -47,7 +47,6 @@ FastSky::FastSky(MM::Engine& engine) {
_vertexBuffer->unbind(GL_ARRAY_BUFFER); _vertexBuffer->unbind(GL_ARRAY_BUFFER);
_vao->unbind(); _vao->unbind();
} }
FastSky::~FastSky(void) { FastSky::~FastSky(void) {
@ -56,6 +55,17 @@ FastSky::~FastSky(void) {
void FastSky::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) { void FastSky::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
ZoneScopedN("MM::OpenGL::RenderTasks::FastSky::render"); 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); rs.targets[target_fbo]->bind(MM::OpenGL::FrameBufferObject::W);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
@ -67,9 +77,8 @@ void FastSky::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
_vertexBuffer->bind(GL_ARRAY_BUFFER); _vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind(); _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; MM::OpenGL::Camera3D tmp_cam = cam;
// create cam with y up, bc shader says so // create cam with y up, bc shader says so
tmp_cam.up = {0, 1, 0}; 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>(); //auto* ctx_ptr = scene.try_ctx<FastSkyContext>();
if (!ctx_ptr) { //if (!ctx_ptr) {
ctx_ptr = &_default_context; //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); _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 { namespace MM::OpenGL::RenderTasks {
SimpleRect::SimpleRect(Engine& engine) { SimpleRect::SimpleRect(Engine& engine) {
default_cam.setOrthographic();
default_cam.updateView();
float vertices[] = { float vertices[] = {
-0.5f, 0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f, -0.5f,
@ -63,6 +60,10 @@ void SimpleRect::render(Services::OpenGLRenderer& rs, Engine& engine) {
auto& scene = ssi->getScene(); auto& scene = ssi->getScene();
if (!scene.ctx().contains<Camera3D>()) {
return; // nothing to draw
}
rs.targets[target_fbo]->bind(FrameBufferObject::RW); rs.targets[target_fbo]->bind(FrameBufferObject::RW);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
@ -71,12 +72,8 @@ void SimpleRect::render(Services::OpenGLRenderer& rs, Engine& engine) {
_shader->bind(); _shader->bind();
_vao->bind(); _vao->bind();
Camera3D* cam = scene.try_ctx<Camera3D>(); Camera3D& cam = scene.ctx().at<Camera3D>();
if (!cam) { auto vp = cam.getViewProjection();
cam = &default_cam;
}
auto vp = cam->getViewProjection();
scene.view<const Components::Transform4x4>().each([this, &scene, &vp](entt::entity e, const auto& t) { scene.view<const Components::Transform4x4>().each([this, &scene, &vp](entt::entity e, const auto& t) {
_shader->setUniformMat4f("_WVP", vp * t.trans); _shader->setUniformMat4f("_WVP", vp * t.trans);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,6 +75,17 @@ Tilemap::~Tilemap(void) {
void Tilemap::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) { void Tilemap::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
ZoneScopedN("MM::OpenGL::Renderers::TilemapRenderer::render"); 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); rs.targets[target_fbo]->bind(MM::OpenGL::FrameBufferObject::W);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
@ -85,17 +96,13 @@ void Tilemap::render(MM::Services::OpenGLRenderer& rs, MM::Engine& engine) {
_vertexBuffer->bind(GL_ARRAY_BUFFER); _vertexBuffer->bind(GL_ARRAY_BUFFER);
_vao->bind(); _vao->bind();
MM::OpenGL::Camera3D& cam = scene.ctx().at<MM::OpenGL::Camera3D>();
auto& scene = engine.tryService<Services::SceneServiceInterface>()->getScene();
MM::OpenGL::Camera3D& cam = scene.ctx<MM::OpenGL::Camera3D>();
auto vp = cam.getViewProjection(); auto vp = cam.getViewProjection();
_shader->setUniform3f("_ambient_color", ambient_color); _shader->setUniform3f("_ambient_color", ambient_color);
scene.view<MM::Components::Transform4x4, OpenGL::TilemapRenderable>() scene.view<MM::Components::Transform4x4, OpenGL::TilemapRenderable>()
.each([&](auto, MM::Components::Transform4x4& t, OpenGL::TilemapRenderable& tilemap) { .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); _shader->setUniformMat4f("_WVP", vp * t.trans);
// for each sprite layer // for each sprite layer

View File

@ -84,6 +84,312 @@ vec3 tonemapReinhard(vec3 x) {
#endif #endif
)") )")
// ==================== tonemapping.glsl ==================== END // ==================== 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 } // MM::OpenGL

View File

@ -6,6 +6,8 @@ namespace MM::OpenGL {
// file list: // file list:
// - sampling.glsl // - sampling.glsl
// - tonemapping.glsl // - tonemapping.glsl
// - hashing.glsl
// - noise.glsl
void load_builtin_shaders_fs(void); void load_builtin_shaders_fs(void);
} // MM::OpenGL } // MM::OpenGL

View File

@ -168,7 +168,6 @@ target_link_libraries(hdr_bloom_pipeline_example
opengl_renderer_s opengl_renderer_s
organizer_scene organizer_scene
clear_render_task clear_render_task
blit_fb_render_task
simple_rect_render_task simple_rect_render_task
#bloom_extraction_render_task #bloom_extraction_render_task
@ -187,3 +186,27 @@ target_link_libraries(hdr_bloom_pipeline_example
add_test(NAME hdr_bloom_pipeline_example COMMAND hdr_bloom_pipeline_example) 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>(); auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<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.horizontalViewPortSize = 5;
cam.setOrthographic(); cam.setOrthographic();
cam.updateView(); cam.updateView();
@ -87,8 +87,8 @@ TEST(batched_spritesheet_render_task, it) {
// setup systems // setup systems
scene.set<float>(0.f); // accu scene.ctx().emplace<float>(0.f); // accu
auto& org = scene.set<entt::organizer>(); auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&update_spritesheet_animation>("update_spritesheet_animation"); org.emplace<&update_spritesheet_animation>("update_spritesheet_animation");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d"); org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -129,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::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 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 // 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::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d"); org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

View File

@ -37,6 +37,8 @@ R"(
#include "/shaders/builtin/sampling.glsl" #include "/shaders/builtin/sampling.glsl"
#include "/shaders/builtin/tonemapping.glsl" #include "/shaders/builtin/tonemapping.glsl"
#include "/shaders/builtin/hashing.glsl"
#include "/shaders/builtin/noise.glsl"
void main() { void main() {
} }

View File

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

View File

@ -119,46 +119,6 @@ static void setup_fbos(MM::Engine& engine) {
const float render_scale = 1.f; const float render_scale = 1.f;
#if 0
rs.targets["clear_opaque"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("albedo"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("opaque_depth"_hs), GL_DEPTH_ATTACHMENT)
.setResize(true)
.finish();
assert(rs.targets["clear_opaque"]);
rs.targets["clear_opaque_normal"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("normal"_hs), GL_COLOR_ATTACHMENT0)
.setResize(true)
.finish();
assert(rs.targets["clear_opaque"]);
rs.targets["opaque"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("albedo"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("normal"_hs), GL_COLOR_ATTACHMENT1)
.attachTexture(rm_t.get("opaque_depth"_hs), GL_DEPTH_ATTACHMENT)
.setResize(true)
.finish();
assert(rs.targets["opaque"]);
rs.targets["tmp_read"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("normal"_hs), GL_COLOR_ATTACHMENT0)
.setResize(false)
.finish();
assert(rs.targets["tmp_read"]);
rs.targets["depth_read"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("opaque_depth"_hs), GL_DEPTH_ATTACHMENT)
.setResize(false)
.finish();
assert(rs.targets["depth_read"]);
rs.targets["deferred_shading"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.setResize(true)
.finish();
assert(rs.targets["deferred_shading"]);
#endif
rs.targets["game_view"] = MM::OpenGL::FBOBuilder::start() rs.targets["game_view"] = MM::OpenGL::FBOBuilder::start()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0) .attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.attachTexture(rm_t.get("depth"_hs), GL_DEPTH_ATTACHMENT) .attachTexture(rm_t.get("depth"_hs), GL_DEPTH_ATTACHMENT)
@ -230,9 +190,12 @@ TEST(hdr_bloom_pipeline, it) {
scene.on_update<MM::Components::Scale2D>().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 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 // 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::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d"); org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");

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::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 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 // 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::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d"); org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); 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::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 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 // 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::simple_rotational_velocity_patching>("simple_rotational_velocity_patching");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d"); org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); 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>(); auto& rs = engine.addService<MM::Services::OpenGLRenderer>();
ASSERT_TRUE(engine.enableService<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.horizontalViewPortSize = 5;
cam.setOrthographic(); cam.setOrthographic();
cam.updateView(); 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>>(); scene.on_update<MM::Components::Scale2D>().connect<&entt::registry::emplace_or_replace<MM::Components::DirtyTransformTag>>();
// setup systems // setup systems
scene.set<float>(0.f); // accu scene.ctx().emplace<float>(0.f); // accu
auto& org = scene.set<entt::organizer>(); auto& org = scene.ctx().emplace<entt::organizer>();
org.emplace<&update_spritesheet_animation>("update_spritesheet_animation"); org.emplace<&update_spritesheet_animation>("update_spritesheet_animation");
org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d"); org.emplace<MM::Systems::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");
@ -103,7 +103,7 @@ TEST(simple_spritesheet_render_task, it) {
auto e = scene.create(); auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e); auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = -1.f; p.pos.x = -1.f;
// zoffset is created by event // zoffset is created by event
auto& s = scene.emplace<MM::Components::Scale2D>(e); auto& s = scene.emplace<MM::Components::Scale2D>(e);

View File

@ -39,7 +39,7 @@ TEST(tilemap_render_task_test, it) {
ASSERT_TRUE(provide_ret); ASSERT_TRUE(provide_ret);
auto& scene = engine.tryService<MM::Services::SceneServiceInterface>()->getScene(); 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.translation = {2.f, -2.f, 0.f};
cam.horizontalViewPortSize = 20.f; cam.horizontalViewPortSize = 20.f;
cam.setOrthographic(); 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::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::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::position3d_from_2d>("position3d_from_2d");
org.emplace<MM::Systems::transform3d_translate>("transform3d_translate"); org.emplace<MM::Systems::transform3d_translate>("transform3d_translate");
org.emplace<MM::Systems::transform_clear_dirty>("transform_clear_dirty"); 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 // default scene
if (!_scene) { if (!_scene) {
_scene = std::make_unique<Scene>(); _scene = std::make_unique<Scene>();
_scene->set<MM::Engine&>(engine); _scene->ctx().emplace<MM::Engine&>(engine);
updateOrganizerVertices(*_scene); updateOrganizerVertices(*_scene);
} }
@ -74,14 +74,14 @@ void OrganizerSceneService::sceneFixedUpdate(Engine&) {
size_t continuous_counter = 0; 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; time_ctx.tickDelta = f_delta * time_ctx.deltaFactor;
while (_accumulator >= f_delta){ while (_accumulator >= f_delta){
_accumulator -= f_delta; _accumulator -= f_delta;
continuous_counter++; 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); v.callback()(v.data(), *_scene);
} }
@ -97,8 +97,8 @@ void OrganizerSceneService::changeSceneFixedUpdate(Engine& engine) {
if (_next_scene) { if (_next_scene) {
LOG_OSS("changing scene..."); LOG_OSS("changing scene...");
_scene = std::move(_next_scene); _scene = std::move(_next_scene);
if (!_scene->try_ctx<MM::Engine>()) { if (!_scene->ctx().contains<MM::Engine>()) {
_scene->set<MM::Engine&>(engine); // make engine accessible from scene _scene->ctx().emplace<MM::Engine&>(engine); // make engine accessible from scene
} }
updateOrganizerVertices(*_scene); updateOrganizerVertices(*_scene);
} }
@ -119,14 +119,14 @@ void OrganizerSceneService::changeSceneNow(std::unique_ptr<Scene>&& new_scene) {
} }
void OrganizerSceneService::updateOrganizerVertices(Scene& scene) { void OrganizerSceneService::updateOrganizerVertices(Scene& scene) {
scene.ctx_or_set<std::vector<entt::organizer::vertex>>() = scene.ctx().emplace<std::vector<entt::organizer::vertex>>() =
scene.ctx_or_set<entt::organizer>().graph(); scene.ctx().emplace<entt::organizer>().graph();
if (!scene.try_ctx<MM::Components::TimeDelta>()) { if (!scene.ctx().contains<MM::Components::TimeDelta>()) {
scene.set<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) { void OrganizerSceneService::resetTime(void) {

View File

@ -85,7 +85,8 @@ class ResourceManager {
discard(entt::hashed_string{id}.value()); 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) { for (auto& it : _storage) {
fn(it.first, it.second); fn(it.first, it.second);
} }

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 new_scene = std::make_unique<MM::Scene>();
auto& scene = *new_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"); 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::transform3d_scale2d>("transform3d_scale2d");
//org.emplace<MM::Systems::transform_clear_dirty>("transform_clear_dirty"); //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.horizontalViewPortSize = 89.f;
cam.setOrthographic(); cam.setOrthographic();
cam.updateView(); cam.updateView();

View File

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

View File

@ -11,13 +11,13 @@ TEST(simple_velocity_2d, basic_run) {
MM::Scene scene; MM::Scene scene;
// setup v system // 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_positional_velocity>("simple_positional_velocity");
org.emplace<&MM::Systems::simple_rotational_velocity>("simple_rotational_velocity"); org.emplace<&MM::Systems::simple_rotational_velocity>("simple_rotational_velocity");
auto graph = org.graph(); auto graph = org.graph();
// setup delta // 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; time_ctx.tickDelta = 1.f/60.f * time_ctx.deltaFactor;
// setup test entity // setup test entity