Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/configuration/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ void Configuration::CanvasSettings::read(const QSettings &conf)
connectionNormalColor = lookupColor(KEY_CONNECTION_NORMAL_COLOR, Colors::white.toHex());
roomDarkColor = lookupColor(KEY_ROOM_DARK_COLOR, DEFAULT_DARK_COLOR);
roomDarkLitColor = lookupColor(KEY_ROOM_DARK_LIT_COLOR, DEFAULT_NO_SUNDEATH_COLOR);
antialiasingSamples = conf.value(KEY_NUMBER_OF_ANTI_ALIASING_SAMPLES, 0).toInt();
advanced.antialiasingSamples.set(conf.value(KEY_NUMBER_OF_ANTI_ALIASING_SAMPLES, 0).toInt());
trilinearFiltering = conf.value(KEY_USE_TRILINEAR_FILTERING, true).toBool();
advanced.use3D.set(conf.value(KEY_3D_CANVAS, false).toBool());
advanced.autoTilt.set(conf.value(KEY_3D_AUTO_TILT, true).toBool());
Expand Down Expand Up @@ -779,7 +779,7 @@ void Configuration::CanvasSettings::write(QSettings &conf) const
conf.setValue(KEY_ROOM_DARK_COLOR, getQColorName(roomDarkColor));
conf.setValue(KEY_ROOM_DARK_LIT_COLOR, getQColorName(roomDarkLitColor));
conf.setValue(KEY_CONNECTION_NORMAL_COLOR, getQColorName(connectionNormalColor));
conf.setValue(KEY_NUMBER_OF_ANTI_ALIASING_SAMPLES, antialiasingSamples);
conf.setValue(KEY_NUMBER_OF_ANTI_ALIASING_SAMPLES, advanced.antialiasingSamples.get());
conf.setValue(KEY_USE_TRILINEAR_FILTERING, trilinearFiltering);
conf.setValue(KEY_3D_CANVAS, advanced.use3D.get());
conf.setValue(KEY_3D_AUTO_TILT, advanced.autoTilt.get());
Expand Down
2 changes: 1 addition & 1 deletion src/configuration/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ class NODISCARD Configuration final
NamedConfig<bool> showUnmappedExits{"SHOW_UNMAPPED_EXITS", false};
bool drawUpperLayersTextured = false;
bool drawDoorNames = false;
int antialiasingSamples = 0;
bool trilinearFiltering = false;
bool softwareOpenGL = false;
QString resourcesDirectory;
Expand All @@ -155,6 +154,7 @@ class NODISCARD Configuration final

struct NODISCARD Advanced final
{
NamedConfig<int> antialiasingSamples{"ANTIALIASING_SAMPLES", 0};
NamedConfig<bool> use3D{"MMAPPER_3D", true};
NamedConfig<bool> autoTilt{"MMAPPER_AUTO_TILT", true};
NamedConfig<bool> printPerfStats{"MMAPPER_GL_PERFSTATS", IS_DEBUG_BUILD};
Expand Down
38 changes: 34 additions & 4 deletions src/display/Characters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include <QtCore>

static constexpr float CHAR_ARROW_LINE_WIDTH = 2.f;
static constexpr float PATH_LINE_WIDTH = 4.f;
static constexpr float PATH_LINE_WIDTH = 0.1f;
static constexpr float PATH_POINT_SIZE = 8.f;

DistantObjectTransform DistantObjectTransform::construct(const glm::vec3 &pos,
Expand Down Expand Up @@ -102,6 +102,29 @@ void CharacterBatch::drawCharacter(const Coordinate &c, const Color &color, bool
gl.drawBox(c, fill, beacon, isFar);
}

void CharacterBatch::CharFakeGL::drawPathSegment(const glm::vec3 &p1,
const glm::vec3 &p2,
const Color &color)
{
// Calculate direction vector and perpendicular vector
glm::vec3 direction = glm::normalize(p2 - p1);
glm::vec3 perpendicular = glm::vec3(-direction.y, direction.x, 0.0f);

glm::vec3 offset = perpendicular * (PATH_LINE_WIDTH * 0.5f);

// Vertices for the quad
glm::vec3 v1 = p1 + offset;
glm::vec3 v2 = p1 - offset;
glm::vec3 v3 = p2 - offset;
glm::vec3 v4 = p2 + offset;

// Add vertices to m_pathLineQuads as a quad
m_pathLineQuads.emplace_back(color, v1);
m_pathLineQuads.emplace_back(color, v2);
m_pathLineQuads.emplace_back(color, v3);
m_pathLineQuads.emplace_back(color, v4);
}

void CharacterBatch::drawPreSpammedPath(const Coordinate &c1,
const std::vector<Coordinate> &path,
const Color &color)
Expand All @@ -127,7 +150,13 @@ void CharacterBatch::drawPreSpammedPath(const Coordinate &c1,
}();

auto &gl = getOpenGL();
gl.drawPathLineStrip(color, verts);

// Generate vertices for the thick line
for (size_t i = 0; i < verts.size() - 1; ++i) {
const glm::vec3 p1 = verts[i];
const glm::vec3 p2 = verts[i + 1];
gl.drawPathSegment(p1, p2, color);
}
gl.drawPathPoint(color, verts.back());
}

Expand Down Expand Up @@ -343,8 +372,9 @@ void CharacterBatch::CharFakeGL::reallyDrawPaths(OpenGL &gl)
= GLRenderState().withDepthFunction(std::nullopt).withBlend(BlendModeEnum::TRANSPARENCY);

gl.renderPoints(m_pathPoints, blended_noDepth.withPointSize(PATH_POINT_SIZE));
gl.renderColoredLines(m_pathLineVerts,
blended_noDepth.withLineParams(LineParams{PATH_LINE_WIDTH}));
if (!m_pathLineQuads.empty()) {
gl.renderColoredQuads(m_pathLineQuads, blended_noDepth);
}
}

