Проблема мерцания Nuklear OpenGL - PullRequest
0 голосов
/ 19 апреля 2020

Я скачал демонстрационную программу для Nucklear в LWJGL, мне удалось скомпилировать ее и заставить ее успешно работать. Затем я попытался внедрить тот же код в свой игровой движок, и ничего не появляется, если я не отключу glClear (GL_COLOR_BUFFER_BIT). В этот момент я вижу, как мерцает маленькое окно Nuklear.

Это мой класс Window

package Engine.Renderer;

import Engine.Messages.AppMsg;
import Engine.IntApplication;
import Engine.Messages.Type;  
import Simulator.Application;
import org.lwjgl.nuklear.NkColorf;
import org.lwjgl.nuklear.NkMouse;
import org.lwjgl.opengl.*;
import org.lwjgl.system.Callback;
import org.lwjgl.system.MemoryStack;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.nuklear.Nuklear.*;
import static org.lwjgl.opengl.ARBDebugOutput.*;
import static org.lwjgl.opengl.ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.*;

public class Window
{
public Window(int width, int height, String title)
{
    m_Width = width;
    m_Height = height;
    m_Title = title;

    if(!glfwInit())
    {
        ///TODO: LOG
        Runtime.getRuntime().exit(1);
    }

    glfwDefaultWindowHints();
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

    m_Window = glfwCreateWindow(m_Width, m_Height, m_Title, NULL, NULL);
    if(m_Window == NULL)
    {
        ///TODO: LOG
        Runtime.getRuntime().exit(1);
    }

    glfwMakeContextCurrent(m_Window);
    GLCapabilities caps = GL.createCapabilities();
    glfwShowWindow(m_Window);

    Callback debugProc = GLUtil.setupDebugMessageCallback();

    if (caps.OpenGL43) {
        GL43.glDebugMessageControl(GL43.GL_DEBUG_SOURCE_API, GL43.GL_DEBUG_TYPE_OTHER, GL43.GL_DEBUG_SEVERITY_NOTIFICATION, (IntBuffer)null, false);
    } else if (caps.GL_KHR_debug) {
        KHRDebug.glDebugMessageControl(
                KHRDebug.GL_DEBUG_SOURCE_API,
                KHRDebug.GL_DEBUG_TYPE_OTHER,
                KHRDebug.GL_DEBUG_SEVERITY_NOTIFICATION,
                (IntBuffer)null,
                false
        );
    } else if (caps.GL_ARB_debug_output) {
        glDebugMessageControlARB(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_LOW_ARB, (IntBuffer)null, false);
    }

    SetUpWindow();
    renderer = new GUIRenderer();
}

public void Draw()
{

}

public void processEvents()
{
    try (MemoryStack stack = stackPush()) {
        IntBuffer w = stack.mallocInt(1);
        IntBuffer h = stack.mallocInt(1);

        glfwGetWindowSize(m_Window, w, h);
        m_Width = w.get(0);
        m_Height = h.get(0);

        glfwGetFramebufferSize(m_Window, w, h);
        m_DisplayWidth = w.get(0);
        m_DisplayHeight = h.get(0);
    }

    nk_input_begin(NuklearContainer.ctx);
    glfwPollEvents();

    NkMouse mouse = NuklearContainer.ctx.input().mouse();
    if (mouse.grab()) {
        glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
    } else if (mouse.grabbed()) {
        float prevX = mouse.prev().x();
        float prevY = mouse.prev().y();
        glfwSetCursorPos(m_Window, prevX, prevY);
        mouse.pos().x(prevX);
        mouse.pos().y(prevY);
    } else if (mouse.ungrab()) {
        glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
    }

    nk_input_end(NuklearContainer.ctx);

    try (MemoryStack stack = stackPush()) {
        IntBuffer width  = stack.mallocInt(1);
        IntBuffer height = stack.mallocInt(1);

        glfwGetWindowSize(m_Window, width, height);
        glViewport(0, 0, width.get(0), height.get(0));

        NkColorf bg = NkColorf.create().r(0.10f).g(0.18f).b(0.24f).a(1.0f);
        glClearColor(bg.r(), bg.g(), bg.b(), bg.a());
    }
    //glClear(GL_COLOR_BUFFER_BIT);
    renderer.Render();
    glfwSwapBuffers(m_Window);
}

public void CleanUp()
{
    glfwFreeCallbacks(m_Window);
    glfwDestroyWindow(m_Window);
    glfwTerminate();
}

private void SetUpWindow()
{
    glfwSetKeyCallback(m_Window, (window, key, scancode, action, mods) ->
    {
        AppMsg msg = new AppMsg();
        msg.key = key;
        msg.scancode = scancode;
        msg.mods = mods;

        if(action == 0)
            msg.type = Type.KEYUP;
        if(action == 1)
            msg.type = Type.KEYDOWN;
        if(action == 2)
            msg.type = Type.KEYREPEAT;

        IntApplication.OnMsgProc(msg);
    });

    glfwSetMouseButtonCallback(m_Window, (window, button, action, mods) ->
    {
        AppMsg msg = new AppMsg();
        msg.button = button;
        msg.mods = mods;

        if(action == 0)
            msg.type = Type.MOUSEUP;
        if(action == 1)
            msg.type = Type.MOUSEDOWN;

        Application.OnMsgProc(msg);
    });

    glfwSetCursorPosCallback(m_Window, (window, xpos, ypos) ->
    {
        AppMsg msg = new AppMsg();
        msg.xpos = xpos;
        msg.ypos = ypos;
        msg.type = Type.MOUSEMOVE;

        Application.OnMsgProc(msg);
    });

    glfwSetWindowCloseCallback(m_Window, (window) ->
            IntApplication.OnClose());

    nk_init(NuklearContainer.ctx, NuklearContainer.ALLOCATOR, null);
    NuklearContainer.ctx.clip()
            .copy((handle, text, len) -> {
                if (len == 0) {
                    return;
                }

                try (MemoryStack stack = stackPush()) {
                    ByteBuffer str = stack.malloc(len + 1);
                    memCopy(text, memAddress(str), len);
                    str.put(len, (byte)0);

                    glfwSetClipboardString(m_Window, str);
                }
            })
            .paste((handle, edit) -> {
                long text = nglfwGetClipboardString(m_Window);
                if (text != NULL) {
                    nnk_textedit_paste(edit, text, nnk_strlen(text));
                }
            });
}

private long m_Window;
static int m_Width, m_Height;
static int m_DisplayWidth, m_DisplayHeight;
private String m_Title;
GUIRenderer renderer;
}

