Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5f61af8
Add unfinished quad gpu tesselation
NoahBussingerUbique Oct 22, 2025
d465d5b
Change curving from normalize to sin cos
NoahBussingerUbique Oct 22, 2025
34aed00
Quad tessellation blend and non 3d
NoahBussingerUbique Nov 5, 2025
8c79523
Refactor and unfinished polygon mask tessellation
NoahBussingerUbique Nov 6, 2025
5e686b7
Partially working polygon mask tessellation
NoahBussingerUbique Nov 6, 2025
65d716d
Complete polygon mask tessellation
NoahBussingerUbique Nov 19, 2025
80bb224
Begin android quad tessellation
NoahBussingerUbique Nov 26, 2025
bd43bad
Complete android quad tessellation
NoahBussingerUbique Dec 3, 2025
c05337f
Complete and improve polygon tessellation
NoahBussingerUbique Dec 4, 2025
f7ce250
Begin clean up
NoahBussingerUbique Dec 4, 2025
904f23f
Remove elevation test and fix 2d subdivision
NoahBussingerUbique Dec 10, 2025
999af35
Refactor vertex structs and properties
NoahBussingerUbique Dec 10, 2025
a4b7f01
Refactor is3d and subdivisionFactor handling
NoahBussingerUbique Dec 10, 2025
392b619
Remove comments and add wireframe macro
NoahBussingerUbique Dec 11, 2025
dbabf12
Port polygon layer to tessellation
NoahBussingerUbique Dec 11, 2025
e87eb01
Optimize tessellation shaders with reordering
NoahBussingerUbique Dec 11, 2025
95bf953
Optimize tessellation shaders with halfs
NoahBussingerUbique Dec 17, 2025
b26420d
Split tessellation and legacy path by compile time flag
NoahBussingerUbique Dec 17, 2025
1d89e9d
Minor improvements in android backend
NoahBussingerUbique Dec 17, 2025
dfd1d3b
Prevent tessellation on ios simulator
NoahBussingerUbique Jan 21, 2026
8083aad
Rename tessellation constants
NoahBussingerUbique Jan 22, 2026
a605587
Consistent subdivision factor space
NoahBussingerUbique Jan 26, 2026
8f22cb0
Fix IconLayer subdivision error
NoahBussingerUbique Jan 28, 2026
7ce8fcf
Disable tessellation in 2d cases with hardware support
NoahBussingerUbique Jan 28, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,28 @@
#include "Text2dInstancedOpenGl.h"
#include "Quad2dStretchedInstancedOpenGl.h"
#include "IcosahedronOpenGl.h"
#include "Quad2dTessellatedOpenGl.h"
#include "Polygon2dTessellatedOpenGl.h"
#include "TessellatedColorShaderOpenGl.h"

std::shared_ptr<Quad2dInterface> GraphicsObjectFactoryOpenGl::createQuad(const std::shared_ptr<::ShaderProgramInterface> &shader) {
return std::make_shared<Quad2dOpenGl>(enforceGlShader(shader));
}

std::shared_ptr<Quad2dInterface> GraphicsObjectFactoryOpenGl::createQuadTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) {
return std::make_shared<Quad2dTessellatedOpenGl>(enforceGlShader(shader));
}

std::shared_ptr<Polygon2dInterface>
GraphicsObjectFactoryOpenGl::createPolygon(const std::shared_ptr<::ShaderProgramInterface> &shader) {
return std::make_shared<Polygon2dOpenGl>(enforceGlShader(shader));
}

std::shared_ptr<Polygon2dInterface>
GraphicsObjectFactoryOpenGl::createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) {
return std::make_shared<Polygon2dTessellatedOpenGl>(enforceGlShader(shader));
}

std::shared_ptr<LineGroup2dInterface>
GraphicsObjectFactoryOpenGl::createLineGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) {
return std::make_shared<LineGroup2dOpenGl>(enforceGlShader(shader));
Expand All @@ -55,6 +67,12 @@ std::shared_ptr<Polygon2dInterface> GraphicsObjectFactoryOpenGl::createPolygonMa
return std::make_shared<Polygon2dOpenGl>(enforceGlShader(shader));
}

