3d gui - closes #1225
This commit is contained in:
parent
0686c3c439
commit
b96a3c137a
9 changed files with 188 additions and 112 deletions
|
@ -1,10 +1,12 @@
|
|||
include "pipelines/common.glsl"
|
||||
|
||||
vertex_shader [[
|
||||
layout(location = 0) in vec2 a_position;
|
||||
layout(location = 1) in vec2 a_uv;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
layout(std140, binding = 4) uniform Bones {
|
||||
vec2 u_canvas_size;
|
||||
mat4 u_matrix;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec4 v_color;
|
||||
|
@ -12,10 +14,9 @@ vertex_shader [[
|
|||
|
||||
void main() {
|
||||
v_color = a_color;
|
||||
vec2 pos = a_position / u_canvas_size * 2 - 1;
|
||||
pos.y = - pos.y;
|
||||
vec4 pos = u_matrix * vec4(a_position, 0, 1);
|
||||
v_uv = a_uv;
|
||||
gl_Position = vec4(pos, 0, 1);
|
||||
gl_Position = vec4(pos);
|
||||
}
|
||||
]]
|
||||
|
||||
|
|
|
@ -426,6 +426,7 @@ function main()
|
|||
end
|
||||
|
||||
if GAME_VIEW or APP then
|
||||
setRenderTargetsReadonlyDS(res, gbuffer_depth)
|
||||
renderUI()
|
||||
if renderIngameGUI ~= nil then
|
||||
renderIngameGUI()
|
||||
|
|
Binary file not shown.
|
@ -22,6 +22,7 @@ namespace Lumix
|
|||
{
|
||||
|
||||
static const ComponentType GUI_RECT_TYPE = Reflection::getComponentType("gui_rect");
|
||||
static const ComponentType GUI_CANVAS_TYPE = Reflection::getComponentType("gui_canvas");
|
||||
|
||||
|
||||
PropertyGrid::PropertyGrid(StudioApp& app)
|
||||
|
@ -745,7 +746,7 @@ void PropertyGrid::showCoreProperties(const Array<EntityRef>& entities) const
|
|||
ImGuiEx::Label("Parent");
|
||||
ImGui::Text("%s", name);
|
||||
|
||||
if (!universe.hasComponent(entities[0], GUI_RECT_TYPE)) {
|
||||
if (!universe.hasComponent(entities[0], GUI_RECT_TYPE) || universe.hasComponent(entities[0], GUI_CANVAS_TYPE)) {
|
||||
Transform tr = universe.getLocalTransform(entities[0]);
|
||||
DVec3 old_pos = tr.pos;
|
||||
ImGuiEx::Label("Local position");
|
||||
|
@ -772,7 +773,7 @@ void PropertyGrid::showCoreProperties(const Array<EntityRef>& entities) const
|
|||
}
|
||||
|
||||
|
||||
if (!universe.hasComponent(entities[0], GUI_RECT_TYPE)) {
|
||||
if (!universe.hasComponent(entities[0], GUI_RECT_TYPE) || universe.hasComponent(entities[0], GUI_CANVAS_TYPE)) {
|
||||
DVec3 pos = universe.getPosition(entities[0]);
|
||||
DVec3 old_pos = pos;
|
||||
ImGuiEx::Label("Position");
|
||||
|
|
|
@ -100,12 +100,6 @@ struct GUIButton
|
|||
};
|
||||
|
||||
|
||||
struct GUICanvas {
|
||||
EntityRef entity;
|
||||
bool is_3d = false;
|
||||
};
|
||||
|
||||
|
||||
struct GUIInputField
|
||||
{
|
||||
int cursor = 0;
|
||||
|
@ -160,6 +154,10 @@ struct GUIRect
|
|||
|
||||
struct GUISceneImpl final : GUIScene
|
||||
{
|
||||
enum class Version : i32 {
|
||||
CANVAS_3D,
|
||||
LATEST
|
||||
};
|
||||
GUISceneImpl(GUISystem& system, Universe& context, IAllocator& allocator)
|
||||
: m_allocator(allocator)
|
||||
, m_universe(context)
|
||||
|
@ -168,6 +166,7 @@ struct GUISceneImpl final : GUIScene
|
|||
, m_buttons(allocator)
|
||||
, m_canvas(allocator)
|
||||
, m_rect_hovered(allocator)
|
||||
, m_draw_2d(allocator)
|
||||
, m_rect_hovered_out(allocator)
|
||||
, m_rect_mouse_down(allocator)
|
||||
, m_unhandled_mouse_button(allocator)
|
||||
|
@ -206,6 +205,8 @@ struct GUISceneImpl final : GUIScene
|
|||
m_font_manager = (FontManager*)system.getEngine().getResourceManager().get(FontResource::TYPE);
|
||||
}
|
||||
|
||||
i32 getVersion() const { return (i32)Version::LATEST; }
|
||||
|
||||
void renderTextCursor(GUIRect& rect, Draw2D& draw, const Vec2& pos)
|
||||
{
|
||||
if (!rect.input_field) return;
|
||||
|
@ -222,8 +223,7 @@ struct GUISceneImpl final : GUIScene
|
|||
, 1);
|
||||
}
|
||||
|
||||
|
||||
void renderRect(GUIRect& rect, Pipeline& pipeline, const Rect& parent_rect, bool is_main)
|
||||
void renderRect(GUIRect& rect, Draw2D& draw, const Rect& parent_rect, bool is_main)
|
||||
{
|
||||
if (!rect.flags.isSet(GUIRect::IS_VALID)) return;
|
||||
if (!rect.flags.isSet(GUIRect::IS_ENABLED)) return;
|
||||
|
@ -233,7 +233,6 @@ struct GUISceneImpl final : GUIScene
|
|||
float t = parent_rect.y + rect.top.points + parent_rect.h * rect.top.relative;
|
||||
float b = parent_rect.y + rect.bottom.points + parent_rect.h * rect.bottom.relative;
|
||||
|
||||
Draw2D& draw = pipeline.getDraw2D();
|
||||
if (rect.flags.isSet(GUIRect::IS_CLIP)) draw.pushClipRect({ l, t }, { r, b });
|
||||
|
||||
auto button_iter = m_buttons.find(rect.entity);
|
||||
|
@ -340,7 +339,7 @@ struct GUISceneImpl final : GUIScene
|
|||
auto iter = m_rects.find((EntityRef)child);
|
||||
if (iter.isValid())
|
||||
{
|
||||
renderRect(*iter.value(), pipeline, { l, t, r - l, b - t }, is_main);
|
||||
renderRect(*iter.value(), draw, { l, t, r - l, b - t }, is_main);
|
||||
}
|
||||
child = m_universe.getNextSibling((EntityRef)child);
|
||||
}
|
||||
|
@ -349,6 +348,23 @@ struct GUISceneImpl final : GUIScene
|
|||
|
||||
IVec2 getCursorPosition() override { return m_cursor_pos; }
|
||||
|
||||
void draw3D(GUICanvas& canvas, Pipeline& pipeline) {
|
||||
m_draw_2d.clear({2, 2});
|
||||
|
||||
EntityPtr child = m_universe.getFirstChild(canvas.entity);
|
||||
while (child.isValid())
|
||||
{
|
||||
auto iter = m_rects.find((EntityRef)child);
|
||||
if (iter.isValid())
|
||||
{
|
||||
renderRect(*iter.value(), m_draw_2d, { 0, 0, canvas.virtual_size.x, canvas.virtual_size.y }, false);
|
||||
}
|
||||
child = m_universe.getNextSibling((EntityRef)child);
|
||||
}
|
||||
|
||||
pipeline.render3DUI(canvas.entity, m_draw_2d, canvas.virtual_size, canvas.orient_to_camera);
|
||||
}
|
||||
|
||||
void render(Pipeline& pipeline, const Vec2& canvas_size, bool is_main) override {
|
||||
m_canvas_size = canvas_size;
|
||||
if (is_main) {
|
||||
|
@ -356,10 +372,15 @@ struct GUISceneImpl final : GUIScene
|
|||
m_cursor_set = false;
|
||||
}
|
||||
for (GUICanvas& canvas : m_canvas) {
|
||||
if (canvas.is_3d) {
|
||||
draw3D(canvas, pipeline);
|
||||
}
|
||||
else {
|
||||
auto iter = m_rects.find(canvas.entity);
|
||||
if (iter.isValid()) {
|
||||
GUIRect* r = iter.value();
|
||||
renderRect(*r, pipeline, {0, 0, canvas_size.x, canvas_size.y}, is_main);
|
||||
renderRect(*r, pipeline.getDraw2D(), {0, 0, canvas_size.x, canvas_size.y}, is_main);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -422,6 +443,9 @@ struct GUISceneImpl final : GUIScene
|
|||
return image->sprite ? image->sprite->getPath() : Path();
|
||||
}
|
||||
|
||||
GUICanvas& getCanvas(EntityRef entity) override {
|
||||
return m_canvas[entity];
|
||||
}
|
||||
|
||||
void setImageSprite(EntityRef entity, const Path& path) override
|
||||
{
|
||||
|
@ -1150,6 +1174,8 @@ struct GUISceneImpl final : GUIScene
|
|||
for (GUICanvas& c : m_canvas) {
|
||||
serializer.write(c.entity);
|
||||
serializer.write(c.is_3d);
|
||||
serializer.write(c.orient_to_camera);
|
||||
serializer.write(c.virtual_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1242,6 +1268,11 @@ struct GUISceneImpl final : GUIScene
|
|||
GUICanvas canvas;
|
||||
serializer.read(canvas.entity);
|
||||
serializer.read(canvas.is_3d);
|
||||
if (version > (i32)Version::CANVAS_3D) {
|
||||
serializer.read(canvas.orient_to_camera);
|
||||
serializer.read(canvas.virtual_size);
|
||||
}
|
||||
|
||||
canvas.entity = entity_map.get(canvas.entity);
|
||||
m_canvas.insert(canvas.entity, canvas);
|
||||
|
||||
|
@ -1286,6 +1317,7 @@ struct GUISceneImpl final : GUIScene
|
|||
DelegateList<void(EntityRef)> m_rect_hovered_out;
|
||||
DelegateList<void(EntityRef, float, float)> m_rect_mouse_down;
|
||||
DelegateList<void(bool, i32, i32)> m_unhandled_mouse_button;
|
||||
Draw2D m_draw_2d;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@ namespace OS { enum class CursorType : u32; }
|
|||
|
||||
template <typename T> struct DelegateList;
|
||||
|
||||
struct GUICanvas {
|
||||
EntityRef entity;
|
||||
bool is_3d = false;
|
||||
bool orient_to_camera = true;
|
||||
Vec2 virtual_size = Vec2(1000);
|
||||
};
|
||||
|
||||
struct GUIScene : IScene
|
||||
{
|
||||
|
@ -84,6 +90,8 @@ struct GUIScene : IScene
|
|||
virtual struct Path getImageSprite(EntityRef entity) = 0;
|
||||
virtual void setImageSprite(EntityRef entity, const Path& path) = 0;
|
||||
|
||||
virtual GUICanvas& getCanvas(EntityRef entity) = 0;
|
||||
|
||||
virtual void setText(EntityRef entity, const char* text) = 0;
|
||||
virtual const char* getText(EntityRef entity) = 0;
|
||||
virtual TextHAlign getTextHAlign(EntityRef entity) = 0;
|
||||
|
|
|
@ -125,7 +125,11 @@ struct GUISystemImpl final : GUISystem
|
|||
property("Color", LUMIX_PROP(GUIScene, TextColorRGBA), ColorAttribute())
|
||||
),
|
||||
component("gui_input_field"),
|
||||
component("gui_canvas"),
|
||||
component("gui_canvas",
|
||||
var_property("Is 3D", &GUIScene::getCanvas, &GUICanvas::is_3d),
|
||||
var_property("Orient to camera", &GUIScene::getCanvas, &GUICanvas::orient_to_camera),
|
||||
var_property("Virtual size", &GUIScene::getCanvas, &GUICanvas::virtual_size)
|
||||
),
|
||||
component("gui_button",
|
||||
property("Hovered color", LUMIX_PROP(GUIScene, ButtonHoveredColorRGBA), ColorAttribute()),
|
||||
enum_property("Cursor", LUMIX_PROP(GUIScene, ButtonHoveredCursor), CursorEnum())
|
||||
|
|
|
@ -1075,6 +1075,36 @@ struct PipelineImpl final : Pipeline
|
|||
return true;
|
||||
}
|
||||
|
||||
void render3DUI(EntityRef e, const Draw2D& drawdata, Vec2 canvas_size, bool orient_to_cam) override {
|
||||
if (!m_draw2d_shader->isReady()) return;
|
||||
if (drawdata.getIndices().empty()) return;
|
||||
|
||||
const Texture* atlas_texture = m_renderer.getFontManager().getAtlasTexture();
|
||||
|
||||
IAllocator& allocator = m_renderer.getAllocator();
|
||||
Draw2DJob& cmd = m_renderer.createJob<Draw2DJob>(allocator);
|
||||
cmd.pipeline = this;
|
||||
cmd.atlas_texture = atlas_texture->handle;
|
||||
cmd.prepare(drawdata);
|
||||
cmd.matrix = m_scene->getUniverse().getRelativeMatrix(e, m_viewport.pos);
|
||||
cmd.is_3d = true;
|
||||
Matrix normalize(
|
||||
Vec4(1 / canvas_size.x, 0, 0, 0),
|
||||
Vec4(0, -1 / canvas_size.x, 0, 0),
|
||||
Vec4(0, 0, 1, 0),
|
||||
Vec4(-0.5f, 0.5f * canvas_size.y / canvas_size.x, 0, 1)
|
||||
);
|
||||
if (orient_to_cam) {
|
||||
const Transform tr = m_scene->getUniverse().getTransform(e);
|
||||
cmd.matrix = m_viewport.rot.toMatrix();
|
||||
cmd.matrix.setTranslation((tr.pos - m_viewport.pos).toFloat());
|
||||
cmd.matrix.multiply3x3(tr.scale);
|
||||
}
|
||||
cmd.matrix = m_viewport.getProjection() * m_viewport.getViewRotation() * cmd.matrix * normalize;
|
||||
m_renderer.queue(cmd, m_profiler_link);
|
||||
}
|
||||
|
||||
|
||||
bool render(bool only_2d) override
|
||||
{
|
||||
PROFILE_FUNCTION();
|
||||
|
@ -1324,28 +1354,11 @@ struct PipelineImpl final : Pipeline
|
|||
//renderDebugPoints();
|
||||
}
|
||||
|
||||
void render2D()
|
||||
{
|
||||
if (!m_draw2d_shader->isReady()) {
|
||||
m_draw2d.clear(getAtlasSize());
|
||||
return;
|
||||
}
|
||||
struct Draw2DJob : Renderer::RenderJob {
|
||||
Draw2DJob(IAllocator& allocator) : cmd_buffer(allocator) {}
|
||||
|
||||
if (m_draw2d.getIndices().empty()) {
|
||||
m_draw2d.clear(getAtlasSize());
|
||||
return;
|
||||
}
|
||||
|
||||
struct Cmd : Renderer::RenderJob
|
||||
{
|
||||
Cmd(IAllocator& allocator) : cmd_buffer(allocator) {}
|
||||
|
||||
void setup()
|
||||
{
|
||||
void prepare(const Draw2D& draw2d) {
|
||||
PROFILE_FUNCTION();
|
||||
size.set((float)pipeline->m_viewport.w, (float)pipeline->m_viewport.h);
|
||||
|
||||
Draw2D& draw2d = pipeline->m_draw2d;
|
||||
|
||||
num_indices = draw2d.getIndices().size();
|
||||
num_vertices = draw2d.getVertices().size();
|
||||
|
@ -1357,23 +1370,23 @@ struct PipelineImpl final : Pipeline
|
|||
cmd_buffer.resize(draw2d.getCmds().size());
|
||||
memcpy(&cmd_buffer[0], draw2d.getCmds().begin(), sizeof(cmd_buffer[0]) * cmd_buffer.size());
|
||||
|
||||
draw2d.clear(pipeline->getAtlasSize());
|
||||
|
||||
program = pipeline->m_draw2d_shader->getProgram(pipeline->m_2D_decl, 0);
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
void setup() override {}
|
||||
|
||||
void execute() override {
|
||||
PROFILE_FUNCTION();
|
||||
|
||||
if (cmd_buffer.empty()) return;
|
||||
|
||||
gpu::pushDebugGroup("draw2d");
|
||||
|
||||
gpu::update(pipeline->m_drawcall_ub, &size.x, sizeof(size));
|
||||
gpu::update(pipeline->m_drawcall_ub, &matrix.columns[0].x, sizeof(matrix));
|
||||
u32 elem_offset = 0;
|
||||
gpu::StateFlags state = gpu::getBlendStateBits(gpu::BlendFactors::SRC_ALPHA, gpu::BlendFactors::ONE_MINUS_SRC_ALPHA, gpu::BlendFactors::ONE, gpu::BlendFactors::ONE);
|
||||
state = state | gpu::StateFlags::SCISSOR_TEST;
|
||||
if (is_3d) state = state | gpu::StateFlags::DEPTH_TEST;
|
||||
gpu::setState(state);
|
||||
gpu::useProgram(program);
|
||||
gpu::bindIndexBuffer(idx_buffer_mem.buffer);
|
||||
|
@ -1419,17 +1432,32 @@ struct PipelineImpl final : Pipeline
|
|||
int num_indices;
|
||||
int num_vertices;
|
||||
Array<Draw2D::Cmd> cmd_buffer;
|
||||
Vec2 size;
|
||||
PipelineImpl* pipeline;
|
||||
gpu::ProgramHandle program;
|
||||
Matrix matrix;
|
||||
bool is_3d = false;
|
||||
};
|
||||
|
||||
void render2D() {
|
||||
if (!m_draw2d_shader->isReady()) {
|
||||
m_draw2d.clear(getAtlasSize());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_draw2d.getIndices().empty()) {
|
||||
m_draw2d.clear(getAtlasSize());
|
||||
return;
|
||||
}
|
||||
|
||||
const Texture* atlas_texture = m_renderer.getFontManager().getAtlasTexture();
|
||||
|
||||
IAllocator& allocator = m_renderer.getAllocator();
|
||||
Cmd& cmd = m_renderer.createJob<Cmd>(allocator);
|
||||
Draw2DJob& cmd = m_renderer.createJob<Draw2DJob>(allocator);
|
||||
cmd.pipeline = this;
|
||||
cmd.atlas_texture = atlas_texture->handle;
|
||||
cmd.matrix.setOrtho(0, (float)m_viewport.w, (float)m_viewport.h, 0, 0, 1, false);
|
||||
cmd.prepare(m_draw2d);
|
||||
m_draw2d.clear(getAtlasSize());
|
||||
m_renderer.queue(cmd, m_profiler_link);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ struct LUMIX_RENDERER_API Pipeline
|
|||
virtual ~Pipeline() {}
|
||||
|
||||
virtual bool render(bool only_2d) = 0;
|
||||
virtual void render3DUI(EntityRef e, const struct Draw2D& drawdata, Vec2 canvas_size, bool orient_to_cam) = 0;
|
||||
virtual void setUniverse(struct Universe* universe) = 0;
|
||||
virtual RenderScene* getScene() const = 0;
|
||||
virtual CustomCommandHandler& addCustomCommandHandler(const char* name) = 0;
|
||||
|
|
Loading…
Reference in a new issue