А вот мой класс GUIRender

package Engine.Renderer;

import static org.lwjgl.nuklear.Nuklear.*;
import static org.lwjgl.opengl.GL20C.*;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.stb.STBTruetype.*;
import static org.lwjgl.stb.STBTruetype.stbtt_GetCodepointHMetrics;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.*;
import static org.lwjgl.system.MemoryUtil.memAddress;

import Engine.IOUtil;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Objects;

import org.lwjgl.nuklear.*;
import org.lwjgl.stb.STBTTAlignedQuad;
import org.lwjgl.stb.STBTTFontinfo;
import org.lwjgl.stb.STBTTPackContext;
import org.lwjgl.stb.STBTTPackedchar;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.Platform;

public class GUIRenderer extends Renderer
{
public GUIRenderer()
{
    super();

    try
    {
        this.ttf = IOUtil.ioResourceToByteBuffer("C:/Windows/Fonts/Arial.ttf", 512 * 1024);
    } catch (IOException e)
    {
        throw new RuntimeException(e);
    }

    if (!m_Initialized)
        Initialize();

    try (MemoryStack stack = stackPush()) {
        NkRect rect = NkRect.mallocStack(stack);

        if (nk_begin(
                NuklearContainer.ctx,
                "Hello World",
                nk_rect(50, 50, 230, 250, rect),
                NK_WINDOW_BORDER | NK_WINDOW_NO_INPUT | NK_WINDOW_NO_INPUT | NK_WINDOW_NO_INPUT | NK_WINDOW_TITLE
        )) {
            nk_layout_row_static(NuklearContainer.ctx, 20, 80, 1);
            nk_label(NuklearContainer.ctx, "background:", NK_TEXT_LEFT);
            }
        }
        nk_end(NuklearContainer.ctx);
}

private void Initialize()
{
    String NK_SHADER_VERSION = Platform.get() == Platform.MACOSX ? "#version 150\n" : "#version 300 es\n";
    String vertex_shader =
            NK_SHADER_VERSION +
                    "uniform mat4 ProjMtx;\n" +
                    "in vec2 Position;\n" +
                    "in vec2 TexCoord;\n" +
                    "in vec4 Color;\n" +
                    "out vec2 Frag_UV;\n" +
                    "out vec4 Frag_Color;\n" +
                    "void main() {\n" +
                    "   Frag_UV = TexCoord;\n" +
                    "   Frag_Color = Color;\n" +
                    "   gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" +
                    "}\n";
    String fragment_shader =
            NK_SHADER_VERSION +
                    "precision mediump float;\n" +
                    "uniform sampler2D Texture;\n" +
                    "in vec2 Frag_UV;\n" +
                    "in vec4 Frag_Color;\n" +
                    "out vec4 Out_Color;\n" +
                    "void main(){\n" +
                    "   Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" +
                    "}\n";

    nk_buffer_init(NuklearContainer.cmds, NuklearContainer.ALLOCATOR, BUFFER_INITIAL_SIZE);
    m_Program = glCreateProgram();
    m_Vertex_Shader = glCreateShader(GL_VERTEX_SHADER);
    m_Fragment_Shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(m_Vertex_Shader, vertex_shader);
    glShaderSource(m_Fragment_Shader, fragment_shader);
    glCompileShader(m_Vertex_Shader);
    glCompileShader(m_Fragment_Shader);
    if (glGetShaderi(m_Vertex_Shader, GL_COMPILE_STATUS) != GL_TRUE)
    {
        throw new IllegalStateException();
    }
    if (glGetShaderi(m_Fragment_Shader, GL_COMPILE_STATUS) != GL_TRUE)
    {
        throw new IllegalStateException();
    }
    glAttachShader(m_Program, m_Vertex_Shader);
    glAttachShader(m_Program, m_Fragment_Shader);
    glLinkProgram(m_Program);
    if (glGetProgrami(m_Program, GL_LINK_STATUS) != GL_TRUE)
    {
        throw new IllegalStateException();
    }

    m_Uniform_Texture = glGetUniformLocation(m_Program, "Texture");
    m_Uniform_Proj = glGetUniformLocation(m_Program, "ProjMtx");
    int attrib_pos = glGetAttribLocation(m_Program, "Position");
    int attrib_uv = glGetAttribLocation(m_Program, "TexCoord");
    int attrib_col = glGetAttribLocation(m_Program, "Color");

    {
        // buffer setup
        m_Vbo = glGenBuffers();
        m_Ebo = glGenBuffers();
        m_Vao = glGenVertexArrays();

        glBindVertexArray(m_Vao);
        glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Ebo);

        glEnableVertexAttribArray(attrib_pos);
        glEnableVertexAttribArray(attrib_uv);
        glEnableVertexAttribArray(attrib_col);

        glVertexAttribPointer(attrib_pos, 2, GL_FLOAT, false, 20, 0);
        glVertexAttribPointer(attrib_uv, 2, GL_FLOAT, false, 20, 8);
        glVertexAttribPointer(attrib_col, 4, GL_UNSIGNED_BYTE, true, 20, 16);
    }

