컴퓨터 그래픽스/OpenGL

#9 Tinylender 개발일지 : TextBox 개발 및 인스펙터

san10 2024. 7. 28. 00:28

우리가 개발할때 아무렇지도 않게 쓰는 TextBox...

타이니렌더에서 직접 다 만들어야한다...

imgui쓸걸....

 

FreeType

https://learnopengl.com/In-Practice/Text-Rendering

 

LearnOpenGL - Text Rendering

Text Rendering In-Practice/Text-Rendering At some stage of your graphics adventures you will want to draw text in OpenGL. Contrary to what you may expect, getting a simple string to render on screen is all but easy with a low-level API like OpenGL. If you

learnopengl.com

텍스트는 FreeType이라는 라이브러리를 활용해서 렌더링했다.

 

이 라이브러리도 활용하는데 문제가 있었던게..

LearnOpenGL 사이트에도 나와있는데

여러군데에서 include하면 conflict이 날 수도 있다고 한다

(실제로 conflict나서인지 라이브러리 인식이 안됬다.)

 

???????

아니 여러곳에서 라이브러리를 써야하는데?

어떻게해요..

 

FreeTypeImporter.h

#pragma once

#include <ft2build.h>
#include FT_FREETYPE_H

이런식으로 FreeTypeImporter.h라는 헤더파일을 만들어

여기에만 인클루드 한 다음에

FreeTypeImporter.h 이 헤더파일을 불러왔다.

 

왜 이렇게 해야하는지는 사실 잘 모르겠다.

(아는게없음)

 

어쨌든 라이브러리는 이렇게 불러왔고..

텍스트 관련 코드는 LearnOpenGL에서 가져왔다.

폰트는 라인씨드

 

TextBox

우선 키보드 입력을 받기위해

인터페이스를 작성했다.

class IKeyDown{
    public:
        virtual ~IKeyDown() {};
        virtual void GeyKeyDown(std::string str) =0;
};
void InputEventSystem::HandleKeyEvent(int key, int action)
{
    if (key == GLFW_KEY_0 && action == GLFW_PRESS)
    {
        for (IKeyDown *child : mKeyDown)
        {
            child->GeyKeyDown("0");
        }
        return;
    }

    if (key == GLFW_KEY_1 && action == GLFW_PRESS)
    {
        for (IKeyDown *child : mKeyDown)
        {
            child->GeyKeyDown("1");
        }
        return;
    }
    ....
}

이런식으로 등록된 인터페이스에 string으로 바로 값을 보낸다.

enum을 따로 선언해서 보낼까도 생각했는데

굳이? 라는 생각이 들어서 바로 string을 보냈다.

 

 

TextBox.h

class TextBox : public Widget,public IPressedDown,public IKeyDown{
    public:
        TextBox(glm::vec3 textBoxPos,float sizeX, float sizeY,std::string text, float textSize,glm::vec3 textColor, bool stringFlag);
        void OnPointerDown(float xpos, float ypos,float xdelta,float ydelta) override;
        void GeyKeyDown(std::string str) override;
        void SetText(std::string str);
        void SetEventCallback(std::function<void(std::string)> func);
        std::string GetText() const {return mStr;}
        void Draw() override;

        
    private:
        bool bString; //입력이 string인지 number인지
        bool bTextActive=true;
        unsigned int mVAO, mVBO, mEBO;
        float mVertexArray[20];
        unsigned int mIndices[6]={
        0,1,2,
        2,1,3
        };
        unsigned int mTextVAO, mTextVBO;
        Shader* UIShader= new Shader("Shader/UIVertexShader.glsl", "Shader/FragShader.glsl");
        Shader* mTextShader = new Shader("Shader/fontVertex.glsl", "Shader/fontFrag.glsl");
        float mTextSize=1.0f;
        std::string mStr;
        glm::vec3 mTextColor;
      
        FT_Library mFreeType;
        std::string mFont = "resource/font/LINESeedSans_A_Th.ttf";
        FT_Face mFace;
        std::map<GLchar, Character> mCharacters;
        std::function<void(std::string str)> mEventCallback=NULL;

        void renderText(Shader* shader, std::string text, float scale, glm::vec3 color);
};

 

void TextBox::OnPointerDown(float xpos, float ypos, float xdelta, float ydelta)
{
    glm::vec3 pointNDC = ScreenToNDC(glm::vec2(xpos, ypos));
    glm::vec2 sizeHalf = glm::vec2(mSizeX / 2, mSizeY / 2);

    if (pointNDC.x >= (mPos.x - sizeHalf.x) && pointNDC.x <= (mPos.x + sizeHalf.x) && pointNDC.y >= (mPos.y - sizeHalf.y) && pointNDC.y <= (mPos.y + sizeHalf.y))
    {
        bTextActive = true;
    }
    else
    {
        bTextActive = false;
    }
}

먼저 마우스 좌클릭 입력이 들어오면..

입력이 텍스트박스 범위 내인지를 판별하고

범위 내라면 텍스트 박스를 활성화시킨다.

 

void TextBox::GeyKeyDown(std::string str)
{
    if (!bTextActive)
        return;
    
    if(str=="BACKSPACE"){
        mStr.pop_back();
        return;
    }

    if (!bString && str[0] < 48 && str[0] > 58)
    {
        return;
    }

    mStr.append(str);
}

키보드 입력이 들어오면

텍스트박스가 활성화 상태인지 판별하고

mStr에 추가한다.

 

그리고 범위를 튀어나가지 않도록 마스킹도 추가했다!

 

 

인스펙터

그리고 인스펙터도 만들었다.

인스펙터는 현재 레이어의 상태를 보여주고 수정할 수 있도록 하는 역할이다.

우선 위치, 회전, 크기 정보를 보여주고 수정할 수 있는 transform을 구현했다.

 

????????

position, rotation 바꿔도 아무 변화가 없는데요?

당연함

지금 텍스트박스 내부적으로만 텍스트를 가지고 있어서

외부에서는 어떤 이벤트가 일어났고 어떤 텍스트를 가지고 있는지 알 수가 없다.

 

그래서 텍스트가 변하는 이벤트가 발생할 때마다 해야하는 일을

함수포인터에 등록해서 실행되도록 했다.

std::function<void(std::string str)> mEventCallback=NULL;
void TextBox::GeyKeyDown(std::string str)
{
    if (!bTextActive)
        return;
    
    if(str=="BACKSPACE"){
        mStr.pop_back();
        if(mEventCallback)
            mEventCallback(mStr);
        return;
    }

    if (!bString && str[0] < 48 && str[0] > 58)
    {
        return;
    }

    mStr.append(str);
    if(mEventCallback)
        mEventCallback(mStr);
}

mStr에 string이 추가될때마다

등록된 콜백함수를 호출해서 원하는 동작을 할 수 있도록 구현했다.

이동

 

회전