Unravel Engine C++ Reference
Loading...
Searching...
No Matches
splash_scene.cpp
Go to the documentation of this file.
1#include "splash_scene.h"
2
8#include <engine/ui/ui_tree.h>
9#include <logging/logging.h>
10#include <seq/seq.h>
11
12#include <RmlUi/Core/Animation.h>
13#include <RmlUi/Core/Element.h>
14#include <RmlUi/Core/ElementDocument.h>
15#include <RmlUi/Core/Property.h>
16#include <RmlUi/Core/StyleTypes.h>
17#include <RmlUi/Core/Tween.h>
18
19#include <algorithm>
20#include <chrono>
21
22namespace unravel
23{
24namespace
25{
26constexpr const char* default_splash_document = "engine:/data/ui/splash.rhtml";
27constexpr const char* splash_seq_scope = "splash_scene";
28constexpr const char* made_with_logo = "engine:/data/logos/made_with.png";
29constexpr float made_with_duration_sec = 5.0f;
30
31struct splash_logo_slot
32{
33 std::string texture_path;
34 float duration_sec = 0.0f;
35};
36
37auto to_duration(float seconds) -> seq::duration_t
38{
39 return std::chrono::duration_cast<seq::duration_t>(delta_t(std::max(0.0f, seconds)));
40}
41
42auto collect_logo_slots(rtti::context& ctx) -> std::vector<splash_logo_slot>
43{
44 std::vector<splash_logo_slot> slots;
45 if(!ctx.has<settings>())
46 {
47 return slots;
48 }
49
50 const auto& splash_settings = ctx.get<settings>().splash;
51 slots.reserve(splash_settings.logos.size() + 1);
52
53
54 if(splash_settings.show_made_with)
55 {
56 slots.push_back({made_with_logo, made_with_duration_sec});
57 }
58
59 for(const auto& entry : splash_settings.logos)
60 {
61 if(!entry.logo)
62 {
63 continue;
64 }
65 slots.push_back({entry.logo.id(), std::max(0.0f, entry.duration_sec)});
66 }
67
68 return slots;
69}
70
71auto get_splash_logo_element(entt::handle ui_entity) -> Rml::Element*
72{
73 if(!ui_entity || !ui_entity.all_of<ui_document_component>())
74 {
75 return nullptr;
76 }
77 auto& ui_comp = ui_entity.get<ui_document_component>();
78 if(!ui_comp.document)
79 {
80 return nullptr;
81 }
82 return ui_comp.document->GetElementById("splash-logo");
83}
84
85void set_logo_animation(Rml::Element* logo_el, const char* keyframe_name, float duration_sec)
86{
87 if(!logo_el || duration_sec <= 0.0f)
88 {
89 return;
90 }
91
92 Rml::Animation animation;
93 animation.duration = duration_sec;
94 animation.tween = Rml::Tween(Rml::Tween::Cubic, Rml::Tween::Out);
95 animation.name = keyframe_name;
96 animation.num_iterations = 1;
97
98 Rml::AnimationList animations;
99 animations.push_back(animation);
100 logo_el->SetProperty(Rml::PropertyId::Animation, Rml::Property(animations, Rml::Unit::ANIMATION));
101}
102
103void set_logo_visible(entt::handle ui_entity, bool visible)
104{
105 if(!ui_entity)
106 {
107 return;
108 }
109 if(!ui_entity.all_of<ui_document_component>())
110 {
111 return;
112 }
113 auto* logo_el = get_splash_logo_element(ui_entity);
114 if(!logo_el)
115 {
116 return;
117 }
118 if(visible)
119 {
120 logo_el->SetClass("visible", true);
121 logo_el->SetProperty(Rml::PropertyId::Display, Rml::Property(Rml::Style::Display::Block));
122 }
123 else
124 {
125 logo_el->SetClass("visible", false);
126 logo_el->SetProperty(Rml::PropertyId::Display, Rml::Property(Rml::Style::Display::None));
127 }
128}
129
130void show_logo(entt::handle ui_entity, const std::string& texture_path, float duration_sec)
131{
132 if(!ui_entity || texture_path.empty() || duration_sec <= 0.0f)
133 {
134 return;
135 }
136 auto* logo_el = get_splash_logo_element(ui_entity);
137 if(!logo_el)
138 {
139 return;
140 }
141 logo_el->SetAttribute("src", texture_path);
142 logo_el->SetClass("visible", false);
143 logo_el->SetProperty(Rml::PropertyId::Display, Rml::Property(Rml::Style::Display::Block));
144 logo_el->SetClass("visible", true);
145 set_logo_animation(logo_el, "splash-logo-show", duration_sec);
146}
147
148void fade_out_logo(entt::handle ui_entity, float duration_sec)
149{
150 if(!ui_entity || duration_sec <= 0.0f)
151 {
152 return;
153 }
154 auto* logo_el = get_splash_logo_element(ui_entity);
155 if(!logo_el)
156 {
157 return;
158 }
159 set_logo_animation(logo_el, "splash-logo-fade-out", duration_sec);
160}
161
162auto build_logo_sequence(entt::handle ui_entity, const std::vector<splash_logo_slot>& slots, float fade_in_sec, float fade_out_sec)
164{
165 std::vector<seq::seq_action> actions;
166 actions.reserve(slots.size() * 2 + 2);
167
168 if(fade_in_sec > 0.0f)
169 {
170 actions.push_back(seq::delay(to_duration(fade_in_sec)));
171 }
172
173 for(size_t i = 0; i < slots.size(); ++i)
174 {
175 const auto& slot = slots[i];
176 auto show_action = seq::delay(to_duration(slot.duration_sec));
177 show_action.on_begin.connect([ui_entity, path = slot.texture_path, duration = slot.duration_sec]()
178 {
179 show_logo(ui_entity, path, duration);
180 });
181 if(fade_out_sec <= 0.0f && i + 1 == slots.size())
182 {
183 show_action.on_end.connect([ui_entity]()
184 {
185 set_logo_visible(ui_entity, false);
186 });
187 }
188 actions.push_back(show_action);
189 }
190
191 if(fade_out_sec > 0.0f)
192 {
193 auto hide_action = seq::delay(to_duration(fade_out_sec));
194 hide_action.on_begin.connect([ui_entity, duration = fade_out_sec]()
195 {
196 fade_out_logo(ui_entity, duration);
197 });
198 hide_action.on_end.connect([ui_entity]()
199 {
200 set_logo_visible(ui_entity, false);
201 });
202 actions.push_back(hide_action);
203 }
204
205 return seq::sequence(actions);
206}
207
208} // namespace
209
211{
212 (void)ctx;
213 return default_splash_document;
214}
215
217{
218 return !collect_logo_slots(ctx).empty();
219}
220
222{
223 teardown(state);
224 state.setup_failed = false;
225 state.ui_entity = {};
226
227 const auto slots = collect_logo_slots(ctx);
228 if(slots.empty())
229 {
230 state.setup_failed = true;
231 return;
232 }
233
234 float fade_in_sec = 0.5f;
235 float fade_out_sec = 0.5f;
236 if(ctx.has<settings>())
237 {
238 const auto& splash_settings = ctx.get<settings>().splash;
239 fade_in_sec = std::max(0.0f, splash_settings.fade_in_sec);
240 fade_out_sec = std::max(0.0f, splash_settings.fade_out_sec);
241 }
242
243 scn.unload();
244
245 auto& am = ctx.get_cached<asset_manager>();
246 const auto document_key = get_document_key(ctx);
247 auto document_asset = am.get_asset<ui_tree>(document_key);
248 if(!document_asset)
249 {
250 APPLOG_ERROR("Failed to load splash UI document asset: {}", document_key);
251 state.setup_failed = true;
252 return;
253 }
254
255 defaults::create_camera_entity(ctx, scn, "Splash Camera");
256
257 state.ui_entity = defaults::create_ui_document_entity(ctx, scn, "Splash UI");
258
259 state.ui_entity.get<transform_component>().set_active(true);
260
261 auto& ui_comp = state.ui_entity.get<ui_document_component>();
262 ui_comp.asset = document_asset;
263 ui_comp.render_mode = ui_render_mode::screen_space_overlay;
264
265 set_logo_visible(state.ui_entity, false);
266
267 state.pending_sequence = build_logo_sequence(state.ui_entity, slots, fade_in_sec, fade_out_sec);
268}
269
271{
272 (void)ctx;
273 if(state.setup_failed || state.action != 0 || !state.pending_sequence.is_valid())
274 {
275 return;
276 }
277 if(!state.ui_entity || !state.ui_entity.all_of<ui_document_component>())
278 {
279 return;
280 }
281 if(!state.ui_entity.get<ui_document_component>().is_loaded())
282 {
283 return;
284 }
285
286 set_logo_visible(state.ui_entity, false);
287 state.action = seq::start(std::move(state.pending_sequence), splash_seq_scope);
288 state.pending_sequence = {};
289}
290
292{
293 if(state.action != 0)
294 {
295 seq::stop(state.action);
296 state.action = 0;
297 }
298 state.pending_sequence = {};
299 state.ui_entity = {};
300 state.setup_failed = false;
301}
302
304{
305 if(state.setup_failed)
306 {
307 return true;
308 }
309 if(state.action != 0)
310 {
311 return seq::is_finished(state.action);
312 }
313 if(state.pending_sequence.is_valid())
314 {
315 return false;
316 }
317 return true;
318}
319
320} // namespace unravel
Manages assets, including loading, unloading, and storage.
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
std::chrono::duration< float > delta_t
#define APPLOG_ERROR(...)
Definition logging.h:20
auto is_finished(seq_id_t id) -> bool
Checks if the action has finished.
Definition seq.cpp:60
auto start(seq_action action, const seq_scope_policy &scope_policy, hpp::source_location location) -> seq_id_t
Starts a new action.
Definition seq.cpp:8
auto delay(const duration_t &duration, const sentinel_t &sentinel) -> seq_action
Creates a delay action.
Definition seq_core.cpp:182
auto sequence(const std::vector< seq_action > &actions, const sentinel_t &sentinel) -> seq_action
Creates a sequential action that executes a list of actions one after another.
Definition seq_core.cpp:99
void stop(seq_id_t id)
Stops the action associated with the given ID.
Definition seq.cpp:20
std::chrono::nanoseconds duration_t
Represents a duration in nanoseconds.
Definition seq_common.h:41
void teardown(splash_scene_state &state)
auto is_finished(const splash_scene_state &state) -> bool
void update(rtti::context &ctx, splash_scene_state &state)
auto has_content(rtti::context &ctx) -> bool
auto get_document_key(rtti::context &ctx) -> std::string
void setup(rtti::context &ctx, scene &scn, splash_scene_state &state)
@ screen_space_overlay
Screen-space overlay (viewport-sized or positioned)
std::string texture_path
float duration_sec
auto get_cached() -> T &
Definition context.hpp:49
auto has() const -> bool
Definition context.hpp:28
auto get() -> T &
Definition context.hpp:35
Represents an action within the sequence management system. Contains lifecycle events and management ...
Definition seq_action.h:18
auto is_valid() const noexcept -> bool
Checks if the action is valid.
static auto create_ui_document_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a UI document entity.
static auto create_camera_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a camera entity.
Definition defaults.cpp:999
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:70
void unload()
Unloads the scene, removing all entities.
Definition scene.cpp:200
seq::seq_action pending_sequence
Component that holds a reference to a UI document for RmlUi rendering.
asset_handle< ui_tree > asset
Path to the UI document file (HTML/RML)
auto is_loaded() const -> bool
Check if document is currently loaded.
Represents a UI visual tree asset (HTML/RML document).
Definition ui_tree.h:25