mirror of
https://github.com/MadeOfJelly/MushMachine.git
synced 2024-10-29 22:45:34 +01:00
378 lines
9.7 KiB
C++
378 lines
9.7 KiB
C++
#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/services/count_down.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>());
|
|
|
|
#ifdef MM_AUTOTEST
|
|
engine.addService<MM::Services::CountDown>(50); // 50 frames
|
|
ASSERT_TRUE(engine.enableService<MM::Services::CountDown>());
|
|
#endif
|
|
|
|
// 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();
|
|
}
|
|
|