std::shared_ptr<Polygon2dInterface> GraphicsObjectFactoryOpenGl::createPolygonMaskTessellated(bool is3D) {
std::shared_ptr<TessellatedColorShaderOpenGl> shader = std::make_shared<TessellatedColorShaderOpenGl>(is3D);
shader->setColor(1, 1, 1, 1);
return std::make_shared<Polygon2dTessellatedOpenGl>(enforceGlShader(shader));
}

std::shared_ptr<TextInterface> GraphicsObjectFactoryOpenGl::createText(const std::shared_ptr<::ShaderProgramInterface> &shader) {
return std::make_shared<Text2dOpenGl>(enforceGlShader(shader));
}
Expand Down Expand Up @@ -83,4 +101,4 @@ std::shared_ptr<BaseShaderProgramOpenGl> GraphicsObjectFactoryOpenGl::enforceGlS
throw std::runtime_error("GraphicsObjectFactoryOpenGl: ShaderProgramInterface doesn't extend BaseShaderProgramOpenGl!");
}
return glShader;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ class GraphicsObjectFactoryOpenGl : public GraphicsObjectFactoryInterface {
public:
std::shared_ptr<Quad2dInterface> createQuad(const std::shared_ptr<::ShaderProgramInterface> &shader) override;

std::shared_ptr<Quad2dInterface> createQuadTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) override;

std::shared_ptr<Polygon2dInterface> createPolygon(const std::shared_ptr<::ShaderProgramInterface> &shader) override;

std::shared_ptr<Polygon2dInterface> createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) override;

std::shared_ptr<LineGroup2dInterface> createLineGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) override;