    {
        // null texture setup
        int nullTexID = glGenTextures();

        NuklearContainer.null_texture.texture().id(nullTexID);
        NuklearContainer.null_texture.uv().set(0.5f, 0.5f);

        glBindTexture(GL_TEXTURE_2D, nullTexID);
        try (MemoryStack stack = stackPush())
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, stack.ints(0xFFFFFFFF));
        }
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    }

    glBindTexture(GL_TEXTURE_2D, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    FontInit();
}

private void FontInit()
{
    int BITMAP_W = 1024;
    int BITMAP_H = 1024;

    int FONT_HEIGHT = 18;
    int fontTexID   = glGenTextures();

    STBTTFontinfo fontInfo = STBTTFontinfo.create();
    STBTTPackedchar.Buffer cdata    = STBTTPackedchar.create(95);

    float scale;
    float descent;

    try (MemoryStack stack = stackPush()) {
        stbtt_InitFont(fontInfo, ttf);
        scale = stbtt_ScaleForPixelHeight(fontInfo, FONT_HEIGHT);

        IntBuffer d = stack.mallocInt(1);
        stbtt_GetFontVMetrics(fontInfo, null, d, null);
        descent = d.get(0) * scale;

        ByteBuffer bitmap = memAlloc(BITMAP_W * BITMAP_H);

        STBTTPackContext pc = STBTTPackContext.mallocStack(stack);
        stbtt_PackBegin(pc, bitmap, BITMAP_W, BITMAP_H, 0, 1, NULL);
        stbtt_PackSetOversampling(pc, 4, 4);
        stbtt_PackFontRange(pc, ttf, 0, FONT_HEIGHT, 32, cdata);
        stbtt_PackEnd(pc);

        // Convert R8 to RGBA8
        ByteBuffer texture = memAlloc(BITMAP_W * BITMAP_H * 4);
        for (int i = 0; i < bitmap.capacity(); i++) {
            texture.putInt((bitmap.get(i) << 24) | 0x00FFFFFF);
        }
        texture.flip();

        glBindTexture(GL_TEXTURE_2D, fontTexID);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BITMAP_W, BITMAP_H, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        memFree(texture);
        memFree(bitmap);
    }

    NuklearContainer.default_font
            .width((handle, h, text, len) -> {
                float text_width = 0;
                try (MemoryStack stack = stackPush()) {
                    IntBuffer unicode = stack.mallocInt(1);

                    int glyph_len = nnk_utf_decode(text, memAddress(unicode), len);
                    int text_len  = glyph_len;

                    if (glyph_len == 0) {
                        return 0;
                    }

                    IntBuffer advance = stack.mallocInt(1);
                    while (text_len <= len && glyph_len != 0) {
                        if (unicode.get(0) == NK_UTF_INVALID) {
                            break;
                        }

                        /* query currently drawn glyph information */
                        stbtt_GetCodepointHMetrics(fontInfo, unicode.get(0), advance, null);
                        text_width += advance.get(0) * scale;

                        /* offset next glyph */
                        glyph_len = nnk_utf_decode(text + text_len, memAddress(unicode), len - text_len);
                        text_len += glyph_len;
                    }
                }
                return text_width;
            })
            .height(FONT_HEIGHT)
            .query((handle, font_height, glyph, codepoint, next_codepoint) -> {
                try (MemoryStack stack = stackPush()) {
                    FloatBuffer x = stack.floats(0.0f);
                    FloatBuffer y = stack.floats(0.0f);

                    STBTTAlignedQuad q       = STBTTAlignedQuad.mallocStack(stack);
                    IntBuffer        advance = stack.mallocInt(1);

                    stbtt_GetPackedQuad(cdata, BITMAP_W, BITMAP_H, codepoint - 32, x, y, q, false);
                    stbtt_GetCodepointHMetrics(fontInfo, codepoint, advance, null);

                    NkUserFontGlyph ufg = NkUserFontGlyph.create(glyph);

                    ufg.width(q.x1() - q.x0());
                    ufg.height(q.y1() - q.y0());
                    ufg.offset().set(q.x0(), q.y0() + (FONT_HEIGHT + descent));
                    ufg.xadvance(advance.get(0) * scale);
                    ufg.uv(0).set(q.s0(), q.t0());
                    ufg.uv(1).set(q.s1(), q.t1());
                }
            })
            .texture(it -> it
                    .id(fontTexID));

    nk_style_set_font(NuklearContainer.ctx, NuklearContainer.default_font);
}

