22 Commits

Author SHA1 Message Date
96722abe4e load vulkan using volk and create window instance surface 2022-06-01 02:39:20 +02:00
0f361f6505 add mushmachine ascii art 2022-05-16 18:25:41 +02:00
6b06d5cb99 update all cmake files to version 3.9
other misc cmake fixes

version 3.9 allows for ipo (lto)
2022-05-03 19:33:19 +02:00
052bdb45ec update entt to v3.10.1 2022-04-28 16:12:51 +02:00
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
89 changed files with 2203 additions and 249 deletions

3
.gitmodules vendored
View File

@ -37,3 +37,6 @@
path = external/physfs/physfs
url = https://github.com/icculus/physfs.git
branch = main
[submodule "external/volk"]
path = external/volk
url = https://github.com/zeux/volk.git

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
# cmake setup begin
project(MushMachine C CXX)
@ -19,6 +19,11 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
# add this to your projects cmake to enable ipo
#include(CheckIPOSupported)
#check_ipo_supported()
#set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
# enable test
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)

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

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
# external libs
@ -38,6 +38,13 @@ if(NOT MM_HEADLESS)
add_subdirectory("glad-debug")
endif()
# TODO: determain if we need this
#if (WIN32)
#set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR)
#elseif()
#endif()
add_subdirectory("volk")
# stb utilies
add_subdirectory("stb")
@ -54,4 +61,3 @@ if(NOT MM_HEADLESS)
add_subdirectory("soloud")
endif()

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
add_library(imgui_color_text_edit
"${CMAKE_CURRENT_LIST_DIR}/ImGuiColorTextEdit/TextEditor.h"

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(SquirrelNoise)
add_library(squirrel_noise

2
external/entt vendored

2
external/entt.cmake vendored
View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
add_library(entt INTERFACE)
target_include_directories(entt INTERFACE "${CMAKE_CURRENT_LIST_DIR}/entt/src")
target_compile_features(entt INTERFACE cxx_std_17)

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(glad C)
set(C_FILES
@ -15,8 +16,6 @@ add_library(glad ${C_FILES} ${H_FILES})
target_include_directories(glad PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
if(UNIX)
target_link_libraries(glad
dl
)
target_link_libraries(glad dl)
endif()

2
external/glm.cmake vendored
View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
add_library(glm INTERFACE)
target_include_directories(glm INTERFACE "${CMAKE_CURRENT_LIST_DIR}/glm")

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
add_library(icon_font_cpp_headers INTERFACE)
target_include_directories(icon_font_cpp_headers INTERFACE "${CMAKE_CURRENT_LIST_DIR}/IconFontCppHeaders/")

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
##
## PROJECT

View File

@ -13,7 +13,7 @@
# compile, using preprocessor checks for platform-specific bits instead of
# testing in here.
cmake_minimum_required(VERSION 3.0.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(PhysicsFS)
set(PHYSFS_VERSION 3.1.0)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(soloud CXX)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(stb CXX)

View File

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

1
external/volk vendored Submodule

Submodule external/volk added at 2784718c91

View File

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.2)
project(framework CXX)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(framework)
add_subdirectory(engine)
add_subdirectory(logger)

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(common_components CXX)
add_library(common_components INTERFACE)

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(engine CXX)
add_library(engine

View File

@ -30,6 +30,14 @@ void Engine::setup(void) {
Engine::Engine(void) {
setup();
LOG_INFO(R"text(
__ __ _ __ __ _ _
| \/ | | | | \/ | | | (_)
| \ / |_ _ ___| |__ | \ / | __ _ ___| |__ _ _ __ ___
| |\/| | | | / __| '_ \| |\/| |/ _` |/ __| '_ \| | '_ \ / _ \
| | | | |_| \__ \ | | | | | | (_| | (__| | | | | | | | __/
|_| |_|\__,_|___/_| |_|_| |_|\__,_|\___|_| |_|_|_| |_|\___|)text");
_update_strategy = std::make_unique<MM::UpdateStrategies::Sequential>();
LOG_INFO("defaulting to Sequential (single threaded) UpdateStrategy");
//_update_strategy = std::make_unique<MM::UpdateStrategies::Dummy>();

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(filesystem_service CXX)
add_library(filesystem_service

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(imgui_lib CXX)

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

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(input_service CXX)
add_library(input_service

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(logger CXX)
file(GLOB_RECURSE CPP_FILES src/*.cpp)

View File

@ -1,10 +1,33 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(opengl_primitives CXX)
file(GLOB_RECURSE CPP_FILES src/*.cpp)
file(GLOB_RECURSE HPP_FILES src/*.hpp)
add_library(opengl_primitives ${CPP_FILES} ${HPP_FILES})
add_library(opengl_primitives
src/mm/opengl/buffer.hpp
src/mm/opengl/buffer.cpp
src/mm/opengl/fbo_builder.hpp
src/mm/opengl/fbo_builder.cpp
src/mm/opengl/frame_buffer_object.hpp
src/mm/opengl/frame_buffer_object.cpp
src/mm/opengl/instance_buffer.hpp
src/mm/opengl/shader.hpp
src/mm/opengl/shader.cpp
src/mm/opengl/shader_builder.hpp
src/mm/opengl/shader_builder.cpp
src/mm/opengl/spritesheet.hpp
src/mm/opengl/texture.hpp
src/mm/opengl/texture.cpp
src/mm/opengl/texture_loader.hpp
src/mm/opengl/texture_loader.cpp
src/mm/opengl/vertex_array_object.hpp
src/mm/opengl/vertex_array_object.cpp
src/mm/opengl/components/texture.hpp
)
target_include_directories(opengl_primitives PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")

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

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

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(opengl_renderer CXX)
add_library(opengl_renderer_s
@ -268,6 +269,27 @@ target_link_libraries(bloom
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

@ -26,14 +26,16 @@ void setup_bloom(
#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
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_RGB16F;
//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_RGB16F;
const auto bloom_internal_format = GL_R11F_G11F_B10F;
const auto bloom_format_type = GL_FLOAT;
#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 {
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

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

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

@ -75,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);
@ -85,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

@ -84,6 +84,312 @@ vec3 tonemapReinhard(vec3 x) {
#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

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

View File

@ -168,7 +168,6 @@ target_link_libraries(hdr_bloom_pipeline_example
opengl_renderer_s
organizer_scene
clear_render_task
blit_fb_render_task
simple_rect_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)
################# 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

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

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

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

@ -119,46 +119,6 @@ static void setup_fbos(MM::Engine& engine) {
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()
.attachTexture(rm_t.get("hdr_color"_hs), GL_COLOR_ATTACHMENT0)
.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::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,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");
@ -103,7 +103,7 @@ TEST(simple_spritesheet_render_task, it) {
auto e = scene.create();
auto& p = scene.emplace<MM::Components::Position2D>(e);
p.pos.x = -1.f;
// zoffset is created by event
auto& s = scene.emplace<MM::Components::Scale2D>(e);

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

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(organizer_scene CXX)
add_library(organizer_scene

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

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(random CXX)
add_library(random

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(resource_manager CXX)
add_library(resource_manager INTERFACE)

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,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(screen_director CXX)
add_library(screen_director

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(sdl_service CXX)
add_library(sdl_service
@ -39,6 +40,9 @@ else()
target_link_libraries(sdl_service glad)
endif()
# TODO: conditionaly
target_link_libraries(sdl_service volk::volk)
if(VCPKG_TARGET_TRIPLET)
target_link_libraries(sdl_service SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static)
endif()

View File

@ -1,4 +1,7 @@
add_executable(sdl_service_test start_test.cpp)
add_executable(sdl_service_test
./start_test.cpp
./volk_test1.cpp
)
target_include_directories(sdl_service_test PRIVATE ".")

View File

@ -0,0 +1,82 @@
#include <gtest/gtest.h>
#include <mm/engine.hpp>
#include <mm/services/sdl_service.hpp>
#include <SDL_vulkan.h>
#include <volk.h>
TEST(sdl_service, window_volk) {
MM::Engine engine;
engine.addService<MM::Services::SDLService>();
ASSERT_TRUE(engine.enableService<MM::Services::SDLService>());
auto* sdl_ss_ptr = engine.tryService<MM::Services::SDLService>();
ASSERT_NE(sdl_ss_ptr, nullptr);
// init volk
auto init_res = volkInitialize();
ASSERT_EQ(init_res, VK_SUCCESS);
// create window
ASSERT_EQ(sdl_ss_ptr->win, nullptr);
ASSERT_TRUE(sdl_ss_ptr->createWindow("test vulkan window", 800, 600, SDL_WINDOW_VULKAN));
ASSERT_NE(sdl_ss_ptr->win, nullptr);
// create vulkan instance
// Get the required extension count
unsigned int count;
ASSERT_TRUE(
SDL_Vulkan_GetInstanceExtensions(
sdl_ss_ptr->win,
&count,
nullptr
)
);
std::vector<const char*> extensions {
//VK_EXT_DEBUG_REPORT_EXTENSION_NAME // Sample additional extension
};
size_t additional_extension_count = extensions.size();
extensions.resize(additional_extension_count + count);
ASSERT_TRUE(
SDL_Vulkan_GetInstanceExtensions(
sdl_ss_ptr->win,
&count,
extensions.data() + additional_extension_count
)
);
// Now we can make the Vulkan instance
VkInstanceCreateInfo create_info {};
//create_info.pApplicationInfo;
create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
create_info.ppEnabledExtensionNames = extensions.data();
VkInstance instance;
VkResult result = vkCreateInstance(&create_info, nullptr, &instance);
ASSERT_EQ(result, VK_SUCCESS);
// finish setting up volk ?
volkLoadInstance(instance);
// the surface for the window
VkSurfaceKHR surface;
ASSERT_TRUE(
SDL_Vulkan_CreateSurface(
sdl_ss_ptr->win,
instance,
&surface
)
);
engine.disableService<MM::Services::SDLService>();
ASSERT_EQ(sdl_ss_ptr->win, nullptr);
}

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(simple_sdl_renderer_service CXX)
add_library(simple_sdl_renderer_service

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(sound_service CXX)
add_library(sound_service

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(std_utils CXX)
add_library(std_utils INTERFACE)

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(tilemap CXX)
add_library(tilemap

View File

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.2)
project(screens CXX)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(screens)
if(NOT MM_HEADLESS)
add_subdirectory(mm_logo)

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(mm_logo_screen CXX)
add_library(mm_logo_screen

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

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.8)
project(systems CXX)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(systems)
add_subdirectory(transform)
add_subdirectory(simple_velocity)

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(fast_sky_sun_system CXX)
add_library(fast_sky_sun_system

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(player_velocity_system CXX)
add_library(player_velocity_system

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

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(simple_velocity_system CXX)
add_library(simple_velocity_system

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

View File

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(transfrom_system CXX)
add_library(transform_system