std::shared_ptr<PolygonGroup2dInterface> createPolygonGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) override;
Expand All @@ -29,6 +33,8 @@ class GraphicsObjectFactoryOpenGl : public GraphicsObjectFactoryInterface {

std::shared_ptr<Polygon2dInterface> createPolygonMask(bool is3D) override;

std::shared_ptr<Polygon2dInterface> createPolygonMaskTessellated(bool is3D) override;

std::shared_ptr<TextInterface> createText(const std::shared_ptr<::ShaderProgramInterface> &shader) override;

std::shared_ptr<TextInstancedInterface> createTextInstanced(const std::shared_ptr<::ShaderProgramInterface> & shader) override;
Expand Down
17 changes: 11 additions & 6 deletions android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ std::shared_ptr<MaskingObjectInterface> Polygon2dOpenGl::asMaskingObject() { ret

bool Polygon2dOpenGl::isReady() { return ready; }

void Polygon2dOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin) {
void Polygon2dOpenGl::setSubdivisionFactor(int32_t factor) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be confusing that the tesselated / non-tesselated types are mixed behind the same interface. We need to manually keep track of where a tesselated object is used.
Would it be an option to have a separate type hierarchy for the tesselated types, or does that make integration much more complicated?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.
Thinking about it, the tessellated interfaces which would extend the normal ones would be almost empty though. The only change i did to the interfaces i think was the addition of the setSubdivisionFactor to the polygon interface. The reason i did this was for consistency with the quad interface, there for some reason the subdivision gets done on the level of the metal/opengl backend.


void Polygon2dOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, bool is3d) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
ready = false;
dataReady = false;
Expand All @@ -30,11 +32,11 @@ void Polygon2dOpenGl::setVertices(const ::SharedBytes & vertices_, const ::Share
vertices.resize(vertices_.elementCount);
polygonOrigin = origin;

if(indices_.elementCount > 0) {
if (indices_.elementCount > 0) {
std::memcpy(indices.data(), (void *)indices_.address, indices_.elementCount * indices_.bytesPerElement);
}

if(vertices_.elementCount > 0) {
if (vertices_.elementCount > 0) {
std::memcpy(vertices.data(), (void *)vertices_.address,
vertices_.elementCount * vertices_.bytesPerElement);
}
Expand All @@ -44,8 +46,9 @@ void Polygon2dOpenGl::setVertices(const ::SharedBytes & vertices_, const ::Share

void Polygon2dOpenGl::setup(const std::shared_ptr<::RenderingContextInterface> &context) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
if (ready || !dataReady)
if (ready || !dataReady) {
return;
}

std::shared_ptr<OpenGlContext> openGlContext = std::static_pointer_cast<OpenGlContext>(context);
programName = shaderProgram->getProgramName();
Expand Down Expand Up @@ -118,8 +121,9 @@ void Polygon2dOpenGl::render(const std::shared_ptr<::RenderingContextInterface>
int64_t vpMatrix, int64_t mMatrix, const ::Vec3D &origin, bool isMasked,
double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
if (!ready || !shaderProgram->isRenderable())
if (!ready || !shaderProgram->isRenderable()) {
return;
}

std::shared_ptr<OpenGlContext> openGlContext = std::static_pointer_cast<OpenGlContext>(context);

Expand Down Expand Up @@ -171,8 +175,9 @@ void Polygon2dOpenGl::drawPolygon(const std::shared_ptr<::RenderingContextInterf
void Polygon2dOpenGl::renderAsMask(const std::shared_ptr<::RenderingContextInterface> &context,
const ::RenderPassConfig &renderPass, int64_t vpMatrix, int64_t mMatrix,
const ::Vec3D &origin, double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) {
if (!ready)
if (!ready) {
return;
}

std::shared_ptr<OpenGlContext> openGlContext = std::static_pointer_cast<OpenGlContext>(context);

Expand Down
4 changes: 3 additions & 1 deletion android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ class Polygon2dOpenGl : public GraphicsObjectInterface,
int64_t vpMatrix, int64_t mMatrix, const ::Vec3D & origin,
double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) override;

virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin) override;
void setSubdivisionFactor(int32_t factor) override;

virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin, bool is3d) override;

virtual std::shared_ptr<GraphicsObjectInterface> asGraphicsObject() override;

Expand Down
212 changes: 212 additions & 0 deletions android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/

#include "Polygon2dTessellatedOpenGl.h"
#include "OpenGlHelper.h"
#include <cstring>

Polygon2dTessellatedOpenGl::Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader)
: shaderProgram(shader) {}

std::shared_ptr<GraphicsObjectInterface> Polygon2dTessellatedOpenGl::asGraphicsObject() { return shared_from_this(); }

std::shared_ptr<MaskingObjectInterface> Polygon2dTessellatedOpenGl::asMaskingObject() { return shared_from_this(); }

bool Polygon2dTessellatedOpenGl::isReady() { return ready; }

void Polygon2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
if (factor != subdivisionFactor) {
subdivisionFactor = factor;
}
}

void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, bool is3d) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
ready = false;
dataReady = false;

indices.resize(indices_.elementCount);
vertices.resize(vertices_.elementCount);
polygonOrigin = origin;

this->is3d = is3d;

if (indices_.elementCount > 0) {
std::memcpy(indices.data(), (void *)indices_.address, indices_.elementCount * indices_.bytesPerElement);
}

if (vertices_.elementCount > 0) {
std::memcpy(vertices.data(), (void *)vertices_.address,
vertices_.elementCount * vertices_.bytesPerElement);
}

dataReady = true;
}

void Polygon2dTessellatedOpenGl::setup(const std::shared_ptr<::RenderingContextInterface> &context) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
if (ready || !dataReady) {
return;
}

std::shared_ptr<OpenGlContext> openGlContext = std::static_pointer_cast<OpenGlContext>(context);
programName = shaderProgram->getProgramName();
program = openGlContext->getProgram(programName);
if (program == 0) {
shaderProgram->setupProgram(openGlContext);
program = openGlContext->getProgram(programName);
}

prepareGlData(program);
ready = true;
}

void Polygon2dTessellatedOpenGl::prepareGlData(int program) {
glUseProgram(program);

if (!glDataBuffersGenerated) {
glGenVertexArrays(1, &vao);
}
glBindVertexArray(vao);

positionHandle = glGetAttribLocation(program, "vPosition");
frameCoordHandle = glGetAttribLocation(program, "vFrameCoord");
if (!glDataBuffersGenerated) {
glGenBuffers(1, &vertexBuffer);
}
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

size_t stride = sizeof(GLfloat) * 5;
glEnableVertexAttribArray(positionHandle);
glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, stride, nullptr);
glEnableVertexAttribArray(frameCoordHandle);
glVertexAttribPointer(frameCoordHandle, 2, GL_FLOAT, false, stride, (float*)(sizeof(GLfloat) * 3));

