diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp index ef12df175..26dace960 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp @@ -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 GraphicsObjectFactoryOpenGl::createQuad(const std::shared_ptr<::ShaderProgramInterface> &shader) { return std::make_shared(enforceGlShader(shader)); } +std::shared_ptr GraphicsObjectFactoryOpenGl::createQuadTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) { + return std::make_shared(enforceGlShader(shader)); +} + std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygon(const std::shared_ptr<::ShaderProgramInterface> &shader) { return std::make_shared(enforceGlShader(shader)); } +std::shared_ptr +GraphicsObjectFactoryOpenGl::createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) { + return std::make_shared(enforceGlShader(shader)); +} + std::shared_ptr GraphicsObjectFactoryOpenGl::createLineGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) { return std::make_shared(enforceGlShader(shader)); @@ -55,6 +67,12 @@ std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMa return std::make_shared(enforceGlShader(shader)); } +std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMaskTessellated(bool is3D) { + std::shared_ptr shader = std::make_shared(is3D); + shader->setColor(1, 1, 1, 1); + return std::make_shared(enforceGlShader(shader)); +} + std::shared_ptr GraphicsObjectFactoryOpenGl::createText(const std::shared_ptr<::ShaderProgramInterface> &shader) { return std::make_shared(enforceGlShader(shader)); } @@ -83,4 +101,4 @@ std::shared_ptr GraphicsObjectFactoryOpenGl::enforceGlS throw std::runtime_error("GraphicsObjectFactoryOpenGl: ShaderProgramInterface doesn't extend BaseShaderProgramOpenGl!"); } return glShader; -} \ No newline at end of file +} diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h index e3c03a314..b7726939e 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h @@ -17,8 +17,12 @@ class GraphicsObjectFactoryOpenGl : public GraphicsObjectFactoryInterface { public: std::shared_ptr createQuad(const std::shared_ptr<::ShaderProgramInterface> &shader) override; + std::shared_ptr createQuadTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) override; + std::shared_ptr createPolygon(const std::shared_ptr<::ShaderProgramInterface> &shader) override; + std::shared_ptr createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) override; + std::shared_ptr createLineGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) override; std::shared_ptr createPolygonGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) override; @@ -29,6 +33,8 @@ class GraphicsObjectFactoryOpenGl : public GraphicsObjectFactoryInterface { std::shared_ptr createPolygonMask(bool is3D) override; + std::shared_ptr createPolygonMaskTessellated(bool is3D) override; + std::shared_ptr createText(const std::shared_ptr<::ShaderProgramInterface> &shader) override; std::shared_ptr createTextInstanced(const std::shared_ptr<::ShaderProgramInterface> & shader) override; diff --git a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp index 9d0ad6838..db325bdb5 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp @@ -21,7 +21,9 @@ std::shared_ptr 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) {} + +void Polygon2dOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, bool is3d) { std::lock_guard lock(dataMutex); ready = false; dataReady = false; @@ -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); } @@ -44,8 +46,9 @@ void Polygon2dOpenGl::setVertices(const ::SharedBytes & vertices_, const ::Share void Polygon2dOpenGl::setup(const std::shared_ptr<::RenderingContextInterface> &context) { std::lock_guard lock(dataMutex); - if (ready || !dataReady) + if (ready || !dataReady) { return; + } std::shared_ptr openGlContext = std::static_pointer_cast(context); programName = shaderProgram->getProgramName(); @@ -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 lock(dataMutex); - if (!ready || !shaderProgram->isRenderable()) + if (!ready || !shaderProgram->isRenderable()) { return; + } std::shared_ptr openGlContext = std::static_pointer_cast(context); @@ -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 = std::static_pointer_cast(context); diff --git a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h index bedf4bcd0..0b77a1676 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h @@ -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 asGraphicsObject() override; diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp new file mode 100644 index 000000000..afcfb2bd1 --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + +Polygon2dTessellatedOpenGl::Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader) + : shaderProgram(shader) {} + +std::shared_ptr Polygon2dTessellatedOpenGl::asGraphicsObject() { return shared_from_this(); } + +std::shared_ptr Polygon2dTessellatedOpenGl::asMaskingObject() { return shared_from_this(); } + +bool Polygon2dTessellatedOpenGl::isReady() { return ready; } + +void Polygon2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { + std::lock_guard lock(dataMutex); + if (factor != subdivisionFactor) { + subdivisionFactor = factor; + } +} + +void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, bool is3d) { + std::lock_guard 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 lock(dataMutex); + if (ready || !dataReady) { + return; + } + + std::shared_ptr openGlContext = std::static_pointer_cast(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 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 lock(dataMutex); + if (!ready || !shaderProgram->isRenderable()) { + return; + } + + std::shared_ptr openGlContext = std::static_pointer_cast(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 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 = std::static_pointer_cast(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 +} diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h new file mode 100644 index 000000000..d5af2fb3d --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +#pragma once + +#include "GraphicsObjectInterface.h" +#include "MaskingObjectInterface.h" +#include "OpenGlContext.h" +#include "Polygon2dInterface.h" +#include "ShaderProgramInterface.h" +#include "BaseShaderProgramOpenGl.h" +#include "opengl_wrapper.h" +#include + +class Polygon2dTessellatedOpenGl : public GraphicsObjectInterface, + public MaskingObjectInterface, + public Polygon2dInterface, + public std::enable_shared_from_this { + public: + Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader); + + ~Polygon2dTessellatedOpenGl(){}; + + virtual bool isReady() override; + + virtual void setup(const std::shared_ptr<::RenderingContextInterface> &context) override; + + virtual void clear() override; + + virtual void 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) override; + + virtual void renderAsMask(const std::shared_ptr<::RenderingContextInterface> &context, const ::RenderPassConfig &renderPass, + int64_t vpMatrix, int64_t mMatrix, const ::Vec3D & origin, + double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) 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 asGraphicsObject() override; + + virtual std::shared_ptr asMaskingObject() override; + + virtual void setIsInverseMasked(bool inversed) override; + + void setDebugLabel(const std::string &label) override; + +protected: + void prepareGlData(int program); + + void removeGlBuffers(); + + inline void drawPolygon(const std::shared_ptr<::RenderingContextInterface> &context, int program, int64_t vpMatrix, + int64_t mMatrix, const Vec3D &origin, bool isScreenSpaceCoords); + + std::shared_ptr shaderProgram; + std::string programName; + int program = 0; + + int mMatrixHandle; + int originOffsetHandle; + int subdivisionFactorHandle; + int originHandle; + int is3dHandle; + int positionHandle; + int frameCoordHandle; + GLuint vao; + GLuint vertexBuffer; + std::vector vertices; + GLuint indexBuffer; + std::vector indices; + bool glDataBuffersGenerated = false; + Vec3D polygonOrigin = Vec3D(0.0, 0.0, 0.0); + + bool is3d = false; + int32_t subdivisionFactor = 0; + + bool dataReady = false; + bool ready = false; + std::recursive_mutex dataMutex; + + bool isMaskInversed = false; +}; diff --git a/android/src/main/cpp/graphics/objects/Quad2dOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dOpenGl.cpp index d32dd23c3..a047f2c3e 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dOpenGl.cpp @@ -12,6 +12,7 @@ #include "TextureHolderInterface.h" #include "TextureFilterType.h" #include +#include "Logger.h" Quad2dOpenGl::Quad2dOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader) : shaderProgram(shader) {} @@ -320,7 +321,7 @@ void Quad2dOpenGl::renderAsMask(const std::shared_ptr<::RenderingContextInterfac render(context, renderPass, vpMatrix, mMatrix, origin, false, screenPixelAsRealMeterFactor, isScreenSpaceCoords); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } -#include "Logger.h" + void Quad2dOpenGl::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) { diff --git a/android/src/main/cpp/graphics/objects/Quad2dOpenGl.h b/android/src/main/cpp/graphics/objects/Quad2dOpenGl.h index a6c6ed75f..eeaaa915e 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Quad2dOpenGl.h @@ -94,7 +94,9 @@ class Quad2dOpenGl : public GraphicsObjectInterface, GLuint indexBuffer; std::vector indices; Vec3D quadOrigin = Vec3D(0.0, 0.0, 0.0); + bool is3d = false; + int32_t subdivisionFactor = 0; std::shared_ptr textureHolder; int texturePointer; @@ -102,7 +104,6 @@ class Quad2dOpenGl : public GraphicsObjectInterface, bool usesTextureCoords = false; - int32_t subdivisionFactor = 0; Quad3dD frame = Quad3dD(Vec3D(0.0, 0.0, 0.0), Vec3D(0.0, 0.0, 0.0), Vec3D(0.0, 0.0, 0.0), Vec3D(0.0, 0.0, 0.0)); RectD textureCoordinates = RectD(0.0, 0.0, 0.0, 0.0); double factorHeight = 1.0; diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp new file mode 100644 index 000000000..fde1c4f91 --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 "Quad2dTessellatedOpenGl.h" +#include "TextureHolderInterface.h" +#include "TextureFilterType.h" +#include +#include "Logger.h" + +Quad2dTessellatedOpenGl::Quad2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader) + : shaderProgram(shader) {} + +bool Quad2dTessellatedOpenGl::isReady() { return ready && (!usesTextureCoords || textureHolder); } + +std::shared_ptr Quad2dTessellatedOpenGl::asGraphicsObject() { return shared_from_this(); } + +std::shared_ptr Quad2dTessellatedOpenGl::asMaskingObject() { return shared_from_this(); } + +void Quad2dTessellatedOpenGl::clear() { + std::lock_guard lock(dataMutex); + if (ready) { + removeGlBuffers(); + } + if (textureCoordsReady) { + removeTextureCoordsGlBuffers(); + } + if (textureHolder) { + removeTexture(); + } + ready = false; +} + +void Quad2dTessellatedOpenGl::setIsInverseMasked(bool inversed) { isMaskInversed = inversed; } + +void Quad2dTessellatedOpenGl::setFrame(const Quad3dD &frame, const RectD &textureCoordinates, const Vec3D &origin, bool is3d) { + std::lock_guard lock(dataMutex); + ready = false; + this->frame = frame; + this->textureCoordinates = textureCoordinates; + this->quadOrigin = origin; + this->is3d = is3d; +} + +void Quad2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { + std::lock_guard lock(dataMutex); + if (factor != subdivisionFactor) { + subdivisionFactor = factor; + } +} + +void Quad2dTessellatedOpenGl::setMinMagFilter(TextureFilterType filterType) { + std::lock_guard lock(dataMutex); + textureFilterType = filterType; +} + +void Quad2dTessellatedOpenGl::setup(const std::shared_ptr<::RenderingContextInterface> &context) { + std::lock_guard lock(dataMutex); + if (ready) { + return; + } + + computeGeometry(false); + + std::shared_ptr openGlContext = std::static_pointer_cast(context); + programName = shaderProgram->getProgramName(); + program = openGlContext->getProgram(programName); + if (program == 0) { + shaderProgram->setupProgram(openGlContext); + program = openGlContext->getProgram(programName); + } + + prepareGlData(program); + prepareTextureCoordsGlData(program); + + ready = true; +} + +void Quad2dTessellatedOpenGl::computeGeometry(bool texCoordsOnly) { + // Data mutex covered by caller Quad2dTessellatedOpenGL::setup() + if (!texCoordsOnly) { + if (is3d) { + vertices = { + // Position + (float) (1.0 * std::sin(frame.topLeft.y) * std::cos(frame.topLeft.x) - quadOrigin.x), + (float) (1.0 * cos(frame.topLeft.y) - quadOrigin.y), + (float) (-1.0 * std::sin(frame.topLeft.x) * std::sin(frame.topLeft.y) - quadOrigin.z), + // Frame Coord + (float) (frame.topLeft.x), + (float) (frame.topLeft.y), + + // Position + (float) (1.0 * std::sin(frame.topRight.y) * std::cos(frame.topRight.x) - quadOrigin.x), + (float) (1.0 * cos(frame.topRight.y) - quadOrigin.y), + (float) (-1.0 * std::sin(frame.topRight.x) * std::sin(frame.topRight.y) - quadOrigin.z), + // Frame Coord + (float) (frame.topRight.x), + (float) (frame.topRight.y), + + // Position + (float) (1.0 * std::sin(frame.bottomLeft.y) * std::cos(frame.bottomLeft.x) - quadOrigin.x), + (float) (1.0 * cos(frame.bottomLeft.y) - quadOrigin.y), + (float) (-1.0 * std::sin(frame.bottomLeft.x) * std::sin(frame.bottomLeft.y) - quadOrigin.z), + // Frame Coord + (float) (frame.bottomLeft.x), + (float) (frame.bottomLeft.y), + + // Position + (float) (1.0 * std::sin(frame.bottomRight.y) * std::cos(frame.bottomRight.x) - quadOrigin.x), + (float) (1.0 * cos(frame.bottomRight.y) - quadOrigin.y), + (float) (-1.0 * std::sin(frame.bottomRight.x) * std::sin(frame.bottomRight.y) - quadOrigin.z), + // Frame Coord + (float) (frame.bottomRight.x), + (float) (frame.bottomRight.y), + }; + } else { + vertices = { + // Position + (float) (frame.topLeft.x - quadOrigin.x), + (float) (frame.topLeft.y - quadOrigin.y), + (float) (-quadOrigin.z), + // Frame Coord + (float) (frame.topLeft.x), + (float) (frame.topLeft.y), + + // Position + (float) (frame.topRight.x - quadOrigin.x), + (float) (frame.topRight.y - quadOrigin.y), + (float) (-quadOrigin.z), + // Frame Coord + (float) (frame.topRight.x), + (float) (frame.topRight.y), + + // Position + (float) (frame.bottomLeft.x - quadOrigin.x), + (float) (frame.bottomLeft.y - quadOrigin.y), + (float) (-quadOrigin.z), + // Frame Coord + (float) (frame.bottomLeft.x), + (float) (frame.bottomLeft.y), + + // Position + (float) (frame.bottomRight.x - quadOrigin.x), + (float) (frame.bottomRight.y - quadOrigin.y), + (float) (-quadOrigin.z), + // Frame Coord + (float) (frame.bottomRight.x), + (float) (frame.bottomRight.y), + }; + } + } + + float tMinX = factorWidth * textureCoordinates.x; + float tMaxX = factorWidth * (textureCoordinates.x + textureCoordinates.width); + float tMinY = factorHeight * textureCoordinates.y; + float tMaxY = factorHeight * (textureCoordinates.y + textureCoordinates.height); + + textureCoords = {tMinX, tMinY, tMaxX, tMinY, tMinX, tMaxY, tMaxX, tMaxY}; +} + +void Quad2dTessellatedOpenGl::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); + + 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 Quad2dTessellatedOpenGl::prepareTextureCoordsGlData(int program) { + glUseProgram(program); + glBindVertexArray(vao); + + textureCoordinateHandle = glGetAttribLocation(program, "texCoordinate"); + if (textureCoordinateHandle < 0) { + usesTextureCoords = false; + return; + } + + textureUniformHandle = glGetUniformLocation(program, "textureSampler"); + + if (!texCoordBufferGenerated) { + glGenBuffers(1, &textureCoordsBuffer); + texCoordBufferGenerated = true; + } + + glBindBuffer(GL_ARRAY_BUFFER, textureCoordsBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * textureCoords.size(), &textureCoords[0], GL_STATIC_DRAW); + + glEnableVertexAttribArray(textureCoordinateHandle); + glVertexAttribPointer(textureCoordinateHandle, 2, GL_FLOAT, false, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + usesTextureCoords = true; + textureCoordsReady = true; + + glBindVertexArray(0); +} + +void Quad2dTessellatedOpenGl::removeGlBuffers() { + if (glDataBuffersGenerated) { + glDeleteBuffers(1, &vertexBuffer); + glDeleteVertexArrays(1, &vao); + glDataBuffersGenerated = false; + } +} + +void Quad2dTessellatedOpenGl::removeTextureCoordsGlBuffers() { + if (textureCoordsReady) { + if (texCoordBufferGenerated) { + glDeleteBuffers(1, &textureCoordsBuffer); + texCoordBufferGenerated = false; + } + textureCoordsReady = false; + } +} + +void Quad2dTessellatedOpenGl::loadTexture(const std::shared_ptr<::RenderingContextInterface> &context, + const std::shared_ptr &textureHolder) { + std::lock_guard lock(dataMutex); + if (this->textureHolder == textureHolder) { + return; + } + + if (this->textureHolder != nullptr) { + removeTexture(); + } + + if (textureHolder != nullptr) { + texturePointer = textureHolder->attachToGraphics(); + + factorHeight = textureHolder->getImageHeight() * 1.0f / textureHolder->getTextureHeight(); + factorWidth = textureHolder->getImageWidth() * 1.0f / textureHolder->getTextureWidth(); + computeGeometry(true); + + if (ready) { + prepareTextureCoordsGlData(program); + } + this->textureHolder = textureHolder; + } +} + +void Quad2dTessellatedOpenGl::removeTexture() { + std::lock_guard lock(dataMutex); + if (textureHolder) { + textureHolder->clearFromGraphics(); + textureHolder = nullptr; + texturePointer = -1; + if (textureCoordsReady) { + removeTextureCoordsGlBuffers(); + } + } +} + +void Quad2dTessellatedOpenGl::renderAsMask(const std::shared_ptr<::RenderingContextInterface> &context, const RenderPassConfig &renderPass, + int64_t vpMatrix, int64_t mMatrix, const ::Vec3D &origin, + double screenPixelAsRealMeterFactor, bool isScreenSpaceCoords) { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + render(context, renderPass, vpMatrix, mMatrix, origin, false, screenPixelAsRealMeterFactor, isScreenSpaceCoords); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +void Quad2dTessellatedOpenGl::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 lock(dataMutex); + if (!ready || (usesTextureCoords && !textureCoordsReady) || !shaderProgram->isRenderable()) { + return; + } + + 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); + } + + glUseProgram(program); + glBindVertexArray(vao); + + if (usesTextureCoords) { + prepareTextureDraw(program); + } + + shaderProgram->preRender(context, isScreenSpaceCoords); + + if(shaderProgram->usesModelMatrix()) { + glUniformMatrix4fv(mMatrixHandle, 1, false, (GLfloat *) mMatrix); + } + + glUniform4f(originOffsetHandle, quadOrigin.x - origin.x, quadOrigin.y - origin.y, quadOrigin.z - origin.z, 0.0); + + glPatchParameteri(GL_PATCH_VERTICES, 4); + + glUniform1i(subdivisionFactorHandle, pow(2, subdivisionFactor)); + + glUniform4f(originHandle, origin.x, origin.y, origin.z, 0.0); + + glUniform1i(is3dHandle, is3d); + + glDrawArrays(GL_PATCHES, 0, 4); + + glBindVertexArray(0); + + glDisable(GL_BLEND); +} + +void Quad2dTessellatedOpenGl::prepareTextureDraw(int program) { + if (!textureHolder) { + return; + } + + // Set the active texture unit to texture unit 0. + glActiveTexture(GL_TEXTURE0); + + // Bind the texture to this unit. + glBindTexture(GL_TEXTURE_2D, (unsigned int)texturePointer); + if (textureFilterType.has_value()) { + GLint filterParam = *textureFilterType == TextureFilterType::LINEAR ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterParam); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterParam); + } + + // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. + glUniform1i(textureUniformHandle, 0); +} + +void Quad2dTessellatedOpenGl::setDebugLabel(const std::string &label) { + // not used +} diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h new file mode 100644 index 000000000..9d623182b --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +#pragma once + +#include "GraphicsObjectInterface.h" +#include "MaskingObjectInterface.h" +#include "OpenGlContext.h" +#include "Quad2dInterface.h" +#include "ShaderProgramInterface.h" +#include "BaseShaderProgramOpenGl.h" +#include "opengl_wrapper.h" +#include +#include + +class Quad2dTessellatedOpenGl : public GraphicsObjectInterface, + public MaskingObjectInterface, + public Quad2dInterface, + public std::enable_shared_from_this { + public: + Quad2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader); + + ~Quad2dTessellatedOpenGl(){}; + + virtual bool isReady() override; + + virtual void setup(const std::shared_ptr<::RenderingContextInterface> &context) override; + + virtual void clear() override; + + virtual void renderAsMask(const std::shared_ptr<::RenderingContextInterface> &context, const ::RenderPassConfig &renderPass, + int64_t vpMatrix, int64_t mMatrix, const ::Vec3D & origin, double screenPixelAsRealMeterFactor, + bool isScreenSpaceCoords) override; + + virtual void 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) override; + + virtual void setFrame(const ::Quad3dD &frame, const ::RectD &textureCoordinates, const Vec3D &origin, bool is3D) override; + + void setSubdivisionFactor(int32_t factor) override; + + void setMinMagFilter(TextureFilterType filterType) override; + + virtual void loadTexture(const std::shared_ptr<::RenderingContextInterface> &context, + const std::shared_ptr &textureHolder) override; + + virtual void removeTexture() override; + + virtual std::shared_ptr asGraphicsObject() override; + + virtual std::shared_ptr asMaskingObject() override; + + virtual void setIsInverseMasked(bool inversed) override; + + void setDebugLabel(const std::string &label) override; + +protected: + void computeGeometry(bool texCoordsOnly); + + void prepareGlData(int program); + + void prepareTextureCoordsGlData(int program); + + void removeGlBuffers(); + + void removeTextureCoordsGlBuffers(); + + virtual void prepareTextureDraw(int mProgram); + + std::shared_ptr shaderProgram; + std::string programName; + int program; + + bool glDataBuffersGenerated = false; + bool texCoordBufferGenerated = false; + int mMatrixHandle; + int originOffsetHandle; + int subdivisionFactorHandle; + int originHandle; + int is3dHandle; + int positionHandle; + int frameCoordHandle; + GLuint vao; + GLuint vertexBuffer; + std::vector vertices; + int textureUniformHandle; + int textureCoordinateHandle; + GLuint textureCoordsBuffer; + std::vector textureCoords; + Vec3D quadOrigin = Vec3D(0.0, 0.0, 0.0); + + bool is3d = false; + int32_t subdivisionFactor = 0; + + std::shared_ptr textureHolder; + int texturePointer; + std::optional textureFilterType = std::nullopt; + + bool usesTextureCoords = false; + + Quad3dD frame = Quad3dD(Vec3D(0.0, 0.0, 0.0), Vec3D(0.0, 0.0, 0.0), Vec3D(0.0, 0.0, 0.0), Vec3D(0.0, 0.0, 0.0)); + RectD textureCoordinates = RectD(0.0, 0.0, 0.0, 0.0); + double factorHeight = 1.0; + double factorWidth = 1.0; + + bool ready = false; + bool textureCoordsReady = false; + std::recursive_mutex dataMutex; + + bool isMaskInversed = false; +}; diff --git a/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.cpp index 545777854..c444bcf03 100644 --- a/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.cpp @@ -15,6 +15,10 @@ ColorShaderOpenGl::ColorShaderOpenGl(bool projectOntoUnitSphere) : programName(projectOntoUnitSphere ? "UBMAP_ColorShaderUnitSphereOpenGl" : "UBMAP_ColorShaderOpenGl") {} +ColorShaderOpenGl::ColorShaderOpenGl(const std::string &programName) + : programName(programName) +{} + std::string ColorShaderOpenGl::getProgramName() { return programName; } void ColorShaderOpenGl::setupProgram(const std::shared_ptr<::RenderingContextInterface> &context) { @@ -58,27 +62,28 @@ void ColorShaderOpenGl::setColor(float red, float green, float blue, float alpha std::string ColorShaderOpenGl::getVertexShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, precision highp float; - uniform mat4 umMatrix; - uniform vec4 uOriginOffset; - in vec4 vPosition; - - void main() { - gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vPosition) + uOriginOffset); - } - ); + uniform mat4 umMatrix; + uniform vec4 uOriginOffset; + in vec4 vPosition; + + void main() { + gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vPosition) + uOriginOffset); + } + ); } std::string ColorShaderOpenGl::getFragmentShader() { return OMMVersionedGlesShaderCode(320 es, - precision mediump float; - uniform vec4 vColor; - out vec4 fragmentColor; - - void main() { - fragmentColor = vColor; - fragmentColor.a = 1.0; - fragmentColor *= vColor.a; - }); + precision mediump float; + uniform vec4 vColor; + out vec4 fragmentColor; + + void main() { + fragmentColor = vColor; + fragmentColor.a = 1.0; + fragmentColor *= vColor.a; + } + ); } std::shared_ptr ColorShaderOpenGl::asShaderProgramInterface() { return shared_from_this(); } diff --git a/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.h b/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.h index b6d39ff66..8787f10ae 100644 --- a/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/ColorShaderOpenGl.h @@ -20,6 +20,7 @@ class ColorShaderOpenGl : public BaseShaderProgramOpenGl, public std::enable_shared_from_this { public: ColorShaderOpenGl(bool projectOntoUnitSphere); + ColorShaderOpenGl(const std::string &programName); virtual std::shared_ptr asShaderProgramInterface() override; diff --git a/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.cpp index 36f4f7f1f..4564c6840 100644 --- a/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.cpp @@ -16,6 +16,10 @@ RasterShaderOpenGl::RasterShaderOpenGl(bool projectOntoUnitSphere) : programName(projectOntoUnitSphere ? "UBMAP_RasterShaderUnitSphereOpenGl" : "UBMAP_RasterShaderOpenGl") {} +RasterShaderOpenGl::RasterShaderOpenGl(const std::string &programName) + : programName(programName) +{} + std::string RasterShaderOpenGl::getProgramName() { return programName; } diff --git a/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.h b/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.h index 59073998a..bff3d072f 100644 --- a/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/RasterShaderOpenGl.h @@ -22,6 +22,7 @@ class RasterShaderOpenGl : public BaseShaderProgramOpenGl, public std::enable_shared_from_this { public: RasterShaderOpenGl(bool projectOntoUnitSphere); + RasterShaderOpenGl(const std::string &programName); std::string getProgramName() override; diff --git a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp index bec3d2026..55bc2f788 100644 --- a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp @@ -24,6 +24,8 @@ #include "IcosahedronColorShaderOpenGl.h" #include "SphereEffectShaderOpenGl.h" #include "SkySphereShaderOpenGl.h" +#include "TessellatedRasterShaderOpenGl.h" +#include "TessellatedColorShaderOpenGl.h" #include "ElevationInterpolationShaderOpenGl.h" std::shared_ptr ShaderFactoryOpenGl::createAlphaShader() { @@ -50,6 +52,10 @@ std::shared_ptr ShaderFactoryOpenGl::createUnitSphereRast return std::make_shared(true); } +std::shared_ptr ShaderFactoryOpenGl::createQuadTessellatedShader() { + return std::make_shared(true); +} + std::shared_ptr ShaderFactoryOpenGl::createLineGroupShader() { return std::make_shared(false, false); } @@ -74,6 +80,10 @@ std::shared_ptr ShaderFactoryOpenGl::createUnitSphereColor return std::make_shared(true); } +std::shared_ptr ShaderFactoryOpenGl::createPolygonTessellatedShader(bool projectOntoUnitSphere) { + return std::make_shared(projectOntoUnitSphere); +} + std::shared_ptr ShaderFactoryOpenGl::createColorCircleShader() { return std::make_shared(false); } diff --git a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h index 367a55137..ddf06eb8e 100644 --- a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h +++ b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h @@ -33,6 +33,8 @@ class ShaderFactoryOpenGl : public ShaderFactoryInterface { std::shared_ptr createUnitSphereColorShader() override; + std::shared_ptr createPolygonTessellatedShader(bool projectOntoUnitSphere) override; + std::shared_ptr createColorCircleShader() override; std::shared_ptr createUnitSphereAlphaInstancedShader() override; @@ -53,6 +55,8 @@ class ShaderFactoryOpenGl : public ShaderFactoryInterface { std::shared_ptr createUnitSphereRasterShader() override; + std::shared_ptr createQuadTessellatedShader() override; + std::shared_ptr createStretchShader() override; std::shared_ptr createStretchInstancedShader(bool unitSphere) override; diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp new file mode 100644 index 000000000..2f5bbc2af --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 "TessellatedColorShaderOpenGl.h" +#include "OpenGlContext.h" +#include "Tiled2dMapVectorLayerConstants.h" + +TessellatedColorShaderOpenGl::TessellatedColorShaderOpenGl(bool projectOntoUnitSphere) + : ColorShaderOpenGl(projectOntoUnitSphere ? "UBMAP_TessellatedColorShaderUnitSphereOpenGl" : "UBMAP_TessellatedColorShaderOpenGl") +{} + +void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::RenderingContextInterface> &context) { + std::shared_ptr openGlContext = std::static_pointer_cast(context); + + int vertexShader = loadShader(GL_VERTEX_SHADER, getVertexShader()); + int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); + int evaluationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + +#if HARDWARE_TESSELLATION_WIREFRAME + int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); +#endif + + int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); + + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glDeleteShader(vertexShader); + glAttachShader(program, controlShader); + glDeleteShader(controlShader); + glAttachShader(program, evaluationShader); + glDeleteShader(evaluationShader); + glAttachShader(program, fragmentShader); + glDeleteShader(fragmentShader); + +#if HARDWARE_TESSELLATION_WIREFRAME + glAttachShader(program, geometryShader); + glDeleteShader(geometryShader); +#endif + + glLinkProgram(program); + + checkGlProgramLinking(program); + + openGlContext->storeProgram(TessellatedColorShaderOpenGl::getProgramName(), program); +} + +std::string TessellatedColorShaderOpenGl::getVertexShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + in vec4 vPosition; + in vec2 vFrameCoord; + out vec2 c_framecoord; + + void main() { + gl_Position = vPosition; + c_framecoord = vFrameCoord; + } + ); +} + +std::string TessellatedColorShaderOpenGl::getControlShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout (vertices = 3) out; + + uniform int uSubdivisionFactor; + + in vec2 c_framecoord[]; + out vec2 e_framecoord[]; + + void main() + { + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + e_framecoord[gl_InvocationID] = c_framecoord[gl_InvocationID]; + + if (gl_InvocationID == 0) + { + float tessFactor = float(uSubdivisionFactor); + + gl_TessLevelOuter[0] = tessFactor; + gl_TessLevelOuter[1] = tessFactor; + gl_TessLevelOuter[2] = tessFactor; + + gl_TessLevelInner[0] = tessFactor; + } + } + ); +} + +std::string TessellatedColorShaderOpenGl::getEvaluationShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout(triangles, equal_spacing, ccw) in; + + uniform mat4 umMatrix; + uniform vec4 uOriginOffset; + uniform vec4 uOrigin; + uniform bool uIs3d; + + in vec2 e_framecoord[]; + + const float BlendScale = 1000.0; + const float BlendOffset = 0.01; + + vec2 baryinterp(vec2 c0, vec2 c1, vec2 c2, vec3 bary) { + return c0 * bary.x + c1 * bary.y + c2 * bary.z; + } + + vec4 baryinterp(vec4 c0, vec4 c1, vec4 c2, vec3 bary) { + return c0 * bary.x + c1 * bary.y + c2 * bary.z; + } + + vec4 transform(vec2 coordinate, vec4 origin) { + float x = 1.0 * sin(coordinate.y) * cos(coordinate.x) - origin.x; + float y = 1.0 * cos(coordinate.y) - origin.y; + float z = -1.0 * sin(coordinate.y) * sin(coordinate.x) - origin.z; + return vec4(x, y, z, 0.0); + } + + void main() { + vec3 bary = gl_TessCoord.xyz; + + vec4 p0 = gl_in[0].gl_Position; + vec4 p1 = gl_in[1].gl_Position; + vec4 p2 = gl_in[2].gl_Position; + vec4 position = baryinterp(p0, p1, p2, bary); + + if (uIs3d) { + vec2 f0 = e_framecoord[0]; + vec2 f1 = e_framecoord[1]; + vec2 f2 = e_framecoord[2]; + vec2 frameCoord = baryinterp(f0, f1, f2, bary); + + vec4 bent = transform(frameCoord, uOrigin) - uOriginOffset; + float blend = clamp(length(uOriginOffset) * BlendScale - BlendOffset, 0.0, 1.0); + position = mix(position, bent, blend); + } + + gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vec4(position.xyz, 1.0)) + uOriginOffset); + } + ); +} + +#if HARDWARE_TESSELLATION_WIREFRAME +std::string TessellatedColorShaderOpenGl::getGeometryShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout(triangles) in; + layout(line_strip, max_vertices = 6) out; + + void edgeLine(int a, int b) { + gl_Position = gl_in[a].gl_Position; + EmitVertex(); + + gl_Position = gl_in[b].gl_Position; + EmitVertex(); + + EndPrimitive(); + } + + void main() { + edgeLine(0, 1); + edgeLine(1, 2); + edgeLine(2, 0); + } + ); +} +#endif diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h new file mode 100644 index 000000000..84562b649 --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +#pragma once + +#include "ColorShaderOpenGl.h" +#include "RenderingContextInterface.h" +#include "Tiled2dMapVectorLayerConstants.h" + +class TessellatedColorShaderOpenGl : public ColorShaderOpenGl { +public: + TessellatedColorShaderOpenGl(bool projectOntoUnitSphere); + + void setupProgram(const std::shared_ptr &context) override; + +protected: + std::string getVertexShader() override; + + virtual std::string getControlShader(); + + virtual std::string getEvaluationShader(); + +#if HARDWARE_TESSELLATION_WIREFRAME + virtual std::string getGeometryShader(); +#endif +}; + diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp new file mode 100644 index 000000000..75600df58 --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 "TessellatedRasterShaderOpenGl.h" +#include "OpenGlContext.h" +#include "Tiled2dMapVectorLayerConstants.h" + +TessellatedRasterShaderOpenGl::TessellatedRasterShaderOpenGl(bool projectOntoUnitSphere) + : RasterShaderOpenGl(projectOntoUnitSphere ? "UBMAP_TessellatedRasterShaderUnitSphereOpenGl" : "UBMAP_TessellatedRasterShaderOpenGl") +{} + +void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::RenderingContextInterface> &context) { + std::shared_ptr openGlContext = std::static_pointer_cast(context); + + int vertexShader = loadShader(GL_VERTEX_SHADER, getVertexShader()); + int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); + int evaluationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + +#if HARDWARE_TESSELLATION_WIREFRAME + int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); +#endif + + int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); + + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glDeleteShader(vertexShader); + glAttachShader(program, controlShader); + glDeleteShader(controlShader); + glAttachShader(program, evaluationShader); + glDeleteShader(evaluationShader); + glAttachShader(program, fragmentShader); + glDeleteShader(fragmentShader); + +#if HARDWARE_TESSELLATION_WIREFRAME + glAttachShader(program, geometryShader); + glDeleteShader(geometryShader); +#endif + + glLinkProgram(program); + + checkGlProgramLinking(program); + + openGlContext->storeProgram(RasterShaderOpenGl::getProgramName(), program); +} + +std::string TessellatedRasterShaderOpenGl::getVertexShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + in vec4 vPosition; + in vec2 vFrameCoord; + in vec2 texCoordinate; + out vec2 c_framecoord; + out vec2 c_texcoord; + + void main() { + gl_Position = vPosition; + c_framecoord = vFrameCoord; + c_texcoord = texCoordinate; + } + ); +} + +std::string TessellatedRasterShaderOpenGl::getControlShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout (vertices = 4) out; + + uniform int uSubdivisionFactor; + + in vec2 c_framecoord[]; + in vec2 c_texcoord[]; + out vec2 e_framecoord[]; + out vec2 e_texcoord[]; + + void main() + { + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + e_framecoord[gl_InvocationID] = c_framecoord[gl_InvocationID]; + e_texcoord[gl_InvocationID] = c_texcoord[gl_InvocationID]; + + if (gl_InvocationID == 0) + { + float tessFactor = float(uSubdivisionFactor); + + gl_TessLevelOuter[0] = tessFactor; + gl_TessLevelOuter[1] = tessFactor; + gl_TessLevelOuter[2] = tessFactor; + gl_TessLevelOuter[3] = tessFactor; + + gl_TessLevelInner[0] = tessFactor; + gl_TessLevelInner[1] = tessFactor; + } + } + ); +} + +std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout(quads, equal_spacing, cw) in; + + uniform mat4 umMatrix; + uniform vec4 uOriginOffset; + uniform vec4 uOrigin; + uniform bool uIs3d; + + in vec2 e_framecoord[]; + in vec2 e_texcoord[]; +#if HARDWARE_TESSELLATION_WIREFRAME + out vec2 g_texcoord; +#else + out vec2 v_texcoord; +#endif + const float BlendScale = 1000.0; + const float BlendOffset = 0.01; + + vec2 bilerp(vec2 c00, vec2 c01, vec2 c10, vec2 c11, vec2 uv) { + vec2 c0 = mix(c00, c01, uv.x); + vec2 c1 = mix(c10, c11, uv.x); + return mix(c0, c1, uv.y); + } + + vec4 bilerp(vec4 c00, vec4 c01, vec4 c10, vec4 c11, vec2 uv) { + vec4 c0 = mix(c00, c01, uv.x); + vec4 c1 = mix(c10, c11, uv.x); + return mix(c0, c1, uv.y); + } + + vec4 transform(vec2 coordinate, vec4 origin) { + float x = 1.0 * sin(coordinate.y) * cos(coordinate.x) - origin.x; + float y = 1.0 * cos(coordinate.y) - origin.y; + float z = -1.0 * sin(coordinate.y) * sin(coordinate.x) - origin.z; + return vec4(x, y, z, 0.0); + } + + void main() { + vec2 uv = gl_TessCoord.xy; + + vec4 p00 = gl_in[0].gl_Position; + vec4 p01 = gl_in[1].gl_Position; + vec4 p10 = gl_in[2].gl_Position; + vec4 p11 = gl_in[3].gl_Position; + vec4 position = bilerp(p00, p01, p10, p11, uv); + + if (uIs3d) { + vec2 f00 = e_framecoord[0]; + vec2 f01 = e_framecoord[1]; + vec2 f10 = e_framecoord[2]; + vec2 f11 = e_framecoord[3]; + vec2 frameCoord = bilerp(f00, f01, f10, f11, uv); + + vec4 bent = transform(frameCoord, uOrigin) - uOriginOffset; + float blend = clamp(length(uOriginOffset) * BlendScale - BlendOffset, 0.0, 1.0); + position = mix(position, bent, blend); + } + + vec2 t00 = e_texcoord[0]; + vec2 t01 = e_texcoord[1]; + vec2 t10 = e_texcoord[2]; + vec2 t11 = e_texcoord[3]; + vec2 texCoord = bilerp(t00, t01, t10, t11, uv); + + gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vec4(position.xyz, 1.0)) + uOriginOffset); +#if HARDWARE_TESSELLATION_WIREFRAME + g_texcoord = texCoord; +#else + v_texcoord = texCoord; +#endif + } + ); +} + +#if HARDWARE_TESSELLATION_WIREFRAME +std::string TessellatedRasterShaderOpenGl::getGeometryShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout(triangles) in; + layout(line_strip, max_vertices = 6) out; + + in vec2 g_texcoord[]; + out vec2 v_texcoord; + + void edgeLine(int a, int b) { + gl_Position = gl_in[a].gl_Position; + v_texcoord = g_texcoord[a]; + EmitVertex(); + + gl_Position = gl_in[b].gl_Position; + v_texcoord = g_texcoord[b]; + EmitVertex(); + + EndPrimitive(); + } + + void main() { + edgeLine(0, 1); + edgeLine(1, 2); + edgeLine(2, 0); + } + ); +} +#endif diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h new file mode 100644 index 000000000..dac389648 --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +#pragma once + +#include "RasterShaderOpenGl.h" +#include "RenderingContextInterface.h" +#include "Tiled2dMapVectorLayerConstants.h" + +class TessellatedRasterShaderOpenGl : public RasterShaderOpenGl { +public: + TessellatedRasterShaderOpenGl(bool projectOntoUnitSphere); + + void setupProgram(const std::shared_ptr &context) override; + +protected: + std::string getVertexShader() override; + + virtual std::string getControlShader(); + + virtual std::string getEvaluationShader(); + +#if HARDWARE_TESSELLATION_WIREFRAME + virtual std::string getGeometryShader(); +#endif +}; + diff --git a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/GraphicsObjectFactoryInterface.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/GraphicsObjectFactoryInterface.kt index 5c1bfce97..cfcaaaee3 100644 --- a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/GraphicsObjectFactoryInterface.kt +++ b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/GraphicsObjectFactoryInterface.kt @@ -10,8 +10,12 @@ abstract class GraphicsObjectFactoryInterface { abstract fun createQuad(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Quad2dInterface + abstract fun createQuadTessellated(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Quad2dInterface + abstract fun createPolygon(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Polygon2dInterface + abstract fun createPolygonTessellated(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Polygon2dInterface + abstract fun createIcosahedronObject(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): IcosahedronInterface abstract fun createQuadInstanced(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Quad2dInstancedInterface @@ -28,6 +32,8 @@ abstract class GraphicsObjectFactoryInterface { abstract fun createPolygonMask(is3d: Boolean): Polygon2dInterface + abstract fun createPolygonMaskTessellated(is3d: Boolean): Polygon2dInterface + abstract fun createText(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): TextInterface abstract fun createTextInstanced(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): TextInstancedInterface @@ -53,12 +59,24 @@ abstract class GraphicsObjectFactoryInterface { } private external fun native_createQuad(_nativeRef: Long, shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Quad2dInterface + override fun createQuadTessellated(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Quad2dInterface { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_createQuadTessellated(this.nativeRef, shader) + } + private external fun native_createQuadTessellated(_nativeRef: Long, shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Quad2dInterface + override fun createPolygon(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Polygon2dInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } return native_createPolygon(this.nativeRef, shader) } private external fun native_createPolygon(_nativeRef: Long, shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Polygon2dInterface + override fun createPolygonTessellated(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Polygon2dInterface { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_createPolygonTessellated(this.nativeRef, shader) + } + private external fun native_createPolygonTessellated(_nativeRef: Long, shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): Polygon2dInterface + override fun createIcosahedronObject(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): IcosahedronInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } return native_createIcosahedronObject(this.nativeRef, shader) @@ -107,6 +125,12 @@ abstract class GraphicsObjectFactoryInterface { } private external fun native_createPolygonMask(_nativeRef: Long, is3d: Boolean): Polygon2dInterface + override fun createPolygonMaskTessellated(is3d: Boolean): Polygon2dInterface { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_createPolygonMaskTessellated(this.nativeRef, is3d) + } + private external fun native_createPolygonMaskTessellated(_nativeRef: Long, is3d: Boolean): Polygon2dInterface + override fun createText(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): TextInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } return native_createText(this.nativeRef, shader) diff --git a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface.kt index 5990b0014..f3f0cdb15 100644 --- a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface.kt +++ b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface.kt @@ -8,7 +8,9 @@ import java.util.concurrent.atomic.AtomicBoolean abstract class Polygon2dInterface { - abstract fun setVertices(vertices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, indices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, origin: io.openmobilemaps.mapscore.shared.graphics.common.Vec3D) + abstract fun setVertices(vertices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, indices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, origin: io.openmobilemaps.mapscore.shared.graphics.common.Vec3D, is3d: Boolean) + + abstract fun setSubdivisionFactor(factor: Int) abstract fun asGraphicsObject(): GraphicsObjectInterface @@ -29,11 +31,17 @@ abstract class Polygon2dInterface { external fun nativeDestroy(nativeRef: Long) } - override fun setVertices(vertices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, indices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, origin: io.openmobilemaps.mapscore.shared.graphics.common.Vec3D) { + override fun setVertices(vertices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, indices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, origin: io.openmobilemaps.mapscore.shared.graphics.common.Vec3D, is3d: Boolean) { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + native_setVertices(this.nativeRef, vertices, indices, origin, is3d) + } + private external fun native_setVertices(_nativeRef: Long, vertices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, indices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, origin: io.openmobilemaps.mapscore.shared.graphics.common.Vec3D, is3d: Boolean) + + override fun setSubdivisionFactor(factor: Int) { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } - native_setVertices(this.nativeRef, vertices, indices, origin) + native_setSubdivisionFactor(this.nativeRef, factor) } - private external fun native_setVertices(_nativeRef: Long, vertices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, indices: io.openmobilemaps.mapscore.shared.graphics.common.SharedBytes, origin: io.openmobilemaps.mapscore.shared.graphics.common.Vec3D) + private external fun native_setSubdivisionFactor(_nativeRef: Long, factor: Int) override fun asGraphicsObject(): GraphicsObjectInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } diff --git a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/ShaderFactoryInterface.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/ShaderFactoryInterface.kt index 3efdf4fa7..c977f2e0b 100644 --- a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/ShaderFactoryInterface.kt +++ b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/ShaderFactoryInterface.kt @@ -28,6 +28,8 @@ abstract class ShaderFactoryInterface { abstract fun createColorShader(): ColorShaderInterface + abstract fun createPolygonTessellatedShader(unitSphere: Boolean): ColorShaderInterface + abstract fun createColorCircleShader(): ColorCircleShaderInterface abstract fun createUnitSphereColorCircleShader(): ColorCircleShaderInterface @@ -46,6 +48,8 @@ abstract class ShaderFactoryInterface { abstract fun createUnitSphereRasterShader(): RasterShaderInterface + abstract fun createQuadTessellatedShader(): RasterShaderInterface + abstract fun createStretchShader(): StretchShaderInterface abstract fun createStretchInstancedShader(unitSphere: Boolean): StretchInstancedShaderInterface @@ -133,6 +137,12 @@ abstract class ShaderFactoryInterface { } private external fun native_createColorShader(_nativeRef: Long): ColorShaderInterface + override fun createPolygonTessellatedShader(unitSphere: Boolean): ColorShaderInterface { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_createPolygonTessellatedShader(this.nativeRef, unitSphere) + } + private external fun native_createPolygonTessellatedShader(_nativeRef: Long, unitSphere: Boolean): ColorShaderInterface + override fun createColorCircleShader(): ColorCircleShaderInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } return native_createColorCircleShader(this.nativeRef) @@ -187,6 +197,12 @@ abstract class ShaderFactoryInterface { } private external fun native_createUnitSphereRasterShader(_nativeRef: Long): RasterShaderInterface + override fun createQuadTessellatedShader(): RasterShaderInterface { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_createQuadTessellatedShader(this.nativeRef) + } + private external fun native_createQuadTessellatedShader(_nativeRef: Long): RasterShaderInterface + override fun createStretchShader(): StretchShaderInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } return native_createStretchShader(this.nativeRef) diff --git a/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/TessellationMode.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/TessellationMode.kt new file mode 100644 index 000000000..94181e245 --- /dev/null +++ b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/TessellationMode.kt @@ -0,0 +1,10 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from shader.djinni + +package io.openmobilemaps.mapscore.shared.graphics.shader + +enum class TessellationMode { + NONE, + QUAD, + TRIANGLE, +} diff --git a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp index eff7e4fde..02f15faee 100644 --- a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp +++ b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp @@ -34,6 +34,15 @@ NativeGraphicsObjectFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativeQuad2dInterface::toCpp(jniEnv, jret); } +/*not-null*/ std::shared_ptr<::Quad2dInterface> NativeGraphicsObjectFactoryInterface::JavaProxy::createQuadTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::NativeGraphicsObjectFactoryInterface>::get(); + auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_createQuadTessellated, + ::djinni::get(::djinni_generated::NativeShaderProgramInterface::fromCpp(jniEnv, c_shader))); + ::djinni::jniExceptionCheck(jniEnv); + return ::djinni_generated::NativeQuad2dInterface::toCpp(jniEnv, jret); +} /*not-null*/ std::shared_ptr<::Polygon2dInterface> NativeGraphicsObjectFactoryInterface::JavaProxy::createPolygon(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) { auto jniEnv = ::djinni::jniGetThreadEnv(); ::djinni::JniLocalScope jscope(jniEnv, 10); @@ -43,6 +52,15 @@ NativeGraphicsObjectFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativePolygon2dInterface::toCpp(jniEnv, jret); } +/*not-null*/ std::shared_ptr<::Polygon2dInterface> NativeGraphicsObjectFactoryInterface::JavaProxy::createPolygonTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::NativeGraphicsObjectFactoryInterface>::get(); + auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_createPolygonTessellated, + ::djinni::get(::djinni_generated::NativeShaderProgramInterface::fromCpp(jniEnv, c_shader))); + ::djinni::jniExceptionCheck(jniEnv); + return ::djinni_generated::NativePolygon2dInterface::toCpp(jniEnv, jret); +} /*not-null*/ std::shared_ptr<::IcosahedronInterface> NativeGraphicsObjectFactoryInterface::JavaProxy::createIcosahedronObject(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) { auto jniEnv = ::djinni::jniGetThreadEnv(); ::djinni::JniLocalScope jscope(jniEnv, 10); @@ -115,6 +133,15 @@ NativeGraphicsObjectFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativePolygon2dInterface::toCpp(jniEnv, jret); } +/*not-null*/ std::shared_ptr<::Polygon2dInterface> NativeGraphicsObjectFactoryInterface::JavaProxy::createPolygonMaskTessellated(bool c_is3d) { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::NativeGraphicsObjectFactoryInterface>::get(); + auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_createPolygonMaskTessellated, + ::djinni::get(::djinni::Bool::fromCpp(jniEnv, c_is3d))); + ::djinni::jniExceptionCheck(jniEnv); + return ::djinni_generated::NativePolygon2dInterface::toCpp(jniEnv, jret); +} /*not-null*/ std::shared_ptr<::TextInterface> NativeGraphicsObjectFactoryInterface::JavaProxy::createText(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) { auto jniEnv = ::djinni::jniGetThreadEnv(); ::djinni::JniLocalScope jscope(jniEnv, 10); @@ -150,6 +177,15 @@ CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objec } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_GraphicsObjectFactoryInterface_00024CppProxy_native_1createQuadTessellated(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeShaderProgramInterface::JniType j_shader) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::GraphicsObjectFactoryInterface>(nativeRef); + auto r = ref->createQuadTessellated(::djinni_generated::NativeShaderProgramInterface::toCpp(jniEnv, j_shader)); + return ::djinni::release(::djinni_generated::NativeQuad2dInterface::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_GraphicsObjectFactoryInterface_00024CppProxy_native_1createPolygon(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeShaderProgramInterface::JniType j_shader) { try { @@ -159,6 +195,15 @@ CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objec } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_GraphicsObjectFactoryInterface_00024CppProxy_native_1createPolygonTessellated(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeShaderProgramInterface::JniType j_shader) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::GraphicsObjectFactoryInterface>(nativeRef); + auto r = ref->createPolygonTessellated(::djinni_generated::NativeShaderProgramInterface::toCpp(jniEnv, j_shader)); + return ::djinni::release(::djinni_generated::NativePolygon2dInterface::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_GraphicsObjectFactoryInterface_00024CppProxy_native_1createIcosahedronObject(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeShaderProgramInterface::JniType j_shader) { try { @@ -231,6 +276,15 @@ CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objec } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_GraphicsObjectFactoryInterface_00024CppProxy_native_1createPolygonMaskTessellated(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jboolean j_is3d) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::GraphicsObjectFactoryInterface>(nativeRef); + auto r = ref->createPolygonMaskTessellated(::djinni::Bool::toCpp(jniEnv, j_is3d)); + return ::djinni::release(::djinni_generated::NativePolygon2dInterface::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_GraphicsObjectFactoryInterface_00024CppProxy_native_1createText(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeShaderProgramInterface::JniType j_shader) { try { diff --git a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h index 241b82df6..220d565f4 100644 --- a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h +++ b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h @@ -34,7 +34,9 @@ class NativeGraphicsObjectFactoryInterface final : ::djinni::JniInterface<::Grap ~JavaProxy(); /*not-null*/ std::shared_ptr<::Quad2dInterface> createQuad(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; + /*not-null*/ std::shared_ptr<::Quad2dInterface> createQuadTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygon(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; + /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygonTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; /*not-null*/ std::shared_ptr<::IcosahedronInterface> createIcosahedronObject(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; /*not-null*/ std::shared_ptr<::Quad2dInstancedInterface> createQuadInstanced(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; /*not-null*/ std::shared_ptr<::Quad2dStretchedInstancedInterface> createQuadStretchedInstanced(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; @@ -43,6 +45,7 @@ class NativeGraphicsObjectFactoryInterface final : ::djinni::JniInterface<::Grap /*not-null*/ std::shared_ptr<::PolygonPatternGroup2dInterface> createPolygonPatternGroup(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; /*not-null*/ std::shared_ptr<::Quad2dInterface> createQuadMask(bool is3d) override; /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygonMask(bool is3d) override; + /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygonMaskTessellated(bool is3d) override; /*not-null*/ std::shared_ptr<::TextInterface> createText(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; /*not-null*/ std::shared_ptr<::TextInstancedInterface> createTextInstanced(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) override; @@ -52,7 +55,9 @@ class NativeGraphicsObjectFactoryInterface final : ::djinni::JniInterface<::Grap const ::djinni::GlobalRef clazz { ::djinni::jniFindClass("io/openmobilemaps/mapscore/shared/graphics/objects/GraphicsObjectFactoryInterface") }; const jmethodID method_createQuad { ::djinni::jniGetMethodID(clazz.get(), "createQuad", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/Quad2dInterface;") }; + const jmethodID method_createQuadTessellated { ::djinni::jniGetMethodID(clazz.get(), "createQuadTessellated", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/Quad2dInterface;") }; const jmethodID method_createPolygon { ::djinni::jniGetMethodID(clazz.get(), "createPolygon", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface;") }; + const jmethodID method_createPolygonTessellated { ::djinni::jniGetMethodID(clazz.get(), "createPolygonTessellated", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface;") }; const jmethodID method_createIcosahedronObject { ::djinni::jniGetMethodID(clazz.get(), "createIcosahedronObject", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/IcosahedronInterface;") }; const jmethodID method_createQuadInstanced { ::djinni::jniGetMethodID(clazz.get(), "createQuadInstanced", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/Quad2dInstancedInterface;") }; const jmethodID method_createQuadStretchedInstanced { ::djinni::jniGetMethodID(clazz.get(), "createQuadStretchedInstanced", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/Quad2dStretchedInstancedInterface;") }; @@ -61,6 +66,7 @@ class NativeGraphicsObjectFactoryInterface final : ::djinni::JniInterface<::Grap const jmethodID method_createPolygonPatternGroup { ::djinni::jniGetMethodID(clazz.get(), "createPolygonPatternGroup", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/PolygonPatternGroup2dInterface;") }; const jmethodID method_createQuadMask { ::djinni::jniGetMethodID(clazz.get(), "createQuadMask", "(Z)Lio/openmobilemaps/mapscore/shared/graphics/objects/Quad2dInterface;") }; const jmethodID method_createPolygonMask { ::djinni::jniGetMethodID(clazz.get(), "createPolygonMask", "(Z)Lio/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface;") }; + const jmethodID method_createPolygonMaskTessellated { ::djinni::jniGetMethodID(clazz.get(), "createPolygonMaskTessellated", "(Z)Lio/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface;") }; const jmethodID method_createText { ::djinni::jniGetMethodID(clazz.get(), "createText", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/TextInterface;") }; const jmethodID method_createTextInstanced { ::djinni::jniGetMethodID(clazz.get(), "createTextInstanced", "(Lio/openmobilemaps/mapscore/shared/graphics/shader/ShaderProgramInterface;)Lio/openmobilemaps/mapscore/shared/graphics/objects/TextInstancedInterface;") }; }; diff --git a/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp index afdd6a562..2429cbb03 100644 --- a/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp +++ b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp @@ -2,6 +2,7 @@ // This file was generated by Djinni from graphicsobjects.djinni #include "NativePolygon2dInterface.h" // my header +#include "Marshal.hpp" #include "NativeGraphicsObjectInterface.h" #include "NativeMaskingObjectInterface.h" #include "NativeSharedBytes.h" @@ -17,14 +18,23 @@ NativePolygon2dInterface::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jni NativePolygon2dInterface::JavaProxy::~JavaProxy() = default; -void NativePolygon2dInterface::JavaProxy::setVertices(const ::SharedBytes & c_vertices, const ::SharedBytes & c_indices, const ::Vec3D & c_origin) { +void NativePolygon2dInterface::JavaProxy::setVertices(const ::SharedBytes & c_vertices, const ::SharedBytes & c_indices, const ::Vec3D & c_origin, bool c_is3d) { auto jniEnv = ::djinni::jniGetThreadEnv(); ::djinni::JniLocalScope jscope(jniEnv, 10); const auto& data = ::djinni::JniClass<::djinni_generated::NativePolygon2dInterface>::get(); jniEnv->CallVoidMethod(Handle::get().get(), data.method_setVertices, ::djinni::get(::djinni_generated::NativeSharedBytes::fromCpp(jniEnv, c_vertices)), ::djinni::get(::djinni_generated::NativeSharedBytes::fromCpp(jniEnv, c_indices)), - ::djinni::get(::djinni_generated::NativeVec3D::fromCpp(jniEnv, c_origin))); + ::djinni::get(::djinni_generated::NativeVec3D::fromCpp(jniEnv, c_origin)), + ::djinni::get(::djinni::Bool::fromCpp(jniEnv, c_is3d))); + ::djinni::jniExceptionCheck(jniEnv); +} +void NativePolygon2dInterface::JavaProxy::setSubdivisionFactor(int32_t c_factor) { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::NativePolygon2dInterface>::get(); + jniEnv->CallVoidMethod(Handle::get().get(), data.method_setSubdivisionFactor, + ::djinni::get(::djinni::I32::fromCpp(jniEnv, c_factor))); ::djinni::jniExceptionCheck(jniEnv); } /*not-null*/ std::shared_ptr<::GraphicsObjectInterface> NativePolygon2dInterface::JavaProxy::asGraphicsObject() { @@ -51,13 +61,22 @@ CJNIEXPORT void JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_ } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) } -CJNIEXPORT void JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_Polygon2dInterface_00024CppProxy_native_1setVertices(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeSharedBytes::JniType j_vertices, ::djinni_generated::NativeSharedBytes::JniType j_indices, ::djinni_generated::NativeVec3D::JniType j_origin) +CJNIEXPORT void JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_Polygon2dInterface_00024CppProxy_native_1setVertices(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, ::djinni_generated::NativeSharedBytes::JniType j_vertices, ::djinni_generated::NativeSharedBytes::JniType j_indices, ::djinni_generated::NativeVec3D::JniType j_origin, jboolean j_is3d) { try { const auto& ref = ::djinni::objectFromHandleAddress<::Polygon2dInterface>(nativeRef); ref->setVertices(::djinni_generated::NativeSharedBytes::toCpp(jniEnv, j_vertices), ::djinni_generated::NativeSharedBytes::toCpp(jniEnv, j_indices), - ::djinni_generated::NativeVec3D::toCpp(jniEnv, j_origin)); + ::djinni_generated::NativeVec3D::toCpp(jniEnv, j_origin), + ::djinni::Bool::toCpp(jniEnv, j_is3d)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) +} + +CJNIEXPORT void JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_objects_Polygon2dInterface_00024CppProxy_native_1setSubdivisionFactor(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jint j_factor) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::Polygon2dInterface>(nativeRef); + ref->setSubdivisionFactor(::djinni::I32::toCpp(jniEnv, j_factor)); } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) } diff --git a/bridging/android/jni/graphics/objects/NativePolygon2dInterface.h b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.h index 428596f7d..ac27fb134 100644 --- a/bridging/android/jni/graphics/objects/NativePolygon2dInterface.h +++ b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.h @@ -33,7 +33,8 @@ class NativePolygon2dInterface final : ::djinni::JniInterface<::Polygon2dInterfa JavaProxy(JniType j); ~JavaProxy(); - void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin) override; + void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin, bool is3d) override; + void setSubdivisionFactor(int32_t factor) override; /*not-null*/ std::shared_ptr<::GraphicsObjectInterface> asGraphicsObject() override; /*not-null*/ std::shared_ptr<::MaskingObjectInterface> asMaskingObject() override; @@ -42,7 +43,8 @@ class NativePolygon2dInterface final : ::djinni::JniInterface<::Polygon2dInterfa }; const ::djinni::GlobalRef clazz { ::djinni::jniFindClass("io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface") }; - const jmethodID method_setVertices { ::djinni::jniGetMethodID(clazz.get(), "setVertices", "(Lio/openmobilemaps/mapscore/shared/graphics/common/SharedBytes;Lio/openmobilemaps/mapscore/shared/graphics/common/SharedBytes;Lio/openmobilemaps/mapscore/shared/graphics/common/Vec3D;)V") }; + const jmethodID method_setVertices { ::djinni::jniGetMethodID(clazz.get(), "setVertices", "(Lio/openmobilemaps/mapscore/shared/graphics/common/SharedBytes;Lio/openmobilemaps/mapscore/shared/graphics/common/SharedBytes;Lio/openmobilemaps/mapscore/shared/graphics/common/Vec3D;Z)V") }; + const jmethodID method_setSubdivisionFactor { ::djinni::jniGetMethodID(clazz.get(), "setSubdivisionFactor", "(I)V") }; const jmethodID method_asGraphicsObject { ::djinni::jniGetMethodID(clazz.get(), "asGraphicsObject", "()Lio/openmobilemaps/mapscore/shared/graphics/objects/GraphicsObjectInterface;") }; const jmethodID method_asMaskingObject { ::djinni::jniGetMethodID(clazz.get(), "asMaskingObject", "()Lio/openmobilemaps/mapscore/shared/graphics/objects/MaskingObjectInterface;") }; }; diff --git a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp index 2aec30110..3241db576 100644 --- a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp +++ b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp @@ -109,6 +109,15 @@ NativeShaderFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativeColorShaderInterface::toCpp(jniEnv, jret); } +/*not-null*/ std::shared_ptr<::ColorShaderInterface> NativeShaderFactoryInterface::JavaProxy::createPolygonTessellatedShader(bool c_unitSphere) { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::NativeShaderFactoryInterface>::get(); + auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_createPolygonTessellatedShader, + ::djinni::get(::djinni::Bool::fromCpp(jniEnv, c_unitSphere))); + ::djinni::jniExceptionCheck(jniEnv); + return ::djinni_generated::NativeColorShaderInterface::toCpp(jniEnv, jret); +} /*not-null*/ std::shared_ptr<::ColorCircleShaderInterface> NativeShaderFactoryInterface::JavaProxy::createColorCircleShader() { auto jniEnv = ::djinni::jniGetThreadEnv(); ::djinni::JniLocalScope jscope(jniEnv, 10); @@ -185,6 +194,14 @@ NativeShaderFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativeRasterShaderInterface::toCpp(jniEnv, jret); } +/*not-null*/ std::shared_ptr<::RasterShaderInterface> NativeShaderFactoryInterface::JavaProxy::createQuadTessellatedShader() { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::NativeShaderFactoryInterface>::get(); + auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_createQuadTessellatedShader); + ::djinni::jniExceptionCheck(jniEnv); + return ::djinni_generated::NativeRasterShaderInterface::toCpp(jniEnv, jret); +} /*not-null*/ std::shared_ptr<::StretchShaderInterface> NativeShaderFactoryInterface::JavaProxy::createStretchShader() { auto jniEnv = ::djinni::jniGetThreadEnv(); ::djinni::JniLocalScope jscope(jniEnv, 10); @@ -332,6 +349,15 @@ CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_shade } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_shader_ShaderFactoryInterface_00024CppProxy_native_1createPolygonTessellatedShader(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jboolean j_unitSphere) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::ShaderFactoryInterface>(nativeRef); + auto r = ref->createPolygonTessellatedShader(::djinni::Bool::toCpp(jniEnv, j_unitSphere)); + return ::djinni::release(::djinni_generated::NativeColorShaderInterface::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_shader_ShaderFactoryInterface_00024CppProxy_native_1createColorCircleShader(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef) { try { @@ -415,6 +441,15 @@ CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_shade } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } +CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_shader_ShaderFactoryInterface_00024CppProxy_native_1createQuadTessellatedShader(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::ShaderFactoryInterface>(nativeRef); + auto r = ref->createQuadTessellatedShader(); + return ::djinni::release(::djinni_generated::NativeRasterShaderInterface::fromCpp(jniEnv, r)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) +} + CJNIEXPORT jobject JNICALL Java_io_openmobilemaps_mapscore_shared_graphics_shader_ShaderFactoryInterface_00024CppProxy_native_1createStretchShader(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef) { try { diff --git a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h index 48ff3c84e..913a51a63 100644 --- a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h +++ b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h @@ -43,6 +43,7 @@ class NativeShaderFactoryInterface final : ::djinni::JniInterface<::ShaderFactor /*not-null*/ std::shared_ptr<::LineGroupShaderInterface> createUnitSphereSimpleLineGroupShader() override; /*not-null*/ std::shared_ptr<::ColorShaderInterface> createUnitSphereColorShader() override; /*not-null*/ std::shared_ptr<::ColorShaderInterface> createColorShader() override; + /*not-null*/ std::shared_ptr<::ColorShaderInterface> createPolygonTessellatedShader(bool unitSphere) override; /*not-null*/ std::shared_ptr<::ColorCircleShaderInterface> createColorCircleShader() override; /*not-null*/ std::shared_ptr<::ColorCircleShaderInterface> createUnitSphereColorCircleShader() override; /*not-null*/ std::shared_ptr<::PolygonGroupShaderInterface> createPolygonGroupShader(bool isStriped, bool unitSphere) override; @@ -52,6 +53,7 @@ class NativeShaderFactoryInterface final : ::djinni::JniInterface<::ShaderFactor /*not-null*/ std::shared_ptr<::TextInstancedShaderInterface> createUnitSphereTextInstancedShader() override; /*not-null*/ std::shared_ptr<::RasterShaderInterface> createRasterShader() override; /*not-null*/ std::shared_ptr<::RasterShaderInterface> createUnitSphereRasterShader() override; + /*not-null*/ std::shared_ptr<::RasterShaderInterface> createQuadTessellatedShader() override; /*not-null*/ std::shared_ptr<::StretchShaderInterface> createStretchShader() override; /*not-null*/ std::shared_ptr<::StretchInstancedShaderInterface> createStretchInstancedShader(bool unitSphere) override; /*not-null*/ std::shared_ptr<::ColorShaderInterface> createIcosahedronColorShader() override; @@ -74,6 +76,7 @@ class NativeShaderFactoryInterface final : ::djinni::JniInterface<::ShaderFactor const jmethodID method_createUnitSphereSimpleLineGroupShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereSimpleLineGroupShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/LineGroupShaderInterface;") }; const jmethodID method_createUnitSphereColorShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereColorShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/ColorShaderInterface;") }; const jmethodID method_createColorShader { ::djinni::jniGetMethodID(clazz.get(), "createColorShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/ColorShaderInterface;") }; + const jmethodID method_createPolygonTessellatedShader { ::djinni::jniGetMethodID(clazz.get(), "createPolygonTessellatedShader", "(Z)Lio/openmobilemaps/mapscore/shared/graphics/shader/ColorShaderInterface;") }; const jmethodID method_createColorCircleShader { ::djinni::jniGetMethodID(clazz.get(), "createColorCircleShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/ColorCircleShaderInterface;") }; const jmethodID method_createUnitSphereColorCircleShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereColorCircleShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/ColorCircleShaderInterface;") }; const jmethodID method_createPolygonGroupShader { ::djinni::jniGetMethodID(clazz.get(), "createPolygonGroupShader", "(ZZ)Lio/openmobilemaps/mapscore/shared/graphics/shader/PolygonGroupShaderInterface;") }; @@ -83,6 +86,7 @@ class NativeShaderFactoryInterface final : ::djinni::JniInterface<::ShaderFactor const jmethodID method_createUnitSphereTextInstancedShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereTextInstancedShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/TextInstancedShaderInterface;") }; const jmethodID method_createRasterShader { ::djinni::jniGetMethodID(clazz.get(), "createRasterShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/RasterShaderInterface;") }; const jmethodID method_createUnitSphereRasterShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereRasterShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/RasterShaderInterface;") }; + const jmethodID method_createQuadTessellatedShader { ::djinni::jniGetMethodID(clazz.get(), "createQuadTessellatedShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/RasterShaderInterface;") }; const jmethodID method_createStretchShader { ::djinni::jniGetMethodID(clazz.get(), "createStretchShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/StretchShaderInterface;") }; const jmethodID method_createStretchInstancedShader { ::djinni::jniGetMethodID(clazz.get(), "createStretchInstancedShader", "(Z)Lio/openmobilemaps/mapscore/shared/graphics/shader/StretchInstancedShaderInterface;") }; const jmethodID method_createIcosahedronColorShader { ::djinni::jniGetMethodID(clazz.get(), "createIcosahedronColorShader", "()Lio/openmobilemaps/mapscore/shared/graphics/shader/ColorShaderInterface;") }; diff --git a/bridging/android/jni/graphics/shader/NativeTessellationMode.h b/bridging/android/jni/graphics/shader/NativeTessellationMode.h new file mode 100644 index 000000000..911b586d0 --- /dev/null +++ b/bridging/android/jni/graphics/shader/NativeTessellationMode.h @@ -0,0 +1,26 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from shader.djinni + +#pragma once + +#include "TessellationMode.h" +#include "djinni_support.hpp" + +namespace djinni_generated { + +class NativeTessellationMode final : ::djinni::JniEnum { +public: + using CppType = ::TessellationMode; + using JniType = jobject; + + using Boxed = NativeTessellationMode; + + static CppType toCpp(JNIEnv* jniEnv, JniType j) { return static_cast(::djinni::JniClass::get().ordinal(jniEnv, j)); } + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, CppType c) { return ::djinni::JniClass::get().create(jniEnv, static_cast(c)); } + +private: + NativeTessellationMode() : JniEnum("io/openmobilemaps/mapscore/shared/graphics/shader/TessellationMode") {} + friend ::djinni::JniClass; +}; + +} // namespace djinni_generated diff --git a/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm b/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm index 9d69b17a6..d96695b5a 100644 --- a/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm +++ b/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm @@ -49,6 +49,13 @@ - (id)initWithCpp:(const std::shared_ptr<::GraphicsObjectFactoryInterface>&)cppR } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable id)createQuadTessellated:(nullable id)shader { + try { + auto objcpp_result_ = _cppRefHandle.get()->createQuadTessellated(::djinni_generated::ShaderProgramInterface::toCpp(shader)); + return ::djinni_generated::Quad2dInterface::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + - (nullable id)createPolygon:(nullable id)shader { try { auto objcpp_result_ = _cppRefHandle.get()->createPolygon(::djinni_generated::ShaderProgramInterface::toCpp(shader)); @@ -56,6 +63,13 @@ - (id)initWithCpp:(const std::shared_ptr<::GraphicsObjectFactoryInterface>&)cppR } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable id)createPolygonTessellated:(nullable id)shader { + try { + auto objcpp_result_ = _cppRefHandle.get()->createPolygonTessellated(::djinni_generated::ShaderProgramInterface::toCpp(shader)); + return ::djinni_generated::Polygon2dInterface::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + - (nullable id)createIcosahedronObject:(nullable id)shader { try { auto objcpp_result_ = _cppRefHandle.get()->createIcosahedronObject(::djinni_generated::ShaderProgramInterface::toCpp(shader)); @@ -112,6 +126,13 @@ - (id)initWithCpp:(const std::shared_ptr<::GraphicsObjectFactoryInterface>&)cppR } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable id)createPolygonMaskTessellated:(BOOL)is3d { + try { + auto objcpp_result_ = _cppRefHandle.get()->createPolygonMaskTessellated(::djinni::Bool::toCpp(is3d)); + return ::djinni_generated::Polygon2dInterface::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + - (nullable id)createText:(nullable id)shader { try { auto objcpp_result_ = _cppRefHandle.get()->createText(::djinni_generated::ShaderProgramInterface::toCpp(shader)); @@ -142,6 +163,13 @@ - (id)initWithCpp:(const std::shared_ptr<::GraphicsObjectFactoryInterface>&)cppR return ::djinni_generated::Quad2dInterface::toCpp(objcpp_result_); } } + /*not-null*/ std::shared_ptr<::Quad2dInterface> createQuadTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) override + { + @autoreleasepool { + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createQuadTessellated:(::djinni_generated::ShaderProgramInterface::fromCpp(c_shader))]; + return ::djinni_generated::Quad2dInterface::toCpp(objcpp_result_); + } + } /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygon(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) override { @autoreleasepool { @@ -149,6 +177,13 @@ - (id)initWithCpp:(const std::shared_ptr<::GraphicsObjectFactoryInterface>&)cppR return ::djinni_generated::Polygon2dInterface::toCpp(objcpp_result_); } } + /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygonTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) override + { + @autoreleasepool { + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createPolygonTessellated:(::djinni_generated::ShaderProgramInterface::fromCpp(c_shader))]; + return ::djinni_generated::Polygon2dInterface::toCpp(objcpp_result_); + } + } /*not-null*/ std::shared_ptr<::IcosahedronInterface> createIcosahedronObject(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) override { @autoreleasepool { @@ -205,6 +240,13 @@ - (id)initWithCpp:(const std::shared_ptr<::GraphicsObjectFactoryInterface>&)cppR return ::djinni_generated::Polygon2dInterface::toCpp(objcpp_result_); } } + /*not-null*/ std::shared_ptr<::Polygon2dInterface> createPolygonMaskTessellated(bool c_is3d) override + { + @autoreleasepool { + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createPolygonMaskTessellated:(::djinni::Bool::fromCpp(c_is3d))]; + return ::djinni_generated::Polygon2dInterface::toCpp(objcpp_result_); + } + } /*not-null*/ std::shared_ptr<::TextInterface> createText(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & c_shader) override { @autoreleasepool { diff --git a/bridging/ios/MCGraphicsObjectFactoryInterface.h b/bridging/ios/MCGraphicsObjectFactoryInterface.h index 40541d6a6..6d8567607 100644 --- a/bridging/ios/MCGraphicsObjectFactoryInterface.h +++ b/bridging/ios/MCGraphicsObjectFactoryInterface.h @@ -19,8 +19,12 @@ - (nullable id)createQuad:(nullable id)shader; +- (nullable id)createQuadTessellated:(nullable id)shader; + - (nullable id)createPolygon:(nullable id)shader; +- (nullable id)createPolygonTessellated:(nullable id)shader; + - (nullable id)createIcosahedronObject:(nullable id)shader; - (nullable id)createQuadInstanced:(nullable id)shader; @@ -37,6 +41,8 @@ - (nullable id)createPolygonMask:(BOOL)is3d; +- (nullable id)createPolygonMaskTessellated:(BOOL)is3d; + - (nullable id)createText:(nullable id)shader; - (nullable id)createTextInstanced:(nullable id)shader; diff --git a/bridging/ios/MCPolygon2dInterface+Private.mm b/bridging/ios/MCPolygon2dInterface+Private.mm index 823b3154a..d06addd9b 100644 --- a/bridging/ios/MCPolygon2dInterface+Private.mm +++ b/bridging/ios/MCPolygon2dInterface+Private.mm @@ -37,11 +37,19 @@ - (id)initWithCpp:(const std::shared_ptr<::Polygon2dInterface>&)cppRef - (void)setVertices:(nonnull MCSharedBytes *)vertices indices:(nonnull MCSharedBytes *)indices - origin:(nonnull MCVec3D *)origin { + origin:(nonnull MCVec3D *)origin + is3d:(BOOL)is3d { try { _cppRefHandle.get()->setVertices(::djinni_generated::SharedBytes::toCpp(vertices), ::djinni_generated::SharedBytes::toCpp(indices), - ::djinni_generated::Vec3D::toCpp(origin)); + ::djinni_generated::Vec3D::toCpp(origin), + ::djinni::Bool::toCpp(is3d)); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + +- (void)setSubdivisionFactor:(int32_t)factor { + try { + _cppRefHandle.get()->setSubdivisionFactor(::djinni::I32::toCpp(factor)); } DJINNI_TRANSLATE_EXCEPTIONS() } @@ -68,12 +76,19 @@ - (void)setVertices:(nonnull MCSharedBytes *)vertices friend class ::djinni_generated::Polygon2dInterface; public: using ObjcProxyBase::ObjcProxyBase; - void setVertices(const ::SharedBytes & c_vertices, const ::SharedBytes & c_indices, const ::Vec3D & c_origin) override + void setVertices(const ::SharedBytes & c_vertices, const ::SharedBytes & c_indices, const ::Vec3D & c_origin, bool c_is3d) override { @autoreleasepool { [djinni_private_get_proxied_objc_object() setVertices:(::djinni_generated::SharedBytes::fromCpp(c_vertices)) indices:(::djinni_generated::SharedBytes::fromCpp(c_indices)) - origin:(::djinni_generated::Vec3D::fromCpp(c_origin))]; + origin:(::djinni_generated::Vec3D::fromCpp(c_origin)) + is3d:(::djinni::Bool::fromCpp(c_is3d))]; + } + } + void setSubdivisionFactor(int32_t c_factor) override + { + @autoreleasepool { + [djinni_private_get_proxied_objc_object() setSubdivisionFactor:(::djinni::I32::fromCpp(c_factor))]; } } /*not-null*/ std::shared_ptr<::GraphicsObjectInterface> asGraphicsObject() override diff --git a/bridging/ios/MCPolygon2dInterface.h b/bridging/ios/MCPolygon2dInterface.h index 36881e5bc..d5ba2f9e5 100644 --- a/bridging/ios/MCPolygon2dInterface.h +++ b/bridging/ios/MCPolygon2dInterface.h @@ -12,7 +12,10 @@ - (void)setVertices:(nonnull MCSharedBytes *)vertices indices:(nonnull MCSharedBytes *)indices - origin:(nonnull MCVec3D *)origin; + origin:(nonnull MCVec3D *)origin + is3d:(BOOL)is3d; + +- (void)setSubdivisionFactor:(int32_t)factor; - (nullable id)asGraphicsObject; diff --git a/bridging/ios/MCShaderFactoryInterface+Private.mm b/bridging/ios/MCShaderFactoryInterface+Private.mm index adb2eb5e2..d44109e7f 100644 --- a/bridging/ios/MCShaderFactoryInterface+Private.mm +++ b/bridging/ios/MCShaderFactoryInterface+Private.mm @@ -116,6 +116,13 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable id)createPolygonTessellatedShader:(BOOL)unitSphere { + try { + auto objcpp_result_ = _cppRefHandle.get()->createPolygonTessellatedShader(::djinni::Bool::toCpp(unitSphere)); + return ::djinni_generated::ColorShaderInterface::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + - (nullable id)createColorCircleShader { try { auto objcpp_result_ = _cppRefHandle.get()->createColorCircleShader(); @@ -183,6 +190,13 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable id)createQuadTessellatedShader { + try { + auto objcpp_result_ = _cppRefHandle.get()->createQuadTessellatedShader(); + return ::djinni_generated::RasterShaderInterface::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + - (nullable id)createStretchShader { try { auto objcpp_result_ = _cppRefHandle.get()->createStretchShader(); @@ -304,6 +318,13 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef return ::djinni_generated::ColorShaderInterface::toCpp(objcpp_result_); } } + /*not-null*/ std::shared_ptr<::ColorShaderInterface> createPolygonTessellatedShader(bool c_unitSphere) override + { + @autoreleasepool { + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createPolygonTessellatedShader:(::djinni::Bool::fromCpp(c_unitSphere))]; + return ::djinni_generated::ColorShaderInterface::toCpp(objcpp_result_); + } + } /*not-null*/ std::shared_ptr<::ColorCircleShaderInterface> createColorCircleShader() override { @autoreleasepool { @@ -369,6 +390,13 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef return ::djinni_generated::RasterShaderInterface::toCpp(objcpp_result_); } } + /*not-null*/ std::shared_ptr<::RasterShaderInterface> createQuadTessellatedShader() override + { + @autoreleasepool { + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createQuadTessellatedShader]; + return ::djinni_generated::RasterShaderInterface::toCpp(objcpp_result_); + } + } /*not-null*/ std::shared_ptr<::StretchShaderInterface> createStretchShader() override { @autoreleasepool { diff --git a/bridging/ios/MCShaderFactoryInterface.h b/bridging/ios/MCShaderFactoryInterface.h index 68ae0372a..5caefd733 100644 --- a/bridging/ios/MCShaderFactoryInterface.h +++ b/bridging/ios/MCShaderFactoryInterface.h @@ -41,6 +41,8 @@ - (nullable id)createColorShader; +- (nullable id)createPolygonTessellatedShader:(BOOL)unitSphere; + - (nullable id)createColorCircleShader; - (nullable id)createUnitSphereColorCircleShader; @@ -61,6 +63,8 @@ - (nullable id)createUnitSphereRasterShader; +- (nullable id)createQuadTessellatedShader; + - (nullable id)createStretchShader; - (nullable id)createStretchInstancedShader:(BOOL)unitSphere; diff --git a/bridging/ios/MCTessellationMode+Private.h b/bridging/ios/MCTessellationMode+Private.h new file mode 100644 index 000000000..2270bd1e3 --- /dev/null +++ b/bridging/ios/MCTessellationMode+Private.h @@ -0,0 +1,10 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from shader.djinni +#ifdef __cplusplus +#ifndef __OBJC__ + +#include "TessellationMode.h" +#import "DJIMarshal+Private.h" + +#endif +#endif diff --git a/bridging/ios/MCTessellationMode.h b/bridging/ios/MCTessellationMode.h new file mode 100644 index 000000000..573b48921 --- /dev/null +++ b/bridging/ios/MCTessellationMode.h @@ -0,0 +1,11 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from shader.djinni + +#import + +typedef NS_ENUM(NSInteger, MCTessellationMode) +{ + MCTessellationModeNONE = 0, + MCTessellationModeQUAD = 1, + MCTessellationModeTRIANGLE = 2, +}; diff --git a/djinni/graphics/objects/graphicsobjects.djinni b/djinni/graphics/objects/graphicsobjects.djinni index 615c4129a..96367150e 100644 --- a/djinni/graphics/objects/graphicsobjects.djinni +++ b/djinni/graphics/objects/graphicsobjects.djinni @@ -36,7 +36,9 @@ compute_object_interface = interface +c +j +o { graphics_object_factory_interface = interface +c +j +o { create_quad(shader: shader_program_interface) : quad_2d_interface; + create_quad_tessellated(shader: shader_program_interface) : quad_2d_interface; create_polygon(shader: shader_program_interface) : polygon_2d_interface; + create_polygon_tessellated(shader: shader_program_interface) : polygon_2d_interface; create_icosahedron_object(shader: shader_program_interface) : icosahedron_interface; create_quad_instanced(shader: shader_program_interface): quad_2d_instanced_interface; @@ -47,6 +49,7 @@ graphics_object_factory_interface = interface +c +j +o { create_quad_mask(is_3d: bool) : quad_2d_interface; create_polygon_mask(is_3d: bool) : polygon_2d_interface; + create_polygon_mask_tessellated(is_3d: bool) : polygon_2d_interface; create_text(shader: shader_program_interface) : text_interface; @@ -130,7 +133,8 @@ line_group_2d_interface = interface +c +j +o { } polygon_2d_interface = interface +c +j +o { - set_vertices(vertices: shared_bytes, indices: shared_bytes, origin: vec_3_d); + set_vertices(vertices: shared_bytes, indices: shared_bytes, origin: vec_3_d, is_3d: bool); + set_subdivision_factor(factor: i32); as_graphics_object(): graphics_object_interface; as_masking_object(): masking_object_interface; diff --git a/djinni/graphics/shader/shader.djinni b/djinni/graphics/shader/shader.djinni index bfbc35023..5890b7d05 100644 --- a/djinni/graphics/shader/shader.djinni +++ b/djinni/graphics/shader/shader.djinni @@ -11,6 +11,12 @@ blend_mode = enum { multiply; } +tessellation_mode = enum { + none; + quad; + triangle; +} + shader_program_interface = interface +c +o { get_program_name() : string; setup_program(context: rendering_context_interface); @@ -35,6 +41,7 @@ shader_factory_interface = interface +c +j +o { create_unit_sphere_color_shader() : color_shader_interface; create_color_shader() : color_shader_interface; + create_polygon_tessellated_shader(unit_sphere: bool) : color_shader_interface; create_color_circle_shader() : color_circle_shader_interface; create_unit_sphere_color_circle_shader() : color_circle_shader_interface; @@ -48,6 +55,7 @@ shader_factory_interface = interface +c +j +o { create_raster_shader() : raster_shader_interface; create_unit_sphere_raster_shader() : raster_shader_interface; + create_quad_tessellated_shader() : raster_shader_interface; create_stretch_shader(): stretch_shader_interface; create_stretch_instanced_shader(unit_sphere: bool): stretch_instanced_shader_interface; diff --git a/djinni/yaml/tessellation_mode.yaml b/djinni/yaml/tessellation_mode.yaml new file mode 100644 index 000000000..46641916a --- /dev/null +++ b/djinni/yaml/tessellation_mode.yaml @@ -0,0 +1,39 @@ +# AUTOGENERATED FILE - DO NOT MODIFY! +# This file was generated by Djinni from shader.djinni +name: tessellation_mode +typedef: 'enum' +params: [] +prefix: "" +cpp: + typename: '::TessellationMode' + header: '"TessellationMode.h"' + byValue: true +objc: + typename: 'MCTessellationMode' + pointer: false + hash: '%s.hash' + boxed: 'NSNumber' + header: '"MCTessellationMode.h"' +objcpp: + translator: '::djinni::Enum<::TessellationMode, MCTessellationMode>' + header: '"MCTessellationMode+Private.h"' +java: + reference: true + typename: 'io.openmobilemaps.mapscore.shared.graphics.shader.TessellationMode' + writeToParcel: '%s.writeToParcel(out, flags)' + generic: true + readFromParcel: 'new %s(in)' + hash: '%s.hashCode()' + boxed: 'io.openmobilemaps.mapscore.shared.graphics.shader.TessellationMode' +jni: + translator: '::djinni_generated::NativeTessellationMode' + header: '"NativeTessellationMode.h"' + typename: jobject + typeSignature: 'Lio/openmobilemaps/mapscore/shared/graphics/shader/TessellationMode;' +wasm: + translator: '::djinni_generated::NativeTessellationMode' + header: '"NativeTessellationMode.h"' + typename: int32_t +ts: + typename: TessellationMode + module: './module' diff --git a/ios/graphics/Model/GraphicsFactory.swift b/ios/graphics/Model/GraphicsFactory.swift index d438c1223..9897467f5 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -12,6 +12,7 @@ import Foundation import MapCoreSharedModule class GraphicsFactory: MCGraphicsObjectFactoryInterface { + func createQuadMask(_ is3d: Bool) -> (any MCQuad2dInterface)? { let shader = ColorShader(shader: .colorShader) return Quad2d(shader: shader, metalContext: .current) @@ -21,6 +22,11 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { let shader = MaskShader() return Polygon2d(shader: shader, metalContext: .current) } + + func createPolygonMaskTessellated(_ is3d: Bool) -> (any MCPolygon2dInterface)? { + let shader = MaskShader(shader: .maskTessellatedShader) + return Polygon2dTessellated(shader: shader, metalContext: .current) + } func createPolygonGroup(_ shader: MCShaderProgramInterface?) -> MCPolygonGroup2dInterface? { guard let shader else { fatalError("No Shader provided") } @@ -36,6 +42,11 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { guard let shader else { fatalError("No Shader provided") } return Quad2d(shader: shader, metalContext: .current) } + + func createQuadTessellated(_ shader: MCShaderProgramInterface?) -> MCQuad2dInterface? { + guard let shader else { fatalError("No Shader provided") } + return Quad2dTessellated(shader: shader, metalContext: .current) + } func createQuadInstanced(_ shader: MCShaderProgramInterface?) -> MCQuad2dInstancedInterface? { guard let shader else { fatalError("No Shader provided") } @@ -56,6 +67,11 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { guard let shader else { fatalError("No Shader provided") } return Polygon2d(shader: shader, metalContext: .current) } + + func createPolygonTessellated(_ shader: MCShaderProgramInterface?) -> MCPolygon2dInterface? { + guard let shader else { fatalError("No Shader provided") } + return Polygon2dTessellated(shader: shader, metalContext: .current) + } func createText(_ shader: MCShaderProgramInterface?) -> MCTextInterface? { guard let shader else { fatalError("No Shader provided") } diff --git a/ios/graphics/Model/Polygon/Polygon2d.swift b/ios/graphics/Model/Polygon/Polygon2d.swift index dca140cd9..bb084bfdc 100644 --- a/ios/graphics/Model/Polygon/Polygon2d.swift +++ b/ios/graphics/Model/Polygon/Polygon2d.swift @@ -214,8 +214,10 @@ extension Polygon2d: MCMaskingObjectInterface { } extension Polygon2d: MCPolygon2dInterface { + func setSubdivisionFactor(_ factor: Int32) {} + func setVertices( - _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, is3d: Bool ) { lock.withCritical { self.verticesBuffer.copyOrCreate(from: vertices, device: device) diff --git a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift new file mode 100644 index 000000000..9e2f7912e --- /dev/null +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +import Foundation +import MapCoreSharedModule +@preconcurrency import Metal +import simd + +final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { + private var shader: MCShaderProgramInterface + + private var verticesBuffer: MTLBuffer? + private var indicesBuffer: MTLBuffer? + private var indicesCount: Int = 0 + + private var tessellationFactorsBuffer: MTLBuffer? + private var originBuffers: MultiBuffer + + private var is3d = false + private var subdivisionFactor: Int32 = 0 + + private var stencilState: MTLDepthStencilState? + private var renderPassStencilState: MTLDepthStencilState? + + init(shader: MCShaderProgramInterface, metalContext: MetalContext) { + self.shader = shader + originBuffers = .init(device: metalContext.device) + super + .init( + device: metalContext.device, + sampler: metalContext.samplerLibrary.value( + Sampler.magLinear.rawValue)!, + label: "Polygon2dTessellated") + + let factorH = Half(pow(2, Float(self.subdivisionFactor))).bits; + + var tessellationFactors = MTLTriangleTessellationFactorsHalf( + edgeTessellationFactor: (factorH, factorH, factorH), + insideTessellationFactor: factorH + ); + + self.tessellationFactorsBuffer.copyOrCreate( + bytes: &tessellationFactors, + length: MemoryLayout.stride, + device: device) + } + + override func render( + encoder: MTLRenderCommandEncoder, + context: RenderingContext, + renderPass pass: MCRenderPassConfig, + vpMatrix: Int64, + mMatrix: Int64, + origin: MCVec3D, + isMasked: Bool, + screenPixelAsRealMeterFactor _: Double, + isScreenSpaceCoords: Bool + ) { + lock.lock() + defer { + lock.unlock() + } + + #if DEBUG + encoder.pushDebugGroup(label) + defer { + encoder.popDebugGroup() + } + #endif + + if isMasked { + if stencilState == nil { + setupStencilStates() + } + encoder.setDepthStencilState(stencilState) + if maskInverse { + encoder.setStencilReferenceValue(0b0000_0000) + } else { + encoder.setStencilReferenceValue(0b1100_0000) + } + } + + if pass.isPassMasked { + if renderPassStencilState == nil { + renderPassStencilState = self.renderPassMaskStencilState() + } + + encoder.setDepthStencilState(renderPassStencilState) + encoder.setStencilReferenceValue(0b0000_0000) + } + + renderMain( + encoder: encoder, + context: context, + vpMatrix: vpMatrix, + mMatrix: mMatrix, + origin: origin, + isScreenSpaceCoords: isScreenSpaceCoords) + } + + private func renderMain( + encoder: MTLRenderCommandEncoder, + context: RenderingContext, + vpMatrix: Int64, + mMatrix: Int64, + origin: MCVec3D, + isScreenSpaceCoords: Bool + ) { + guard let verticesBuffer, + let indicesBuffer, + let tessellationFactorsBuffer + else { return } + + shader.setupProgram(context) + shader.preRender(context, isScreenSpaceCoords: isScreenSpaceCoords) + + encoder.setVertexBuffer(verticesBuffer, offset: 0, index: 0) + + let vpMatrixBuffer = vpMatrixBuffers.getNextBuffer(context) + if let matrixPointer = UnsafeRawPointer(bitPattern: Int(vpMatrix)) { + vpMatrixBuffer?.contents() + .copyMemory( + from: matrixPointer, byteCount: 64) + } + encoder.setVertexBuffer(vpMatrixBuffer, offset: 0, index: 1) + + if shader.usesModelMatrix() { + if let mMatrixPointer = UnsafeRawPointer(bitPattern: Int(mMatrix)) { + encoder.setVertexBytes(mMatrixPointer, length: 64, index: 2) + } + } + + let originOffsetBuffer = originOffsetBuffers.getNextBuffer(context) + if let bufferPointer = originOffsetBuffer?.contents() + .assumingMemoryBound(to: simd_float4.self) + { + bufferPointer.pointee.x = Float(originOffset.x - origin.x) + bufferPointer.pointee.y = Float(originOffset.y - origin.y) + bufferPointer.pointee.z = Float(originOffset.z - origin.z) + } + encoder.setVertexBuffer(originOffsetBuffer, offset: 0, index: 3) + + let originBuffer = originBuffers.getNextBuffer(context) + if let bufferPointer = originBuffer?.contents() + .assumingMemoryBound( + to: simd_float4.self) + { + bufferPointer.pointee.x = Float(origin.x) + bufferPointer.pointee.y = Float(origin.y) + bufferPointer.pointee.z = Float(origin.z) + } else { + fatalError() + } + encoder.setVertexBuffer(originBuffer, offset: 0, index: 4) + + encoder.setVertexBytes(&self.is3d, length: MemoryLayout.stride, index: 5) + + encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) + + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.lines) + + encoder.drawIndexedPatches( + numberOfPatchControlPoints: 3, + patchStart: 0, + patchCount: indicesCount / 3, + patchIndexBuffer: nil, + patchIndexBufferOffset: 0, + controlPointIndexBuffer: indicesBuffer, + controlPointIndexBufferOffset: 0, + instanceCount: 1, + baseInstance: 0) + + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.fill) + } + + private func setupStencilStates() { + let ss2 = MTLStencilDescriptor() + ss2.stencilCompareFunction = .equal + ss2.stencilFailureOperation = .zero + ss2.depthFailureOperation = .keep + ss2.depthStencilPassOperation = .keep + ss2.readMask = 0b1100_0000 + ss2.writeMask = 0b0000_0000 + + let s2 = MTLDepthStencilDescriptor() + s2.frontFaceStencil = ss2 + s2.backFaceStencil = ss2 + + stencilState = device.makeDepthStencilState(descriptor: s2) + } +} + +extension Polygon2dTessellated: MCMaskingObjectInterface { + func render( + asMask context: MCRenderingContextInterface?, + renderPass _: MCRenderPassConfig, + vpMatrix: Int64, + mMatrix: Int64, + origin: MCVec3D, + screenPixelAsRealMeterFactor _: Double, + isScreenSpaceCoords: Bool + ) { + + lock.lock() + defer { + lock.unlock() + } + + guard isReady(), + let context = context as? RenderingContext, + let encoder = context.encoder + else { return } + + #if DEBUG + encoder.pushDebugGroup(label) + defer { + encoder.popDebugGroup() + } + #endif + + if let mask = context.polygonMask { + encoder.setStencilReferenceValue(0xFF) + encoder.setDepthStencilState(mask) + } + + // stencil prepare pass + renderMain( + encoder: encoder, + context: context, + vpMatrix: vpMatrix, + mMatrix: mMatrix, + origin: origin, + isScreenSpaceCoords: isScreenSpaceCoords) + } +} + +extension Polygon2dTessellated: MCPolygon2dInterface { + func setSubdivisionFactor(_ factor: Int32) { + lock.withCritical { + if self.subdivisionFactor != factor { + self.subdivisionFactor = factor + + let factorH = Half(pow(2, Float(self.subdivisionFactor))).bits; + + var tessellationFactors = MTLTriangleTessellationFactorsHalf( + edgeTessellationFactor: (factorH, factorH, factorH), + insideTessellationFactor: factorH + ); + + self.tessellationFactorsBuffer.copyOrCreate( + bytes: &tessellationFactors, + length: MemoryLayout.stride, + device: device) + } + } + } + + func setVertices( + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, is3d: Bool + ) { + lock.withCritical { + self.is3d = is3d + self.verticesBuffer.copyOrCreate(from: vertices, device: device) + self.indicesBuffer.copyOrCreate(from: indices, device: device) + if self.verticesBuffer != nil, self.indicesBuffer != nil { + self.indicesCount = Int(indices.elementCount) + } else { + self.indicesCount = 0 + } + self.originOffset = origin + } + } + + func asGraphicsObject() -> MCGraphicsObjectInterface? { self } + + func asMaskingObject() -> MCMaskingObjectInterface? { self } +} diff --git a/ios/graphics/Model/Quad/Quad2d.swift b/ios/graphics/Model/Quad/Quad2d.swift index 887a5f49c..154828828 100644 --- a/ios/graphics/Model/Quad/Quad2d.swift +++ b/ios/graphics/Model/Quad/Quad2d.swift @@ -246,8 +246,7 @@ extension Quad2d: MCQuad2dInterface { } func setFrame( - _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, - is3d: Bool + _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, is3d: Bool ) { var vertices: [Vertex3DTexture] = [] var indices: [UInt16] = [] diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift new file mode 100644 index 000000000..919a3c33a --- /dev/null +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +import Foundation +import MapCoreSharedModule +@preconcurrency import Metal +import UIKit +import simd + +final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { + private var verticesBuffer: MTLBuffer? + + private var tessellationFactorsBuffer: MTLBuffer? + private var originBuffers: MultiBuffer + + private var is3d = false + private var subdivisionFactor: Int32 = 0 + + private var texture: MTLTexture? + + private var shader: MCShaderProgramInterface + + private var stencilState: MTLDepthStencilState? + private var renderPassStencilState: MTLDepthStencilState? + + private var renderAsMask = false + + private var frame: MCQuad3dD? + private var textureCoordinates: MCRectD? + + private var nearestSampler: MTLSamplerState + + private var samplerToUse = Sampler.magLinear + + init( + shader: MCShaderProgramInterface, metalContext: MetalContext, + label: String = "Quad2dTessellated" + ) { + self.shader = shader + originBuffers = .init(device: metalContext.device) + nearestSampler = metalContext.samplerLibrary.value( + Sampler.magNearest.rawValue)! + super + .init( + device: metalContext.device, + sampler: metalContext.samplerLibrary.value( + Sampler.magLinear.rawValue)!, + label: label) + + + let factorH = Half(pow(2, Float(self.subdivisionFactor))).bits; + + var tessellationFactors = MTLQuadTessellationFactorsHalf( + edgeTessellationFactor: (factorH, factorH, factorH, factorH), + insideTessellationFactor: (factorH, factorH) + ); + + self.tessellationFactorsBuffer.copyOrCreate( + bytes: &tessellationFactors, + length: MemoryLayout.stride, + device: device) + } + + private func setupStencilStates() { + let ss2 = MTLStencilDescriptor() + ss2.stencilCompareFunction = .equal + ss2.stencilFailureOperation = .zero + ss2.depthFailureOperation = .keep + ss2.depthStencilPassOperation = .keep + ss2.readMask = 0b1111_1111 + ss2.writeMask = 0b0000_0000 + + let s2 = MTLDepthStencilDescriptor() + s2.frontFaceStencil = ss2 + s2.backFaceStencil = ss2 + + stencilState = device.makeDepthStencilState(descriptor: s2) + } + + override func isReady() -> Bool { + guard ready else { + return false + } + if shader is AlphaShader || shader is RasterShader { + return texture != nil + } + return true + } + + override func render( + encoder: MTLRenderCommandEncoder, + context: RenderingContext, + renderPass: MCRenderPassConfig, + vpMatrix: Int64, + mMatrix: Int64, + origin: MCVec3D, + isMasked: Bool, + screenPixelAsRealMeterFactor _: Double, + isScreenSpaceCoords: Bool + ) { + lock.lock() + defer { + lock.unlock() + } + + guard isReady(), + let verticesBuffer, + let tessellationFactorsBuffer + else { return } + + if shader is AlphaShader || shader is RasterShader, texture == nil { + ready = false + return + } + + #if DEBUG + encoder.pushDebugGroup(label) + defer { + encoder.popDebugGroup() + } + #endif + + if isMasked { + if stencilState == nil { + setupStencilStates() + } + encoder.setDepthStencilState(stencilState) + encoder.setStencilReferenceValue(0b1100_0000) + } else if let mask = context.mask, renderAsMask { + encoder.setDepthStencilState(mask) + encoder.setStencilReferenceValue(0b1100_0000) + } else if renderPass.isPassMasked { + if renderPassStencilState == nil { + renderPassStencilState = self.renderPassMaskStencilState() + } + + encoder.setDepthStencilState(renderPassStencilState) + encoder.setStencilReferenceValue(0b0000_0000) + } else { + encoder.setDepthStencilState(context.defaultMask) + } + + shader.setupProgram(context) + shader.preRender(context, isScreenSpaceCoords: isScreenSpaceCoords) + + encoder.setVertexBuffer(verticesBuffer, offset: 0, index: 0) + + let vpMatrixBuffer = vpMatrixBuffers.getNextBuffer(context) + if let matrixPointer = UnsafeRawPointer(bitPattern: Int(vpMatrix)) { + vpMatrixBuffer?.contents() + .copyMemory( + from: matrixPointer, byteCount: 64) + } + encoder.setVertexBuffer(vpMatrixBuffer, offset: 0, index: 1) + + if shader.usesModelMatrix() { + if let mMatrixPointer = UnsafeRawPointer(bitPattern: Int(mMatrix)) { + encoder.setVertexBytes(mMatrixPointer, length: 64, index: 2) + } + } + + let originOffsetBuffer = originOffsetBuffers.getNextBuffer(context) + if let bufferPointer = originOffsetBuffer?.contents() + .assumingMemoryBound(to: simd_float4.self) + { + bufferPointer.pointee.x = Float(originOffset.x - origin.x) + bufferPointer.pointee.y = Float(originOffset.y - origin.y) + bufferPointer.pointee.z = Float(originOffset.z - origin.z) + } else { + fatalError() + } + encoder.setVertexBuffer(originOffsetBuffer, offset: 0, index: 3) + + if samplerToUse == .magNearest { + encoder.setFragmentSamplerState(nearestSampler, index: 0) + } else { + encoder.setFragmentSamplerState(sampler, index: 0) + } + + if let texture { + encoder.setFragmentTexture(texture, index: 0) + } + + let originBuffer = originBuffers.getNextBuffer(context) + if let bufferPointer = originBuffer?.contents() + .assumingMemoryBound( + to: simd_float4.self) + { + bufferPointer.pointee.x = Float(origin.x) + bufferPointer.pointee.y = Float(origin.y) + bufferPointer.pointee.z = Float(origin.z) + } else { + fatalError() + } + encoder.setVertexBuffer(originBuffer, offset: 0, index: 4) + + encoder.setVertexBytes(&self.is3d, length: MemoryLayout.stride, index: 5) + + encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) + + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.lines) + + encoder.drawPatches( + numberOfPatchControlPoints: 4, + patchStart: 0, + patchCount: 1, + patchIndexBuffer: nil, + patchIndexBufferOffset: 0, + instanceCount: 1, + baseInstance: 0) + + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.fill) + } +} + +extension Quad2dTessellated: MCMaskingObjectInterface { + func render( + asMask context: MCRenderingContextInterface?, + renderPass: MCRenderPassConfig, + vpMatrix: Int64, + mMatrix: Int64, + origin: MCVec3D, + screenPixelAsRealMeterFactor: Double, + isScreenSpaceCoords: Bool + ) { + guard isReady(), + let context = context as? RenderingContext, + let encoder = context.encoder + else { return } + + renderAsMask = true + + render( + encoder: encoder, + context: context, + renderPass: renderPass, + vpMatrix: vpMatrix, + mMatrix: mMatrix, + origin: origin, + isMasked: false, + screenPixelAsRealMeterFactor: screenPixelAsRealMeterFactor, + isScreenSpaceCoords: isScreenSpaceCoords) + } +} + +extension Quad2dTessellated: MCQuad2dInterface { + func setMinMagFilter(_ filterType: MCTextureFilterType) { + switch filterType { + case .NEAREST: + samplerToUse = .magNearest + case .LINEAR: + samplerToUse = .magLinear + default: + break + } + } + + func setSubdivisionFactor(_ factor: Int32) { + lock.withCritical { + if self.subdivisionFactor != factor { + self.subdivisionFactor = factor + + let factorH = Half(pow(2, Float(self.subdivisionFactor))).bits; + + var tessellationFactors = MTLQuadTessellationFactorsHalf( + edgeTessellationFactor: (factorH, factorH, factorH, factorH), + insideTessellationFactor: (factorH, factorH) + ); + + self.tessellationFactorsBuffer.copyOrCreate( + bytes: &tessellationFactors, + length: MemoryLayout.stride, + device: device) + } + } + } + + func setFrame( + _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, is3d: Bool + ) { + var vertices: [Vertex3DTextureTessellated] = [] + + func transform(_ coordinate: MCVec3D) -> MCVec3D { + if is3d { + let x = 1.0 * sin(coordinate.y) * cos(coordinate.x) - origin.x + let y = 1.0 * cos(coordinate.y) - origin.y + let z = -1.0 * sin(coordinate.y) * sin(coordinate.x) - origin.z + return MCVec3D(x: x, y: y, z: z) + } else { + let x = coordinate.x - origin.x + let y = coordinate.y - origin.y + return MCVec3D(x: x, y: y, z: 0) + } + } + + /* + The quad is made out of 4 vertices as following + B----C + | | + | | + A----D + Where A-C are joined to form two triangles + */ + vertices = [ + Vertex3DTextureTessellated( + position: transform(frame.topLeft), + frameCoordX: frame.topLeft.xF, + frameCoordY: frame.topLeft.yF, + textureU: textureCoordinates.xF, + textureV: textureCoordinates.yF), // B + Vertex3DTextureTessellated( + position: transform(frame.topRight), + frameCoordX: frame.topRight.xF, + frameCoordY: frame.topRight.yF, + textureU: textureCoordinates.xF + textureCoordinates.widthF, + textureV: textureCoordinates.yF), // C + Vertex3DTextureTessellated( + position: transform(frame.bottomLeft), + frameCoordX: frame.bottomLeft.xF, + frameCoordY: frame.bottomLeft.yF, + textureU: textureCoordinates.xF, + textureV: textureCoordinates.yF + textureCoordinates.heightF), // A + Vertex3DTextureTessellated( + position: transform(frame.bottomRight), + frameCoordX: frame.bottomRight.xF, + frameCoordY: frame.bottomRight.yF, + textureU: textureCoordinates.xF + textureCoordinates.widthF, + textureV: textureCoordinates.yF + textureCoordinates.heightF), // D + ] + + lock.withCritical { + self.is3d = is3d + self.originOffset = origin + self.frame = frame + self.textureCoordinates = textureCoordinates + self.verticesBuffer.copyOrCreate( + bytes: vertices, + length: MemoryLayout.stride * vertices.count, + device: device) + } + } + + func loadTexture( + _ context: MCRenderingContextInterface?, + textureHolder: MCTextureHolderInterface? + ) { + guard let textureHolder = textureHolder as? TextureHolder else { + fatalError("unexpected TextureHolder") + } + lock.withCritical { + texture = textureHolder.texture + } + } + + func removeTexture() { + lock.withCritical { + texture = nil + } + } + + func asGraphicsObject() -> MCGraphicsObjectInterface? { + self + } + + func asMaskingObject() -> MCMaskingObjectInterface? { self } +} diff --git a/ios/graphics/Model/Vertex3D.swift b/ios/graphics/Model/Vertex3D.swift index 3803682a8..1686f8a46 100644 --- a/ios/graphics/Model/Vertex3D.swift +++ b/ios/graphics/Model/Vertex3D.swift @@ -11,10 +11,11 @@ import MapCoreSharedModule @preconcurrency import MetalKit -struct Vertex4F: Equatable { +struct Vertex3D: Equatable { nonisolated(unsafe) static let descriptor: MTLVertexDescriptor = { let vertexDescriptor = MTLVertexDescriptor() + // Position vertexDescriptor.attributes[0].bufferIndex = 0 vertexDescriptor.attributes[0].format = .float4 vertexDescriptor.attributes[0].offset = 0 diff --git a/ios/graphics/Model/Vertex3DTessellated.swift b/ios/graphics/Model/Vertex3DTessellated.swift new file mode 100644 index 000000000..eea2ac177 --- /dev/null +++ b/ios/graphics/Model/Vertex3DTessellated.swift @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +import MapCoreSharedModule +@preconcurrency import MetalKit + +struct Vertex3DTessellated: Equatable { + nonisolated(unsafe) static let descriptor: MTLVertexDescriptor = { + let vertexDescriptor = MTLVertexDescriptor() + let bufferIndex = 0 + var offset = 0 + + // Position + vertexDescriptor.attributes[0].bufferIndex = bufferIndex + vertexDescriptor.attributes[0].format = .float4 + vertexDescriptor.attributes[0].offset = offset + offset += MemoryLayout>.stride + + // Frame Coord (2D coord used to transform onto unit sphere) + vertexDescriptor.attributes[1].bufferIndex = bufferIndex + vertexDescriptor.attributes[1].format = .float2 + vertexDescriptor.attributes[1].offset = offset + offset += MemoryLayout>.stride + + vertexDescriptor.layouts[0].stride = offset + vertexDescriptor.layouts[0].stepRate = 1 + vertexDescriptor.layouts[0].stepFunction = .perPatchControlPoint + return vertexDescriptor + }() +} diff --git a/ios/graphics/Model/Vertex3DTextureTessellated.swift b/ios/graphics/Model/Vertex3DTextureTessellated.swift new file mode 100644 index 000000000..efdeb51a1 --- /dev/null +++ b/ios/graphics/Model/Vertex3DTextureTessellated.swift @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 + */ + +import MapCoreSharedModule +@preconcurrency import MetalKit + +/// A 3D point in the X-Y coordinate system carrying a texture coordinate +public struct Vertex3DTextureTessellated: Equatable { + /// The 3D position of the vertex in the plane + var position: SIMD3 + /// The 2D coord to project onto unit sphere + var frameCoord: SIMD2 + /// The texture coordinates mapped to the vertex in U-V coordinate space + var textureCoordinate: SIMD2 + /// Returns the descriptor to use when passed to a metal shader + nonisolated(unsafe) public static let descriptor: MTLVertexDescriptor = { + let vertexDescriptor = MTLVertexDescriptor() + var offset = 0 + let bufferIndex = 0 + + // Position + vertexDescriptor.attributes[0].bufferIndex = bufferIndex + vertexDescriptor.attributes[0].format = .float3 + vertexDescriptor.attributes[0].offset = offset + offset += MemoryLayout>.stride + + // Frame Coord (2D coord to project onto unit sphere) + vertexDescriptor.attributes[1].bufferIndex = bufferIndex + vertexDescriptor.attributes[1].format = .float2 + vertexDescriptor.attributes[1].offset = offset + offset += MemoryLayout>.stride + + // UV + vertexDescriptor.attributes[2].bufferIndex = bufferIndex + vertexDescriptor.attributes[2].format = .float2 + vertexDescriptor.attributes[2].offset = offset + offset += MemoryLayout>.stride + + vertexDescriptor.layouts[0].stride = MemoryLayout.stride + vertexDescriptor.layouts[0].stepRate = 1 + vertexDescriptor.layouts[0].stepFunction = .perPatchControlPoint + return vertexDescriptor + }() + + /// Initializes a Vertex + /// - Parameters: + /// - relativeX: The X-coordinate position in the plane + /// - relativeY: The Y-coordinate position in the plane + /// - relativeZ: The Z-coordinate position in the plane + /// - absoluteX: The X-coordinate position in the world + /// - absoluteY: The Y-coordinate position in the world + /// - absoluteZ: The Z-coordinate position in the world + /// - textureU: The texture U-coordinate mapping + /// - textureV: The texture V-coordinate mapping + public init( + x: Float, y: Float, z: Float, + frameX: Float, frameY: Float, + textureU: Float, textureV: Float, + ) { + position = SIMD3([x, y, z]) + frameCoord = SIMD2([frameX, frameY]) + textureCoordinate = SIMD2([textureU, textureV]) + } + + public init(position: MCVec3D, frameCoordX: Float, frameCoordY: Float, textureU: Float, textureV: Float) { + self.init( + x: position.xF, y: position.yF, z: position.zF, + frameX: frameCoordX, frameY: frameCoordY, + textureU: textureU, textureV: textureV, + ) + } +} + +extension Vertex3DTextureTessellated { + /// The X-coordinate position in the plane + var x: Float { position.x } + /// The Y-coordinate position in the plane + var y: Float { position.y } + /// The Z-coordinate position in the plane + var z: Float { position.z } + /// The X-coordinate position in the world + var frameCoordX: Float { frameCoord.x } + /// The Y-coordinate position in the world + var frameCoordY: Float { frameCoord.y } + /// The texture U-coordinate mapping + var textureU: Float { textureCoordinate.x } + /// The texture V-coordinate mapping + var textureV: Float { textureCoordinate.x } +} + +extension Vertex3DTextureTessellated: CustomDebugStringConvertible { + public var debugDescription: String { + "" + } +} diff --git a/ios/graphics/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index cb4ead977..c1f2ba5ce 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -10,6 +10,7 @@ @preconcurrency import Metal import OSLog +import MapCoreSharedModule public enum PipelineDescriptorFactory { public static func pipelineDescriptor( @@ -19,7 +20,8 @@ public enum PipelineDescriptorFactory { fragmentShader: String, blendMode: MCBlendMode, library: MTLLibrary, - constants: MTLFunctionConstantValues? = nil + constants: MTLFunctionConstantValues? = nil, + tessellation: MCTessellationMode = MCTessellationMode.NONE ) -> MTLRenderPipelineDescriptor { let pipelineDescriptor = MTLRenderPipelineDescriptor() pipelineDescriptor.colorAttachments[0].pixelFormat = MetalContext.colorPixelFormat @@ -74,7 +76,21 @@ public enum PipelineDescriptorFactory { pipelineDescriptor.vertexFunction = vertexFunction pipelineDescriptor.fragmentFunction = fragmentFunction } - + + if tessellation != MCTessellationMode.NONE { + pipelineDescriptor.maxTessellationFactor = 64 + pipelineDescriptor.tessellationPartitionMode = .pow2 + pipelineDescriptor.tessellationFactorFormat = .half + pipelineDescriptor.tessellationFactorStepFunction = .constant + pipelineDescriptor.tessellationOutputWindingOrder = .clockwise + pipelineDescriptor.tessellationControlPointIndexType = .none + pipelineDescriptor.isTessellationFactorScaleEnabled = false + + if tessellation == MCTessellationMode.TRIANGLE { + pipelineDescriptor.tessellationOutputWindingOrder = .counterClockwise + pipelineDescriptor.tessellationControlPointIndexType = .uint16 + } + } return pipelineDescriptor } } @@ -87,7 +103,9 @@ extension PipelineDescriptorFactory { vertexShader: pipeline.type.vertexShader, fragmentShader: pipeline.type.fragmentShader, blendMode: pipeline.blendMode, - library: library) + library: library, + tessellation: pipeline.type.tessellation + ) } } @@ -112,14 +130,18 @@ public struct Pipeline: Codable, CaseIterable, Hashable, Sendable { } public static var allCases: [Pipeline] { - Array( - PipelineType.allCases - .map { type in - MCBlendMode.allCases.map { blendMode in - Pipeline(type: type, blendMode: blendMode) - } - } - .joined()) + let allPipelines = PipelineType.allCases.flatMap { type in + MCBlendMode.allCases.map { blendMode in + Pipeline(type: type, blendMode: blendMode) + } + } + #if targetEnvironment(simulator) + return allPipelines.filter { + $0.type.tessellation == MCTessellationMode.NONE + } + #else + return allPipelines + #endif } } @@ -135,12 +157,15 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case polygonPatternGroupShader case polygonPatternFadeInGroupShader case maskShader + case maskTessellatedShader case colorShader + case polygonTessellatedShader case roundColorShader case clearStencilShader case textShader case textInstancedShader case rasterShader + case quadTessellatedShader case stretchShader case stretchInstancedShader case unitSphereAlphaShader @@ -164,12 +189,15 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .polygonPatternGroupShader: return "Polygon Group Pattern shader" case .polygonPatternFadeInGroupShader: return "Polygon Group Pattern (fade in) shader" case .maskShader: return "Mask shader" + case .maskTessellatedShader: return "Mask Tessellated shader" case .colorShader: return "Color shader" + case .polygonTessellatedShader: return "Polygon Tessellated shader" case .roundColorShader: return "Round color shader" case .clearStencilShader: return "Clear stencil shader" case .textShader: return "Text shader" case .textInstancedShader: return "Text Instanced shader" case .rasterShader: return "Raster shader" + case .quadTessellatedShader: return "Quad Tessellated shader" case .stretchShader: return "Stretch shader" case .stretchInstancedShader: return "Stretch Instanced shader" case .unitSphereAlphaShader: return "Unit Sphere Alpha shader with texture" @@ -184,7 +212,15 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { var vertexShaderUsesModelMatrix: Bool { switch self { - case .rasterShader, .roundColorShader, .unitSphereRoundColorShader, .alphaShader, .unitSphereAlphaShader, .sphereEffectShader, .skySphereShader, .elevationInterpolation: + case .rasterShader, + .quadTessellatedShader, + .roundColorShader, + .unitSphereRoundColorShader, + .alphaShader, + .unitSphereAlphaShader, + .sphereEffectShader, + .skySphereShader, + .elevationInterpolation: return true default: return false @@ -204,12 +240,15 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .polygonPatternGroupShader: return "polygonPatternGroupVertexShader" case .polygonPatternFadeInGroupShader: return "polygonPatternGroupVertexShader" case .maskShader: return "colorVertexShader" + case .maskTessellatedShader: return "polygonTessellationVertexShader" case .colorShader: return "colorVertexShader" + case .polygonTessellatedShader: return "polygonTessellationVertexShader" case .roundColorShader: return "baseVertexShaderModel" case .clearStencilShader: return "stencilClearVertexShader" case .textShader: return "textVertexShader" case .textInstancedShader: return "textInstancedVertexShader" case .rasterShader: return "baseVertexShaderModel" + case .quadTessellatedShader: return "quadTessellationVertexShader" case .stretchShader: return "stretchVertexShader" case .stretchInstancedShader: return "stretchInstancedVertexShader" case .unitSphereAlphaShader: return "baseVertexShader" @@ -235,12 +274,15 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .polygonPatternGroupShader: return "polygonPatternGroupFragmentShader" case .polygonPatternFadeInGroupShader: return "polygonPatternGroupFadeInFragmentShader" case .maskShader: return "maskFragmentShader" + case .maskTessellatedShader: return "maskFragmentShader" case .colorShader: return "colorFragmentShader" + case .polygonTessellatedShader: return "colorFragmentShader" case .roundColorShader: return "roundColorFragmentShader" case .clearStencilShader: return "stencilClearFragmentShader" case .textShader: return "textFragmentShader" case .textInstancedShader: return "textInstancedFragmentShader" case .rasterShader: return "rasterFragmentShader" + case .quadTessellatedShader: return "rasterFragmentShader" case .stretchShader: return "stretchFragmentShader" case .stretchInstancedShader: return "stretchInstancedFragmentShader" case .unitSphereAlphaShader: return "baseFragmentShader" @@ -264,7 +306,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .polygonPatternFadeInGroupShader, .polygonStripedGroupShader, .colorShader, .maskShader: - return Vertex4F.descriptor + return Vertex3D.descriptor case .rasterShader, .clearStencilShader, .alphaShader, @@ -275,10 +317,27 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .roundColorShader, .elevationInterpolation: return Vertex3DTexture.descriptor + case .maskTessellatedShader, + .polygonTessellatedShader: + return Vertex3DTessellated.descriptor + case .quadTessellatedShader: + return Vertex3DTextureTessellated.descriptor default: return Vertex.descriptor } } + + var tessellation: MCTessellationMode { + switch self { + case .quadTessellatedShader: + return MCTessellationMode.QUAD + case .maskTessellatedShader, + .polygonTessellatedShader: + return MCTessellationMode.TRIANGLE + default: + return MCTessellationMode.NONE + } + } } public typealias PipelineLibrary = StaticMetalLibrary diff --git a/ios/graphics/Shader/Metal/BaseShader.metal b/ios/graphics/Shader/Metal/BaseShader.metal index 990343e25..d31bf7568 100644 --- a/ios/graphics/Shader/Metal/BaseShader.metal +++ b/ios/graphics/Shader/Metal/BaseShader.metal @@ -60,7 +60,7 @@ baseFragmentShader(VertexOut in [[stage_in]], vertex VertexOut -colorVertexShader(const Vertex4FIn vertexIn [[stage_in]], +colorVertexShader(const Vertex3DIn vertexIn [[stage_in]], constant float4x4 &vpMatrix [[buffer(1)]], constant float4x4 &mMatrix [[buffer(2)]], constant float4 &originOffset [[buffer(3)]]) diff --git a/ios/graphics/Shader/Metal/DataStructures.metal b/ios/graphics/Shader/Metal/DataStructures.metal index 95eb289ba..dfd5c0131 100644 --- a/ios/graphics/Shader/Metal/DataStructures.metal +++ b/ios/graphics/Shader/Metal/DataStructures.metal @@ -16,15 +16,26 @@ struct VertexIn { float2 uv [[attribute(1)]]; }; +// NOTE: float3 also take up 4 * sizeOf(float) bytes +// therefore we always use float4 for better alignment and to reduce errors when filling the buffer +struct Vertex3DIn { + float4 position [[attribute(0)]]; +}; + +struct Vertex3DTessellatedIn { + float4 position [[attribute(0)]]; + float2 frameCoord [[attribute(1)]]; +}; + struct Vertex3DTextureIn { float4 position [[attribute(0)]]; float2 uv [[attribute(1)]]; }; -// NOTE: float3 also take up 4 * sizeOf(float) bytes -// therefore we always use float4 for better alignment and to reduce errors when filling the buffer -struct Vertex4FIn { +struct Vertex3DTextureTessellatedIn { float4 position [[attribute(0)]]; + float2 frameCoord [[attribute(1)]]; + float2 uv [[attribute(2)]]; }; struct VertexOut { diff --git a/ios/graphics/Shader/Metal/PolygonGroupShader.metal b/ios/graphics/Shader/Metal/PolygonGroupShader.metal index 5d4ed9d10..12c80b148 100644 --- a/ios/graphics/Shader/Metal/PolygonGroupShader.metal +++ b/ios/graphics/Shader/Metal/PolygonGroupShader.metal @@ -36,7 +36,7 @@ struct PolygonGroupStripeStyling { }; vertex PolygonGroupVertexOut -polygonGroupVertexShader(const Vertex4FIn vertexIn [[stage_in]], +polygonGroupVertexShader(const Vertex3DIn vertexIn [[stage_in]], constant float4x4 &vpMatrix [[buffer(1)]], constant float4 &originOffset [[buffer(2)]]) { @@ -63,7 +63,7 @@ struct PolygonPatternGroupVertexOut { }; vertex PolygonGroupStripedVertexOut -polygonStripedGroupVertexShader(const Vertex4FIn vertexIn [[stage_in]], +polygonStripedGroupVertexShader(const Vertex3DIn vertexIn [[stage_in]], constant float4x4 &vpMatrix [[buffer(1)]], constant float4 &originOffset [[buffer(2)]], constant float2 &posOffset [[buffer(3)]] @@ -97,7 +97,7 @@ polygonGroupStripedFragmentShader(PolygonGroupStripedVertexOut in [[stage_in]], } vertex PolygonPatternGroupVertexOut -polygonPatternGroupVertexShader(const Vertex4FIn vertexIn [[stage_in]], +polygonPatternGroupVertexShader(const Vertex3DIn vertexIn [[stage_in]], constant float4x4 &vpMatrix [[buffer(1)]], constant float2 &scalingFactor [[buffer(2)]], constant float2 &posOffset [[buffer(3)]], diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal new file mode 100644 index 000000000..8967ecd27 --- /dev/null +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Ubique Innovation AG + * + * 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 +#include "DataStructures.metal" +using namespace metal; + +const constant float BlendScale = 1000; +const constant float BlendOffset = 0.01; + +template +inline T bilerp(T c00, T c01, T c10, T c11, float2 uv) { + T c0 = mix(c00, c01, T(uv[0])); + T c1 = mix(c10, c11, T(uv[0])); + return mix(c0, c1, T(uv[1])); +} + +template +inline T bilerp_fast(T c00, T c01, T c10, T c11, half2 uv) +{ + half u = uv.x; + half v = uv.y; + half w00 = (half)1 - u; + half w01 = u; + half w10 = (half)1 - u; + half w11 = u; + half oneMinusV = (half)1 - v; + w00 *= oneMinusV; + w01 *= oneMinusV; + w10 *= v; + w11 *= v; + return c00 * float(w00) + c01 * float(w01) + c10 * float(w10) + c11 * float(w11); +} + +template +inline T baryinterp(T c0, T c1, T c2, float3 bary) { + return c0 * bary[0] + c1 * bary[1] + c2 * bary[2]; +} + +inline float4 transform(float2 coordinate, float4 origin) { + float x = 1.0 * sin(coordinate.y) * cos(coordinate.x) - origin.x; + float y = 1.0 * cos(coordinate.y) - origin.y; + float z = -1.0 * sin(coordinate.y) * sin(coordinate.x) - origin.z; + return float4(x, y, z, 0); +} + +[[patch(quad, 4)]] vertex VertexOut +quadTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], + const float2 positionInPatch [[position_in_patch]], + constant float4x4 &vpMatrix [[buffer(1)]], + constant float4x4 &mMatrix [[buffer(2)]], + constant float4 &originOffset [[buffer(3)]], + constant float4 &origin [[buffer(4)]], + constant bool &is3d [[buffer(5)]]) +{ + Vertex3DTextureTessellatedIn vA = controlPoints[0]; + Vertex3DTextureTessellatedIn vB = controlPoints[1]; + Vertex3DTextureTessellatedIn vC = controlPoints[2]; + Vertex3DTextureTessellatedIn vD = controlPoints[3]; + half2 p = half2(positionInPatch); + + float4 position = bilerp_fast(vA.position, vB.position, vC.position, vD.position, p); + if (is3d) { + float2 frameCoord = bilerp_fast(vA.frameCoord, vB.frameCoord, vC.frameCoord, vD.frameCoord, p); + float4 bent = transform(frameCoord, origin) - originOffset; + float blend = saturate(length(originOffset) * BlendScale - BlendOffset); + position = mix(position, bent, blend); + } + float2 uv = bilerp_fast(vA.uv, vB.uv, vC.uv, vD.uv, p); + + VertexOut out { + .position = vpMatrix * ((mMatrix * float4(position.xyz, 1)) + originOffset), + .uv = uv + }; + + return out; +} + +[[patch(triangle, 3)]] vertex VertexOut +polygonTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], + const float3 positionInPatch [[position_in_patch]], + constant float4x4 &vpMatrix [[buffer(1)]], + constant float4x4 &mMatrix [[buffer(2)]], + constant float4 &originOffset [[buffer(3)]], + constant float4 &origin [[buffer(4)]], + constant bool &is3d [[buffer(5)]]) +{ + Vertex3DTessellatedIn vA = controlPoints[0]; + Vertex3DTessellatedIn vB = controlPoints[1]; + Vertex3DTessellatedIn vC = controlPoints[2]; + + float4 position = baryinterp(vA.position, vB.position, vC.position, positionInPatch); + if (is3d) { + float2 frameCoord = baryinterp(vA.frameCoord, vB.frameCoord, vC.frameCoord, positionInPatch); + float4 bent = transform(frameCoord, origin) - originOffset; + float blend = saturate(length(originOffset) * BlendScale - BlendOffset); + position = mix(position, bent, blend); + } + + VertexOut out { + .position = vpMatrix * (float4(position.xyz, 1) + originOffset), + }; + + return out; +} diff --git a/ios/graphics/Shader/ShaderFactory.swift b/ios/graphics/Shader/ShaderFactory.swift index 58edb1aa2..9a9c4a345 100644 --- a/ios/graphics/Shader/ShaderFactory.swift +++ b/ios/graphics/Shader/ShaderFactory.swift @@ -23,6 +23,10 @@ class ShaderFactory: MCShaderFactoryInterface { func createUnitSphereRasterShader() -> MCRasterShaderInterface? { RasterShader() } + + func createQuadTessellatedShader() -> MCRasterShaderInterface? { + RasterShader(shader: .quadTessellatedShader) + } func createTextShader() -> MCTextShaderInterface? { TextShader() @@ -83,6 +87,10 @@ class ShaderFactory: MCShaderFactoryInterface { func createColorShader() -> MCColorShaderInterface? { ColorShader() } + + func createPolygonTessellatedShader(_ unitSphere: Bool) -> MCColorShaderInterface? { + ColorShader(shader: .polygonTessellatedShader) + } func createRasterShader() -> MCRasterShaderInterface? { RasterShader() diff --git a/ios/helpers/Half.swift b/ios/helpers/Half.swift new file mode 100644 index 000000000..5139d4f21 --- /dev/null +++ b/ios/helpers/Half.swift @@ -0,0 +1,50 @@ +// +// Half.swift +// MapCore +// +// Created by Noah Bussinger Ubique on 19.11.2025. +// + +///Polyfill Float16 on unsupported Intel platforms +public struct Half: Equatable, Hashable, Sendable { + + /// Raw Float16 bit pattern + public var bits: UInt16 + + public init(bits: UInt16) { + self.bits = bits + } + + /// Initialize from Float to Half + public init(_ value: Float) { + self.bits = Half.floatToHalfBits(value) + } + + /// Initialize from Int32 to Float to Half + public init(_ value: Int32) { + self.init(Float(value)) + } + + /// Conversion function from Float to Half bits + private static func floatToHalfBits(_ value: Float) -> UInt16 { + let f = value.bitPattern + let sign = UInt16((f >> 31) & 0x1) + let exp = Int((f >> 23) & 0xFF) - 127 + 15 + var mant = UInt16((f >> 13) & 0x3FF) + + if exp <= 0 { + // Subnormal or zero + if exp < -10 { + return sign << 15 + } + mant |= 0x0400 + let shift = UInt16(1 - exp) + return (sign << 15) | (mant >> shift) + } else if exp >= 31 { + // Inf or NaN + return (sign << 15) | 0x7C00 | (mant > 0 ? 1 : 0) + } + + return (sign << 15) | (UInt16(exp) << 10) | mant + } +} diff --git a/shared/public/GraphicsObjectFactoryInterface.h b/shared/public/GraphicsObjectFactoryInterface.h index 4baf1e9c4..06b5e83d8 100644 --- a/shared/public/GraphicsObjectFactoryInterface.h +++ b/shared/public/GraphicsObjectFactoryInterface.h @@ -23,8 +23,12 @@ class GraphicsObjectFactoryInterface { virtual /*not-null*/ std::shared_ptr createQuad(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; + virtual /*not-null*/ std::shared_ptr createQuadTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; + virtual /*not-null*/ std::shared_ptr createPolygon(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; + virtual /*not-null*/ std::shared_ptr createPolygonTessellated(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; + virtual /*not-null*/ std::shared_ptr createIcosahedronObject(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; virtual /*not-null*/ std::shared_ptr createQuadInstanced(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; @@ -41,6 +45,8 @@ class GraphicsObjectFactoryInterface { virtual /*not-null*/ std::shared_ptr createPolygonMask(bool is3d) = 0; + virtual /*not-null*/ std::shared_ptr createPolygonMaskTessellated(bool is3d) = 0; + virtual /*not-null*/ std::shared_ptr createText(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; virtual /*not-null*/ std::shared_ptr createTextInstanced(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; diff --git a/shared/public/Polygon2dInterface.h b/shared/public/Polygon2dInterface.h index a681ffa84..f5cd01f61 100644 --- a/shared/public/Polygon2dInterface.h +++ b/shared/public/Polygon2dInterface.h @@ -5,6 +5,7 @@ #include "SharedBytes.h" #include "Vec3D.h" +#include #include class GraphicsObjectInterface; @@ -14,7 +15,9 @@ class Polygon2dInterface { public: virtual ~Polygon2dInterface() = default; - virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin) = 0; + virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin, bool is3d) = 0; + + virtual void setSubdivisionFactor(int32_t factor) = 0; virtual /*not-null*/ std::shared_ptr asGraphicsObject() = 0; diff --git a/shared/public/PolygonMaskObject.h b/shared/public/PolygonMaskObject.h index ab3489af2..0e1b1ff24 100644 --- a/shared/public/PolygonMaskObject.h +++ b/shared/public/PolygonMaskObject.h @@ -25,10 +25,10 @@ class PolygonMaskObject: public PolygonMaskObjectInterface { bool is3D); virtual void setPolygons(const std::vector<::PolygonCoord> & polygons, - const Vec3D & origin, std::optional maxSegmentLength = std::nullopt); + const Vec3D & origin, std::optional subdivisionFactor = std::nullopt); virtual void setPolygon(const ::PolygonCoord & polygon, - const Vec3D & origin, std::optional maxSegmentLength = std::nullopt); + const Vec3D & origin, std::optional subdivisionFactor = std::nullopt); virtual void setPolygons(const std::vector<::PolygonCoord> & polygons, const Vec3D & origin) override { diff --git a/shared/public/ShaderFactoryInterface.h b/shared/public/ShaderFactoryInterface.h index a7ad2c87b..371d074ca 100644 --- a/shared/public/ShaderFactoryInterface.h +++ b/shared/public/ShaderFactoryInterface.h @@ -45,6 +45,8 @@ class ShaderFactoryInterface { virtual /*not-null*/ std::shared_ptr createColorShader() = 0; + virtual /*not-null*/ std::shared_ptr createPolygonTessellatedShader(bool unitSphere) = 0; + virtual /*not-null*/ std::shared_ptr createColorCircleShader() = 0; virtual /*not-null*/ std::shared_ptr createUnitSphereColorCircleShader() = 0; @@ -63,6 +65,8 @@ class ShaderFactoryInterface { virtual /*not-null*/ std::shared_ptr createUnitSphereRasterShader() = 0; + virtual /*not-null*/ std::shared_ptr createQuadTessellatedShader() = 0; + virtual /*not-null*/ std::shared_ptr createStretchShader() = 0; virtual /*not-null*/ std::shared_ptr createStretchInstancedShader(bool unitSphere) = 0; diff --git a/shared/public/TessellationMode.h b/shared/public/TessellationMode.h new file mode 100644 index 000000000..5f6bfa929 --- /dev/null +++ b/shared/public/TessellationMode.h @@ -0,0 +1,32 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from shader.djinni + +#pragma once + +#include + +enum class TessellationMode : int { + NONE = 0, + QUAD = 1, + TRIANGLE = 2, +}; + +constexpr const char* toString(TessellationMode e) noexcept { + constexpr const char* names[] = { + "none", + "quad", + "triangle", + }; + return names[static_cast(e)]; +} + +namespace std { + +template <> +struct hash<::TessellationMode> { + size_t operator()(::TessellationMode type) const { + return std::hash()(static_cast(type)); + } +}; + +} // namespace std diff --git a/shared/src/map/layers/icon/IconLayer.cpp b/shared/src/map/layers/icon/IconLayer.cpp index 595a2a2a2..26aca12f5 100644 --- a/shared/src/map/layers/icon/IconLayer.cpp +++ b/shared/src/map/layers/icon/IconLayer.cpp @@ -155,9 +155,8 @@ void IconLayer::addIcons(const std::vector> & auto shader = is3D ? shaderFactory->createUnitSphereAlphaInstancedShader() : shaderFactory->createAlphaInstancedShader(); shader->asShaderProgramInterface()->setBlendMode(icon->getBlendMode()); auto quadObject = objectFactory->createQuadInstanced(shader->asShaderProgramInterface()); - if (is3D) { - //quadObject->setSubdivisionFactor(SUBDIVISION_FACTOR_3D_DEFAULT); - } + //int32_t subdivisionFactor = is3D ? SUBDIVISION_FACTOR_3D_DEFAULT : 0; + //quadObject->setSubdivisionFactor(subdivisionFactor); #if DEBUG quadObject->asGraphicsObject()->setDebugLabel("IconLayerID:" + icon->getIdentifier()); diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index d9b92bfdf..97599e261 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -16,6 +16,7 @@ #include "CoordinatesUtil.h" #include "TrigonometryLUT.h" #include +#include "Tiled2dMapVectorLayerConstants.h" Polygon2dLayerObject::Polygon2dLayerObject(const std::shared_ptr &conversionHelper, const std::shared_ptr &polygon, @@ -96,13 +97,17 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons double ry = is3D ? 1.0 * cos(avgY) : avgY; double rz = is3D ? -1.0 * sin(avgY) * sin(avgX) : 0.0; +#ifndef HARDWARE_TESSELLATION_SUPPORTED if (is3D) { auto bboxSize = bbox.getMax() - bbox.getMin(); double threshold = std::max(std::max(bboxSize.x, bboxSize.y), bboxSize.z) / std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT); PolygonHelper::subdivision(vecVertices, indices, threshold); } - +#endif + for (const auto& v : vecVertices) { + + // Position if(is3D) { double sinX, sinY, cosX, cosY; lut::sincos(v.x, sinX, cosX); @@ -116,15 +121,29 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons vertices.push_back(v.y - ry); vertices.push_back(0.0); } - - #ifdef __APPLE__ - vertices.push_back(0.0f); - #endif + #ifdef __APPLE__ + vertices.push_back(0.0f); + #endif + + #ifdef HARDWARE_TESSELLATION_SUPPORTED + if(is3D) { + // Frame Coord + vertices.push_back(v.x); + vertices.push_back(v.y); + } + #endif } auto attr = SharedBytes((int64_t)vertices.data(), (int32_t)vertices.size(), (int32_t)sizeof(float)); auto ind = SharedBytes((int64_t)indices.data(), (int32_t)indices.size(), (int32_t)sizeof(uint16_t)); - polygon->setVertices(attr, ind, Vec3D(rx, ry, rz)); + polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); + +#ifdef HARDWARE_TESSELLATION_SUPPORTED + if(is3D) { + int32_t subdivisionFactor = SUBDIVISION_FACTOR_3D_DEFAULT; + polygon->setSubdivisionFactor(subdivisionFactor); + } +#endif } void Polygon2dLayerObject::setColor(const Color &color) { diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index 5f6041520..bf9aa8b79 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -12,6 +12,7 @@ #include "EarcutVec2D.h" #include "PolygonHelper.h" #include +#include "Tiled2dMapVectorLayerConstants.h" std::shared_ptr PolygonMaskObjectInterface::create(const std::shared_ptr<::GraphicsObjectFactoryInterface> &graphicsObjectFactory, @@ -24,7 +25,11 @@ PolygonMaskObject::PolygonMaskObject(const std::shared_ptr &conversionHelper, bool is3D) : conversionHelper(conversionHelper) +#ifdef HARDWARE_TESSELLATION_SUPPORTED + , polygon(is3D ? graphicsObjectFactory->createPolygonMaskTessellated(is3D) : graphicsObjectFactory->createPolygonMask(is3D)) +#else , polygon(graphicsObjectFactory->createPolygonMask(is3D)) +#endif , is3D(is3D) {} void PolygonMaskObject::setPositions(const std::vector &positions, @@ -34,13 +39,13 @@ void PolygonMaskObject::setPositions(const std::vector &positions, } void PolygonMaskObject::setPolygon(const ::PolygonCoord &polygon, - const Vec3D & origin, std::optional maxSegmentLength) { - setPolygons({polygon}, origin, maxSegmentLength); + const Vec3D & origin, std::optional subdivisionFactor) { + setPolygons({polygon}, origin, subdivisionFactor); } void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, const Vec3D & origin, - std::optional maxSegmentLength) { + std::optional subdivisionFactor) { std::vector indices; std::vector vertices; int32_t indexOffset = 0; @@ -75,15 +80,18 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, for (auto const &list : renderCoords) { indexOffset += list.size(); - for(auto& i : list) { + for (auto& i : list) { vecVertices.push_back(i); } } } - - if(maxSegmentLength) { - PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); + +#ifndef HARDWARE_TESSELLATION_SUPPORTED + if (subdivisionFactor) { + PolygonHelper::subdivision(vecVertices, indices, *subdivisionFactor); } +#endif + for (const auto& v : vecVertices) { double rx = origin.x; double ry = origin.y; @@ -93,18 +101,32 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, double y = is3D ? (1.0 * cos(v.y) - ry) : v.y - ry; double z = is3D ? (-1.0 * sin(v.y) * sin(v.x) - rz) : 0.0; + // Position vertices.push_back(x); vertices.push_back(y); vertices.push_back(z); #ifdef __APPLE__ vertices.push_back(0.0f); #endif + + #ifdef HARDWARE_TESSELLATION_SUPPORTED + if (subdivisionFactor) { + // Frame Coord + vertices.push_back(v.x); + vertices.push_back(v.y); + } + #endif } auto attr = SharedBytes((int64_t)vertices.data(), (int32_t)vertices.size(), (int32_t)sizeof(float)); auto ind = SharedBytes((int64_t)indices.data(), (int32_t)indices.size(), (int32_t)sizeof(uint16_t)); - - polygon->setVertices(attr, ind, origin); + polygon->setVertices(attr, ind, origin, is3D); + +#ifdef HARDWARE_TESSELLATION_SUPPORTED + if (subdivisionFactor) { + polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(0.0f))); + } +#endif } std::shared_ptr PolygonMaskObject::getPolygonObject() { return polygon; } diff --git a/shared/src/map/layers/polygon/PolygonLayer.cpp b/shared/src/map/layers/polygon/PolygonLayer.cpp index 4c47daa36..530780c01 100644 --- a/shared/src/map/layers/polygon/PolygonLayer.cpp +++ b/shared/src/map/layers/polygon/PolygonLayer.cpp @@ -19,7 +19,7 @@ #include "RenderPass.h" #include #include - +#include "Tiled2dMapVectorLayerConstants.h" #include "PolygonCompare.h" PolygonLayer::PolygonLayer() @@ -123,9 +123,16 @@ void PolygonLayer::addAll(const std::vector &polygons) { std::lock_guard lock(polygonsMutex); for (const auto &polygon : polygons) { + #ifdef HARDWARE_TESSELLATION_SUPPORTED + auto shader = mapInterface->is3d() ? shaderFactory->createPolygonTessellatedShader(mapInterface->is3d()) : + shaderFactory->createColorShader(); + auto polygonGraphicsObject = mapInterface->is3d() ? objectFactory->createPolygonTessellated(shader->asShaderProgramInterface()) : + objectFactory->createPolygon(shader->asShaderProgramInterface()); + #else auto shader = mapInterface->is3d() ? shaderFactory->createUnitSphereColorShader() : shaderFactory->createColorShader(); auto polygonGraphicsObject = objectFactory->createPolygon(shader->asShaderProgramInterface()); - + #endif + auto polygonObject = std::make_shared(mapInterface->getCoordinateConverterHelper(), polygonGraphicsObject, shader, is3d); diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index 0cc2b4857..f3ad923a5 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "Tiled2dMapVectorLayerConstants.h" struct TileInfoHasherIgnoringT { std::size_t operator()(const Tiled2dMapTileInfo& info) const { @@ -302,10 +303,18 @@ std::vector sortedTileInfos(currentTileInfos.begin(), quad->setMinMagFilter(textureFilterType); tileObject = std::make_shared(quad, mapInterface, is3D); } else { + + #ifdef HARDWARE_TESSELLATION_SUPPORTED + auto rasterShader = is3D ? shaderFactory->createQuadTessellatedShader() : shaderFactory->createRasterShader(); + auto quad = is3D ? graphicsFactory->createQuadTessellated(rasterShader->asShaderProgramInterface()) : graphicsFactory->createQuad(rasterShader->asShaderProgramInterface()); + #else auto rasterShader = is3D ? shaderFactory->createUnitSphereRasterShader() : shaderFactory->createRasterShader(); - rasterShader->asShaderProgramInterface()->setBlendMode(blendMode); auto quad = graphicsFactory->createQuad(rasterShader->asShaderProgramInterface()); + #endif + + rasterShader->asShaderProgramInterface()->setBlendMode(blendMode); quad->setMinMagFilter(textureFilterType); + tileObject = std::make_shared( quad, rasterShader, mapInterface, is3D); if (zoomInfo.numDrawPreviousLayers == 0 || !animationsEnabled || zoomInfo.maskTile || is3D) { diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index 9744eec8a..7f9040667 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -10,6 +10,18 @@ #pragma once -#define POLYGON_SUBDIVISION_FACTOR 10.0 -#define POLYGON_MASK_SUBDIVISION_FACTOR 20.0 +#define POLYGON_SUBDIVISION_FACTOR 4 +#define POLYGON_MASK_SUBDIVISION_FACTOR 4 +#if defined(__ANDROID__) + #define HARDWARE_TESSELLATION_SUPPORTED +#endif + +#if defined(__APPLE__) + #include + #if !TARGET_OS_SIMULATOR + #define HARDWARE_TESSELLATION_SUPPORTED + #endif +#endif + +#define HARDWARE_TESSELLATION_WIREFRAME 0 diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index 5a1a95773..5f592c39a 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -98,11 +98,7 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std coordinateConverterHelper, is3D); auto convertedTileBounds = mapInterface->getCoordinateConverterHelper()->convertRectToRenderSystem(tileEntry.tileInfo.tileInfo.bounds); - std::optional maxSegmentLength = std::nullopt; - if (is3D) { - maxSegmentLength = std::min(std::abs(convertedTileBounds.bottomRight.x - convertedTileBounds.topLeft.x) / - POLYGON_MASK_SUBDIVISION_FACTOR, (M_PI * 2.0) / POLYGON_MASK_SUBDIVISION_FACTOR); - } + double cx = (convertedTileBounds.bottomRight.x + convertedTileBounds.topLeft.x) / 2.0; double cy = (convertedTileBounds.bottomRight.y + convertedTileBounds.topLeft.y) / 2.0; double rx = is3D ? 1.0 * sin(cy) * cos(cx) : cx; @@ -110,9 +106,23 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std double rz = is3D ? -1.0 * sin(cy) * sin(cx) : 0.0; Vec3D origin(rx, ry, rz); - + + #ifdef HARDWARE_TESSELLATION_SUPPORTED + std::optional subdivisionFactor = std::nullopt; + if (is3D) { + subdivisionFactor = std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)); + } + tileMask->setPolygons(tileEntry.masks, origin, subdivisionFactor); + #else + std::optional maxSegmentLength = std::nullopt; + if (is3D) { + maxSegmentLength = std::min( + std::abs(convertedTileBounds.bottomRight.x - convertedTileBounds.topLeft.x) / std::pow(2, POLYGON_MASK_SUBDIVISION_FACTOR), + (M_PI * 2.0) / std::pow(2, POLYGON_MASK_SUBDIVISION_FACTOR)); + } tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); - + #endif + newTileMasks[tileEntry.tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); } } diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp index 89b892bc0..8f9e896d4 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -106,11 +106,7 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std coordinateConverterHelper, is3D); auto convertedTileBounds = coordinateConverterHelper->convertRectToRenderSystem(tileEntry->tileInfo.tileInfo.bounds); - std::optional maxSegmentLength = std::nullopt; - if (is3D) { - maxSegmentLength = std::min(std::abs(convertedTileBounds.bottomRight.x - convertedTileBounds.topLeft.x) / - POLYGON_MASK_SUBDIVISION_FACTOR, (M_PI * 2.0) / POLYGON_MASK_SUBDIVISION_FACTOR); - } + double cx = (convertedTileBounds.bottomRight.x + convertedTileBounds.topLeft.x) / 2.0; double cy = (convertedTileBounds.bottomRight.y + convertedTileBounds.topLeft.y) / 2.0; double rx = is3D ? 1.0 * sin(cy) * cos(cx) : cx; @@ -118,8 +114,23 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std double rz = is3D ? -1.0 * sin(cy) * sin(cx) : 0.0; Vec3D origin(rx, ry, rz); - + + #ifdef HARDWARE_TESSELLATION_SUPPORTED + std::optional subdivisionFactor = std::nullopt; + if (is3D) { + subdivisionFactor = std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)); + } + tileMask->setPolygons(tileEntry->masks, origin, subdivisionFactor); + #else + std::optional maxSegmentLength = std::nullopt; + if (is3D) { + maxSegmentLength = std::min( + std::abs(convertedTileBounds.bottomRight.x - convertedTileBounds.topLeft.x) / std::pow(2, POLYGON_MASK_SUBDIVISION_FACTOR), + (M_PI * 2.0) / std::pow(2, POLYGON_MASK_SUBDIVISION_FACTOR)); + } + tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); + #endif newTileMasks[tileEntry->tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); } diff --git a/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp b/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp index 954145a5a..9f9a6be23 100644 --- a/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/polygon/Tiled2dMapVectorPolygonTile.cpp @@ -284,7 +284,10 @@ void Tiled2dMapVectorPolygonTile::setVectorTileData(const Tiled2dMapVectorTileDa auto indices = polygon.indices; if (is3d) { - auto maxSegmentLength = std::min(std::abs(convertedTileBounds.bottomRight.x - convertedTileBounds.topLeft.x) / POLYGON_SUBDIVISION_FACTOR, (M_PI * 2.0) / POLYGON_SUBDIVISION_FACTOR); + auto maxSegmentLength = std::min( + std::abs(convertedTileBounds.bottomRight.x - convertedTileBounds.topLeft.x) / std::pow(2, POLYGON_SUBDIVISION_FACTOR), + (M_PI * 2.0) / std::pow(2, POLYGON_SUBDIVISION_FACTOR) + ); PolygonHelper::subdivision(coordinates, indices, maxSegmentLength); } diff --git a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp index 50162a238..923a6dbaf 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -14,6 +14,7 @@ #include "RasterShaderInterface.h" #include "RenderPass.h" #include "Tiled2dMapVectorStyleParser.h" +#include "Tiled2dMapVectorLayerConstants.h" Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptr &mapInterface, const std::weak_ptr &vectorLayer, @@ -28,12 +29,21 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptris3d() ? pMapInterface->getShaderFactory()->createQuadTessellatedShader() : pMapInterface->getShaderFactory()->createRasterShader(); + auto quad = pMapInterface->is3d() ? pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()) : + pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); + #else auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); - shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); -#if DEBUG + #endif + + shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); + + #if DEBUG quad->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); -#endif + #endif tileObject = std::make_shared(quad, shader, pMapInterface, pMapInterface->is3d()); tileObject->setRectCoord(tileInfo.tileInfo.bounds);