void CharacterBatch::CharFakeGL::addScreenSpaceArrow(const glm::vec3 &pos,
Expand Down
14 changes: 3 additions & 11 deletions src/display/Characters.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ class NODISCARD CharacterBatch final
std::vector<ColorVert> m_charTris;
std::vector<ColorVert> m_charBeaconQuads;
std::vector<ColorVert> m_charLines;
std::vector<ColorVert> m_pathPoints;
std::vector<ColorVert> m_pathLineVerts;
std::vector<ColoredTexVert> m_charRoomQuads;
std::vector<ColorVert> m_pathPoints;
std::vector<ColorVert> m_pathLineQuads;
std::vector<FontVert3d> m_screenSpaceArrows;
std::map<Coordinate, int, CoordCompare> m_coordCounts;

Expand Down Expand Up @@ -151,15 +151,7 @@ class NODISCARD CharacterBatch final
void drawArrow(bool fill, bool beacon);
void drawBox(const Coordinate &coord, bool fill, bool beacon, bool isFar);
void addScreenSpaceArrow(const glm::vec3 &pos, float degrees, const Color &color, bool fill);

// with blending, without depth; always size 4
void drawPathLineStrip(const Color &color, const std::vector<glm::vec3> &points)
{
for (size_t i = 1, size = points.size(); i < size; ++i) {
m_pathLineVerts.emplace_back(color, points[i - 1]);
m_pathLineVerts.emplace_back(color, points[i]);
}
}
void drawPathSegment(const glm::vec3 &p1, const glm::vec3 &p2, const Color &color);

// with blending, without depth; always size 8
void drawPathPoint(const Color &color, const glm::vec3 &pos)
Expand Down
146 changes: 116 additions & 30 deletions src/display/Connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include <QMessageLogContext>
#include <QtCore>

static constexpr const float CONNECTION_LINE_WIDTH = 2.f;
static constexpr const float CONNECTION_LINE_WIDTH = 0.025f;
static constexpr const float VALID_CONNECTION_POINT_SIZE = 6.f;
static constexpr const float NEW_CONNECTION_POINT_SIZE = 8.f;

Expand Down Expand Up @@ -615,10 +615,10 @@ void ConnectionDrawer::drawConnEndTriUpDownUnknown(float dX, float dY, float dst
ConnectionMeshes ConnectionDrawerBuffers::getMeshes(OpenGL &gl) const
{
ConnectionMeshes result;
result.normalLines = gl.createColoredLineBatch(normal.lineVerts);
result.normalTris = gl.createColoredTriBatch(normal.triVerts);
result.redLines = gl.createColoredLineBatch(red.lineVerts);
result.redTris = gl.createColoredTriBatch(red.triVerts);
result.normalQuads = gl.createColoredQuadBatch(normal.quadVerts);
result.redQuads = gl.createColoredQuadBatch(red.quadVerts);
return result;
}

Expand All @@ -630,19 +630,16 @@ void ConnectionMeshes::render(const int thisLayer, const int focusedLayer) const
}
return Colors::gray70.withAlpha(FAINT_CONNECTION_ALPHA);
}();
const auto common_style = GLRenderState()
.withBlend(BlendModeEnum::TRANSPARENCY)
.withLineParams(LineParams{CONNECTION_LINE_WIDTH})
.withColor(color);
const auto common_style = GLRenderState().withBlend(BlendModeEnum::TRANSPARENCY).withColor(color);

