2#include "GuiContainer.h"
3#include "GuiDocument.h"
4#include "GuiRenderComponent.h"
5#include "GuiRenderSystem.h"
8#include "ViewContext.h"
10#include "Resources/ResourceStore.h"
11#include "Resources/Texture.h"
13#include "Renderer/Renderer.h"
15#include "Systems/Core/TransformSystem.h"
16#include "Systems/Core/CameraSystem.h"
18#include "Math/Projection.h"
19#include "Input/InputManager.h"
21#include "Foundation/ComponentModel/Entity.h"
22#include "Foundation/Logging/Logger.h"
23#include "Foundation/Platform/Mouse.h"
30Cogs::Core::GuiData::~GuiData() {
32 GuiContainer* container =
static_cast<GuiContainer*
>(htmlDocument->container());
34 htmlDocument =
nullptr;
39Cogs::Core::GuiSystem::~GuiSystem() {
40 for (
auto i = themes.begin(), e = themes.end(); i != e; ++i) {
49 if (!renderer->getRenderContext().device)
return;
51 for (
const auto & guiComponent : pool) {
52 auto& guiData = getData(&guiComponent);
53 auto guiDocument = guiComponent.document.resolve();
55 if (guiDocument && !guiData.htmlDocument && guiDocument->htmlContent.size()) {
58 if (guiComponent.listenerID) {
61 container->initialize(&renderer->getRenderContext(), context->
renderer->getInspectorGuiRenderer());
62 container->pushState(guiComponent.getContainer(), context->
renderer->
getSize());
64 guiData.htmlDocument = litehtml::document::createFromString(guiDocument->htmlContent.c_str(), container, &guiDocument->htmlContext);
66 if (!guiData.htmlDocument) {
67 LOG_ERROR(logger,
"Could not create GUI document from string.");
71 guiData.htmlDocument->set_userdata(guiComponent.getContainer());
73 container->on_load(guiData.htmlDocument);
75 guiData.invalidated =
true;
78 if (!guiDocument)
continue;
79 if (!guiData.htmlDocument)
continue;
81 if (guiComponent.alwaysInvalidate) guiData.invalidated =
true;
83 if (guiComponent.theme != guiData.theme) {
84 auto i = themes.find(guiComponent.theme);
87 if (i == themes.end()) {
88 std::string css = context->
resourceStore->getResourceContentString(guiComponent.theme, ResourceStoreFlags::None);
91 theme->parse_stylesheet(css.c_str(), guiComponent.theme.c_str(), std::shared_ptr<litehtml::document>(), litehtml::media_query_list::ptr());
92 theme->sort_selectors();
93 themes[guiComponent.theme] = theme;
98 guiData.htmlDocument->apply_styles(theme);
99 guiData.invalidated =
true;
100 guiData.theme = guiComponent.theme;
103 updateInputs(guiComponent, guiData);
106 auto & texture = renderComponent->target;
108 guiData.size = texture ? glm::vec2{ texture->description.width, texture->description.height } : context->
renderer->
getSize();
110 static_cast<GuiContainer*
>(guiData.htmlDocument->container())->pushState(guiComponent.getContainer(), guiData.size);
112 float x = guiData.pointerX;
113 float y = guiData.pointerY;
115 if (guiData.pointerRelative) {
120 litehtml::position::vector pos;
121 if (guiData.pointerInside) {
122 guiData.htmlDocument->on_mouse_over((
int)x, (int)y, 0, 0, pos);
124 if (guiData.pointerX != guiData.lastX || guiData.pointerY != guiData.lastY) {
125 guiData.htmlDocument->on_mouse_move((
int)x, (int)y);
127 guiData.lastX = guiData.pointerX;
128 guiData.lastY = guiData.pointerY;
131 if (guiData.pointerDown) {
132 guiData.htmlDocument->on_lbutton_down((
int)x, (int)y, 0, 0, pos);
133 }
else if (guiData.pointerUp) {
134 guiData.htmlDocument->on_lbutton_up((
int)x, (int)y, 0, 0, pos);
137 guiData.htmlDocument->on_mouse_leave(pos);
140 guiData.invalidated = guiData.invalidated ||
static_cast<GuiContainer*
>(guiData.htmlDocument->container())->isInvalidated() || !renderComponent->target;
146 auto & mouseState = context->getDefaultView()->refMouse().getState();
147 const bool pointerState = context->getDefaultView()->inputManager->getActionState(guiComponent.pointerAction);
148 int x = mouseState.position.x;
149 int y = mouseState.position.y;
151 data.pointerDown = data.pointerUp =
false;
152 data.pointerInside =
false;
154 if (pointerState != data.pointerState) {
155 data.pointerState = pointerState;
157 data.pointerDown =
true;
159 data.pointerUp =
true;
163 auto pointerSource = guiComponent.pointerSource;
164 if (guiComponent.pointerSource == GuiPointerSource::Auto) {
165 auto guiRenderComponent = guiComponent.
getComponent<GuiRenderComponent>();
167 if (!guiComponent.projector && !guiRenderComponent->target) {
168 pointerSource = GuiPointerSource::Mouse;
169 }
else if (!guiComponent.projector && guiRenderComponent->target) {
170 pointerSource = GuiPointerSource::ProjectedMouse;
171 }
else if (guiComponent.projector) {
172 pointerSource = GuiPointerSource::Projector;
176 auto transformComponent = guiComponent.
getComponent<TransformComponent>();
178 if (!transformComponent)
return;
180 auto worldTransform = context->transformSystem->getLocalToWorld(transformComponent);
182 glm::vec3 planeNormal = glm::vec3(worldTransform * glm::vec4(0, 0, 1, 0));
183 glm::vec3 planeOrigin = glm::vec3(worldTransform * glm::vec4(0, 0, 0, 1));
185 auto inverseWorldTransform = glm::inverse(worldTransform);
187 auto projectPointer = [&](
float ,
float ,
const glm::vec3 & rayOrigin,
const glm::vec3 & rayDirection) ->
bool
191 bool intersects = Cogs::Geometry::projectToPlane(
200 if (intersects && t < 0.0f) {
201 auto inPlaneCoords = glm::vec3(inverseWorldTransform * glm::vec4(coords, 1));
202 data.pointerInside =
true;
205 glm::vec3 ll = -glm::vec3(guiComponent.planeSize.x * 0.5f, guiComponent.planeSize.y * 0.5f, 0);
206 glm::vec3 diff = inPlaneCoords - ll;
208 diff.x *= (1.0f / guiComponent.planeSize.x);
209 diff.y *= (1.0f / guiComponent.planeSize.y);
211 data.pointerX = diff.x;
212 data.pointerY = 1.0f - diff.y;
213 data.pointerRelative =
true;
221 auto projectViewPointer = [&](
float nX,
float nY,
const glm::mat4 & invViewProj) ->
bool
223 glm::vec4 hp = invViewProj * glm::vec4(nX, nY, -1.f, 1.f);
224 glm::vec3 rayOrigin = (1.f / hp.w) * glm::vec3(hp);
226 glm::vec4 hq = invViewProj * glm::vec4(nX, nY, 1.f, 1.f);
227 glm::vec3 rayDirection = -glm::normalize((1.f / hq.w) * glm::vec3(hq) - rayOrigin);
229 return projectPointer(nX, nY, rayOrigin, rayDirection);
232 switch (pointerSource)
234 case GuiPointerSource::Mouse:
236 data.pointerInside =
true;
237 data.pointerX = (float)x;
238 data.pointerY = (float)y;
239 data.pointerRelative =
false;
242 case GuiPointerSource::ProjectedMouse:
244 auto & cameraData = context->cameraSystem->getData(context->cameraSystem->getMainCamera());
245 auto invViewProj = glm::inverse(cameraData.rawProjectionMatrix * cameraData.viewMatrix);
247 const auto size = context->renderer->getSize();
249 const float nx = ((2.0f * (
static_cast<float>(x) / size.x)) - 1.0f);
250 const float ny = -((2.0f * (
static_cast<float>(y) / size.y)) - 1.0f);
252 projectViewPointer(nx, ny, invViewProj);
256 case GuiPointerSource::ProjectedCenter:
258 auto & cameraData = context->cameraSystem->getData(context->cameraSystem->getMainCamera());
259 auto invViewProj = glm::inverse(cameraData.rawProjectionMatrix * cameraData.viewMatrix);
261 projectViewPointer(0, 0, invViewProj);
265 case GuiPointerSource::Projector:
267 const auto projectorTransform = guiComponent.projector->getComponent<TransformComponent>();
268 const auto projectorWorld = context->transformSystem->getLocalToWorld(projectorTransform);
270 auto rayOrigin = glm::vec3(projectorWorld * glm::vec4(0, 0, 0, 1));
271 auto rayDir = -glm::normalize(glm::vec3(projectorWorld * glm::vec4(0, 0, -1, 0)));
273 projectPointer(0, 0, rayOrigin, rayDir);
278 LOG_ERROR(logger,
"Invalid pointer source.");
ComponentType * getComponent() const
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
class IRenderer * renderer
Renderer.
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
virtual glm::vec2 getSize() const =0
Get the output surface size of the renderer.
Log implementation class.
bool addListener(MessageHub *hub, bool bidirectional=false)
Adds the specified hub as a listener to this hub.
constexpr Log getLogger(const char(&name)[LEN]) noexcept