3d gui - closes #1225

This commit is contained in:
Mikulas Florek 2020-11-28 23:33:49 +01:00
parent 0686c3c439
commit b96a3c137a
9 changed files with 188 additions and 112 deletions

View file

@ -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);
}
]]

View file

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

View file

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

View file

@ -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) {
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);
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.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;
};

View file

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

View file

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

View file

@ -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,8 +1354,91 @@ struct PipelineImpl final : Pipeline
//renderDebugPoints();
}
void render2D()
{
struct Draw2DJob : Renderer::RenderJob {
Draw2DJob(IAllocator& allocator) : cmd_buffer(allocator) {}
void prepare(const Draw2D& draw2d) {
PROFILE_FUNCTION();
num_indices = draw2d.getIndices().size();
num_vertices = draw2d.getVertices().size();
idx_buffer_mem = pipeline->m_renderer.allocTransient(draw2d.getIndices().byte_size());
vtx_buffer_mem = pipeline->m_renderer.allocTransient(draw2d.getVertices().byte_size());
memcpy(idx_buffer_mem.ptr, draw2d.getIndices().begin(), draw2d.getIndices().byte_size());
memcpy(vtx_buffer_mem.ptr, draw2d.getVertices().begin(), draw2d.getVertices().byte_size());
cmd_buffer.resize(draw2d.getCmds().size());
memcpy(&cmd_buffer[0], draw2d.getCmds().begin(), sizeof(cmd_buffer[0]) * cmd_buffer.size());
program = pipeline->m_draw2d_shader->getProgram(pipeline->m_2D_decl, 0);
}
void setup() override {}
void execute() override {
PROFILE_FUNCTION();
if (cmd_buffer.empty()) return;
gpu::pushDebugGroup("draw2d");
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);
gpu::bindVertexBuffer(0, vtx_buffer_mem.buffer, vtx_buffer_mem.offset, 20);
gpu::bindVertexBuffer(1, gpu::INVALID_BUFFER, 0, 0);
for (Draw2D::Cmd& cmd : cmd_buffer) {
if(cmd.clip_size.x < 0) {
gpu::scissor(0, 0, pipeline->m_viewport.w, pipeline->m_viewport.h);
}
else {
const u32 h = u32(clamp(cmd.clip_size.y, 0.f, 65535.f));
if (gpu::isOriginBottomLeft()) {
gpu::scissor(u32(maximum(cmd.clip_pos.x, 0.0f)),
pipeline->m_viewport.h - u32(maximum(cmd.clip_pos.y, 0.0f)) - h,
u32(minimum(cmd.clip_size.x, 65535.0f)),
u32(minimum(cmd.clip_size.y, 65535.0f)));
}
else {
gpu::scissor(u32(maximum(cmd.clip_pos.x, 0.0f)),
u32(maximum(cmd.clip_pos.y, 0.0f)),
u32(minimum(cmd.clip_size.x, 65535.0f)),
u32(minimum(cmd.clip_size.y, 65535.0f)));
}
}
gpu::TextureHandle texture_id = atlas_texture;
if (cmd.texture) texture_id = *cmd.texture;
if (!texture_id) texture_id = atlas_texture;
gpu::bindTextures(&texture_id, 0, 1);
gpu::drawElements(idx_buffer_mem.offset + elem_offset * sizeof(u32), cmd.indices_count, gpu::PrimitiveType::TRIANGLES, gpu::DataType::U32);
elem_offset += cmd.indices_count;
}
gpu::popDebugGroup();
}
gpu::TextureHandle atlas_texture;
Renderer::TransientSlice idx_buffer_mem;
Renderer::TransientSlice vtx_buffer_mem;
int num_indices;
int num_vertices;
Array<Draw2D::Cmd> cmd_buffer;
PipelineImpl* pipeline;
gpu::ProgramHandle program;
Matrix matrix;
bool is_3d = false;
};
void render2D() {
if (!m_draw2d_shader->isReady()) {
m_draw2d.clear(getAtlasSize());
return;
@ -1336,100 +1449,15 @@ struct PipelineImpl final : Pipeline
return;
}
struct Cmd : Renderer::RenderJob
{
Cmd(IAllocator& allocator) : cmd_buffer(allocator) {}
void setup()
{
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();
idx_buffer_mem = pipeline->m_renderer.allocTransient(draw2d.getIndices().byte_size());
vtx_buffer_mem = pipeline->m_renderer.allocTransient(draw2d.getVertices().byte_size());
memcpy(idx_buffer_mem.ptr, draw2d.getIndices().begin(), draw2d.getIndices().byte_size());
memcpy(vtx_buffer_mem.ptr, draw2d.getVertices().begin(), draw2d.getVertices().byte_size());
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()
{
PROFILE_FUNCTION();
if (cmd_buffer.empty()) return;
gpu::pushDebugGroup("draw2d");
gpu::update(pipeline->m_drawcall_ub, &size.x, sizeof(size));
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;
gpu::setState(state);
gpu::useProgram(program);
gpu::bindIndexBuffer(idx_buffer_mem.buffer);
gpu::bindVertexBuffer(0, vtx_buffer_mem.buffer, vtx_buffer_mem.offset, 20);
gpu::bindVertexBuffer(1, gpu::INVALID_BUFFER, 0, 0);
for (Draw2D::Cmd& cmd : cmd_buffer) {
if(cmd.clip_size.x < 0) {
gpu::scissor(0, 0, pipeline->m_viewport.w, pipeline->m_viewport.h);
}
else {
const u32 h = u32(clamp(cmd.clip_size.y, 0.f, 65535.f));
if (gpu::isOriginBottomLeft()) {
gpu::scissor(u32(maximum(cmd.clip_pos.x, 0.0f)),
pipeline->m_viewport.h - u32(maximum(cmd.clip_pos.y, 0.0f)) - h,
u32(minimum(cmd.clip_size.x, 65535.0f)),
u32(minimum(cmd.clip_size.y, 65535.0f)));
}
else {
gpu::scissor(u32(maximum(cmd.clip_pos.x, 0.0f)),
u32(maximum(cmd.clip_pos.y, 0.0f)),
u32(minimum(cmd.clip_size.x, 65535.0f)),
u32(minimum(cmd.clip_size.y, 65535.0f)));
}
}
gpu::TextureHandle texture_id = atlas_texture;
if (cmd.texture) texture_id = *cmd.texture;
if (!texture_id) texture_id = atlas_texture;
gpu::bindTextures(&texture_id, 0, 1);
gpu::drawElements(idx_buffer_mem.offset + elem_offset * sizeof(u32), cmd.indices_count, gpu::PrimitiveType::TRIANGLES, gpu::DataType::U32);
elem_offset += cmd.indices_count;
}
gpu::popDebugGroup();
}
gpu::TextureHandle atlas_texture;
Renderer::TransientSlice idx_buffer_mem;
Renderer::TransientSlice vtx_buffer_mem;
int num_indices;
int num_vertices;
Array<Draw2D::Cmd> cmd_buffer;
Vec2 size;
PipelineImpl* pipeline;
gpu::ProgramHandle program;
};
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);
}

View file

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