// Even though we can draw colored lines and tris,
// the reason for having separate lines is so red will always be on top.
// If you don't think that's important, you can combine the batches.

normalLines.render(common_style);
normalTris.render(common_style);
redLines.render(common_style);
redTris.render(common_style);
normalQuads.render(common_style);
redQuads.render(common_style);
}

void MapCanvas::paintNearbyConnectionPoints()
Expand Down Expand Up @@ -760,8 +757,24 @@ void MapCanvas::paintSelectedConnection()
auto &gl = getOpenGL();
const auto rs = GLRenderState().withColor(Colors::red);

const std::vector<glm::vec3> verts{pos1, pos2};
gl.renderPlainLines(verts, rs.withLineParams(LineParams{CONNECTION_LINE_WIDTH}));
{
std::vector<ColorVert> verts;
const glm::vec3 dir = glm::normalize(pos2 - pos1);
const glm::vec3 perp_normal = glm::vec3(-dir.y, dir.x, 0.0f);
const float half_width = CONNECTION_LINE_WIDTH / 2.0f;

glm::vec3 v1 = pos1 + perp_normal * half_width;
glm::vec3 v2 = pos1 - perp_normal * half_width;
glm::vec3 v3 = pos2 - perp_normal * half_width;
glm::vec3 v4 = pos2 + perp_normal * half_width;

verts.emplace_back(Colors::red, v1);
verts.emplace_back(Colors::red, v2);
verts.emplace_back(Colors::red, v3);
verts.emplace_back(Colors::red, v4);

gl.renderColoredQuads(verts, rs);
}

