컴퓨터 그래픽스/OpenGL

#6 tinylender 개발일지 : 펜툴 제작

san10 2024. 5. 31. 14:42

3D 모델을 만드는 방식은 여러가지가 있지만,

tinylender는 일러스트레이터처럼 먼저 펜툴로 2D 도형을 그린 다음, 그걸 3D화 시킬 생각이다.

 

 

그래서 우선 2d 메시를 만들 수 있는 펜툴을 만들었다.

결과

 

pen

class Pen: public IPressedDown,public IMoved,public IState{
    public:
        Pen();
        void DrawMesh();
        void OnPointerDown(float xpos, float ypos,float xdelta,float ydelta) override;
        void OnMove(float xpos, float ypos,float xdelta,float ydelta) override;
        void Handle() override;
        void HandleOut() override;
    
    private:
        std::vector<Vertex> mVertices;
        std::vector<unsigned int> mIndice;
        unsigned int mVBO;
        unsigned int mVAO;
        unsigned int mEBO;

        float mLineVertices[9] = {
        0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f};
    
        unsigned int mLineVBO;
        unsigned int mLineVAO;
        bool bDraw=false;
        bool bFirst=true;

        glm::vec3 screenToLocal(glm::vec2 screen);
    
};

 

 

void Pen::OnPointerDown(float xpos, float ypos,float xdelta,float ydelta){
    if(!bDraw)
        return;

    glm::vec3 point = screenToLocal(glm::vec2(xpos,ypos));
    Vertex vert = {point,glm::vec3(1.0f),glm::vec2(1.0f),glm::vec3(1.0f)};
   
    if(glm::length(point-mVertices[0].Position) <= 0.1f &&mVertices.size()>3){
        //mesh로 만들어서 collection에 추가
        std::vector<std::vector<unsigned int>> faces;
        std::vector<unsigned int> f;
        faces.push_back(f);

        for(int i=0;i<mVertices.size();i++){
            faces[0].push_back(i);
            faces[0].push_back(i);
            faces[0].push_back(i);
        }
        
        Mesh* mesh= new Mesh(mVertices,mIndice,std::vector<Texture>(0),faces,"Shader/vertexShader.glsl","Shader/fragmentShader.glsl");
        Collection::GetInstance()->SetMesh(mesh);
        mVertices.clear();
        mVertices.push_back({glm::vec3(0.0f),glm::vec3(0.0f),glm::vec2(0.0f),glm::vec3(0.0f)});
        mIndice.clear();
        bFirst=true;
        return;
    }
    else{
        mLineVertices[0] = point.x;
        mLineVertices[1] = point.y;
        mLineVertices[2] = point.z;


        if(bFirst){
            mVertices[0] = vert;
            mLineVertices[6] = vert.Position.x;
            mLineVertices[7] = vert.Position.y;
            mLineVertices[8] = vert.Position.z;

             glBindBuffer(GL_ARRAY_BUFFER,mLineVBO);
             glBufferSubData(GL_ARRAY_BUFFER,6*sizeof(float),3*sizeof(float),&mLineVertices[6]);

        }
        else{
            if(mVertices.size()>=3){

            Vertex vert2 = mVertices[mVertices.size()-1];
            mVertices.push_back(mVertices[0]);
            mVertices.push_back(vert2);
            mVertices.push_back(vert);
            }
            else{
                mVertices.push_back(vert);
            }
        }
        
        glBindVertexArray(mVAO);
        glBindBuffer(GL_ARRAY_BUFFER,mVBO);
        glBufferData(GL_ARRAY_BUFFER,mVertices.size()*sizeof(Vertex),&mVertices[0],GL_DYNAMIC_DRAW);

        //glBindVertexArray(mLineVAO);
        glBindBuffer(GL_ARRAY_BUFFER,mLineVBO);
        glBufferSubData(GL_ARRAY_BUFFER,0,3*sizeof(float),&mLineVertices);

    }
    bFirst=false;
}

 

OnPointerDown은 마우스 클릭을 할 때 한번 호출된다.

일단 현재 클릭한 위치를 ndc로 변환하고,

mVertices에 해당 값을 넣고 VAO, VBO를 업데이트 해준다.

 

만약 클릭한 위치가 첫 포인트와 거리가 0.1 미만이고,

mVertices에 들어있는 버텍스가 3개 이상이면

메시로 만들어서 콜렉션에 추가한다.

 

void Pen::OnMove(float xpos, float ypos,float xdelta,float ydelta){
    if(!bDraw)
        return;

    glm::vec3 point = screenToLocal(glm::vec2(xpos,ypos));

    mLineVertices[3] = point.x * PEN_SPEED;
    mLineVertices[4] = point.y * PEN_SPEED;
    mLineVertices[5] = point.z * PEN_SPEED;

}

추가로 IMoved라는 인터페이스를 만들어서

마우스가 움직일 때마다 호출되도록 만들었다.

 

Pen에서의 OnMove의 역할은

어떤 도형이 만들어질지 미리 보여주는 역할이다.

 이런 미리보기는 라인으로 보여주게 했다.

 

 

이렇게 색깔바꾸기도 가능하다 ^_^

일단은 가장 기본적인 기능만 만들었고

곡선이나 원, 사각형 등의 기능은 앞으로 추가할 예정이다.