0130

2026년 1월 30일

카테고리 : 게임엔진, 3D기즈모

0130_0.gif

3D 기즈모를 만들어 보았다.

완벽하지는 않지만,

상태가 Pos / Rot 로 된 각 경우

마우스에 따라서 위치 / 회전을 바꿀 수 있다.

그거, 물체 주변에 축 기즈모랑

¼ 원 3개 띄우는거를 만들어야 한다.

물론 오브젝트 피킹으로 끝나는 게 아니라

기즈모 자체도 피킹 검사를 해야한다.


위치 조정은 이렇게 시작한다.

            if (dragging) {
                pos_offset_deferred.x = ComputeTranslationAlongAxis(
                    cam3d->get_world_position(),
                    cam3d->get_view_matrix().get_matrix(),
                    cam3d->get_projection_matrix().get_matrix(),
                    cursor_start,
                    get_screen_mouse_position(),
                    cube->get_world_position(),
                    { 1.0f, 0.0f, 0.0f },
                    cam3d->get_projection_size()
                ).x;

                // z축도 함께.
                pos_offset_deferred.z = ComputeTranslationAlongAxis(
                    cam3d->get_world_position(),
                    cam3d->get_view_matrix().get_matrix(),
                    cam3d->get_projection_matrix().get_matrix(),
                    cursor_start,
                    get_screen_mouse_position(),
                    cube->get_world_position(),
                    { 0.0f, 0.0f, 1.0f },
                    cam3d->get_projection_size()
                ).z;
            }

어딘가에서 긁어온 ComputeTranslationAlongAxis 함수를 설명해보겠다.

glm::vec3 ComputeTranslationAlongAxis(
    const glm::vec3& cameraPos,
    const glm::mat4& viewMatrix,
    const glm::mat4& projectionMatrix,
    const glm::vec2& mousePosStart,
    const glm::vec2& mousePosEnd,
    const glm::vec3& objectPos,
    const glm::vec3& axis,
    const glm::vec2& viewportSize)
{
    glm::vec3 rayStartDir = ScreenToWorldRay(mousePosStart, viewMatrix, projectionMatrix, viewportSize);
    glm::vec3 rayEndDir = ScreenToWorldRay(mousePosEnd, viewMatrix, projectionMatrix, viewportSize);

    glm::vec3 p1 = cameraPos;
    glm::vec3 p2 = cameraPos + rayStartDir * 1000.0f;
    glm::vec3 q1 = cameraPos;
    glm::vec3 q2 = cameraPos + rayEndDir * 1000.0f;

    glm::vec3 delta = q2 - p2;
    float projected = glm::dot(delta, axis);
    return axis * projected;
}

먼저, 뷰 행렬과 투영 행렬을 받기 때문에

스크린 공간의 '마우스 시작위치', '마우스 끝 위치' 를 가지고,

3차원 월드 공간에서의

광선 2개를 정의한다.

월드 카메라 위치를 기준으로,

'처음 드래그를 시작한 커서위치 기반 광선' 하나와

'현재 드래그하면서 실시간으로 갱신된 커서위치 기반 광선' 하나를 만든다.

그리고 (후자 - 전자) 해서 delta 만들고,

걍 이걸 단위 벡터에다가 투영하면 된다.

별거 없다.

시각화하면 밑에처럼 된다.

참고로 X축 (1, 0, 0) 에다가 투영하는 상황이고,

XZ 평면을 위에서 바라본 시점이다.

0130_1.png