glBindBuffer(GL_ARRAY_BUFFER, 0);

if (!glDataBuffersGenerated) {
glGenBuffers(1, &indexBuffer);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0], GL_STATIC_DRAW);

glBindVertexArray(0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

mMatrixHandle = glGetUniformLocation(program, "umMatrix");
originOffsetHandle = glGetUniformLocation(program, "uOriginOffset");
subdivisionFactorHandle = glGetUniformLocation(program, "uSubdivisionFactor");
originHandle = glGetUniformLocation(program, "uOrigin");
is3dHandle = glGetUniformLocation(program, "uIs3d");

glDataBuffersGenerated = true;
}

void Polygon2dTessellatedOpenGl::clear() {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
if (ready) {
removeGlBuffers();
ready = false;
}
}

void Polygon2dTessellatedOpenGl::removeGlBuffers() {
if (glDataBuffersGenerated) {
glDeleteBuffers(1, &vertexBuffer);
glDeleteBuffers(1, &indexBuffer);
glDeleteVertexArrays(1, &vao);
glDataBuffersGenerated = false;
}
}

void Polygon2dTessellatedOpenGl::setIsInverseMasked(bool inversed) { isMaskInversed = inversed; }

void Polygon2dTessellatedOpenGl::render(const std::shared_ptr<::RenderingContextInterface> &context, const RenderPassConfig &renderPass,
int64_t vpMatrix, int64_t mMatrix, const ::Vec3D &origin, bool isMasked,
double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
if (!ready || !shaderProgram->isRenderable()) {
return;
}

std::shared_ptr<OpenGlContext> openGlContext = std::static_pointer_cast<OpenGlContext>(context);

GLuint stencilMask = 0;
GLuint validTarget = 0;
GLenum zpass = GL_KEEP;
if (isMasked) {
stencilMask += 128;
validTarget = isMaskInversed ? 0 : 128;
}
if (renderPass.isPassMasked) {
stencilMask += 127;
zpass = GL_INCR;
}

if (stencilMask != 0) {
glStencilFunc(GL_EQUAL, validTarget, stencilMask);
glStencilOp(GL_KEEP, GL_KEEP, zpass);
}

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

drawPolygon(openGlContext, program, vpMatrix, mMatrix, origin, isScreenSpaceCoords);
}

void Polygon2dTessellatedOpenGl::drawPolygon(const std::shared_ptr<::RenderingContextInterface> &context, int program, int64_t vpMatrix,
int64_t mMatrix, const Vec3D &origin, bool isScreenSpaceCoords) {
std::lock_guard<std::recursive_mutex> lock(dataMutex);
// Add program to OpenGL environment
glUseProgram(program);
glBindVertexArray(vao);

shaderProgram->preRender(context, isScreenSpaceCoords);

if(shaderProgram->usesModelMatrix()) {
glUniformMatrix4fv(mMatrixHandle, 1, false, (GLfloat *) mMatrix);
}

glUniform4f(originOffsetHandle, polygonOrigin.x - origin.x, polygonOrigin.y - origin.y, polygonOrigin.z - origin.z, 0.0);

glPatchParameteri(GL_PATCH_VERTICES, 3);

glUniform1i(subdivisionFactorHandle, pow(2, subdivisionFactor));

glUniform4f(originHandle, origin.x, origin.y, origin.z, 0.0);

glUniform1i(is3dHandle, is3d);

glDrawElements(GL_PATCHES, (unsigned short)indices.size(), GL_UNSIGNED_SHORT, nullptr);

glBindVertexArray(0);

glDisable(GL_BLEND);
}

void Polygon2dTessellatedOpenGl::renderAsMask(const std::shared_ptr<::RenderingContextInterface> &context,
const ::RenderPassConfig &renderPass, int64_t vpMatrix, int64_t mMatrix,
const ::Vec3D &origin, double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) {
if (!ready) {
return;
}

std::shared_ptr<OpenGlContext> openGlContext = std::static_pointer_cast<OpenGlContext>(context);

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
drawPolygon(openGlContext, program, vpMatrix, mMatrix, origin, isScreenSpaceCoords);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}

void Polygon2dTessellatedOpenGl::setDebugLabel(const std::string &label) {
// not used
}
Loading
Loading