std::vector<ColorVert> points;
points.emplace_back(Colors::red, pos1);
Expand Down Expand Up @@ -790,35 +803,108 @@ void ConnectionDrawer::ConnectionFakeGL::drawTriangle(const glm::vec3 &a,

void ConnectionDrawer::ConnectionFakeGL::drawLineStrip(const std::vector<glm::vec3> &points)
{
const auto &connectionNormalColor
= isNormal() ? getCanvasNamedColorOptions().connectionNormalColor.getColor() : Colors::red;

const auto transform = [this](const glm::vec3 &vert) { return vert + m_offset; };
auto &verts = deref(m_currentBuffer).lineVerts;
auto drawLine = [&verts](const Color &color, const glm::vec3 &a, const glm::vec3 &b) {
verts.emplace_back(color, a);
verts.emplace_back(color, b);
const float extension = CONNECTION_LINE_WIDTH * 0.5f;

// Helper lambda to generate a quad between two points with a specific color.
auto generateQuad = [&](const glm::vec3 &p1, const glm::vec3 &p2, const Color &quad_color) {
auto &verts = deref(m_currentBuffer).quadVerts;

// Zero-length segment, draw a small square.
if (p1 == p2) {
float half_size = CONNECTION_LINE_WIDTH / 2.0f;
glm::vec3 v_1 = p1 + glm::vec3(-half_size, -half_size, 0.0f);
glm::vec3 v_2 = p1 + glm::vec3(half_size, -half_size, 0.0f);
glm::vec3 v_3 = p1 + glm::vec3(half_size, half_size, 0.0f);
glm::vec3 v_4 = p1 + glm::vec3(-half_size, half_size, 0.0f);

verts.emplace_back(quad_color, v_1);
verts.emplace_back(quad_color, v_2);
verts.emplace_back(quad_color, v_3);
verts.emplace_back(quad_color, v_4);
return;
}

const glm::vec3 dir = glm::normalize(p2 - p1);
// Original perpendicular to the line segment in the XY plane.
const glm::vec3 perp_normal_1 = glm::vec3(-dir.y, dir.x, 0.0f);
// Second perpendicular, rotated 90 degrees relative to the first (within the XY plane).
const glm::vec3 perp_normal_2 = glm::vec3(dir.x, dir.y, 0.0f);
const float half_width = CONNECTION_LINE_WIDTH / 2.0f;

// Generate the first quad.
glm::vec3 v1_1 = p1 + perp_normal_1 * half_width;
glm::vec3 v1_2 = p1 - perp_normal_1 * half_width;
glm::vec3 v1_3 = p2 + perp_normal_1 * half_width;
glm::vec3 v1_4 = p2 - perp_normal_1 * half_width;

verts.emplace_back(quad_color, v1_1);
verts.emplace_back(quad_color, v1_2);
verts.emplace_back(quad_color, v1_4);
verts.emplace_back(quad_color, v1_3);

// Generate the second (orthogonal) quad.
glm::vec3 v2_1 = p1 + perp_normal_2 * half_width;
glm::vec3 v2_2 = p1 - perp_normal_2 * half_width;
glm::vec3 v2_3 = p2 + perp_normal_2 * half_width;
glm::vec3 v2_4 = p2 - perp_normal_2 * half_width;

verts.emplace_back(quad_color, v2_1);
verts.emplace_back(quad_color, v2_2);
verts.emplace_back(quad_color, v2_4);
verts.emplace_back(quad_color, v2_3);
};

const auto size = points.size();
assert(size >= 2);

for (size_t i = 1; i < size; ++i) {
const auto start = transform(points[i - 1u]);
const auto end = transform(points[i]);
const glm::vec3 start_orig = points[i - 1u];
const glm::vec3 end_orig = points[i];

const glm::vec3 start_v = transform(start_orig);
const glm::vec3 end_v = transform(end_orig);

const Color current_segment_color = isNormal() ? getCanvasNamedColorOptions()
.connectionNormalColor.getColor()
: Colors::red;

if (!isLongLine(start, end)) {
drawLine(connectionNormalColor, start, end);
// Handle original zero-length segments first.
if (glm::length(end_v - start_v) < 1e-6f) {
generateQuad(start_v, end_v, current_segment_color);
continue;
}

const auto len = glm::length(start - end);
const auto faintCutoff = LONG_LINE_HALFLEN / len;
const auto mid1 = glm::mix(start, end, faintCutoff);
const auto mid2 = glm::mix(start, end, 1.f - faintCutoff);
const auto faint = connectionNormalColor.withAlpha(FAINT_CONNECTION_ALPHA);
glm::vec3 quad_start_v = start_v;
glm::vec3 quad_end_v = end_v;
const glm::vec3 segment_dir = glm::normalize(end_v - start_v);

// Extend the first and last segments for better visual continuity.
if (i == 1) {
// First segment of the polyline.
quad_start_v = start_v - segment_dir * extension;
}
if (i == size - 1) {
// Last segment of the polyline.
quad_end_v = end_v + segment_dir * extension;
}

// If it's not a long line, just draw a single quad.
if (!isLongLine(quad_start_v, quad_end_v)) {
generateQuad(quad_start_v, quad_end_v, current_segment_color);
continue;
}

// It is a long line, apply fading.
const float len = glm::length(quad_end_v - quad_start_v);
const float faintCutoff = (len > 1e-6f) ? (LONG_LINE_HALFLEN / len) : 0.5f;

const glm::vec3 mid1 = glm::mix(quad_start_v, quad_end_v, faintCutoff);
const glm::vec3 mid2 = glm::mix(quad_start_v, quad_end_v, 1.f - faintCutoff);
const Color faint_color = current_segment_color.withAlpha(FAINT_CONNECTION_ALPHA);

drawLine(connectionNormalColor, start, mid1);
drawLine(faint, mid1, mid2);
drawLine(connectionNormalColor, mid2, end);
generateQuad(quad_start_v, mid1, current_segment_color);
generateQuad(mid1, mid2, faint_color);
generateQuad(mid2, quad_end_v, current_segment_color);
}
}
10 changes: 5 additions & 5 deletions src/display/Connections.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,27 @@ using BatchedRoomNames = std::unordered_map<int, UniqueMesh>;

struct NODISCARD ConnectionDrawerColorBuffer final
{
std::vector<ColorVert> lineVerts;
std::vector<ColorVert> triVerts;
std::vector<ColorVert> quadVerts;

ConnectionDrawerColorBuffer() = default;
DEFAULT_MOVES_DELETE_COPIES(ConnectionDrawerColorBuffer);
~ConnectionDrawerColorBuffer() = default;

void clear()
{
lineVerts.clear();
triVerts.clear();
quadVerts.clear();
}
NODISCARD bool empty() const { return lineVerts.empty() && triVerts.empty(); }
NODISCARD bool empty() const { return triVerts.empty() && quadVerts.empty(); }
};

struct NODISCARD ConnectionMeshes final
{
UniqueMesh normalLines;
UniqueMesh normalTris;
UniqueMesh redLines;
UniqueMesh redTris;
UniqueMesh normalQuads;
UniqueMesh redQuads;

ConnectionMeshes() = default;
DEFAULT_MOVES_DELETE_COPIES(ConnectionMeshes);
Expand Down
Loading
Loading