bool Label::updateScreenTransform(const glm::mat4& _mvp, const glm::vec2& _screenSize, bool _testVisibility) { glm::vec2 screenPosition; float rot = 0; glm::vec2 ap1, ap2; switch (m_type) { case Type::debug: case Type::point: { glm::vec4 v1 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition1, 0.0, 1.0)); if (_testVisibility && (v1.w <= 0)) { return false; } screenPosition = clipToScreenSpace(v1, _screenSize); ap1 = ap2 = screenPosition; break; } case Type::line: { // project label position from mercator world space to clip coordinates glm::vec4 v1 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition1, 0.0, 1.0)); glm::vec4 v2 = worldToClipSpace(_mvp, glm::vec4(m_transform.modelPosition2, 0.0, 1.0)); // check whether the label is behind the camera using the perspective division factor if (_testVisibility && (v1.w <= 0 || v2.w <= 0)) { return false; } // project to screen space glm::vec2 p1 = clipToScreenSpace(v1, _screenSize); glm::vec2 p2 = clipToScreenSpace(v2, _screenSize); rot = angleBetweenPoints(p1, p2) + M_PI_2; if (rot > M_PI_2 || rot < -M_PI_2) { // un-readable labels rot += M_PI; } else { std::swap(p1, p2); } float length = glm::length(p2 - p1); float exceedHeuristic = 30; // default heuristic : 30% if (_testVisibility && (m_dim.x > length)) { float exceed = (1 - (length / m_dim.x)) * 100; if (exceed > exceedHeuristic) { return false; } } ap1 = p1; ap2 = p2; break; } } align(screenPosition, ap1, ap2); // update screen position glm::vec2 offset = m_options.offset; if (m_transform.state.rotation != 0.f) { offset = glm::rotate(offset, m_transform.state.rotation); } glm::vec2 newScreenPos = screenPosition + offset; if (newScreenPos != m_transform.state.screenPos) { m_transform.state.screenPos = newScreenPos; m_dirty = true; } // update screen rotation if (m_transform.state.rotation != rot) { m_transform.state.rotation = rot; m_dirty = true; } return true; }
glm::vec2 worldToScreenSpace(const glm::mat4& _mvp, const glm::vec4& _worldPosition, const glm::vec2& _screenSize) { return clipToScreenSpace(worldToClipSpace(_mvp, _worldPosition), _screenSize); }