public void Render()
{
    try (MemoryStack stack = stackPush())
    {
        // setup global state
        glEnable(GL_BLEND);
        glBlendEquation(GL_FUNC_ADD);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDisable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_SCISSOR_TEST);
        glActiveTexture(GL_TEXTURE0);

        // setup program
        glUseProgram(m_Program);
        glUniform1i(m_Uniform_Texture, 0);
        glUniformMatrix4fv(m_Uniform_Proj, false, stack.floats(
                2.0f / Window.m_Width, 0.0f, 0.0f, 0.0f,
                0.0f, -2.0f / Window.m_Height, 0.0f, 0.0f,
                0.0f, 0.0f, -1.0f, 0.0f,
                -1.0f, 1.0f, 0.0f, 1.0f
        ));
        glViewport(0, 0, Window.m_DisplayWidth, Window.m_DisplayHeight);
    }

    {
        // convert from command queue into draw list and draw to screen

        // allocate vertex and element buffer
        glBindVertexArray(m_Vao);
        glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Ebo);

        glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, GL_STREAM_DRAW);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, GL_STREAM_DRAW);

        // load draw vertices & elements directly into vertex + element buffer
        ByteBuffer vertices = Objects.requireNonNull(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, max_vertex_buffer, null));
        ByteBuffer elements = Objects.requireNonNull(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY, max_element_buffer, null));
        try (MemoryStack stack = stackPush())
        {
            // fill convert configuration
            NkConvertConfig config = NkConvertConfig.callocStack(stack)
                    .vertex_layout(VERTEX_LAYOUT)
                    .vertex_size(20)
                    .vertex_alignment(4)
                    .null_texture(NuklearContainer.null_texture)
                    .circle_segment_count(22)
                    .curve_segment_count(22)
                    .arc_segment_count(22)
                    .global_alpha(1.0f)
                    .shape_AA(NK_ANTI_ALIASING_ON)
                    .line_AA(NK_ANTI_ALIASING_ON);

            // setup buffers to load vertices and elements
            NkBuffer vbuf = NkBuffer.mallocStack(stack);
            NkBuffer ebuf = NkBuffer.mallocStack(stack);

            nk_buffer_init_fixed(vbuf, vertices/*, max_vertex_buffer*/);
            nk_buffer_init_fixed(ebuf, elements/*, max_element_buffer*/);
            nk_convert(NuklearContainer.ctx, NuklearContainer.cmds, vbuf, ebuf, config);
        }
        glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
        glUnmapBuffer(GL_ARRAY_BUFFER);

        // iterate over and execute each draw command
        float fb_scale_x = (float) Window.m_DisplayWidth / (float) Window.m_Width;
        float fb_scale_y = (float) Window.m_DisplayHeight / (float) Window.m_Height;

        long offset = NULL;
        for (NkDrawCommand cmd = nk__draw_begin(NuklearContainer.ctx, NuklearContainer.cmds); cmd != null; cmd = nk__draw_next(cmd, NuklearContainer.cmds, NuklearContainer.ctx))
        {
            if (cmd.elem_count() == 0)
            {
                continue;
            }
            glBindTexture(GL_TEXTURE_2D, cmd.texture().id());
            glScissor(
                    (int) (cmd.clip_rect().x() * fb_scale_x),
                    (int) ((Window.m_Height - (int) (cmd.clip_rect().y() + cmd.clip_rect().h())) * fb_scale_y),
                    (int) (cmd.clip_rect().w() * fb_scale_x),
                    (int) (cmd.clip_rect().h() * fb_scale_y)
            );
            glDrawElements(GL_TRIANGLES, cmd.elem_count(), GL_UNSIGNED_SHORT, offset);
            offset += cmd.elem_count() * 2;
        }
        nk_clear(NuklearContainer.ctx);
    }

    glUseProgram(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    glDisable(GL_BLEND);
    glDisable(GL_SCISSOR_TEST);
}

private boolean m_Initialized;
public boolean IsInitialized() { return m_Initialized; }
private int BUFFER_INITIAL_SIZE = 4 * 1024;
long max_vertex_buffer = 512 * 1024;
long max_element_buffer = 128 * 1024;

private NkDrawVertexLayoutElement.Buffer VERTEX_LAYOUT = NkDrawVertexLayoutElement.create(4)
        .position(0).attribute(NK_VERTEX_POSITION).format(NK_FORMAT_FLOAT).offset(0)
        .position(1).attribute(NK_VERTEX_TEXCOORD).format(NK_FORMAT_FLOAT).offset(8)
        .position(2).attribute(NK_VERTEX_COLOR).format(NK_FORMAT_R8G8B8A8).offset(16)
        .position(3).attribute(NK_VERTEX_ATTRIBUTE_COUNT).format(NK_FORMAT_COUNT).offset(0)
        .flip();

private ByteBuffer ttf;
}

Я очень жаль, если код огромен, но даже GLFWDemo имеет длину 600+ строк.

...