From 5f61af8a935e07d906bcfa190510a2ae7d2b801d Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 22 Oct 2025 08:48:31 +0200 Subject: [PATCH 01/24] Add unfinished quad gpu tesselation --- .../objects/GraphicsObjectFactoryInterface.kt | 8 + .../graphics/shader/ShaderFactoryInterface.kt | 8 + .../NativeGraphicsObjectFactoryInterface.cpp | 18 + .../NativeGraphicsObjectFactoryInterface.h | 2 + .../shader/NativeShaderFactoryInterface.cpp | 17 + .../shader/NativeShaderFactoryInterface.h | 2 + ...CGraphicsObjectFactoryInterface+Private.mm | 14 + .../ios/MCGraphicsObjectFactoryInterface.h | 2 + .../ios/MCShaderFactoryInterface+Private.mm | 14 + bridging/ios/MCShaderFactoryInterface.h | 2 + .../graphics/objects/graphicsobjects.djinni | 1 + djinni/graphics/shader/shader.djinni | 1 + ios/graphics/Model/GraphicsFactory.swift | 5 + .../Model/Quad/Quad2dTessellated.swift | 368 ++++++++++++++++++ ios/graphics/Pipelines/PipelineLibrary.swift | 22 +- .../Shader/Metal/TessellatedShader.metal | 59 +++ ios/graphics/Shader/ShaderFactory.swift | 4 + .../public/GraphicsObjectFactoryInterface.h | 2 + shared/public/ShaderFactoryInterface.h | 2 + .../raster/Tiled2dMapVectorRasterTile.cpp | 7 +- 20 files changed, 555 insertions(+), 3 deletions(-) create mode 100644 ios/graphics/Model/Quad/Quad2dTessellated.swift create mode 100644 ios/graphics/Shader/Metal/TessellatedShader.metal 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..56f9d2fdd 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,6 +10,8 @@ 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 createIcosahedronObject(shader: io.openmobilemaps.mapscore.shared.graphics.shader.ShaderProgramInterface): IcosahedronInterface @@ -53,6 +55,12 @@ 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) 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..a5e25962c 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 @@ -46,6 +46,8 @@ abstract class ShaderFactoryInterface { abstract fun createUnitSphereRasterShader(): RasterShaderInterface + abstract fun createUnitSphereTessellatedShader(): RasterShaderInterface + abstract fun createStretchShader(): StretchShaderInterface abstract fun createStretchInstancedShader(unitSphere: Boolean): StretchInstancedShaderInterface @@ -187,6 +189,12 @@ abstract class ShaderFactoryInterface { } private external fun native_createUnitSphereRasterShader(_nativeRef: Long): RasterShaderInterface + override fun createUnitSphereTessellatedShader(): RasterShaderInterface { + assert(!this.destroyed.get()) { error("trying to use a destroyed object") } + return native_createUnitSphereTessellatedShader(this.nativeRef) + } + private external fun native_createUnitSphereTessellatedShader(_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/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp index eff7e4fde..ca3dd4568 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); @@ -150,6 +159,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 { diff --git a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h index 241b82df6..974cdf0d2 100644 --- a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h +++ b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h @@ -34,6 +34,7 @@ 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<::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; @@ -52,6 +53,7 @@ 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_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;") }; diff --git a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp index 2aec30110..fbae3b2fe 100644 --- a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp +++ b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp @@ -185,6 +185,14 @@ NativeShaderFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativeRasterShaderInterface::toCpp(jniEnv, jret); } +/*not-null*/ std::shared_ptr<::RasterShaderInterface> NativeShaderFactoryInterface::JavaProxy::createUnitSphereTessellatedShader() { + 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_createUnitSphereTessellatedShader); + ::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); @@ -415,6 +423,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_1createUnitSphereTessellatedShader(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef) +{ + try { + const auto& ref = ::djinni::objectFromHandleAddress<::ShaderFactoryInterface>(nativeRef); + auto r = ref->createUnitSphereTessellatedShader(); + 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..11efd286c 100644 --- a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h +++ b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h @@ -52,6 +52,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> createUnitSphereTessellatedShader() 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; @@ -83,6 +84,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_createUnitSphereTessellatedShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereTessellatedShader", "()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/ios/MCGraphicsObjectFactoryInterface+Private.mm b/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm index 9d69b17a6..856358c82 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)); @@ -142,6 +149,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 { diff --git a/bridging/ios/MCGraphicsObjectFactoryInterface.h b/bridging/ios/MCGraphicsObjectFactoryInterface.h index 40541d6a6..a88aa316f 100644 --- a/bridging/ios/MCGraphicsObjectFactoryInterface.h +++ b/bridging/ios/MCGraphicsObjectFactoryInterface.h @@ -19,6 +19,8 @@ - (nullable id)createQuad:(nullable id)shader; +- (nullable id)createQuadTessellated:(nullable id)shader; + - (nullable id)createPolygon:(nullable id)shader; - (nullable id)createIcosahedronObject:(nullable id)shader; diff --git a/bridging/ios/MCShaderFactoryInterface+Private.mm b/bridging/ios/MCShaderFactoryInterface+Private.mm index adb2eb5e2..cb756f133 100644 --- a/bridging/ios/MCShaderFactoryInterface+Private.mm +++ b/bridging/ios/MCShaderFactoryInterface+Private.mm @@ -183,6 +183,13 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef } DJINNI_TRANSLATE_EXCEPTIONS() } +- (nullable id)createUnitSphereTessellatedShader { + try { + auto objcpp_result_ = _cppRefHandle.get()->createUnitSphereTessellatedShader(); + return ::djinni_generated::RasterShaderInterface::fromCpp(objcpp_result_); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + - (nullable id)createStretchShader { try { auto objcpp_result_ = _cppRefHandle.get()->createStretchShader(); @@ -369,6 +376,13 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef return ::djinni_generated::RasterShaderInterface::toCpp(objcpp_result_); } } + /*not-null*/ std::shared_ptr<::RasterShaderInterface> createUnitSphereTessellatedShader() override + { + @autoreleasepool { + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createUnitSphereTessellatedShader]; + 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..3a0c12838 100644 --- a/bridging/ios/MCShaderFactoryInterface.h +++ b/bridging/ios/MCShaderFactoryInterface.h @@ -61,6 +61,8 @@ - (nullable id)createUnitSphereRasterShader; +- (nullable id)createUnitSphereTessellatedShader; + - (nullable id)createStretchShader; - (nullable id)createStretchInstancedShader:(BOOL)unitSphere; diff --git a/djinni/graphics/objects/graphicsobjects.djinni b/djinni/graphics/objects/graphicsobjects.djinni index 615c4129a..2e4de37b9 100644 --- a/djinni/graphics/objects/graphicsobjects.djinni +++ b/djinni/graphics/objects/graphicsobjects.djinni @@ -36,6 +36,7 @@ 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_icosahedron_object(shader: shader_program_interface) : icosahedron_interface; diff --git a/djinni/graphics/shader/shader.djinni b/djinni/graphics/shader/shader.djinni index bfbc35023..60e2f63b7 100644 --- a/djinni/graphics/shader/shader.djinni +++ b/djinni/graphics/shader/shader.djinni @@ -48,6 +48,7 @@ shader_factory_interface = interface +c +j +o { create_raster_shader() : raster_shader_interface; create_unit_sphere_raster_shader() : raster_shader_interface; + create_unit_sphere_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/ios/graphics/Model/GraphicsFactory.swift b/ios/graphics/Model/GraphicsFactory.swift index d438c1223..dd333cdc5 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -36,6 +36,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") } diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift new file mode 100644 index 000000000..a457d1c4c --- /dev/null +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -0,0 +1,368 @@ +/* + * 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 originBuffers: MultiBuffer + + private var tessellationFactorsBuffer: MTLBuffer? + + private var is3d = false + + private var texture: MTLTexture? + + private var shader: MCShaderProgramInterface + + private var stencilState: MTLDepthStencilState? + private var renderPassStencilState: MTLDepthStencilState? + + private var renderAsMask = false + + private var subdivisionFactor: Int32 = 0 + + 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 + nearestSampler = metalContext.samplerLibrary.value( + Sampler.magNearest.rawValue)! + originBuffers = .init(device: metalContext.device) + super + .init( + device: metalContext.device, + sampler: metalContext.samplerLibrary.value( + Sampler.magLinear.rawValue)!, + label: label) + } + + 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.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) + + //encoder.setTriangleFillMode(.lines) + + encoder.drawPatches( + numberOfPatchControlPoints: 4, + patchStart: 0, + patchCount: 1, + patchIndexBuffer: nil, + patchIndexBufferOffset: 0, + instanceCount: 1, + baseInstance: 0) + + //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) { + let (optFrame, optTextureCoordinates) = lock.withCritical { + () -> (MCQuad3dD?, MCRectD?) in + if self.subdivisionFactor != factor { + self.subdivisionFactor = factor + return (frame, textureCoordinates) + } else { + return (nil, nil) + } + } + if let frame = optFrame, + let textureCoordinates = optTextureCoordinates + { + setFrame( + frame, textureCoordinates: textureCoordinates, + origin: self.originOffset, is3d: is3d) + } + } + + func setFrame( + _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, + is3d: Bool + ) { + //let sFactor = lock.withCritical { subdivisionFactor } + let tFactor: Float16 = 10; //Float16(sFactor) + + let tessellationFactors: [Float16] = [ + tFactor, // edge 0 + tFactor, // edge 1 + tFactor, // edge 2 + tFactor, // edge 3 + tFactor, // inside 0 + tFactor // inside 1 + ] + + var vertices: [Vertex3DTexture] = [] + + 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 = [ + Vertex3DTexture( + position: transform(frame.topLeft), + textureU: textureCoordinates.xF, + textureV: textureCoordinates.yF), // B + Vertex3DTexture( + position: transform(frame.topRight), + textureU: textureCoordinates.xF + textureCoordinates.widthF, + textureV: textureCoordinates.yF), // C + Vertex3DTexture( + position: transform(frame.bottomLeft), + textureU: textureCoordinates.xF, + textureV: textureCoordinates.yF + textureCoordinates.heightF), // A + Vertex3DTexture( + position: transform(frame.bottomRight), + 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) + self.tessellationFactorsBuffer.copyOrCreate( + bytes: tessellationFactors, + length: MemoryLayout.stride * tessellationFactors.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/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index cb4ead977..14b8840fd 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -74,6 +74,21 @@ public enum PipelineDescriptorFactory { pipelineDescriptor.vertexFunction = vertexFunction pipelineDescriptor.fragmentFunction = fragmentFunction } + + // TODO: Change to clean input of a optional config + if label == PipelineType.tessellatedShader.label { + + //pipelineDescriptor.vertexDescriptor!.layouts[0].stepRate = 1 + pipelineDescriptor.vertexDescriptor!.layouts[0].stepFunction = .perPatchControlPoint + + //pipelineDescriptor.maxTessellationFactor = 64 + pipelineDescriptor.tessellationPartitionMode = .pow2 + //pipelineDescriptor.tessellationFactorFormat = .half + pipelineDescriptor.tessellationFactorStepFunction = .constant + //pipelineDescriptor.tessellationOutputWindingOrder = .clockwise + pipelineDescriptor.tessellationControlPointIndexType = .none + //pipelineDescriptor.isTessellationFactorScaleEnabled = false + } return pipelineDescriptor } @@ -141,6 +156,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case textShader case textInstancedShader case rasterShader + case tessellatedShader case stretchShader case stretchInstancedShader case unitSphereAlphaShader @@ -170,6 +186,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .textShader: return "Text shader" case .textInstancedShader: return "Text Instanced shader" case .rasterShader: return "Raster shader" + case .tessellatedShader: return "Tessellated shader" case .stretchShader: return "Stretch shader" case .stretchInstancedShader: return "Stretch Instanced shader" case .unitSphereAlphaShader: return "Unit Sphere Alpha shader with texture" @@ -184,7 +201,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { var vertexShaderUsesModelMatrix: Bool { switch self { - case .rasterShader, .roundColorShader, .unitSphereRoundColorShader, .alphaShader, .unitSphereAlphaShader, .sphereEffectShader, .skySphereShader, .elevationInterpolation: + case .rasterShader, .tessellatedShader, .roundColorShader, .unitSphereRoundColorShader, .alphaShader, .unitSphereAlphaShader, .sphereEffectShader, .skySphereShader, .elevationInterpolation: return true default: return false @@ -210,6 +227,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .textShader: return "textVertexShader" case .textInstancedShader: return "textInstancedVertexShader" case .rasterShader: return "baseVertexShaderModel" + case .tessellatedShader: return "tessellationVertexShader" case .stretchShader: return "stretchVertexShader" case .stretchInstancedShader: return "stretchInstancedVertexShader" case .unitSphereAlphaShader: return "baseVertexShader" @@ -241,6 +259,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .textShader: return "textFragmentShader" case .textInstancedShader: return "textInstancedFragmentShader" case .rasterShader: return "rasterFragmentShader" + case .tessellatedShader: return "rasterFragmentShader" case .stretchShader: return "stretchFragmentShader" case .stretchInstancedShader: return "stretchInstancedFragmentShader" case .unitSphereAlphaShader: return "baseFragmentShader" @@ -266,6 +285,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .colorShader, .maskShader: return Vertex4F.descriptor case .rasterShader, + .tessellatedShader, .clearStencilShader, .alphaShader, .unitSphereAlphaShader, diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal new file mode 100644 index 000000000..19887b6f6 --- /dev/null +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -0,0 +1,59 @@ +/* + * 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; + + +/// Calculate a value by bilinearly interpolating among four control points. +/// The four values c00, c01, c10, and c11 represent, respectively, the +/// upper-left, upper-right, lower-left, and lower-right points of a quad +/// that is parameterized by a normalized space that runs from (0, 0) +/// in the upper left to (1, 1) in the lower right (similar to Metal's texture +/// space). The vector `uv` contains the influence of the points along the +/// x and y axes. +template +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])); +} + + +[[patch(quad, 4)]] vertex VertexOut +tessellationVertexShader(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)]], /* from focus point on surface to quad center */ + constant float4 &cameraOrigin [[buffer(4)]]) /* from earth center to focus point on surface */ +{ + /* from quad center to quad corners */ + Vertex3DTextureIn vA = controlPoints[0]; + Vertex3DTextureIn vB = controlPoints[1]; + Vertex3DTextureIn vC = controlPoints[2]; + Vertex3DTextureIn vD = controlPoints[3]; + + float4 vertexPosition = bilerp(vA.position, vB.position, vC.position, vD.position, positionInPatch); + float2 vertexUV = bilerp(vA.uv, vB.uv, vC.uv, vD.uv, positionInPatch); + + float3 focusToVertex = ((mMatrix * vertexPosition) + originOffset).xyz; /* from focus point on surface to vertex */ + float3 earthToVertex = focusToVertex + cameraOrigin.xyz; /* from earth center to vertex */ + float3 curvedVertex = normalize(earthToVertex); + float3 backToFocus = curvedVertex - cameraOrigin.xyz; /* from focus point on surface to vertex */ + + VertexOut out { + .position = vpMatrix * float4(backToFocus, 1), + .uv = vertexUV + }; + + return out; +} diff --git a/ios/graphics/Shader/ShaderFactory.swift b/ios/graphics/Shader/ShaderFactory.swift index 58edb1aa2..8eba1883b 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 createUnitSphereTessellatedShader() -> MCRasterShaderInterface? { + RasterShader(shader: .tessellatedShader) + } func createTextShader() -> MCTextShaderInterface? { TextShader() diff --git a/shared/public/GraphicsObjectFactoryInterface.h b/shared/public/GraphicsObjectFactoryInterface.h index 4baf1e9c4..2651d69d7 100644 --- a/shared/public/GraphicsObjectFactoryInterface.h +++ b/shared/public/GraphicsObjectFactoryInterface.h @@ -23,6 +23,8 @@ 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 createIcosahedronObject(const /*not-null*/ std::shared_ptr<::ShaderProgramInterface> & shader) = 0; diff --git a/shared/public/ShaderFactoryInterface.h b/shared/public/ShaderFactoryInterface.h index a7ad2c87b..412b37ff0 100644 --- a/shared/public/ShaderFactoryInterface.h +++ b/shared/public/ShaderFactoryInterface.h @@ -63,6 +63,8 @@ class ShaderFactoryInterface { virtual /*not-null*/ std::shared_ptr createUnitSphereRasterShader() = 0; + virtual /*not-null*/ std::shared_ptr createUnitSphereTessellatedShader() = 0; + virtual /*not-null*/ std::shared_ptr createStretchShader() = 0; virtual /*not-null*/ std::shared_ptr createStretchInstancedShader(bool unitSphere) = 0; 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..50ef0f80b 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -28,9 +28,12 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptris3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); + //auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); + // + auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereTessellatedShader() : pMapInterface->getShaderFactory()->createRasterShader(); shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); - auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); + auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); + //auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); #if DEBUG quad->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif From d465d5b34b4c0b38d01615a3e125c257b43d2846 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 22 Oct 2025 14:09:33 +0200 Subject: [PATCH 02/24] Change curving from normalize to sin cos --- .../Model/Quad/Quad2dTessellated.swift | 34 +++++-------------- .../Shader/Metal/TessellatedShader.metal | 25 +++++++++----- .../raster/Tiled2dMapVectorRasterTile.cpp | 11 +++--- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index a457d1c4c..09ca10abb 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -17,9 +17,8 @@ import simd final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var verticesBuffer: MTLBuffer? - private var originBuffers: MultiBuffer - private var tessellationFactorsBuffer: MTLBuffer? + private var originBuffers: MultiBuffer private var is3d = false @@ -46,9 +45,9 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { label: String = "Quad2dTessellated" ) { self.shader = shader + originBuffers = .init(device: metalContext.device) nearestSampler = metalContext.samplerLibrary.value( Sampler.magNearest.rawValue)! - originBuffers = .init(device: metalContext.device) super .init( device: metalContext.device, @@ -192,8 +191,6 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) - //encoder.setTriangleFillMode(.lines) - encoder.drawPatches( numberOfPatchControlPoints: 4, patchStart: 0, @@ -202,8 +199,6 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { patchIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0) - - //encoder.setTriangleFillMode(.fill) } } @@ -272,8 +267,8 @@ extension Quad2dTessellated: MCQuad2dInterface { _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, is3d: Bool ) { - //let sFactor = lock.withCritical { subdivisionFactor } - let tFactor: Float16 = 10; //Float16(sFactor) + let sFactor = lock.withCritical { subdivisionFactor } + let tFactor = Float16(pow(2.0, Double(sFactor))) let tessellationFactors: [Float16] = [ tFactor, // edge 0 @@ -286,19 +281,6 @@ extension Quad2dTessellated: MCQuad2dInterface { var vertices: [Vertex3DTexture] = [] - 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 @@ -309,19 +291,19 @@ extension Quad2dTessellated: MCQuad2dInterface { */ vertices = [ Vertex3DTexture( - position: transform(frame.topLeft), + position: frame.topLeft, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF), // B Vertex3DTexture( - position: transform(frame.topRight), + position: frame.topRight, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF), // C Vertex3DTexture( - position: transform(frame.bottomLeft), + position: frame.bottomLeft, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // A Vertex3DTexture( - position: transform(frame.bottomRight), + position: frame.bottomRight, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // D ] diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 19887b6f6..2a7e6b045 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -27,16 +27,28 @@ T bilerp(T c00, T c01, T c10, T c11, float2 uv) { return mix(c0, c1, T(uv[1])); } +// TODO: is3d +float4 transform(float4 coordinate, float4 origin) { + if (true/*is3d*/) { + 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, 1); + } else { + float x = coordinate.x - origin.x; + float y = coordinate.y - origin.y; + return float4(x, y, 0, 1); + } +} [[patch(quad, 4)]] vertex VertexOut tessellationVertexShader(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)]], /* from focus point on surface to quad center */ - constant float4 &cameraOrigin [[buffer(4)]]) /* from earth center to focus point on surface */ + constant float4 &originOffset [[buffer(3)]], + constant float4 &origin [[buffer(4)]]) { - /* from quad center to quad corners */ Vertex3DTextureIn vA = controlPoints[0]; Vertex3DTextureIn vB = controlPoints[1]; Vertex3DTextureIn vC = controlPoints[2]; @@ -45,13 +57,10 @@ tessellationVertexShader(const patch_control_point controlPoi float4 vertexPosition = bilerp(vA.position, vB.position, vC.position, vD.position, positionInPatch); float2 vertexUV = bilerp(vA.uv, vB.uv, vC.uv, vD.uv, positionInPatch); - float3 focusToVertex = ((mMatrix * vertexPosition) + originOffset).xyz; /* from focus point on surface to vertex */ - float3 earthToVertex = focusToVertex + cameraOrigin.xyz; /* from earth center to vertex */ - float3 curvedVertex = normalize(earthToVertex); - float3 backToFocus = curvedVertex - cameraOrigin.xyz; /* from focus point on surface to vertex */ + float4 position = transform(vertexPosition, origin) - originOffset; VertexOut out { - .position = vpMatrix * float4(backToFocus, 1), + .position = vpMatrix * ((mMatrix * position) + originOffset), .uv = vertexUV }; 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 50ef0f80b..203a9213b 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -28,18 +28,21 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptris3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); - // + /* + auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); + auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); + */ + // /* auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereTessellatedShader() : pMapInterface->getShaderFactory()->createRasterShader(); shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); - //auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); + // */ #if DEBUG quad->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif tileObject = std::make_shared(quad, shader, pMapInterface, pMapInterface->is3d()); tileObject->setRectCoord(tileInfo.tileInfo.bounds); - + if (pMapInterface->is3d()) { quad->setSubdivisionFactor(std::clamp(subdivisionFactor + tileInfo.tileInfo.tessellationFactor, 0, 5)); } From 34aed00b6a36330a259b2ca832bba84230fa2072 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 5 Nov 2025 14:34:15 +0100 Subject: [PATCH 03/24] Quad tessellation blend and non 3d --- .../Model/Quad/Quad2dTessellated.swift | 53 ++++++-- .../Model/TessellatedVertex3DTexture.swift | 104 +++++++++++++++ ios/graphics/Pipelines/PipelineLibrary.swift | 34 ++--- .../Shader/Metal/DataStructures.metal | 6 + .../Shader/Metal/TessellatedShader.metal | 118 +++++++++++++----- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 16 ++- .../raster/Tiled2dMapVectorRasterTile.cpp | 6 +- 7 files changed, 275 insertions(+), 62 deletions(-) create mode 100644 ios/graphics/Model/TessellatedVertex3DTexture.swift diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index 09ca10abb..078aadcab 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -189,6 +189,20 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { } encoder.setVertexBuffer(originBuffer, offset: 0, index: 4) + /* ELEVATION PROTOTYPE TEST + if samplerToUse == .magNearest { + encoder.setVertexSamplerState(nearestSampler, index: 0) + } else { + encoder.setVertexSamplerState(sampler, index: 0) + } + + if let texture { + encoder.setVertexTexture(texture, index: 0) + } + */ + + encoder.setVertexBytes(&self.is3d, length: MemoryLayout.stride, index: 5) + encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) encoder.drawPatches( @@ -279,8 +293,21 @@ extension Quad2dTessellated: MCQuad2dInterface { tFactor // inside 1 ] - var vertices: [Vertex3DTexture] = [] - + var vertices: [TessellatedVertex3DTexture] = [] + + 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 @@ -290,20 +317,24 @@ extension Quad2dTessellated: MCQuad2dInterface { Where A-C are joined to form two triangles */ vertices = [ - Vertex3DTexture( - position: frame.topLeft, + TessellatedVertex3DTexture( + relativePosition: transform(frame.topLeft), + absolutePosition: frame.topLeft, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF), // B - Vertex3DTexture( - position: frame.topRight, + TessellatedVertex3DTexture( + relativePosition: transform(frame.topRight), + absolutePosition: frame.topRight, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF), // C - Vertex3DTexture( - position: frame.bottomLeft, + TessellatedVertex3DTexture( + relativePosition: transform(frame.bottomLeft), + absolutePosition: frame.bottomLeft, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // A - Vertex3DTexture( - position: frame.bottomRight, + TessellatedVertex3DTexture( + relativePosition: transform(frame.bottomRight), + absolutePosition: frame.bottomRight, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // D ] @@ -315,7 +346,7 @@ extension Quad2dTessellated: MCQuad2dInterface { self.textureCoordinates = textureCoordinates self.verticesBuffer.copyOrCreate( bytes: vertices, - length: MemoryLayout.stride * vertices.count, + length: MemoryLayout.stride * vertices.count, device: device) self.tessellationFactorsBuffer.copyOrCreate( bytes: tessellationFactors, diff --git a/ios/graphics/Model/TessellatedVertex3DTexture.swift b/ios/graphics/Model/TessellatedVertex3DTexture.swift new file mode 100644 index 000000000..d584e081c --- /dev/null +++ b/ios/graphics/Model/TessellatedVertex3DTexture.swift @@ -0,0 +1,104 @@ +/* + * 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 TessellatedVertex3DTexture: Equatable { + /// The 3D position of the vertex in the plane + var relativePosition: SIMD3 + /// The 3D position of the vertex in the world + var absolutePosition: SIMD3 + /// 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 + + // Relative Position + vertexDescriptor.attributes[0].bufferIndex = bufferIndex + vertexDescriptor.attributes[0].format = .float3 + vertexDescriptor.attributes[0].offset = offset + offset += MemoryLayout>.stride + + // Absolute Position + vertexDescriptor.attributes[1].bufferIndex = bufferIndex + vertexDescriptor.attributes[1].format = .float3 + 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( + relativeX: Float, relativeY: Float, relativeZ: Float, + absoluteX: Float, absoluteY: Float, absoluteZ: Float, + textureU: Float, textureV: Float, + ) { + relativePosition = SIMD3([relativeX, relativeY, relativeZ]) + absolutePosition = SIMD3([absoluteX, absoluteY, absoluteZ]) + textureCoordinate = SIMD2([textureU, textureV]) + } + + public init(relativePosition: MCVec3D, absolutePosition: MCVec3D, textureU: Float, textureV: Float) { + self.init( + relativeX: relativePosition.xF, relativeY: relativePosition.yF, relativeZ: relativePosition.zF, + absoluteX: absolutePosition.xF, absoluteY: absolutePosition.yF, absoluteZ: absolutePosition.zF, + textureU: textureU, textureV: textureV, + ) + } +} + +extension TessellatedVertex3DTexture { + /// The X-coordinate position in the plane + var relativeX: Float { relativePosition.x } + /// The Y-coordinate position in the plane + var relativeY: Float { relativePosition.y } + /// The Z-coordinate position in the plane + var relativeZ: Float { relativePosition.z } + /// The X-coordinate position in the world + var absoluteX: Float { absolutePosition.x } + /// The Y-coordinate position in the world + var absoluteY: Float { absolutePosition.y } + /// The Z-coordinate position in the world + var absoluteZ: Float { absolutePosition.z } + /// The texture U-coordinate mapping + var textureU: Float { textureCoordinate.x } + /// The texture V-coordinate mapping + var textureV: Float { textureCoordinate.x } +} + +extension TessellatedVertex3DTexture: CustomDebugStringConvertible { + public var debugDescription: String { + "" + } +} diff --git a/ios/graphics/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index 14b8840fd..775f50509 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -19,7 +19,8 @@ public enum PipelineDescriptorFactory { fragmentShader: String, blendMode: MCBlendMode, library: MTLLibrary, - constants: MTLFunctionConstantValues? = nil + constants: MTLFunctionConstantValues? = nil, + tessellated: Bool = false ) -> MTLRenderPipelineDescriptor { let pipelineDescriptor = MTLRenderPipelineDescriptor() pipelineDescriptor.colorAttachments[0].pixelFormat = MetalContext.colorPixelFormat @@ -75,21 +76,14 @@ public enum PipelineDescriptorFactory { pipelineDescriptor.fragmentFunction = fragmentFunction } - // TODO: Change to clean input of a optional config - if label == PipelineType.tessellatedShader.label { - - //pipelineDescriptor.vertexDescriptor!.layouts[0].stepRate = 1 - pipelineDescriptor.vertexDescriptor!.layouts[0].stepFunction = .perPatchControlPoint - - //pipelineDescriptor.maxTessellationFactor = 64 + if tessellated { + pipelineDescriptor.maxTessellationFactor = 64 pipelineDescriptor.tessellationPartitionMode = .pow2 - //pipelineDescriptor.tessellationFactorFormat = .half + pipelineDescriptor.tessellationFactorFormat = .half pipelineDescriptor.tessellationFactorStepFunction = .constant - //pipelineDescriptor.tessellationOutputWindingOrder = .clockwise + pipelineDescriptor.tessellationOutputWindingOrder = .clockwise pipelineDescriptor.tessellationControlPointIndexType = .none - //pipelineDescriptor.isTessellationFactorScaleEnabled = false } - return pipelineDescriptor } } @@ -102,7 +96,9 @@ extension PipelineDescriptorFactory { vertexShader: pipeline.type.vertexShader, fragmentShader: pipeline.type.fragmentShader, blendMode: pipeline.blendMode, - library: library) + library: library, + tessellated: pipeline.type.tessellated + ) } } @@ -285,7 +281,6 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .colorShader, .maskShader: return Vertex4F.descriptor case .rasterShader, - .tessellatedShader, .clearStencilShader, .alphaShader, .unitSphereAlphaShader, @@ -295,10 +290,21 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .roundColorShader, .elevationInterpolation: return Vertex3DTexture.descriptor + case .tessellatedShader: + return TessellatedVertex3DTexture.descriptor default: return Vertex.descriptor } } + + var tessellated: Bool { + switch self { + case .tessellatedShader: + return true + default: + return false + } + } } public typealias PipelineLibrary = StaticMetalLibrary diff --git a/ios/graphics/Shader/Metal/DataStructures.metal b/ios/graphics/Shader/Metal/DataStructures.metal index 95eb289ba..67de7eb7e 100644 --- a/ios/graphics/Shader/Metal/DataStructures.metal +++ b/ios/graphics/Shader/Metal/DataStructures.metal @@ -21,6 +21,12 @@ struct Vertex3DTextureIn { float2 uv [[attribute(1)]]; }; +struct TessellatedVertex3DTextureIn { + float3 relativePosition [[attribute(0)]]; + float3 absolutePosition [[attribute(1)]]; + float2 uv [[attribute(2)]]; +}; + // 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 { diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 2a7e6b045..22ba897ae 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -12,14 +12,65 @@ #include "DataStructures.metal" using namespace metal; +// ELEVATION PROTOTYPE TEST +/** + * Decode elevation from RGBA encoded altitude texture + * This matches the existing decoding logic in the shader + */ +/* +float decodeElevation(float4 rgbaAltitude) { + return (rgbaAltitude.r * 256.0 * 256.0 * 255.0 + + rgbaAltitude.g * 256.0 * 255.0 + + rgbaAltitude.b * 255.0) / 10.0 - 10000.0; +} +*/ + +/** + * Sample elevation with linear interpolation + * This function first samples the four neighboring pixels, decodes each elevation value, + * then performs bilinear interpolation on the decoded values + * + * @param altitudeTexture The altitude texture with encoded elevation data + * @param uv The texture coordinates to sample + * @param linearSampler A linear sampler for texture sampling + * @return The linearly interpolated elevation value + */ +/* +float sampleLinearElevation(texture2d altitudeTexture, float2 uv, sampler linearSampler) { + // Get texture dimensions + float2 texSize = float2(altitudeTexture.get_width(), altitudeTexture.get_height()); + + // Convert UV to pixel coordinates + float2 pixelCoord = uv * texSize - 0.5; + float2 pixelFloor = floor(pixelCoord); + float2 pixelFrac = pixelCoord - pixelFloor; + + // Calculate UV coordinates for the four neighboring pixels + float2 texelSize = 1.0 / texSize; + float2 uv00 = (pixelFloor + float2(0.0, 0.0)) * texelSize; + float2 uv10 = (pixelFloor + float2(1.0, 0.0)) * texelSize; + float2 uv01 = (pixelFloor + float2(0.0, 1.0)) * texelSize; + float2 uv11 = (pixelFloor + float2(1.0, 1.0)) * texelSize; + + // Sample and decode the four neighboring pixels + // Use nearest sampling to get the raw encoded values + constexpr sampler nearestSampler(coord::normalized, address::clamp_to_edge, filter::nearest); + + float elevation00 = decodeElevation(altitudeTexture.sample(nearestSampler, uv00)); + float elevation10 = decodeElevation(altitudeTexture.sample(nearestSampler, uv10)); + float elevation01 = decodeElevation(altitudeTexture.sample(nearestSampler, uv01)); + float elevation11 = decodeElevation(altitudeTexture.sample(nearestSampler, uv11)); + + // Perform bilinear interpolation on the decoded elevation values + float elevation0 = mix(elevation00, elevation10, pixelFrac.x); + float elevation1 = mix(elevation01, elevation11, pixelFrac.x); + return mix(elevation0, elevation1, pixelFrac.y); +} +*/ + +const constant float BlendScale = 1000; +const constant float BlendOffset = 0.01; -/// Calculate a value by bilinearly interpolating among four control points. -/// The four values c00, c01, c10, and c11 represent, respectively, the -/// upper-left, upper-right, lower-left, and lower-right points of a quad -/// that is parameterized by a normalized space that runs from (0, 0) -/// in the upper left to (1, 1) in the lower right (similar to Metal's texture -/// space). The vector `uv` contains the influence of the points along the -/// x and y axes. template T bilerp(T c00, T c01, T c10, T c11, float2 uv) { T c0 = mix(c00, c01, T(uv[0])); @@ -27,40 +78,47 @@ T bilerp(T c00, T c01, T c10, T c11, float2 uv) { return mix(c0, c1, T(uv[1])); } -// TODO: is3d -float4 transform(float4 coordinate, float4 origin) { - if (true/*is3d*/) { - 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, 1); - } else { - float x = coordinate.x - origin.x; - float y = coordinate.y - origin.y; - return float4(x, y, 0, 1); - } +float3 transform(float3 coordinate, float3 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 float3(x, y, z); } [[patch(quad, 4)]] vertex VertexOut -tessellationVertexShader(const patch_control_point controlPoints [[stage_in]], +tessellationVertexShader(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 float3 &originOffset [[buffer(3)]], + constant float3 &origin [[buffer(4)]], + /* ELEVATION PROTOTYPE TEST + texture2d texture0 [[ texture(0)]], + sampler sampler0 [[sampler(0)]], */ + constant bool &is3d [[buffer(5)]]) { - Vertex3DTextureIn vA = controlPoints[0]; - Vertex3DTextureIn vB = controlPoints[1]; - Vertex3DTextureIn vC = controlPoints[2]; - Vertex3DTextureIn vD = controlPoints[3]; + TessellatedVertex3DTextureIn vA = controlPoints[0]; + TessellatedVertex3DTextureIn vB = controlPoints[1]; + TessellatedVertex3DTextureIn vC = controlPoints[2]; + TessellatedVertex3DTextureIn vD = controlPoints[3]; - float4 vertexPosition = bilerp(vA.position, vB.position, vC.position, vD.position, positionInPatch); + float3 vertexRelativePosition = bilerp(vA.relativePosition, vB.relativePosition, vC.relativePosition, vD.relativePosition, positionInPatch); + float3 vertexAbsolutePosition = bilerp(vA.absolutePosition, vB.absolutePosition, vC.absolutePosition, vD.absolutePosition, positionInPatch); float2 vertexUV = bilerp(vA.uv, vB.uv, vC.uv, vD.uv, positionInPatch); - float4 position = transform(vertexPosition, origin) - originOffset; - + float3 position = vertexRelativePosition; + if (is3d) { + float3 bent = transform(vertexAbsolutePosition.xyz, origin) - originOffset; + float blend = saturate(length(originOffset) * BlendScale - BlendOffset); + position = mix(position, bent, blend); + + /* ELEVATION PROTOTYPE TEST + float3 normal = normalize(transform(vertexAbsolutePosition, float3(0, 0, 0))); + position += normal * sampleLinearElevation(texture0, vertexUV, sampler0) * 0.00001 * 1.0; */ + } + VertexOut out { - .position = vpMatrix * ((mMatrix * position) + originOffset), + .position = vpMatrix * ((mMatrix * float4(position, 1)) + float4(originOffset, 0)), .uv = vertexUV }; diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index 0cc2b4857..605accd1d 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -302,12 +302,20 @@ std::vector sortedTileInfos(currentTileInfos.begin(), quad->setMinMagFilter(textureFilterType); tileObject = std::make_shared(quad, mapInterface, is3D); } else { - auto rasterShader = is3D ? shaderFactory->createUnitSphereRasterShader() : shaderFactory->createRasterShader(); - rasterShader->asShaderProgramInterface()->setBlendMode(blendMode); - auto quad = graphicsFactory->createQuad(rasterShader->asShaderProgramInterface()); + /* + auto rShader = is3D ? shaderFactory->createUnitSphereRasterShader() : shaderFactory->createRasterShader(); + rShader->asShaderProgramInterface()->setBlendMode(blendMode); + auto quad = graphicsFactory->createQuad(rShader->asShaderProgramInterface()); + */ + // /* + auto rShader = shaderFactory->createUnitSphereTessellatedShader(); + rShader->asShaderProgramInterface()->setBlendMode(blendMode); + auto quad = graphicsFactory->createQuadTessellated(rShader->asShaderProgramInterface()); + // */ + quad->setMinMagFilter(textureFilterType); tileObject = std::make_shared( - quad, rasterShader, mapInterface, is3D); + quad, rShader, mapInterface, is3D); if (zoomInfo.numDrawPreviousLayers == 0 || !animationsEnabled || zoomInfo.maskTile || is3D) { tileObject->setStyle(style); } else { 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 203a9213b..ff12d3564 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -29,11 +29,11 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptris3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); - auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); + auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); + auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); */ // /* - auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereTessellatedShader() : pMapInterface->getShaderFactory()->createRasterShader(); + auto shader = pMapInterface->getShaderFactory()->createUnitSphereTessellatedShader(); shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); // */ From 8c79523f2c707766edd5a04d556cbef4ead86e1c Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 6 Nov 2025 14:39:25 +0100 Subject: [PATCH 04/24] Refactor and unfinished polygon mask tessellation --- .../objects/GraphicsObjectFactoryInterface.kt | 16 + .../graphics/shader/ShaderFactoryInterface.kt | 8 +- .../NativeGraphicsObjectFactoryInterface.cpp | 36 +++ .../NativeGraphicsObjectFactoryInterface.h | 4 + .../shader/NativeShaderFactoryInterface.cpp | 8 +- .../shader/NativeShaderFactoryInterface.h | 4 +- ...CGraphicsObjectFactoryInterface+Private.mm | 28 ++ .../ios/MCGraphicsObjectFactoryInterface.h | 4 + .../ios/MCShaderFactoryInterface+Private.mm | 8 +- bridging/ios/MCShaderFactoryInterface.h | 2 +- .../graphics/objects/graphicsobjects.djinni | 2 + djinni/graphics/shader/shader.djinni | 2 +- ios/graphics/Model/GraphicsFactory.swift | 14 + .../Model/Polygon/Polygon2dTessellated.swift | 285 ++++++++++++++++++ ios/graphics/Pipelines/PipelineLibrary.swift | 24 +- ios/graphics/Shader/Metal/BaseShader.metal | 2 + .../Shader/Metal/TessellatedShader.metal | 47 ++- ios/graphics/Shader/ShaderFactory.swift | 4 +- .../public/GraphicsObjectFactoryInterface.h | 4 + shared/public/ShaderFactoryInterface.h | 2 +- .../layers/objects/Polygon2dLayerObject.cpp | 2 +- .../map/layers/objects/PolygonMaskObject.cpp | 2 +- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 3 +- .../raster/Tiled2dMapVectorRasterTile.cpp | 4 +- 24 files changed, 474 insertions(+), 41 deletions(-) create mode 100644 ios/graphics/Model/Polygon/Polygon2dTessellated.swift 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 56f9d2fdd..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 @@ -14,6 +14,8 @@ abstract class GraphicsObjectFactoryInterface { 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 @@ -30,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 @@ -67,6 +71,12 @@ abstract class GraphicsObjectFactoryInterface { } 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) @@ -115,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/shader/ShaderFactoryInterface.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/ShaderFactoryInterface.kt index a5e25962c..34f6b4754 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 @@ -46,7 +46,7 @@ abstract class ShaderFactoryInterface { abstract fun createUnitSphereRasterShader(): RasterShaderInterface - abstract fun createUnitSphereTessellatedShader(): RasterShaderInterface + abstract fun createQuadTessellatedShader(): RasterShaderInterface abstract fun createStretchShader(): StretchShaderInterface @@ -189,11 +189,11 @@ abstract class ShaderFactoryInterface { } private external fun native_createUnitSphereRasterShader(_nativeRef: Long): RasterShaderInterface - override fun createUnitSphereTessellatedShader(): RasterShaderInterface { + override fun createQuadTessellatedShader(): RasterShaderInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } - return native_createUnitSphereTessellatedShader(this.nativeRef) + return native_createQuadTessellatedShader(this.nativeRef) } - private external fun native_createUnitSphereTessellatedShader(_nativeRef: Long): RasterShaderInterface + private external fun native_createQuadTessellatedShader(_nativeRef: Long): RasterShaderInterface override fun createStretchShader(): StretchShaderInterface { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } diff --git a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp index ca3dd4568..02f15faee 100644 --- a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp +++ b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.cpp @@ -52,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); @@ -124,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); @@ -177,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 { @@ -249,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 974cdf0d2..220d565f4 100644 --- a/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h +++ b/bridging/android/jni/graphics/objects/NativeGraphicsObjectFactoryInterface.h @@ -36,6 +36,7 @@ class NativeGraphicsObjectFactoryInterface final : ::djinni::JniInterface<::Grap /*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; @@ -44,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; @@ -55,6 +57,7 @@ class NativeGraphicsObjectFactoryInterface final : ::djinni::JniInterface<::Grap 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;") }; @@ -63,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/shader/NativeShaderFactoryInterface.cpp b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp index fbae3b2fe..285423237 100644 --- a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp +++ b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp @@ -185,11 +185,11 @@ NativeShaderFactoryInterface::JavaProxy::~JavaProxy() = default; ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativeRasterShaderInterface::toCpp(jniEnv, jret); } -/*not-null*/ std::shared_ptr<::RasterShaderInterface> NativeShaderFactoryInterface::JavaProxy::createUnitSphereTessellatedShader() { +/*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_createUnitSphereTessellatedShader); + auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_createQuadTessellatedShader); ::djinni::jniExceptionCheck(jniEnv); return ::djinni_generated::NativeRasterShaderInterface::toCpp(jniEnv, jret); } @@ -423,11 +423,11 @@ 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_1createUnitSphereTessellatedShader(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef) +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->createUnitSphereTessellatedShader(); + auto r = ref->createQuadTessellatedShader(); return ::djinni::release(::djinni_generated::NativeRasterShaderInterface::fromCpp(jniEnv, r)); } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } diff --git a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h index 11efd286c..2e74b59f4 100644 --- a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h +++ b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h @@ -52,7 +52,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> createUnitSphereTessellatedShader() 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; @@ -84,7 +84,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_createUnitSphereTessellatedShader { ::djinni::jniGetMethodID(clazz.get(), "createUnitSphereTessellatedShader", "()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/ios/MCGraphicsObjectFactoryInterface+Private.mm b/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm index 856358c82..d96695b5a 100644 --- a/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm +++ b/bridging/ios/MCGraphicsObjectFactoryInterface+Private.mm @@ -63,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)); @@ -119,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)); @@ -163,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 { @@ -219,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 a88aa316f..6d8567607 100644 --- a/bridging/ios/MCGraphicsObjectFactoryInterface.h +++ b/bridging/ios/MCGraphicsObjectFactoryInterface.h @@ -23,6 +23,8 @@ - (nullable id)createPolygon:(nullable id)shader; +- (nullable id)createPolygonTessellated:(nullable id)shader; + - (nullable id)createIcosahedronObject:(nullable id)shader; - (nullable id)createQuadInstanced:(nullable id)shader; @@ -39,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/MCShaderFactoryInterface+Private.mm b/bridging/ios/MCShaderFactoryInterface+Private.mm index cb756f133..77df92c45 100644 --- a/bridging/ios/MCShaderFactoryInterface+Private.mm +++ b/bridging/ios/MCShaderFactoryInterface+Private.mm @@ -183,9 +183,9 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef } DJINNI_TRANSLATE_EXCEPTIONS() } -- (nullable id)createUnitSphereTessellatedShader { +- (nullable id)createQuadTessellatedShader { try { - auto objcpp_result_ = _cppRefHandle.get()->createUnitSphereTessellatedShader(); + auto objcpp_result_ = _cppRefHandle.get()->createQuadTessellatedShader(); return ::djinni_generated::RasterShaderInterface::fromCpp(objcpp_result_); } DJINNI_TRANSLATE_EXCEPTIONS() } @@ -376,10 +376,10 @@ - (id)initWithCpp:(const std::shared_ptr<::ShaderFactoryInterface>&)cppRef return ::djinni_generated::RasterShaderInterface::toCpp(objcpp_result_); } } - /*not-null*/ std::shared_ptr<::RasterShaderInterface> createUnitSphereTessellatedShader() override + /*not-null*/ std::shared_ptr<::RasterShaderInterface> createQuadTessellatedShader() override { @autoreleasepool { - auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createUnitSphereTessellatedShader]; + auto objcpp_result_ = [djinni_private_get_proxied_objc_object() createQuadTessellatedShader]; return ::djinni_generated::RasterShaderInterface::toCpp(objcpp_result_); } } diff --git a/bridging/ios/MCShaderFactoryInterface.h b/bridging/ios/MCShaderFactoryInterface.h index 3a0c12838..84540e4fd 100644 --- a/bridging/ios/MCShaderFactoryInterface.h +++ b/bridging/ios/MCShaderFactoryInterface.h @@ -61,7 +61,7 @@ - (nullable id)createUnitSphereRasterShader; -- (nullable id)createUnitSphereTessellatedShader; +- (nullable id)createQuadTessellatedShader; - (nullable id)createStretchShader; diff --git a/djinni/graphics/objects/graphicsobjects.djinni b/djinni/graphics/objects/graphicsobjects.djinni index 2e4de37b9..2a7c2d01e 100644 --- a/djinni/graphics/objects/graphicsobjects.djinni +++ b/djinni/graphics/objects/graphicsobjects.djinni @@ -38,6 +38,7 @@ 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; @@ -48,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; diff --git a/djinni/graphics/shader/shader.djinni b/djinni/graphics/shader/shader.djinni index 60e2f63b7..bbe612411 100644 --- a/djinni/graphics/shader/shader.djinni +++ b/djinni/graphics/shader/shader.djinni @@ -48,7 +48,7 @@ shader_factory_interface = interface +c +j +o { create_raster_shader() : raster_shader_interface; create_unit_sphere_raster_shader() : raster_shader_interface; - create_unit_sphere_tessellated_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/ios/graphics/Model/GraphicsFactory.swift b/ios/graphics/Model/GraphicsFactory.swift index dd333cdc5..f8ac3c4c5 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -12,6 +12,8 @@ import Foundation import MapCoreSharedModule class GraphicsFactory: MCGraphicsObjectFactoryInterface { + + /* Deprecated? */ func createQuadMask(_ is3d: Bool) -> (any MCQuad2dInterface)? { let shader = ColorShader(shader: .colorShader) return Quad2d(shader: shader, metalContext: .current) @@ -21,6 +23,13 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { let shader = MaskShader() return Polygon2d(shader: shader, metalContext: .current) } + + func createPolygonMaskTessellated(_ is3d: Bool) -> (any MCPolygon2dInterface)? { + let shader = MaskShader() + return Polygon2d(shader: shader, metalContext: .current) + //let shader = MaskShader(shader: .maskTessellatedShader) + //return Polygon2dTessellated(shader: shader, metalContext: .current) + } func createPolygonGroup(_ shader: MCShaderProgramInterface?) -> MCPolygonGroup2dInterface? { guard let shader else { fatalError("No Shader provided") } @@ -61,6 +70,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/Polygon2dTessellated.swift b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift new file mode 100644 index 000000000..5b403d101 --- /dev/null +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -0,0 +1,285 @@ +/* + * 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 stencilState: MTLDepthStencilState? + private var renderPassStencilState: MTLDepthStencilState? + + init(shader: MCShaderProgramInterface, metalContext: MetalContext) { + self.shader = shader + super + .init( + device: metalContext.device, + sampler: metalContext.samplerLibrary.value( + Sampler.magLinear.rawValue)!, + label: "Polygon2dTessellated") + + } + + 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() + } + + guard let verticesBuffer, + let indicesBuffer, + let tessellationFactorsBuffer + else { return } + + #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) + } + + 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) + + /* + encoder.drawIndexedPrimitives( + type: .triangle, + indexCount: indicesCount, + indexType: .uint16, + indexBuffer: indicesBuffer, + indexBufferOffset: 0) + */ + + let tessellationFactors: [Float16] = [ + 1, // edge 0 + 0, // edge 1 + 0, // edge 2 + 0, // inside 0 + ] + self.tessellationFactorsBuffer.copyOrCreate( + bytes: tessellationFactors, + length: MemoryLayout.stride * tessellationFactors.count, + device: device) + + encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) + + encoder.drawPatches( + numberOfPatchControlPoints: 4, + patchStart: 0, + patchCount: 1, + patchIndexBuffer: nil, + patchIndexBufferOffset: 0, + instanceCount: 1, + baseInstance: 0) + } + + 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 } + + guard let verticesBuffer, + let indicesBuffer + else { return } + + #if DEBUG + encoder.pushDebugGroup("Polygon2dMask") + defer { + encoder.popDebugGroup() + } + #endif + + if let mask = context.polygonMask { + encoder.setStencilReferenceValue(0xFF) + encoder.setDepthStencilState(mask) + } + + // stencil prepare pass + 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) + + /* + encoder.drawIndexedPrimitives( + type: .triangle, + indexCount: indicesCount, + indexType: .uint16, + indexBuffer: indicesBuffer, + indexBufferOffset: 0) + */ + + let tessellationFactors: [Float16] = [ + 1, // edge 0 + 0, // edge 1 + 0, // edge 2 + 0, // inside 0 + ] + self.tessellationFactorsBuffer.copyOrCreate( + bytes: tessellationFactors, + length: MemoryLayout.stride * tessellationFactors.count, + device: device) + + encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) + + encoder.drawPatches( + numberOfPatchControlPoints: 4, + patchStart: 0, + patchCount: 1, + patchIndexBuffer: nil, + patchIndexBufferOffset: 0, + instanceCount: 1, + baseInstance: 0) + } +} + +extension Polygon2dTessellated: MCPolygon2dInterface { + func setVertices( + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D + ) { + lock.withCritical { + 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/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index 775f50509..ab4e8ab60 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -146,13 +146,14 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case polygonPatternGroupShader case polygonPatternFadeInGroupShader case maskShader + //case maskTessellatedShader case colorShader case roundColorShader case clearStencilShader case textShader case textInstancedShader case rasterShader - case tessellatedShader + case quadTessellatedShader case stretchShader case stretchInstancedShader case unitSphereAlphaShader @@ -176,13 +177,14 @@ 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 .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 .tessellatedShader: return "Tessellated 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" @@ -197,7 +199,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { var vertexShaderUsesModelMatrix: Bool { switch self { - case .rasterShader, .tessellatedShader, .roundColorShader, .unitSphereRoundColorShader, .alphaShader, .unitSphereAlphaShader, .sphereEffectShader, .skySphereShader, .elevationInterpolation: + case .rasterShader, .quadTessellatedShader, .roundColorShader, .unitSphereRoundColorShader, .alphaShader, .unitSphereAlphaShader, .sphereEffectShader, .skySphereShader, .elevationInterpolation: return true default: return false @@ -217,13 +219,14 @@ 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 .roundColorShader: return "baseVertexShaderModel" case .clearStencilShader: return "stencilClearVertexShader" case .textShader: return "textVertexShader" case .textInstancedShader: return "textInstancedVertexShader" case .rasterShader: return "baseVertexShaderModel" - case .tessellatedShader: return "tessellationVertexShader" + case .quadTessellatedShader: return "quadTessellationVertexShader" case .stretchShader: return "stretchVertexShader" case .stretchInstancedShader: return "stretchInstancedVertexShader" case .unitSphereAlphaShader: return "baseVertexShader" @@ -249,13 +252,14 @@ 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 .roundColorShader: return "roundColorFragmentShader" case .clearStencilShader: return "stencilClearFragmentShader" case .textShader: return "textFragmentShader" case .textInstancedShader: return "textInstancedFragmentShader" case .rasterShader: return "rasterFragmentShader" - case .tessellatedShader: return "rasterFragmentShader" + case .quadTessellatedShader: return "rasterFragmentShader" case .stretchShader: return "stretchFragmentShader" case .stretchInstancedShader: return "stretchInstancedFragmentShader" case .unitSphereAlphaShader: return "baseFragmentShader" @@ -290,17 +294,21 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .roundColorShader, .elevationInterpolation: return Vertex3DTexture.descriptor - case .tessellatedShader: + case .quadTessellatedShader: return TessellatedVertex3DTexture.descriptor + //case .maskTessellatedShader: + // return Vertex4F.descriptor // later... TessellatedVertex4f.descriptor default: return Vertex.descriptor } } - var tessellated: Bool { + var tessellated: Bool { //tessellation config?! switch self { - case .tessellatedShader: + case .quadTessellatedShader: return true + //case .maskTessellatedShader: + // return true // but indexed default: return false } diff --git a/ios/graphics/Shader/Metal/BaseShader.metal b/ios/graphics/Shader/Metal/BaseShader.metal index 990343e25..22df58c55 100644 --- a/ios/graphics/Shader/Metal/BaseShader.metal +++ b/ios/graphics/Shader/Metal/BaseShader.metal @@ -27,6 +27,8 @@ baseVertexShader(const Vertex3DTextureIn vertexIn [[stage_in]], return out; } +// deprecated? (baseVertexShader and baseVertexShaderModel identical) + vertex VertexOut baseVertexShaderModel(const Vertex3DTextureIn vertexIn [[stage_in]], constant float4x4 &vpMatrix [[buffer(1)]], diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 22ba897ae..8a8972078 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -78,6 +78,11 @@ T bilerp(T c00, T c01, T c10, T c11, float2 uv) { return mix(c0, c1, T(uv[1])); } +template +T baryinterp(T c0, T c1, T c2, float3 bary) { + return c0 * bary[0] + c1 * bary[1] + c2 * bary[2]; +} + float3 transform(float3 coordinate, float3 origin) { float x = 1.0 * sin(coordinate.y) * cos(coordinate.x) - origin.x; float y = 1.0 * cos(coordinate.y) - origin.y; @@ -86,16 +91,16 @@ float3 transform(float3 coordinate, float3 origin) { } [[patch(quad, 4)]] vertex VertexOut -tessellationVertexShader(const patch_control_point controlPoints [[stage_in]], - const float2 positionInPatch [[position_in_patch]], - constant float4x4 &vpMatrix [[buffer(1)]], - constant float4x4 &mMatrix [[buffer(2)]], - constant float3 &originOffset [[buffer(3)]], - constant float3 &origin [[buffer(4)]], - /* ELEVATION PROTOTYPE TEST - texture2d texture0 [[ texture(0)]], - sampler sampler0 [[sampler(0)]], */ - constant bool &is3d [[buffer(5)]]) +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 float3 &originOffset [[buffer(3)]], + constant float3 &origin [[buffer(4)]], + /* ELEVATION PROTOTYPE TEST + texture2d texture0 [[ texture(0)]], + sampler sampler0 [[sampler(0)]], */ + constant bool &is3d [[buffer(5)]]) { TessellatedVertex3DTextureIn vA = controlPoints[0]; TessellatedVertex3DTextureIn vB = controlPoints[1]; @@ -121,6 +126,28 @@ tessellationVertexShader(const patch_control_point .position = vpMatrix * ((mMatrix * float4(position, 1)) + float4(originOffset, 0)), .uv = vertexUV }; + + 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 float3 &originOffset [[buffer(3)]]) +{ + Vertex4FIn vA = controlPoints[0]; + Vertex4FIn vB = controlPoints[1]; + Vertex4FIn vC = controlPoints[2]; + + float4 vertexPosition = baryinterp(vA.position, vB.position, vC.position, positionInPatch); + + float3 position = vertexPosition.xyz; + + VertexOut out { + .position = vpMatrix * (float4(position, 1) + float4(originOffset, 0)), + }; return out; } diff --git a/ios/graphics/Shader/ShaderFactory.swift b/ios/graphics/Shader/ShaderFactory.swift index 8eba1883b..1161b9a75 100644 --- a/ios/graphics/Shader/ShaderFactory.swift +++ b/ios/graphics/Shader/ShaderFactory.swift @@ -24,8 +24,8 @@ class ShaderFactory: MCShaderFactoryInterface { RasterShader() } - func createUnitSphereTessellatedShader() -> MCRasterShaderInterface? { - RasterShader(shader: .tessellatedShader) + func createQuadTessellatedShader() -> MCRasterShaderInterface? { + RasterShader(shader: .quadTessellatedShader) } func createTextShader() -> MCTextShaderInterface? { diff --git a/shared/public/GraphicsObjectFactoryInterface.h b/shared/public/GraphicsObjectFactoryInterface.h index 2651d69d7..06b5e83d8 100644 --- a/shared/public/GraphicsObjectFactoryInterface.h +++ b/shared/public/GraphicsObjectFactoryInterface.h @@ -27,6 +27,8 @@ class GraphicsObjectFactoryInterface { 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; @@ -43,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/ShaderFactoryInterface.h b/shared/public/ShaderFactoryInterface.h index 412b37ff0..50493e9f4 100644 --- a/shared/public/ShaderFactoryInterface.h +++ b/shared/public/ShaderFactoryInterface.h @@ -63,7 +63,7 @@ class ShaderFactoryInterface { virtual /*not-null*/ std::shared_ptr createUnitSphereRasterShader() = 0; - virtual /*not-null*/ std::shared_ptr createUnitSphereTessellatedShader() = 0; + virtual /*not-null*/ std::shared_ptr createQuadTessellatedShader() = 0; virtual /*not-null*/ std::shared_ptr createStretchShader() = 0; diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index d9b92bfdf..61c378363 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -99,7 +99,7 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons 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); + PolygonHelper::subdivision(vecVertices, indices, threshold); // here, do on gpu } for (const auto& v : vecVertices) { diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index 5f6041520..90dd33a58 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -82,7 +82,7 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } if(maxSegmentLength) { - PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); + PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); // here, do on gpu } for (const auto& v : vecVertices) { double rx = origin.x; diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index 605accd1d..eeaa6dca9 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -303,12 +303,13 @@ std::vector sortedTileInfos(currentTileInfos.begin(), tileObject = std::make_shared(quad, mapInterface, is3D); } else { /* + // deprecated? (createUnitSphereRasterShader and createRasterShader both return RasterShader) auto rShader = is3D ? shaderFactory->createUnitSphereRasterShader() : shaderFactory->createRasterShader(); rShader->asShaderProgramInterface()->setBlendMode(blendMode); auto quad = graphicsFactory->createQuad(rShader->asShaderProgramInterface()); */ // /* - auto rShader = shaderFactory->createUnitSphereTessellatedShader(); + auto rShader = shaderFactory->createQuadTessellatedShader(); rShader->asShaderProgramInterface()->setBlendMode(blendMode); auto quad = graphicsFactory->createQuadTessellated(rShader->asShaderProgramInterface()); // */ 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 ff12d3564..9a919c8ce 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -29,11 +29,13 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptris3d() ? 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()); */ // /* - auto shader = pMapInterface->getShaderFactory()->createUnitSphereTessellatedShader(); + auto shader = pMapInterface->getShaderFactory()->createQuadTessellatedShader(); shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); // */ From 5e686b78cc941bd661cd1578bca024300bc649f8 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 6 Nov 2025 17:07:48 +0100 Subject: [PATCH 05/24] Partially working polygon mask tessellation --- ios/graphics/Model/GraphicsFactory.swift | 6 +- .../Model/Polygon/Polygon2dTessellated.swift | 75 +++++++------------ ios/graphics/Pipelines/PipelineLibrary.swift | 23 ++++-- .../Shader/Metal/TessellatedShader.metal | 6 +- .../map/layers/objects/PolygonMaskObject.cpp | 8 +- 5 files changed, 51 insertions(+), 67 deletions(-) diff --git a/ios/graphics/Model/GraphicsFactory.swift b/ios/graphics/Model/GraphicsFactory.swift index f8ac3c4c5..3c199f658 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -25,10 +25,8 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { } func createPolygonMaskTessellated(_ is3d: Bool) -> (any MCPolygon2dInterface)? { - let shader = MaskShader() - return Polygon2d(shader: shader, metalContext: .current) - //let shader = MaskShader(shader: .maskTessellatedShader) - //return Polygon2dTessellated(shader: shader, metalContext: .current) + let shader = MaskShader(shader: .maskTessellatedShader) + return Polygon2dTessellated(shader: shader, metalContext: .current) } func createPolygonGroup(_ shader: MCShaderProgramInterface?) -> MCPolygonGroup2dInterface? { diff --git a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift index 5b403d101..757137195 100644 --- a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -113,35 +113,17 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { bufferPointer.pointee.z = Float(originOffset.z - origin.z) } encoder.setVertexBuffer(originOffsetBuffer, offset: 0, index: 3) - - /* - encoder.drawIndexedPrimitives( - type: .triangle, - indexCount: indicesCount, - indexType: .uint16, - indexBuffer: indicesBuffer, - indexBufferOffset: 0) - */ - - let tessellationFactors: [Float16] = [ - 1, // edge 0 - 0, // edge 1 - 0, // edge 2 - 0, // inside 0 - ] - self.tessellationFactorsBuffer.copyOrCreate( - bytes: tessellationFactors, - length: MemoryLayout.stride * tessellationFactors.count, - device: device) encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) - encoder.drawPatches( - numberOfPatchControlPoints: 4, + encoder.drawIndexedPatches( + numberOfPatchControlPoints: 3, patchStart: 0, - patchCount: 1, + patchCount: indicesCount / 3, patchIndexBuffer: nil, patchIndexBufferOffset: 0, + controlPointIndexBuffer: indicesBuffer, + controlPointIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0) } @@ -185,11 +167,12 @@ extension Polygon2dTessellated: MCMaskingObjectInterface { else { return } guard let verticesBuffer, - let indicesBuffer + let indicesBuffer, + let tessellationFactorsBuffer else { return } #if DEBUG - encoder.pushDebugGroup("Polygon2dMask") + encoder.pushDebugGroup("Polygon2dTessellated") defer { encoder.popDebugGroup() } @@ -229,35 +212,17 @@ extension Polygon2dTessellated: MCMaskingObjectInterface { bufferPointer.pointee.z = Float(originOffset.z - origin.z) } encoder.setVertexBuffer(originOffsetBuffer, offset: 0, index: 3) - - /* - encoder.drawIndexedPrimitives( - type: .triangle, - indexCount: indicesCount, - indexType: .uint16, - indexBuffer: indicesBuffer, - indexBufferOffset: 0) - */ - - let tessellationFactors: [Float16] = [ - 1, // edge 0 - 0, // edge 1 - 0, // edge 2 - 0, // inside 0 - ] - self.tessellationFactorsBuffer.copyOrCreate( - bytes: tessellationFactors, - length: MemoryLayout.stride * tessellationFactors.count, - device: device) - + encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) - encoder.drawPatches( - numberOfPatchControlPoints: 4, + encoder.drawIndexedPatches( + numberOfPatchControlPoints: 3, patchStart: 0, - patchCount: 1, + patchCount: indicesCount / 3, patchIndexBuffer: nil, patchIndexBufferOffset: 0, + controlPointIndexBuffer: indicesBuffer, + controlPointIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0) } @@ -276,6 +241,18 @@ extension Polygon2dTessellated: MCPolygon2dInterface { self.indicesCount = 0 } self.originOffset = origin + + // todo determine from cpu version + let tessellationFactors: [Float16] = [ + 2, // edge 0 + 2, // edge 1 + 2, // edge 2 + 2, // inside 0 + ] + self.tessellationFactorsBuffer.copyOrCreate( + bytes: tessellationFactors, + length: MemoryLayout.stride * tessellationFactors.count, + device: device) } } diff --git a/ios/graphics/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index ab4e8ab60..a7367faaf 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -83,6 +83,13 @@ public enum PipelineDescriptorFactory { pipelineDescriptor.tessellationFactorStepFunction = .constant pipelineDescriptor.tessellationOutputWindingOrder = .clockwise pipelineDescriptor.tessellationControlPointIndexType = .none + pipelineDescriptor.isTessellationFactorScaleEnabled = false + + //temporary + if label == PipelineType.maskTessellatedShader.label { + pipelineDescriptor.vertexDescriptor!.layouts[0].stepFunction = .perPatchControlPoint + pipelineDescriptor.tessellationControlPointIndexType = .uint16 + } } return pipelineDescriptor } @@ -146,7 +153,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case polygonPatternGroupShader case polygonPatternFadeInGroupShader case maskShader - //case maskTessellatedShader + case maskTessellatedShader case colorShader case roundColorShader case clearStencilShader @@ -177,7 +184,7 @@ 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 .maskTessellatedShader: return "Mask Tessellated shader" case .colorShader: return "Color shader" case .roundColorShader: return "Round color shader" case .clearStencilShader: return "Clear stencil shader" @@ -219,7 +226,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .polygonPatternGroupShader: return "polygonPatternGroupVertexShader" case .polygonPatternFadeInGroupShader: return "polygonPatternGroupVertexShader" case .maskShader: return "colorVertexShader" - //case .maskTessellatedShader: return "polygonTessellationVertexShader" + case .maskTessellatedShader: return "polygonTessellationVertexShader" case .colorShader: return "colorVertexShader" case .roundColorShader: return "baseVertexShaderModel" case .clearStencilShader: return "stencilClearVertexShader" @@ -252,7 +259,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .polygonPatternGroupShader: return "polygonPatternGroupFragmentShader" case .polygonPatternFadeInGroupShader: return "polygonPatternGroupFadeInFragmentShader" case .maskShader: return "maskFragmentShader" - //case .maskTessellatedShader: return "maskFragmentShader" + case .maskTessellatedShader: return "maskFragmentShader" case .colorShader: return "colorFragmentShader" case .roundColorShader: return "roundColorFragmentShader" case .clearStencilShader: return "stencilClearFragmentShader" @@ -296,8 +303,8 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { return Vertex3DTexture.descriptor case .quadTessellatedShader: return TessellatedVertex3DTexture.descriptor - //case .maskTessellatedShader: - // return Vertex4F.descriptor // later... TessellatedVertex4f.descriptor + case .maskTessellatedShader: + return Vertex4F.descriptor // later... TessellatedVertex4f.descriptor default: return Vertex.descriptor } @@ -307,8 +314,8 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { switch self { case .quadTessellatedShader: return true - //case .maskTessellatedShader: - // return true // but indexed + case .maskTessellatedShader: + return true // but indexed default: return false } diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 8a8972078..0b309425d 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -135,18 +135,16 @@ polygonTessellationVertexShader(const patch_control_point controlPoi const float3 positionInPatch [[position_in_patch]], constant float4x4 &vpMatrix [[buffer(1)]], constant float4x4 &mMatrix [[buffer(2)]], - constant float3 &originOffset [[buffer(3)]]) + constant float4 &originOffset [[buffer(3)]]) { Vertex4FIn vA = controlPoints[0]; Vertex4FIn vB = controlPoints[1]; Vertex4FIn vC = controlPoints[2]; float4 vertexPosition = baryinterp(vA.position, vB.position, vC.position, positionInPatch); - - float3 position = vertexPosition.xyz; VertexOut out { - .position = vpMatrix * (float4(position, 1) + float4(originOffset, 0)), + .position = vpMatrix * (float4(vertexPosition.xyz, 1) + originOffset), }; return out; diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index 90dd33a58..6dbe243e6 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -24,7 +24,8 @@ PolygonMaskObject::PolygonMaskObject(const std::shared_ptr &conversionHelper, bool is3D) : conversionHelper(conversionHelper) - , polygon(graphicsObjectFactory->createPolygonMask(is3D)) + , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) + //, polygon(graphicsObjectFactory->createPolygonMask(is3D)) , is3D(is3D) {} void PolygonMaskObject::setPositions(const std::vector &positions, @@ -81,9 +82,12 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } - if(maxSegmentLength) { + /* todo send to gpu in a absolute factor */ + if (maxSegmentLength) { PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); // here, do on gpu } + + for (const auto& v : vecVertices) { double rx = origin.x; double ry = origin.y; From 65d716d19d1c2e403d7ad43dee6532a459ad67f3 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 19 Nov 2025 15:31:40 +0100 Subject: [PATCH 06/24] Complete polygon mask tessellation --- .../graphics/objects/Polygon2dInterface.kt | 8 +- .../graphics/shader/TessellationMode.kt | 10 ++ .../objects/NativePolygon2dInterface.cpp | 11 +- .../objects/NativePolygon2dInterface.h | 4 +- .../graphics/shader/NativeTessellationMode.h | 26 ++++ bridging/ios/MCPolygon2dInterface+Private.mm | 11 +- bridging/ios/MCPolygon2dInterface.h | 3 +- bridging/ios/MCTessellationMode+Private.h | 10 ++ bridging/ios/MCTessellationMode.h | 11 ++ .../graphics/objects/graphicsobjects.djinni | 2 +- djinni/graphics/shader/shader.djinni | 6 + djinni/yaml/tessellation_mode.yaml | 39 ++++++ ios/graphics/Model/GraphicsFactory.swift | 4 +- ios/graphics/Model/Polygon/Polygon2d.swift | 2 +- .../Model/Polygon/Polygon2dTessellated.swift | 127 +++++++++--------- .../Model/Quad/Quad2dTessellated.swift | 19 +-- ios/graphics/Model/TessellatedVertex3D.swift | 37 +++++ ios/graphics/Pipelines/PipelineLibrary.swift | 21 ++- .../Shader/Metal/DataStructures.metal | 9 +- .../Shader/Metal/TessellatedShader.metal | 44 +++--- ios/helpers/Half.swift | 50 +++++++ shared/public/Polygon2dInterface.h | 3 +- shared/public/TessellationMode.h | 32 +++++ .../layers/objects/Polygon2dLayerObject.cpp | 2 +- .../map/layers/objects/PolygonMaskObject.cpp | 19 ++- ...2dMapVectorSourceRasterTileDataManager.cpp | 13 +- ...2dMapVectorSourceVectorTileDataManager.cpp | 13 +- 27 files changed, 390 insertions(+), 146 deletions(-) create mode 100644 bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/shader/TessellationMode.kt create mode 100644 bridging/android/jni/graphics/shader/NativeTessellationMode.h create mode 100644 bridging/ios/MCTessellationMode+Private.h create mode 100644 bridging/ios/MCTessellationMode.h create mode 100644 djinni/yaml/tessellation_mode.yaml create mode 100644 ios/graphics/Model/TessellatedVertex3D.swift create mode 100644 ios/helpers/Half.swift create mode 100644 shared/public/TessellationMode.h 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..71817743e 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,7 @@ 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, subdivisionFactor: Int) abstract fun asGraphicsObject(): GraphicsObjectInterface @@ -29,11 +29,11 @@ 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, subdivisionFactor: Int) { assert(!this.destroyed.get()) { error("trying to use a destroyed object") } - native_setVertices(this.nativeRef, vertices, indices, origin) + native_setVertices(this.nativeRef, vertices, indices, origin, subdivisionFactor) } - 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_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, subdivisionFactor: 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/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/NativePolygon2dInterface.cpp b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp index afdd6a562..718ddca31 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,15 @@ 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, int32_t c_subdivisionFactor) { 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::I32::fromCpp(jniEnv, c_subdivisionFactor))); ::djinni::jniExceptionCheck(jniEnv); } /*not-null*/ std::shared_ptr<::GraphicsObjectInterface> NativePolygon2dInterface::JavaProxy::asGraphicsObject() { @@ -51,13 +53,14 @@ 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, jint j_subdivisionFactor) { 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::I32::toCpp(jniEnv, j_subdivisionFactor)); } 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..3e6c23537 100644 --- a/bridging/android/jni/graphics/objects/NativePolygon2dInterface.h +++ b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.h @@ -33,7 +33,7 @@ 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, int32_t subdivisionFactor) override; /*not-null*/ std::shared_ptr<::GraphicsObjectInterface> asGraphicsObject() override; /*not-null*/ std::shared_ptr<::MaskingObjectInterface> asMaskingObject() override; @@ -42,7 +42,7 @@ 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;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/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/MCPolygon2dInterface+Private.mm b/bridging/ios/MCPolygon2dInterface+Private.mm index 823b3154a..913b3baf1 100644 --- a/bridging/ios/MCPolygon2dInterface+Private.mm +++ b/bridging/ios/MCPolygon2dInterface+Private.mm @@ -37,11 +37,13 @@ - (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 + subdivisionFactor:(int32_t)subdivisionFactor { 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::I32::toCpp(subdivisionFactor)); } DJINNI_TRANSLATE_EXCEPTIONS() } @@ -68,12 +70,13 @@ - (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, int32_t c_subdivisionFactor) 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)) + subdivisionFactor:(::djinni::I32::fromCpp(c_subdivisionFactor))]; } } /*not-null*/ std::shared_ptr<::GraphicsObjectInterface> asGraphicsObject() override diff --git a/bridging/ios/MCPolygon2dInterface.h b/bridging/ios/MCPolygon2dInterface.h index 36881e5bc..14a3f5b6c 100644 --- a/bridging/ios/MCPolygon2dInterface.h +++ b/bridging/ios/MCPolygon2dInterface.h @@ -12,7 +12,8 @@ - (void)setVertices:(nonnull MCSharedBytes *)vertices indices:(nonnull MCSharedBytes *)indices - origin:(nonnull MCVec3D *)origin; + origin:(nonnull MCVec3D *)origin + subdivisionFactor:(int32_t)subdivisionFactor; - (nullable id)asGraphicsObject; 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 2a7c2d01e..aaca8ddc4 100644 --- a/djinni/graphics/objects/graphicsobjects.djinni +++ b/djinni/graphics/objects/graphicsobjects.djinni @@ -133,7 +133,7 @@ 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, subdivisionFactor: 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 bbe612411..30dd5ef7d 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); 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 3c199f658..323018ab8 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -26,7 +26,7 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { func createPolygonMaskTessellated(_ is3d: Bool) -> (any MCPolygon2dInterface)? { let shader = MaskShader(shader: .maskTessellatedShader) - return Polygon2dTessellated(shader: shader, metalContext: .current) + return Polygon2dTessellated(shader: shader, metalContext: .current, is3d: is3d) } func createPolygonGroup(_ shader: MCShaderProgramInterface?) -> MCPolygonGroup2dInterface? { @@ -71,7 +71,7 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { func createPolygonTessellated(_ shader: MCShaderProgramInterface?) -> MCPolygon2dInterface? { guard let shader else { fatalError("No Shader provided") } - return Polygon2dTessellated(shader: shader, metalContext: .current) + return Polygon2dTessellated(shader: shader, metalContext: .current, is3d: false) //? } func createText(_ shader: MCShaderProgramInterface?) -> MCTextInterface? { diff --git a/ios/graphics/Model/Polygon/Polygon2d.swift b/ios/graphics/Model/Polygon/Polygon2d.swift index dca140cd9..6af340daf 100644 --- a/ios/graphics/Model/Polygon/Polygon2d.swift +++ b/ios/graphics/Model/Polygon/Polygon2d.swift @@ -215,7 +215,7 @@ extension Polygon2d: MCMaskingObjectInterface { extension Polygon2d: MCPolygon2dInterface { func setVertices( - _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, subdivisionFactor: Int32 ) { 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 index 757137195..0a21dc8b8 100644 --- a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -21,12 +21,17 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var indicesCount: Int = 0 private var tessellationFactorsBuffer: MTLBuffer? + private var originBuffers: MultiBuffer + + private var is3d = false private var stencilState: MTLDepthStencilState? private var renderPassStencilState: MTLDepthStencilState? - init(shader: MCShaderProgramInterface, metalContext: MetalContext) { + init(shader: MCShaderProgramInterface, metalContext: MetalContext, is3d: Bool) { self.shader = shader + originBuffers = .init(device: metalContext.device) + self.is3d = is3d super .init( device: metalContext.device, @@ -52,11 +57,6 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { lock.unlock() } - guard let verticesBuffer, - let indicesBuffer, - let tessellationFactorsBuffer - else { return } - #if DEBUG encoder.pushDebugGroup(label) defer { @@ -84,7 +84,29 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { 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) @@ -114,6 +136,21 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { } 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) encoder.drawIndexedPatches( @@ -166,13 +203,8 @@ extension Polygon2dTessellated: MCMaskingObjectInterface { let encoder = context.encoder else { return } - guard let verticesBuffer, - let indicesBuffer, - let tessellationFactorsBuffer - else { return } - #if DEBUG - encoder.pushDebugGroup("Polygon2dTessellated") + encoder.pushDebugGroup(label) defer { encoder.popDebugGroup() } @@ -184,54 +216,27 @@ extension Polygon2dTessellated: MCMaskingObjectInterface { } // stencil prepare pass - 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) - - encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) - - encoder.drawIndexedPatches( - numberOfPatchControlPoints: 3, - patchStart: 0, - patchCount: indicesCount / 3, - patchIndexBuffer: nil, - patchIndexBufferOffset: 0, - controlPointIndexBuffer: indicesBuffer, - controlPointIndexBufferOffset: 0, - instanceCount: 1, - baseInstance: 0) + renderMain( + encoder: encoder, + context: context, + vpMatrix: vpMatrix, + mMatrix: mMatrix, + origin: origin, + isScreenSpaceCoords: isScreenSpaceCoords) } } extension Polygon2dTessellated: MCPolygon2dInterface { func setVertices( - _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, subdivisionFactor: Int32 ) { + let factor = Half(subdivisionFactor).bits; + + var tessellationFactors = MTLTriangleTessellationFactorsHalf( + edgeTessellationFactor: (factor, factor, factor), + insideTessellationFactor: factor + ); + lock.withCritical { self.verticesBuffer.copyOrCreate(from: vertices, device: device) self.indicesBuffer.copyOrCreate(from: indices, device: device) @@ -241,17 +246,9 @@ extension Polygon2dTessellated: MCPolygon2dInterface { self.indicesCount = 0 } self.originOffset = origin - - // todo determine from cpu version - let tessellationFactors: [Float16] = [ - 2, // edge 0 - 2, // edge 1 - 2, // edge 2 - 2, // inside 0 - ] self.tessellationFactorsBuffer.copyOrCreate( - bytes: tessellationFactors, - length: MemoryLayout.stride * tessellationFactors.count, + bytes: &tessellationFactors, + length: MemoryLayout.stride, device: device) } } diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index 078aadcab..421347cd9 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -281,17 +281,12 @@ extension Quad2dTessellated: MCQuad2dInterface { _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, is3d: Bool ) { - let sFactor = lock.withCritical { subdivisionFactor } - let tFactor = Float16(pow(2.0, Double(sFactor))) + let factor = Half(pow(2, Float(lock.withCritical { subdivisionFactor }))).bits; - let tessellationFactors: [Float16] = [ - tFactor, // edge 0 - tFactor, // edge 1 - tFactor, // edge 2 - tFactor, // edge 3 - tFactor, // inside 0 - tFactor // inside 1 - ] + var tessellationFactors = MTLQuadTessellationFactorsHalf( + edgeTessellationFactor: (factor, factor, factor, factor), + insideTessellationFactor: (factor, factor) + ); var vertices: [TessellatedVertex3DTexture] = [] @@ -349,8 +344,8 @@ extension Quad2dTessellated: MCQuad2dInterface { length: MemoryLayout.stride * vertices.count, device: device) self.tessellationFactorsBuffer.copyOrCreate( - bytes: tessellationFactors, - length: MemoryLayout.stride * tessellationFactors.count, + bytes: &tessellationFactors, + length: MemoryLayout.stride, device: device) } } diff --git a/ios/graphics/Model/TessellatedVertex3D.swift b/ios/graphics/Model/TessellatedVertex3D.swift new file mode 100644 index 000000000..c591e57b1 --- /dev/null +++ b/ios/graphics/Model/TessellatedVertex3D.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 TessellatedVertex4F: Equatable { + nonisolated(unsafe) static let descriptor: MTLVertexDescriptor = { + let vertexDescriptor = MTLVertexDescriptor() + let bufferIndex = 0 + var offset = 0 + + // Relative Position + vertexDescriptor.attributes[0].bufferIndex = bufferIndex + vertexDescriptor.attributes[0].format = .float4 + vertexDescriptor.attributes[0].offset = offset + offset += MemoryLayout>.stride + + // Absolute Position + vertexDescriptor.attributes[1].bufferIndex = bufferIndex + vertexDescriptor.attributes[1].format = .float4 + 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/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index a7367faaf..83e26ea07 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -20,7 +20,7 @@ public enum PipelineDescriptorFactory { blendMode: MCBlendMode, library: MTLLibrary, constants: MTLFunctionConstantValues? = nil, - tessellated: Bool = false + tessellation: MCTessellationMode = MCTessellationMode.NONE ) -> MTLRenderPipelineDescriptor { let pipelineDescriptor = MTLRenderPipelineDescriptor() pipelineDescriptor.colorAttachments[0].pixelFormat = MetalContext.colorPixelFormat @@ -76,7 +76,7 @@ public enum PipelineDescriptorFactory { pipelineDescriptor.fragmentFunction = fragmentFunction } - if tessellated { + if tessellation != MCTessellationMode.NONE { pipelineDescriptor.maxTessellationFactor = 64 pipelineDescriptor.tessellationPartitionMode = .pow2 pipelineDescriptor.tessellationFactorFormat = .half @@ -85,9 +85,8 @@ public enum PipelineDescriptorFactory { pipelineDescriptor.tessellationControlPointIndexType = .none pipelineDescriptor.isTessellationFactorScaleEnabled = false - //temporary - if label == PipelineType.maskTessellatedShader.label { - pipelineDescriptor.vertexDescriptor!.layouts[0].stepFunction = .perPatchControlPoint + if tessellation == MCTessellationMode.TRIANGLE { + pipelineDescriptor.tessellationOutputWindingOrder = .counterClockwise pipelineDescriptor.tessellationControlPointIndexType = .uint16 } } @@ -104,7 +103,7 @@ extension PipelineDescriptorFactory { fragmentShader: pipeline.type.fragmentShader, blendMode: pipeline.blendMode, library: library, - tessellated: pipeline.type.tessellated + tessellation: pipeline.type.tessellation ) } } @@ -304,20 +303,20 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case .quadTessellatedShader: return TessellatedVertex3DTexture.descriptor case .maskTessellatedShader: - return Vertex4F.descriptor // later... TessellatedVertex4f.descriptor + return TessellatedVertex4F.descriptor default: return Vertex.descriptor } } - var tessellated: Bool { //tessellation config?! + var tessellation: MCTessellationMode { switch self { case .quadTessellatedShader: - return true + return MCTessellationMode.QUAD case .maskTessellatedShader: - return true // but indexed + return MCTessellationMode.TRIANGLE default: - return false + return MCTessellationMode.NONE } } } diff --git a/ios/graphics/Shader/Metal/DataStructures.metal b/ios/graphics/Shader/Metal/DataStructures.metal index 67de7eb7e..2fac730d8 100644 --- a/ios/graphics/Shader/Metal/DataStructures.metal +++ b/ios/graphics/Shader/Metal/DataStructures.metal @@ -22,8 +22,8 @@ struct Vertex3DTextureIn { }; struct TessellatedVertex3DTextureIn { - float3 relativePosition [[attribute(0)]]; - float3 absolutePosition [[attribute(1)]]; + float4 relativePosition [[attribute(0)]]; + float4 absolutePosition [[attribute(1)]]; float2 uv [[attribute(2)]]; }; @@ -33,6 +33,11 @@ struct Vertex4FIn { float4 position [[attribute(0)]]; }; +struct TessellatedVertex4FIn { + float4 relativePosition [[attribute(0)]]; + float4 absolutePosition [[attribute(1)]]; +}; + struct VertexOut { float4 position [[ position ]]; float2 uv; diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 0b309425d..983f2cebe 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -83,11 +83,11 @@ T baryinterp(T c0, T c1, T c2, float3 bary) { return c0 * bary[0] + c1 * bary[1] + c2 * bary[2]; } -float3 transform(float3 coordinate, float3 origin) { +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 float3(x, y, z); + return float4(x, y, z, 0); } [[patch(quad, 4)]] vertex VertexOut @@ -95,8 +95,8 @@ quadTessellationVertexShader(const patch_control_point texture0 [[ texture(0)]], sampler sampler0 [[sampler(0)]], */ @@ -107,13 +107,13 @@ quadTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], +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 &originOffset [[buffer(3)]], + constant float4 &origin [[buffer(4)]], + constant bool &is3d [[buffer(5)]]) { - Vertex4FIn vA = controlPoints[0]; - Vertex4FIn vB = controlPoints[1]; - Vertex4FIn vC = controlPoints[2]; + TessellatedVertex4FIn vA = controlPoints[0]; + TessellatedVertex4FIn vB = controlPoints[1]; + TessellatedVertex4FIn vC = controlPoints[2]; + + float4 vertexRelativePosition = baryinterp(vA.relativePosition, vB.relativePosition, vC.relativePosition, positionInPatch); + float4 vertexAbsolutePosition = baryinterp(vA.absolutePosition, vB.absolutePosition, vC.absolutePosition, positionInPatch); + + float4 position = vertexRelativePosition; + if (is3d) { + float4 bent = transform(vertexAbsolutePosition.xy, origin) - originOffset; + float blend = saturate(length(originOffset) * BlendScale - BlendOffset); + position = mix(position, bent, blend); + } - float4 vertexPosition = baryinterp(vA.position, vB.position, vC.position, positionInPatch); - VertexOut out { - .position = vpMatrix * (float4(vertexPosition.xyz, 1) + originOffset), + .position = vpMatrix * (float4(position.xyz, 1) + originOffset), }; return out; 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/Polygon2dInterface.h b/shared/public/Polygon2dInterface.h index a681ffa84..c5c32ae22 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,7 @@ 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, int32_t subdivisionFactor) = 0; virtual /*not-null*/ std::shared_ptr asGraphicsObject() = 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/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index 61c378363..cc276c0b9 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -124,7 +124,7 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons 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), 0); } 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 6dbe243e6..a69b99e07 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -82,10 +82,9 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } - /* todo send to gpu in a absolute factor */ - if (maxSegmentLength) { - PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); // here, do on gpu - } + //if (maxSegmentLength) { + // PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); + //} for (const auto& v : vecVertices) { @@ -103,12 +102,20 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, #ifdef __APPLE__ vertices.push_back(0.0f); #endif + + vertices.push_back(v.x); + vertices.push_back(v.y); + vertices.push_back(0.0f); + #ifdef __APPLE__ + vertices.push_back(0.0f); + #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); + int32_t subdivisionFactor = (int32_t)(maxSegmentLength.value_or(1.0f)); + + polygon->setVertices(attr, ind, origin, subdivisionFactor); } std::shared_ptr PolygonMaskObject::getPolygonObject() { return polygon; } diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index 5a1a95773..7ed4f5af2 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -98,11 +98,11 @@ 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); - } + //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; @@ -111,7 +111,8 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); - tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); + //tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); + tileMask->setPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); 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..ae0855e1f 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -106,11 +106,11 @@ 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); - } + //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; @@ -119,7 +119,8 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); - tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); + //tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); + tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); newTileMasks[tileEntry->tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); } From 80bb224353721313ab1a885a6648f7ad21b32f4d Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 26 Nov 2025 17:40:24 +0100 Subject: [PATCH 07/24] Begin android quad tessellation --- .../objects/GraphicsObjectFactoryOpenGl.cpp | 22 ++ .../objects/GraphicsObjectFactoryOpenGl.h | 7 + .../cpp/graphics/objects/Polygon2dOpenGl.cpp | 2 +- .../cpp/graphics/objects/Polygon2dOpenGl.h | 2 +- .../objects/Quad2dTessellatedOpenGl.cpp | 320 ++++++++++++++++++ .../objects/Quad2dTessellatedOpenGl.h | 115 +++++++ .../graphics/shader/RasterShaderOpenGl.cpp | 4 + .../cpp/graphics/shader/RasterShaderOpenGl.h | 1 + .../graphics/shader/ShaderFactoryOpenGl.cpp | 5 + .../cpp/graphics/shader/ShaderFactoryOpenGl.h | 2 + .../shader/TessellatedRasterShaderOpenGl.cpp | 128 +++++++ .../shader/TessellatedRasterShaderOpenGl.h | 31 ++ .../map/layers/objects/PolygonMaskObject.cpp | 20 +- ...2dMapVectorSourceRasterTileDataManager.cpp | 18 +- ...2dMapVectorSourceVectorTileDataManager.cpp | 18 +- 15 files changed, 670 insertions(+), 25 deletions(-) create mode 100644 android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp create mode 100644 android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h create mode 100644 android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp create mode 100644 android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp index ef12df175..ada2bac0a 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" 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)); } +// TODO +std::shared_ptr +GraphicsObjectFactoryOpenGl::createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) { + return std::make_shared(enforceGlShader(shader)); + //return std::make_shared(enforceGlShader(shader)); +} + std::shared_ptr GraphicsObjectFactoryOpenGl::createLineGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) { return std::make_shared(enforceGlShader(shader)); @@ -45,6 +57,7 @@ GraphicsObjectFactoryOpenGl::createPolygonPatternGroup(const std::shared_ptr<::S return std::make_shared(enforceGlShader(shader)); } +/* Deprecated? */ std::shared_ptr GraphicsObjectFactoryOpenGl::createQuadMask(bool is3D) { return std::make_shared(std::make_shared(is3D)); } @@ -55,6 +68,15 @@ std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMa return std::make_shared(enforceGlShader(shader)); } +// TODO +std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMaskTessellated(bool is3D) { + std::shared_ptr shader = std::make_shared(is3D); + //different shader! tessellated! + shader->setColor(1, 1, 1, 1); + return std::make_shared(enforceGlShader(shader)); + //return std::make_shared(enforceGlShader(shader)); +} + std::shared_ptr GraphicsObjectFactoryOpenGl::createText(const std::shared_ptr<::ShaderProgramInterface> &shader) { return std::make_shared(enforceGlShader(shader)); } diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h index e3c03a314..a44398427 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h @@ -17,18 +17,25 @@ 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; std::shared_ptr createPolygonPatternGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) override; + /* Deprecated? */ std::shared_ptr createQuadMask(bool is3D) override; 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..c7c708580 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp @@ -21,7 +21,7 @@ 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::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, int32_t subdivisionFactor) { std::lock_guard lock(dataMutex); ready = false; dataReady = false; diff --git a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h index bedf4bcd0..f2325fc8c 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h @@ -42,7 +42,7 @@ 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; + virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin, int32_t subdivisionFactor) override; virtual std::shared_ptr asGraphicsObject() override; 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..602b0df19 --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -0,0 +1,320 @@ +/* + * 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 + +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) { + if (factor != subdivisionFactor) { + subdivisionFactor = factor; + ready = false; + } +} + +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 = { + (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), + + (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), + + (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), + + (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), + }; + } else { + vertices = { + (float) (frame.topLeft.x - quadOrigin.x), (float) (frame.topLeft.y - quadOrigin.y), (float) (-quadOrigin.z), + (float) (frame.topRight.x - quadOrigin.x), (float) (frame.topRight.y - quadOrigin.y), (float) (-quadOrigin.z), + (float) (frame.bottomLeft.x - quadOrigin.x), (float) (frame.bottomLeft.y - quadOrigin.y), (float) (-quadOrigin.z), + (float) (frame.bottomRight.x - quadOrigin.x), (float) (frame.bottomRight.y - quadOrigin.y), (float) (-quadOrigin.z), + }; + } + } + + 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"); + if (!glDataBuffersGenerated) { + glGenBuffers(1, &vertexBuffer); + } + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices.size(), &vertices[0], GL_STATIC_DRAW); + + // enable vPosition attribs + glEnableVertexAttribArray(positionHandle); + glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindVertexArray(0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + mMatrixHandle = glGetUniformLocation(program, "umMatrix"); + originOffsetHandle = glGetUniformLocation(program, "uOriginOffset"); + uSubdivisionFactorHandle = glGetUniformLocation(program, "uSubdivisionFactor"); + + 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); +} +#include "Logger.h" +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(uSubdivisionFactorHandle, subdivisionFactor); + + 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..b0c91b8b4 --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h @@ -0,0 +1,115 @@ +/* + * 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 uSubdivisionFactorHandle; + int positionHandle; + 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; + + std::shared_ptr textureHolder; + int texturePointer; + std::optional textureFilterType = std::nullopt; + + 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; + 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/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..c8841483e 100644 --- a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp @@ -24,6 +24,7 @@ #include "IcosahedronColorShaderOpenGl.h" #include "SphereEffectShaderOpenGl.h" #include "SkySphereShaderOpenGl.h" +#include "TessellatedRasterShaderOpenGl.h" #include "ElevationInterpolationShaderOpenGl.h" std::shared_ptr ShaderFactoryOpenGl::createAlphaShader() { @@ -50,6 +51,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); } diff --git a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h index 367a55137..d89c463c8 100644 --- a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h +++ b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.h @@ -53,6 +53,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/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp new file mode 100644 index 000000000..a8028cc66 --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -0,0 +1,128 @@ +/* + * 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" + +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 evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); + + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glDeleteShader(vertexShader); + glAttachShader(program, controlShader); + glDeleteShader(controlShader); + glAttachShader(program, evalutationShader); + glDeleteShader(evalutationShader); + glAttachShader(program, fragmentShader); + glDeleteShader(fragmentShader); + glLinkProgram(program); + + checkGlProgramLinking(program); + + openGlContext->storeProgram(RasterShaderOpenGl::getProgramName(), program); +} + +std::string TessellatedRasterShaderOpenGl::getVertexShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + in vec4 vPosition; + in vec2 texCoordinate; + out vec2 c_texcoord; + + void main() { + gl_Position = vPosition; + c_texcoord = texCoordinate; + } + ); +} + +std::string TessellatedRasterShaderOpenGl::getControlShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout (vertices = 4) out; + + uniform int uSubdivisionFactor; + + in vec2 c_texcoord[]; + out vec2 e_texcoord[]; + + void main() + { + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + e_texcoord[gl_InvocationID] = c_texcoord[gl_InvocationID]; + + if (gl_InvocationID == 0) + { + float tessFactor = 16.0; //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, fractional_odd_spacing, cw) in; + + uniform mat4 umMatrix; + uniform vec4 uOriginOffset; + + in vec2 e_texcoord[]; + out vec2 v_texcoord; + + 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); + } + + 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); + gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * position) + uOriginOffset); + + vec2 t00 = e_texcoord[0]; + vec2 t01 = e_texcoord[1]; + vec2 t10 = e_texcoord[2]; + vec2 t11 = e_texcoord[3]; + + v_texcoord = bilerp(t00, t01, t10, t11, uv); + } + ); +} + 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..5223b4008 --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h @@ -0,0 +1,31 @@ +/* + * 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 "RasterShaderStyle.h" +#include + +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(); +}; + diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index a69b99e07..e4c842ffd 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -82,9 +82,10 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } - //if (maxSegmentLength) { - // PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); - //} + // Re-comment-out when implementing polygon tessellation on android ! + if (maxSegmentLength) { + PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); + } for (const auto& v : vecVertices) { @@ -103,12 +104,13 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, vertices.push_back(0.0f); #endif - vertices.push_back(v.x); - vertices.push_back(v.y); - vertices.push_back(0.0f); - #ifdef __APPLE__ - vertices.push_back(0.0f); - #endif + // Remove comment-out when implementing polygon tessellation on android ! + // vertices.push_back(v.x); + // vertices.push_back(v.y); + // vertices.push_back(0.0f); + //#ifdef __APPLE__ + // vertices.push_back(0.0f); + //#endif } auto attr = SharedBytes((int64_t)vertices.data(), (int32_t)vertices.size(), (int32_t)sizeof(float)); diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index 7ed4f5af2..e83d020cb 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -98,11 +98,14 @@ 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); - //} + + // Re-comment-out when implementing polygon tessellation on android ! + 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; @@ -111,8 +114,9 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); - //tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); - tileMask->setPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); + // Re-comment-out when implementing polygon tessellation on android ! + tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); + //tileMask->setPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); 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 ae0855e1f..420b043f9 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -106,11 +106,14 @@ 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); - //} + + // Re-comment-out when implementing polygon tessellation on android ! + 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; @@ -119,8 +122,9 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); - //tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); - tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); + // Re-comment-out when implementing polygon tessellation on android ! + tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); + //tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); newTileMasks[tileEntry->tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); } From bd43bade371e2aa08791773d0de9b6488817ed36 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 3 Dec 2025 11:43:30 +0100 Subject: [PATCH 08/24] Complete android quad tessellation --- .../objects/Quad2dTessellatedOpenGl.cpp | 53 ++++++++++-- .../objects/Quad2dTessellatedOpenGl.h | 5 +- .../shader/TessellatedRasterShaderOpenGl.cpp | 82 +++++++++++++++++-- .../shader/TessellatedRasterShaderOpenGl.h | 3 + 4 files changed, 128 insertions(+), 15 deletions(-) diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp index 602b0df19..f70750ebe 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -89,25 +89,52 @@ void Quad2dTessellatedOpenGl::computeGeometry(bool texCoordsOnly) { (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), + (float) (frame.topLeft.x), + (float) (frame.topLeft.y), (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), + (float) (frame.topRight.x), + (float) (frame.topRight.y), (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), + (float) (frame.bottomLeft.x), + (float) (frame.bottomLeft.y), (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), + (float) (frame.bottomRight.x), + (float) (frame.bottomRight.y), }; } else { vertices = { - (float) (frame.topLeft.x - quadOrigin.x), (float) (frame.topLeft.y - quadOrigin.y), (float) (-quadOrigin.z), - (float) (frame.topRight.x - quadOrigin.x), (float) (frame.topRight.y - quadOrigin.y), (float) (-quadOrigin.z), - (float) (frame.bottomLeft.x - quadOrigin.x), (float) (frame.bottomLeft.y - quadOrigin.y), (float) (-quadOrigin.z), - (float) (frame.bottomRight.x - quadOrigin.x), (float) (frame.bottomRight.y - quadOrigin.y), (float) (-quadOrigin.z), + (float) (frame.topLeft.x - quadOrigin.x), + (float) (frame.topLeft.y - quadOrigin.y), + (float) (-quadOrigin.z), + (float) (frame.topLeft.x), + (float) (frame.topLeft.y), + + (float) (frame.topRight.x - quadOrigin.x), + (float) (frame.topRight.y - quadOrigin.y), + (float) (-quadOrigin.z), + (float) (frame.topRight.x), + (float) (frame.topRight.y), + + (float) (frame.bottomLeft.x - quadOrigin.x), + (float) (frame.bottomLeft.y - quadOrigin.y), + (float) (-quadOrigin.z), + (float) (frame.bottomLeft.x), + (float) (frame.bottomLeft.y), + + (float) (frame.bottomRight.x - quadOrigin.x), + (float) (frame.bottomRight.y - quadOrigin.y), + (float) (-quadOrigin.z), + (float) (frame.bottomRight.x), + (float) (frame.bottomRight.y), }; } } @@ -129,15 +156,19 @@ void Quad2dTessellatedOpenGl::prepareGlData(int program) { glBindVertexArray(vao); positionHandle = glGetAttribLocation(program, "vPosition"); + flatPositionHandle = glGetAttribLocation(program, "vFlatPosition"); + if (!glDataBuffersGenerated) { glGenBuffers(1, &vertexBuffer); } glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertices.size(), &vertices[0], GL_STATIC_DRAW); - // enable vPosition attribs + size_t stride = sizeof(GLfloat) * 5; glEnableVertexAttribArray(positionHandle); - glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 0, nullptr); + glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, stride, nullptr); + glEnableVertexAttribArray(flatPositionHandle); + glVertexAttribPointer(flatPositionHandle, 2, GL_FLOAT, false, stride, (float*)(sizeof(GLfloat) * 3)); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -147,7 +178,9 @@ void Quad2dTessellatedOpenGl::prepareGlData(int program) { mMatrixHandle = glGetUniformLocation(program, "umMatrix"); originOffsetHandle = glGetUniformLocation(program, "uOriginOffset"); - uSubdivisionFactorHandle = glGetUniformLocation(program, "uSubdivisionFactor"); + subdivisionFactorHandle = glGetUniformLocation(program, "uSubdivisionFactor"); + originHandle = glGetUniformLocation(program, "uOrigin"); + is3dHandle = glGetUniformLocation(program, "uIs3d"); glDataBuffersGenerated = true; } @@ -286,7 +319,11 @@ void Quad2dTessellatedOpenGl::render(const std::shared_ptr<::RenderingContextInt glPatchParameteri(GL_PATCH_VERTICES, 4); - glUniform1i(uSubdivisionFactorHandle, subdivisionFactor); + glUniform1i(subdivisionFactorHandle, subdivisionFactor); + + glUniform4f(originHandle, origin.x, origin.y, origin.z, 0.0); + + glUniform1i(is3dHandle, is3d); glDrawArrays(GL_PATCHES, 0, 4); diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h index b0c91b8b4..45807b790 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h @@ -83,8 +83,11 @@ class Quad2dTessellatedOpenGl : public GraphicsObjectInterface, bool texCoordBufferGenerated = false; int mMatrixHandle; int originOffsetHandle; - int uSubdivisionFactorHandle; + int subdivisionFactorHandle; + int originHandle; + int is3dHandle; int positionHandle; + int flatPositionHandle; GLuint vao; GLuint vertexBuffer; std::vector vertices; diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index a8028cc66..2f62a06b0 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -21,6 +21,10 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi int vertexShader = loadShader(GL_VERTEX_SHADER, getVertexShader()); int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); int evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + + /* WIREFRAME DEBUG */ + //int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); + int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); GLuint program = glCreateProgram(); @@ -32,6 +36,11 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi glDeleteShader(evalutationShader); glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); + + /* WIREFRAME DEBUG */ + //glAttachShader(program, geometryShader); + //glDeleteShader(geometryShader); + glLinkProgram(program); checkGlProgramLinking(program); @@ -42,11 +51,14 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi std::string TessellatedRasterShaderOpenGl::getVertexShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, in vec4 vPosition; + in vec2 vFlatPosition; in vec2 texCoordinate; + out vec2 c_flatposition; out vec2 c_texcoord; void main() { gl_Position = vPosition; + c_flatposition = vFlatPosition; c_texcoord = texCoordinate; } ); @@ -58,17 +70,20 @@ std::string TessellatedRasterShaderOpenGl::getControlShader() { uniform int uSubdivisionFactor; + in vec2 c_flatposition[]; in vec2 c_texcoord[]; + out vec2 e_flatposition[]; out vec2 e_texcoord[]; void main() { gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + e_flatposition[gl_InvocationID] = c_flatposition[gl_InvocationID]; e_texcoord[gl_InvocationID] = c_texcoord[gl_InvocationID]; if (gl_InvocationID == 0) { - float tessFactor = 16.0; //float(uSubdivisionFactor); + float tessFactor = pow(2.0, float(uSubdivisionFactor)); gl_TessLevelOuter[0] = tessFactor; gl_TessLevelOuter[1] = tessFactor; @@ -82,16 +97,21 @@ std::string TessellatedRasterShaderOpenGl::getControlShader() { ); } - std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(quads, fractional_odd_spacing, cw) in; uniform mat4 umMatrix; uniform vec4 uOriginOffset; + uniform vec4 uOrigin; + uniform bool uIs3d; + in vec2 e_flatposition[]; in vec2 e_texcoord[]; - out vec2 v_texcoord; + out vec2 v_texcoord; // out vec2 g_texcoord; /* WIREFRAME DEBUG */ + + 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); @@ -105,6 +125,13 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { 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; @@ -112,17 +139,60 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { 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); - gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * position) + uOriginOffset); + + vec2 f00 = e_flatposition[0]; + vec2 f01 = e_flatposition[1]; + vec2 f10 = e_flatposition[2]; + vec2 f11 = e_flatposition[3]; + vec2 flatPosition = bilerp(f00, f01, f10, f11, uv); 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); + + if (uIs3d) { + vec4 bent = transform(flatPosition, uOrigin) - uOriginOffset; + float blend = clamp(length(uOriginOffset) * BlendScale - BlendOffset, 0.0, 1.0); + position = mix(position, bent, blend); + + //vec4 normal = normalize(transform(flatPosition, vec4(0.0, 0.0, 0.0, 0.0))); + //position += normal * 0.1 * sin(flatPosition.x * 10000.0) * sin(flatPosition.y * 10000.0); + } - v_texcoord = bilerp(t00, t01, t10, t11, uv); + gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vec4(position.xyz, 1.0)) + uOriginOffset); + v_texcoord = texCoord; // g_texcoord = texCoord; /* WIREFRAME DEBUG */ } ); } +/* WIREFRAME DEBUG */ +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); + } + ); +} diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h index 5223b4008..f5fda067a 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h @@ -27,5 +27,8 @@ class TessellatedRasterShaderOpenGl : public RasterShaderOpenGl { virtual std::string getControlShader(); virtual std::string getEvaluationShader(); + + /* WIREFRAME DEBUG */ + virtual std::string getGeometryShader(); }; From c05337f9218ed248070b61c17f6cdb965295bbca Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 4 Dec 2025 11:15:54 +0100 Subject: [PATCH 09/24] Complete and improve polygon tessellation --- .../objects/GraphicsObjectFactoryOpenGl.cpp | 13 +- .../objects/Polygon2dTessellatedOpenGl.cpp | 202 ++++++++++++++++++ .../objects/Polygon2dTessellatedOpenGl.h | 89 ++++++++ .../objects/Quad2dTessellatedOpenGl.cpp | 2 +- .../cpp/graphics/shader/ColorShaderOpenGl.cpp | 39 ++-- .../cpp/graphics/shader/ColorShaderOpenGl.h | 1 + .../shader/TessellatedColorShaderOpenGl.cpp | 167 +++++++++++++++ .../shader/TessellatedColorShaderOpenGl.h | 32 +++ .../shader/TessellatedRasterShaderOpenGl.cpp | 4 +- .../shader/TessellatedRasterShaderOpenGl.h | 2 - .../Model/Polygon/Polygon2dTessellated.swift | 10 +- .../Model/Quad/Quad2dTessellated.swift | 14 +- ios/graphics/Model/TessellatedVertex3D.swift | 4 +- .../Model/TessellatedVertex3DTexture.swift | 18 +- .../Shader/Metal/DataStructures.metal | 4 +- .../Shader/Metal/TessellatedShader.metal | 8 +- .../map/layers/objects/PolygonMaskObject.cpp | 16 +- ...2dMapVectorSourceRasterTileDataManager.cpp | 16 +- ...2dMapVectorSourceVectorTileDataManager.cpp | 16 +- 19 files changed, 573 insertions(+), 84 deletions(-) create mode 100644 android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp create mode 100644 android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h create mode 100644 android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp create mode 100644 android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp index ada2bac0a..071ab36be 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp @@ -21,6 +21,8 @@ #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)); @@ -35,11 +37,9 @@ GraphicsObjectFactoryOpenGl::createPolygon(const std::shared_ptr<::ShaderProgram return std::make_shared(enforceGlShader(shader)); } -// TODO std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) { - return std::make_shared(enforceGlShader(shader)); - //return std::make_shared(enforceGlShader(shader)); + return std::make_shared(enforceGlShader(shader), false); // ? } std::shared_ptr @@ -68,13 +68,10 @@ std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMa return std::make_shared(enforceGlShader(shader)); } -// TODO std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMaskTessellated(bool is3D) { - std::shared_ptr shader = std::make_shared(is3D); - //different shader! tessellated! + std::shared_ptr shader = std::make_shared(is3D); shader->setColor(1, 1, 1, 1); - return std::make_shared(enforceGlShader(shader)); - //return std::make_shared(enforceGlShader(shader)); + return std::make_shared(enforceGlShader(shader), is3D); } std::shared_ptr GraphicsObjectFactoryOpenGl::createText(const std::shared_ptr<::ShaderProgramInterface> &shader) { 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..3623db984 --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -0,0 +1,202 @@ +/* + * 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, bool is3D) + : shaderProgram(shader), is3d(is3D) {} + +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::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, int32_t subdivisionFactor) { + std::lock_guard lock(dataMutex); + ready = false; + dataReady = false; + + indices.resize(indices_.elementCount); + vertices.resize(vertices_.elementCount); + polygonOrigin = origin; + + this->subdivisionFactor = subdivisionFactor; + + 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"); + flatPositionHandle = glGetAttribLocation(program, "vFlatPosition"); + 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(flatPositionHandle); + glVertexAttribPointer(flatPositionHandle, 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, 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..8fc034dc6 --- /dev/null +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h @@ -0,0 +1,89 @@ + /* + * 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, bool is3D); + + ~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; + + virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin, int32_t subdivision) 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 flatPositionHandle{}; + 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/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp index f70750ebe..fb63f45fd 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -319,7 +319,7 @@ void Quad2dTessellatedOpenGl::render(const std::shared_ptr<::RenderingContextInt glPatchParameteri(GL_PATCH_VERTICES, 4); - glUniform1i(subdivisionFactorHandle, subdivisionFactor); + glUniform1i(subdivisionFactorHandle, 1 << subdivisionFactor); glUniform4f(originHandle, origin.x, origin.y, origin.z, 0.0); 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/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp new file mode 100644 index 000000000..d3afb5fe7 --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -0,0 +1,167 @@ +/* + * 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" + +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 evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + + /* WIREFRAME DEBUG */ + //int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); + + int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); + + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glDeleteShader(vertexShader); + glAttachShader(program, controlShader); + glDeleteShader(controlShader); + glAttachShader(program, evalutationShader); + glDeleteShader(evalutationShader); + glAttachShader(program, fragmentShader); + glDeleteShader(fragmentShader); + + /* WIREFRAME DEBUG */ + //glAttachShader(program, geometryShader); + //glDeleteShader(geometryShader); + + glLinkProgram(program); + + checkGlProgramLinking(program); + + openGlContext->storeProgram(TessellatedColorShaderOpenGl::getProgramName(), program); +} + +std::string TessellatedColorShaderOpenGl::getVertexShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + in vec4 vPosition; + in vec2 vFlatPosition; + out vec2 c_flatposition; + + void main() { + gl_Position = vPosition; + c_flatposition = vFlatPosition; + } + ); +} + +std::string TessellatedColorShaderOpenGl::getControlShader() { + return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, + layout (vertices = 3) out; + + uniform int uSubdivisionFactor; + + in vec2 c_flatposition[]; + out vec2 e_flatposition[]; + + void main() + { + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + e_flatposition[gl_InvocationID] = c_flatposition[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_flatposition[]; + + 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); + + vec2 f0 = e_flatposition[0]; + vec2 f1 = e_flatposition[1]; + vec2 f2 = e_flatposition[2]; + vec2 flatPosition = baryinterp(f0, f1, f2, bary); + + if (uIs3d) { + vec4 bent = transform(flatPosition, 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); + } + ); +} + +/* WIREFRAME DEBUG */ +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); + } + ); +} 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..9b191c2de --- /dev/null +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h @@ -0,0 +1,32 @@ +/* + * 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" + +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(); + + /* WIREFRAME DEBUG */ + virtual std::string getGeometryShader(); +}; + diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index 2f62a06b0..48509d343 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -83,7 +83,7 @@ std::string TessellatedRasterShaderOpenGl::getControlShader() { if (gl_InvocationID == 0) { - float tessFactor = pow(2.0, float(uSubdivisionFactor)); + float tessFactor = float(uSubdivisionFactor); gl_TessLevelOuter[0] = tessFactor; gl_TessLevelOuter[1] = tessFactor; @@ -99,7 +99,7 @@ std::string TessellatedRasterShaderOpenGl::getControlShader() { std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, - layout(quads, fractional_odd_spacing, cw) in; + layout(quads, equal_spacing, cw) in; uniform mat4 umMatrix; uniform vec4 uOriginOffset; diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h index f5fda067a..6a2de497b 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h @@ -12,8 +12,6 @@ #include "RasterShaderOpenGl.h" #include "RenderingContextInterface.h" -#include "RasterShaderStyle.h" -#include class TessellatedRasterShaderOpenGl : public RasterShaderOpenGl { public: diff --git a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift index 0a21dc8b8..7dbf31db1 100644 --- a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -31,7 +31,7 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { init(shader: MCShaderProgramInterface, metalContext: MetalContext, is3d: Bool) { self.shader = shader originBuffers = .init(device: metalContext.device) - self.is3d = is3d + self.is3d = is3d // move is3d to set vertices or make own setter? if so also make own subdivision setter super .init( device: metalContext.device, @@ -153,6 +153,9 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { encoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0) + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.lines) + encoder.drawIndexedPatches( numberOfPatchControlPoints: 3, patchStart: 0, @@ -163,6 +166,9 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { controlPointIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0) + + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.fill) } private func setupStencilStates() { @@ -228,7 +234,7 @@ extension Polygon2dTessellated: MCMaskingObjectInterface { extension Polygon2dTessellated: MCPolygon2dInterface { func setVertices( - _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, subdivisionFactor: Int32 + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, subdivisionFactor: Int32 // move is3d and subdivisonfactor to own setter? ) { let factor = Half(subdivisionFactor).bits; diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index 421347cd9..f2faac7a9 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -279,7 +279,7 @@ extension Quad2dTessellated: MCQuad2dInterface { func setFrame( _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, - is3d: Bool + is3d: Bool // maybe move subdivision factor also here? or both own setter? same as in polygon 2d tessellation ) { let factor = Half(pow(2, Float(lock.withCritical { subdivisionFactor }))).bits; @@ -314,22 +314,26 @@ extension Quad2dTessellated: MCQuad2dInterface { vertices = [ TessellatedVertex3DTexture( relativePosition: transform(frame.topLeft), - absolutePosition: frame.topLeft, + absolutePositionX: frame.topLeft.xF, + absolutePositionY: frame.topLeft.yF, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF), // B TessellatedVertex3DTexture( relativePosition: transform(frame.topRight), - absolutePosition: frame.topRight, + absolutePositionX: frame.topRight.xF, + absolutePositionY: frame.topRight.yF, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF), // C TessellatedVertex3DTexture( relativePosition: transform(frame.bottomLeft), - absolutePosition: frame.bottomLeft, + absolutePositionX: frame.bottomLeft.xF, + absolutePositionY: frame.bottomLeft.yF, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // A TessellatedVertex3DTexture( relativePosition: transform(frame.bottomRight), - absolutePosition: frame.bottomRight, + absolutePositionX: frame.bottomRight.xF, + absolutePositionY: frame.bottomRight.yF, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // D ] diff --git a/ios/graphics/Model/TessellatedVertex3D.swift b/ios/graphics/Model/TessellatedVertex3D.swift index c591e57b1..ecd78f429 100644 --- a/ios/graphics/Model/TessellatedVertex3D.swift +++ b/ios/graphics/Model/TessellatedVertex3D.swift @@ -25,9 +25,9 @@ struct TessellatedVertex4F: Equatable { // Absolute Position vertexDescriptor.attributes[1].bufferIndex = bufferIndex - vertexDescriptor.attributes[1].format = .float4 + vertexDescriptor.attributes[1].format = .float2 vertexDescriptor.attributes[1].offset = offset - offset += MemoryLayout>.stride + offset += MemoryLayout>.stride vertexDescriptor.layouts[0].stride = offset vertexDescriptor.layouts[0].stepRate = 1 diff --git a/ios/graphics/Model/TessellatedVertex3DTexture.swift b/ios/graphics/Model/TessellatedVertex3DTexture.swift index d584e081c..4c1640df3 100644 --- a/ios/graphics/Model/TessellatedVertex3DTexture.swift +++ b/ios/graphics/Model/TessellatedVertex3DTexture.swift @@ -16,7 +16,7 @@ public struct TessellatedVertex3DTexture: Equatable { /// The 3D position of the vertex in the plane var relativePosition: SIMD3 /// The 3D position of the vertex in the world - var absolutePosition: SIMD3 + var absolutePosition: 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 @@ -33,9 +33,9 @@ public struct TessellatedVertex3DTexture: Equatable { // Absolute Position vertexDescriptor.attributes[1].bufferIndex = bufferIndex - vertexDescriptor.attributes[1].format = .float3 + vertexDescriptor.attributes[1].format = .float2 vertexDescriptor.attributes[1].offset = offset - offset += MemoryLayout>.stride + offset += MemoryLayout>.stride // UV vertexDescriptor.attributes[2].bufferIndex = bufferIndex @@ -61,18 +61,18 @@ public struct TessellatedVertex3DTexture: Equatable { /// - textureV: The texture V-coordinate mapping public init( relativeX: Float, relativeY: Float, relativeZ: Float, - absoluteX: Float, absoluteY: Float, absoluteZ: Float, + absoluteX: Float, absoluteY: Float, textureU: Float, textureV: Float, ) { relativePosition = SIMD3([relativeX, relativeY, relativeZ]) - absolutePosition = SIMD3([absoluteX, absoluteY, absoluteZ]) + absolutePosition = SIMD2([absoluteX, absoluteY]) textureCoordinate = SIMD2([textureU, textureV]) } - public init(relativePosition: MCVec3D, absolutePosition: MCVec3D, textureU: Float, textureV: Float) { + public init(relativePosition: MCVec3D, absolutePositionX: Float, absolutePositionY: Float, textureU: Float, textureV: Float) { self.init( relativeX: relativePosition.xF, relativeY: relativePosition.yF, relativeZ: relativePosition.zF, - absoluteX: absolutePosition.xF, absoluteY: absolutePosition.yF, absoluteZ: absolutePosition.zF, + absoluteX: absolutePositionX, absoluteY: absolutePositionY, textureU: textureU, textureV: textureV, ) } @@ -89,8 +89,6 @@ extension TessellatedVertex3DTexture { var absoluteX: Float { absolutePosition.x } /// The Y-coordinate position in the world var absoluteY: Float { absolutePosition.y } - /// The Z-coordinate position in the world - var absoluteZ: Float { absolutePosition.z } /// The texture U-coordinate mapping var textureU: Float { textureCoordinate.x } /// The texture V-coordinate mapping @@ -99,6 +97,6 @@ extension TessellatedVertex3DTexture { extension TessellatedVertex3DTexture: CustomDebugStringConvertible { public var debugDescription: String { - "" + "" } } diff --git a/ios/graphics/Shader/Metal/DataStructures.metal b/ios/graphics/Shader/Metal/DataStructures.metal index 2fac730d8..9d384cc49 100644 --- a/ios/graphics/Shader/Metal/DataStructures.metal +++ b/ios/graphics/Shader/Metal/DataStructures.metal @@ -23,7 +23,7 @@ struct Vertex3DTextureIn { struct TessellatedVertex3DTextureIn { float4 relativePosition [[attribute(0)]]; - float4 absolutePosition [[attribute(1)]]; + float2 absolutePosition [[attribute(1)]]; float2 uv [[attribute(2)]]; }; @@ -35,7 +35,7 @@ struct Vertex4FIn { struct TessellatedVertex4FIn { float4 relativePosition [[attribute(0)]]; - float4 absolutePosition [[attribute(1)]]; + float2 absolutePosition [[attribute(1)]]; }; struct VertexOut { diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 983f2cebe..76b312387 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -108,12 +108,12 @@ quadTessellationVertexShader(const patch_control_point TessellatedVertex4FIn vC = controlPoints[2]; float4 vertexRelativePosition = baryinterp(vA.relativePosition, vB.relativePosition, vC.relativePosition, positionInPatch); - float4 vertexAbsolutePosition = baryinterp(vA.absolutePosition, vB.absolutePosition, vC.absolutePosition, positionInPatch); + float2 vertexAbsolutePosition = baryinterp(vA.absolutePosition, vB.absolutePosition, vC.absolutePosition, positionInPatch); float4 position = vertexRelativePosition; if (is3d) { - float4 bent = transform(vertexAbsolutePosition.xy, origin) - originOffset; + float4 bent = transform(vertexAbsolutePosition, origin) - originOffset; float blend = saturate(length(originOffset) * BlendScale - BlendOffset); position = mix(position, bent, blend); } diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index e4c842ffd..03e7120d2 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -82,10 +82,9 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } - // Re-comment-out when implementing polygon tessellation on android ! - if (maxSegmentLength) { - PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); - } + //if (maxSegmentLength) { + // PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); + //} for (const auto& v : vecVertices) { @@ -104,13 +103,8 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, vertices.push_back(0.0f); #endif - // Remove comment-out when implementing polygon tessellation on android ! - // vertices.push_back(v.x); - // vertices.push_back(v.y); - // vertices.push_back(0.0f); - //#ifdef __APPLE__ - // vertices.push_back(0.0f); - //#endif + vertices.push_back(v.x); + vertices.push_back(v.y); } auto attr = SharedBytes((int64_t)vertices.data(), (int32_t)vertices.size(), (int32_t)sizeof(float)); diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index e83d020cb..132c020e7 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -99,12 +99,11 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std is3D); auto convertedTileBounds = mapInterface->getCoordinateConverterHelper()->convertRectToRenderSystem(tileEntry.tileInfo.tileInfo.bounds); - // Re-comment-out when implementing polygon tessellation on android ! - 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); - } + //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; @@ -114,9 +113,8 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); - // Re-comment-out when implementing polygon tessellation on android ! - tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); - //tileMask->setPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); + //tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); + tileMask->setPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); 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 420b043f9..4797523e3 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -107,12 +107,11 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std is3D); auto convertedTileBounds = coordinateConverterHelper->convertRectToRenderSystem(tileEntry->tileInfo.tileInfo.bounds); - // Re-comment-out when implementing polygon tessellation on android ! - 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); - } + //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; @@ -122,9 +121,8 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); - // Re-comment-out when implementing polygon tessellation on android ! - tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); - //tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); + //tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); + tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); newTileMasks[tileEntry->tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); } From f7ce25090c399b855a8068b6dc6d4977e0b7dc35 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 4 Dec 2025 14:23:30 +0100 Subject: [PATCH 10/24] Begin clean up --- shared/public/PolygonMaskObject.h | 4 ++-- .../map/layers/objects/PolygonMaskObject.cpp | 18 +++++------------- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 11 +---------- ...d2dMapVectorSourceRasterTileDataManager.cpp | 7 ------- ...d2dMapVectorSourceVectorTileDataManager.cpp | 7 ------- .../raster/Tiled2dMapVectorRasterTile.cpp | 10 +--------- 6 files changed, 9 insertions(+), 48 deletions(-) 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/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index 03e7120d2..7a1d5fa36 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -24,8 +24,7 @@ PolygonMaskObject::PolygonMaskObject(const std::shared_ptr &conversionHelper, bool is3D) : conversionHelper(conversionHelper) - , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) - //, polygon(graphicsObjectFactory->createPolygonMask(is3D)) + , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) //is3d here? , is3D(is3D) {} void PolygonMaskObject::setPositions(const std::vector &positions, @@ -35,13 +34,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; @@ -81,11 +80,6 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } } - - //if (maxSegmentLength) { - // PolygonHelper::subdivision(vecVertices, indices, *maxSegmentLength); - //} - for (const auto& v : vecVertices) { double rx = origin.x; @@ -102,16 +96,14 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, #ifdef __APPLE__ vertices.push_back(0.0f); #endif - vertices.push_back(v.x); vertices.push_back(v.y); } 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)); - int32_t subdivisionFactor = (int32_t)(maxSegmentLength.value_or(1.0f)); - polygon->setVertices(attr, ind, origin, subdivisionFactor); + polygon->setVertices(attr, ind, origin, (int32_t)(subdivisionFactor.value_or(1.0f))); } std::shared_ptr PolygonMaskObject::getPolygonObject() { return polygon; } diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index eeaa6dca9..65be4cf57 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -302,18 +302,9 @@ std::vector sortedTileInfos(currentTileInfos.begin(), quad->setMinMagFilter(textureFilterType); tileObject = std::make_shared(quad, mapInterface, is3D); } else { - /* - // deprecated? (createUnitSphereRasterShader and createRasterShader both return RasterShader) - auto rShader = is3D ? shaderFactory->createUnitSphereRasterShader() : shaderFactory->createRasterShader(); - rShader->asShaderProgramInterface()->setBlendMode(blendMode); - auto quad = graphicsFactory->createQuad(rShader->asShaderProgramInterface()); - */ - // /* auto rShader = shaderFactory->createQuadTessellatedShader(); rShader->asShaderProgramInterface()->setBlendMode(blendMode); auto quad = graphicsFactory->createQuadTessellated(rShader->asShaderProgramInterface()); - // */ - quad->setMinMagFilter(textureFilterType); tileObject = std::make_shared( quad, rShader, mapInterface, is3D); @@ -327,7 +318,7 @@ std::vector sortedTileInfos(currentTileInfos.begin(), } } if (is3D) { - tileObject->getQuadObject()->setSubdivisionFactor( + tileObject->getQuadObject()->setSubdivisionFactor( //todo here! std::clamp(subdivisionFactor + tile.tessellationFactor, 0, 5)); } tileObject->setRectCoord(tile.tileInfo.tileInfo.bounds); diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index 132c020e7..60ed43827 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -99,12 +99,6 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std 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; @@ -113,7 +107,6 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); - //tileMask->setPolygons(tileEntry.masks, origin, maxSegmentLength); tileMask->setPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); 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 4797523e3..f0d55a33e 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -107,12 +107,6 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std 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; @@ -121,7 +115,6 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); - //tileMask->setPolygons(tileEntry->masks, origin, maxSegmentLength); tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); newTileMasks[tileEntry->tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); 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 9a919c8ce..662aaefc4 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -28,23 +28,15 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptris3d() ? 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()); - */ - // /* auto shader = pMapInterface->getShaderFactory()->createQuadTessellatedShader(); shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); - // */ #if DEBUG quad->asGraphicsObject()->setDebugLabel(description->identifier + "_" + tileInfo.tileInfo.to_string_short()); #endif tileObject = std::make_shared(quad, shader, pMapInterface, pMapInterface->is3d()); tileObject->setRectCoord(tileInfo.tileInfo.bounds); - + if (pMapInterface->is3d()) { quad->setSubdivisionFactor(std::clamp(subdivisionFactor + tileInfo.tileInfo.tessellationFactor, 0, 5)); } From 904f23fda55aa77b2c8d8fef54c3b9663df41374 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 10 Dec 2025 08:52:18 +0100 Subject: [PATCH 11/24] Remove elevation test and fix 2d subdivision --- .../shader/TessellatedRasterShaderOpenGl.cpp | 3 - .../Model/Quad/Quad2dTessellated.swift | 18 ++---- .../Shader/Metal/TessellatedShader.metal | 63 ------------------- ...2dMapVectorSourceRasterTileDataManager.cpp | 8 ++- ...2dMapVectorSourceVectorTileDataManager.cpp | 8 ++- 5 files changed, 18 insertions(+), 82 deletions(-) diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index 48509d343..f0a3d42a9 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -157,9 +157,6 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { vec4 bent = transform(flatPosition, uOrigin) - uOriginOffset; float blend = clamp(length(uOriginOffset) * BlendScale - BlendOffset, 0.0, 1.0); position = mix(position, bent, blend); - - //vec4 normal = normalize(transform(flatPosition, vec4(0.0, 0.0, 0.0, 0.0))); - //position += normal * 0.1 * sin(flatPosition.x * 10000.0) * sin(flatPosition.y * 10000.0); } gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vec4(position.xyz, 1.0)) + uOriginOffset); diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index f2faac7a9..9a536ec1d 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -189,22 +189,13 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { } encoder.setVertexBuffer(originBuffer, offset: 0, index: 4) - /* ELEVATION PROTOTYPE TEST - if samplerToUse == .magNearest { - encoder.setVertexSamplerState(nearestSampler, index: 0) - } else { - encoder.setVertexSamplerState(sampler, index: 0) - } - - if let texture { - encoder.setVertexTexture(texture, index: 0) - } - */ - 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, @@ -213,6 +204,9 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { patchIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0) + + /* WIREFRAME DEBUG */ + //encoder.setTriangleFillMode(.fill) } } diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 76b312387..09670da0a 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -12,62 +12,6 @@ #include "DataStructures.metal" using namespace metal; -// ELEVATION PROTOTYPE TEST -/** - * Decode elevation from RGBA encoded altitude texture - * This matches the existing decoding logic in the shader - */ -/* -float decodeElevation(float4 rgbaAltitude) { - return (rgbaAltitude.r * 256.0 * 256.0 * 255.0 + - rgbaAltitude.g * 256.0 * 255.0 + - rgbaAltitude.b * 255.0) / 10.0 - 10000.0; -} -*/ - -/** - * Sample elevation with linear interpolation - * This function first samples the four neighboring pixels, decodes each elevation value, - * then performs bilinear interpolation on the decoded values - * - * @param altitudeTexture The altitude texture with encoded elevation data - * @param uv The texture coordinates to sample - * @param linearSampler A linear sampler for texture sampling - * @return The linearly interpolated elevation value - */ -/* -float sampleLinearElevation(texture2d altitudeTexture, float2 uv, sampler linearSampler) { - // Get texture dimensions - float2 texSize = float2(altitudeTexture.get_width(), altitudeTexture.get_height()); - - // Convert UV to pixel coordinates - float2 pixelCoord = uv * texSize - 0.5; - float2 pixelFloor = floor(pixelCoord); - float2 pixelFrac = pixelCoord - pixelFloor; - - // Calculate UV coordinates for the four neighboring pixels - float2 texelSize = 1.0 / texSize; - float2 uv00 = (pixelFloor + float2(0.0, 0.0)) * texelSize; - float2 uv10 = (pixelFloor + float2(1.0, 0.0)) * texelSize; - float2 uv01 = (pixelFloor + float2(0.0, 1.0)) * texelSize; - float2 uv11 = (pixelFloor + float2(1.0, 1.0)) * texelSize; - - // Sample and decode the four neighboring pixels - // Use nearest sampling to get the raw encoded values - constexpr sampler nearestSampler(coord::normalized, address::clamp_to_edge, filter::nearest); - - float elevation00 = decodeElevation(altitudeTexture.sample(nearestSampler, uv00)); - float elevation10 = decodeElevation(altitudeTexture.sample(nearestSampler, uv10)); - float elevation01 = decodeElevation(altitudeTexture.sample(nearestSampler, uv01)); - float elevation11 = decodeElevation(altitudeTexture.sample(nearestSampler, uv11)); - - // Perform bilinear interpolation on the decoded elevation values - float elevation0 = mix(elevation00, elevation10, pixelFrac.x); - float elevation1 = mix(elevation01, elevation11, pixelFrac.x); - return mix(elevation0, elevation1, pixelFrac.y); -} -*/ - const constant float BlendScale = 1000; const constant float BlendOffset = 0.01; @@ -97,9 +41,6 @@ quadTessellationVertexShader(const patch_control_point texture0 [[ texture(0)]], - sampler sampler0 [[sampler(0)]], */ constant bool &is3d [[buffer(5)]]) { TessellatedVertex3DTextureIn vA = controlPoints[0]; @@ -116,10 +57,6 @@ quadTessellationVertexShader(const patch_control_pointsetPolygons(tileEntry.masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); + + std::optional subdivisionFactor = + is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) + : std::nullopt; + + tileMask->setPolygons(tileEntry.masks, origin, subdivisionFactor); 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 f0d55a33e..f2d5f72d0 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -114,8 +114,12 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std double rz = is3D ? -1.0 * sin(cy) * sin(cx) : 0.0; Vec3D origin(rx, ry, rz); - - tileMask->setPolygons(tileEntry->masks, origin, float(POLYGON_MASK_SUBDIVISION_FACTOR)); + + std::optional subdivisionFactor = + is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) + : std::nullopt; + + tileMask->setPolygons(tileEntry->masks, origin, subdivisionFactor); newTileMasks[tileEntry->tileInfo] = Tiled2dMapLayerMaskWrapper(tileMask, hash); } From 999af35d8be8e1a158614996ddf9601555b20ecb Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 10 Dec 2025 10:35:03 +0100 Subject: [PATCH 12/24] Refactor vertex structs and properties --- .../objects/Polygon2dTessellatedOpenGl.cpp | 6 +-- .../objects/Polygon2dTessellatedOpenGl.h | 2 +- .../objects/Quad2dTessellatedOpenGl.cpp | 6 +-- .../objects/Quad2dTessellatedOpenGl.h | 2 +- .../shader/TessellatedColorShaderOpenGl.cpp | 24 +++++----- .../shader/TessellatedRasterShaderOpenGl.cpp | 26 +++++------ .../Model/Quad/Quad2dTessellated.swift | 36 +++++++-------- ios/graphics/Model/Vertex3D.swift | 3 +- ...rtex3D.swift => Vertex3DTessellated.swift} | 6 +-- ...swift => Vertex3DTextureTessellated.swift} | 44 +++++++++---------- ios/graphics/Pipelines/PipelineLibrary.swift | 8 ++-- ios/graphics/Shader/Metal/BaseShader.metal | 2 +- .../Shader/Metal/DataStructures.metal | 24 +++++----- .../Shader/Metal/PolygonGroupShader.metal | 6 +-- .../Shader/Metal/TessellatedShader.metal | 34 +++++++------- 15 files changed, 115 insertions(+), 114 deletions(-) rename ios/graphics/Model/{TessellatedVertex3D.swift => Vertex3DTessellated.swift} (90%) rename ios/graphics/Model/{TessellatedVertex3DTexture.swift => Vertex3DTextureTessellated.swift} (68%) diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp index 3623db984..fe1e26d4d 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -70,7 +70,7 @@ void Polygon2dTessellatedOpenGl::prepareGlData(int program) { glBindVertexArray(vao); positionHandle = glGetAttribLocation(program, "vPosition"); - flatPositionHandle = glGetAttribLocation(program, "vFlatPosition"); + frameCoordHandle = glGetAttribLocation(program, "vFrameCoord"); if (!glDataBuffersGenerated) { glGenBuffers(1, &vertexBuffer); } @@ -80,8 +80,8 @@ void Polygon2dTessellatedOpenGl::prepareGlData(int program) { size_t stride = sizeof(GLfloat) * 5; glEnableVertexAttribArray(positionHandle); glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, stride, nullptr); - glEnableVertexAttribArray(flatPositionHandle); - glVertexAttribPointer(flatPositionHandle, 2, GL_FLOAT, false, stride, (float*)(sizeof(GLfloat) * 3)); + glEnableVertexAttribArray(frameCoordHandle); + glVertexAttribPointer(frameCoordHandle, 2, GL_FLOAT, false, stride, (float*)(sizeof(GLfloat) * 3)); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h index 8fc034dc6..560f13d8d 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h @@ -70,7 +70,7 @@ class Polygon2dTessellatedOpenGl : public GraphicsObjectInterface, int originHandle{}; int is3dHandle{}; int positionHandle{}; - int flatPositionHandle{}; + int frameCoordHandle{}; GLuint vao{}; GLuint vertexBuffer{}; std::vector vertices; diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp index fb63f45fd..18570a405 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -156,7 +156,7 @@ void Quad2dTessellatedOpenGl::prepareGlData(int program) { glBindVertexArray(vao); positionHandle = glGetAttribLocation(program, "vPosition"); - flatPositionHandle = glGetAttribLocation(program, "vFlatPosition"); + frameCoordHandle = glGetAttribLocation(program, "vFrameCoord"); if (!glDataBuffersGenerated) { glGenBuffers(1, &vertexBuffer); @@ -167,8 +167,8 @@ void Quad2dTessellatedOpenGl::prepareGlData(int program) { size_t stride = sizeof(GLfloat) * 5; glEnableVertexAttribArray(positionHandle); glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, stride, nullptr); - glEnableVertexAttribArray(flatPositionHandle); - glVertexAttribPointer(flatPositionHandle, 2, GL_FLOAT, false, stride, (float*)(sizeof(GLfloat) * 3)); + glEnableVertexAttribArray(frameCoordHandle); + glVertexAttribPointer(frameCoordHandle, 2, GL_FLOAT, false, stride, (float*)(sizeof(GLfloat) * 3)); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h index 45807b790..21b42f30c 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h @@ -87,7 +87,7 @@ class Quad2dTessellatedOpenGl : public GraphicsObjectInterface, int originHandle; int is3dHandle; int positionHandle; - int flatPositionHandle; + int frameCoordHandle; GLuint vao; GLuint vertexBuffer; std::vector vertices; diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp index d3afb5fe7..94d61c354 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -51,12 +51,12 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin std::string TessellatedColorShaderOpenGl::getVertexShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, in vec4 vPosition; - in vec2 vFlatPosition; - out vec2 c_flatposition; + in vec2 vFrameCoord; + out vec2 c_framecoord; void main() { gl_Position = vPosition; - c_flatposition = vFlatPosition; + c_framecoord = vFrameCoord; } ); } @@ -67,13 +67,13 @@ std::string TessellatedColorShaderOpenGl::getControlShader() { uniform int uSubdivisionFactor; - in vec2 c_flatposition[]; - out vec2 e_flatposition[]; + in vec2 c_framecoord[]; + out vec2 e_framecoord[]; void main() { gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; - e_flatposition[gl_InvocationID] = c_flatposition[gl_InvocationID]; + e_framecoord[gl_InvocationID] = c_framecoord[gl_InvocationID]; if (gl_InvocationID == 0) { @@ -98,7 +98,7 @@ std::string TessellatedColorShaderOpenGl::getEvaluationShader() { uniform vec4 uOrigin; uniform bool uIs3d; - in vec2 e_flatposition[]; + in vec2 e_framecoord[]; const float BlendScale = 1000.0; const float BlendOffset = 0.01; @@ -126,13 +126,13 @@ std::string TessellatedColorShaderOpenGl::getEvaluationShader() { vec4 p2 = gl_in[2].gl_Position; vec4 position = baryinterp(p0, p1, p2, bary); - vec2 f0 = e_flatposition[0]; - vec2 f1 = e_flatposition[1]; - vec2 f2 = e_flatposition[2]; - vec2 flatPosition = baryinterp(f0, f1, f2, bary); + vec2 f0 = e_framecoord[0]; + vec2 f1 = e_framecoord[1]; + vec2 f2 = e_framecoord[2]; + vec2 frameCoord = baryinterp(f0, f1, f2, bary); if (uIs3d) { - vec4 bent = transform(flatPosition, uOrigin) - uOriginOffset; + vec4 bent = transform(frameCoord, uOrigin) - uOriginOffset; float blend = clamp(length(uOriginOffset) * BlendScale - BlendOffset, 0.0, 1.0); position = mix(position, bent, blend); } diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index f0a3d42a9..ebb535f19 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -51,14 +51,14 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi std::string TessellatedRasterShaderOpenGl::getVertexShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, in vec4 vPosition; - in vec2 vFlatPosition; + in vec2 vFrameCoord; in vec2 texCoordinate; - out vec2 c_flatposition; + out vec2 c_framecoord; out vec2 c_texcoord; void main() { gl_Position = vPosition; - c_flatposition = vFlatPosition; + c_framecoord = vFrameCoord; c_texcoord = texCoordinate; } ); @@ -70,15 +70,15 @@ std::string TessellatedRasterShaderOpenGl::getControlShader() { uniform int uSubdivisionFactor; - in vec2 c_flatposition[]; + in vec2 c_framecoord[]; in vec2 c_texcoord[]; - out vec2 e_flatposition[]; + out vec2 e_framecoord[]; out vec2 e_texcoord[]; void main() { gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; - e_flatposition[gl_InvocationID] = c_flatposition[gl_InvocationID]; + e_framecoord[gl_InvocationID] = c_framecoord[gl_InvocationID]; e_texcoord[gl_InvocationID] = c_texcoord[gl_InvocationID]; if (gl_InvocationID == 0) @@ -106,7 +106,7 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { uniform vec4 uOrigin; uniform bool uIs3d; - in vec2 e_flatposition[]; + in vec2 e_framecoord[]; in vec2 e_texcoord[]; out vec2 v_texcoord; // out vec2 g_texcoord; /* WIREFRAME DEBUG */ @@ -141,11 +141,11 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { vec4 p11 = gl_in[3].gl_Position; vec4 position = bilerp(p00, p01, p10, p11, uv); - vec2 f00 = e_flatposition[0]; - vec2 f01 = e_flatposition[1]; - vec2 f10 = e_flatposition[2]; - vec2 f11 = e_flatposition[3]; - vec2 flatPosition = bilerp(f00, f01, f10, f11, uv); + 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); vec2 t00 = e_texcoord[0]; vec2 t01 = e_texcoord[1]; @@ -154,7 +154,7 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { vec2 texCoord = bilerp(t00, t01, t10, t11, uv); if (uIs3d) { - vec4 bent = transform(flatPosition, uOrigin) - uOriginOffset; + vec4 bent = transform(frameCoord, uOrigin) - uOriginOffset; float blend = clamp(length(uOriginOffset) * BlendScale - BlendOffset, 0.0, 1.0); position = mix(position, bent, blend); } diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index 9a536ec1d..efcf56d69 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -282,7 +282,7 @@ extension Quad2dTessellated: MCQuad2dInterface { insideTessellationFactor: (factor, factor) ); - var vertices: [TessellatedVertex3DTexture] = [] + var vertices: [Vertex3DTextureTessellated] = [] func transform(_ coordinate: MCVec3D) -> MCVec3D { if is3d { @@ -306,28 +306,28 @@ extension Quad2dTessellated: MCQuad2dInterface { Where A-C are joined to form two triangles */ vertices = [ - TessellatedVertex3DTexture( - relativePosition: transform(frame.topLeft), - absolutePositionX: frame.topLeft.xF, - absolutePositionY: frame.topLeft.yF, + Vertex3DTextureTessellated( + position: transform(frame.topLeft), + frameCoordX: frame.topLeft.xF, + frameCoordY: frame.topLeft.yF, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF), // B - TessellatedVertex3DTexture( - relativePosition: transform(frame.topRight), - absolutePositionX: frame.topRight.xF, - absolutePositionY: frame.topRight.yF, + Vertex3DTextureTessellated( + position: transform(frame.topRight), + frameCoordX: frame.topRight.xF, + frameCoordY: frame.topRight.yF, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF), // C - TessellatedVertex3DTexture( - relativePosition: transform(frame.bottomLeft), - absolutePositionX: frame.bottomLeft.xF, - absolutePositionY: frame.bottomLeft.yF, + Vertex3DTextureTessellated( + position: transform(frame.bottomLeft), + frameCoordX: frame.bottomLeft.xF, + frameCoordY: frame.bottomLeft.yF, textureU: textureCoordinates.xF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // A - TessellatedVertex3DTexture( - relativePosition: transform(frame.bottomRight), - absolutePositionX: frame.bottomRight.xF, - absolutePositionY: frame.bottomRight.yF, + Vertex3DTextureTessellated( + position: transform(frame.bottomRight), + frameCoordX: frame.bottomRight.xF, + frameCoordY: frame.bottomRight.yF, textureU: textureCoordinates.xF + textureCoordinates.widthF, textureV: textureCoordinates.yF + textureCoordinates.heightF), // D ] @@ -339,7 +339,7 @@ extension Quad2dTessellated: MCQuad2dInterface { self.textureCoordinates = textureCoordinates self.verticesBuffer.copyOrCreate( bytes: vertices, - length: MemoryLayout.stride * vertices.count, + length: MemoryLayout.stride * vertices.count, device: device) self.tessellationFactorsBuffer.copyOrCreate( bytes: &tessellationFactors, 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/TessellatedVertex3D.swift b/ios/graphics/Model/Vertex3DTessellated.swift similarity index 90% rename from ios/graphics/Model/TessellatedVertex3D.swift rename to ios/graphics/Model/Vertex3DTessellated.swift index ecd78f429..eea2ac177 100644 --- a/ios/graphics/Model/TessellatedVertex3D.swift +++ b/ios/graphics/Model/Vertex3DTessellated.swift @@ -11,19 +11,19 @@ import MapCoreSharedModule @preconcurrency import MetalKit -struct TessellatedVertex4F: Equatable { +struct Vertex3DTessellated: Equatable { nonisolated(unsafe) static let descriptor: MTLVertexDescriptor = { let vertexDescriptor = MTLVertexDescriptor() let bufferIndex = 0 var offset = 0 - // Relative Position + // Position vertexDescriptor.attributes[0].bufferIndex = bufferIndex vertexDescriptor.attributes[0].format = .float4 vertexDescriptor.attributes[0].offset = offset offset += MemoryLayout>.stride - // Absolute Position + // 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 diff --git a/ios/graphics/Model/TessellatedVertex3DTexture.swift b/ios/graphics/Model/Vertex3DTextureTessellated.swift similarity index 68% rename from ios/graphics/Model/TessellatedVertex3DTexture.swift rename to ios/graphics/Model/Vertex3DTextureTessellated.swift index 4c1640df3..efdeb51a1 100644 --- a/ios/graphics/Model/TessellatedVertex3DTexture.swift +++ b/ios/graphics/Model/Vertex3DTextureTessellated.swift @@ -12,11 +12,11 @@ import MapCoreSharedModule @preconcurrency import MetalKit /// A 3D point in the X-Y coordinate system carrying a texture coordinate -public struct TessellatedVertex3DTexture: Equatable { +public struct Vertex3DTextureTessellated: Equatable { /// The 3D position of the vertex in the plane - var relativePosition: SIMD3 - /// The 3D position of the vertex in the world - var absolutePosition: SIMD2 + 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 @@ -25,13 +25,13 @@ public struct TessellatedVertex3DTexture: Equatable { var offset = 0 let bufferIndex = 0 - // Relative Position + // Position vertexDescriptor.attributes[0].bufferIndex = bufferIndex vertexDescriptor.attributes[0].format = .float3 vertexDescriptor.attributes[0].offset = offset offset += MemoryLayout>.stride - // Absolute Position + // Frame Coord (2D coord to project onto unit sphere) vertexDescriptor.attributes[1].bufferIndex = bufferIndex vertexDescriptor.attributes[1].format = .float2 vertexDescriptor.attributes[1].offset = offset @@ -43,7 +43,7 @@ public struct TessellatedVertex3DTexture: Equatable { vertexDescriptor.attributes[2].offset = offset offset += MemoryLayout>.stride - vertexDescriptor.layouts[0].stride = MemoryLayout.stride + vertexDescriptor.layouts[0].stride = MemoryLayout.stride vertexDescriptor.layouts[0].stepRate = 1 vertexDescriptor.layouts[0].stepFunction = .perPatchControlPoint return vertexDescriptor @@ -60,43 +60,43 @@ public struct TessellatedVertex3DTexture: Equatable { /// - textureU: The texture U-coordinate mapping /// - textureV: The texture V-coordinate mapping public init( - relativeX: Float, relativeY: Float, relativeZ: Float, - absoluteX: Float, absoluteY: Float, + x: Float, y: Float, z: Float, + frameX: Float, frameY: Float, textureU: Float, textureV: Float, ) { - relativePosition = SIMD3([relativeX, relativeY, relativeZ]) - absolutePosition = SIMD2([absoluteX, absoluteY]) + position = SIMD3([x, y, z]) + frameCoord = SIMD2([frameX, frameY]) textureCoordinate = SIMD2([textureU, textureV]) } - public init(relativePosition: MCVec3D, absolutePositionX: Float, absolutePositionY: Float, textureU: Float, textureV: Float) { + public init(position: MCVec3D, frameCoordX: Float, frameCoordY: Float, textureU: Float, textureV: Float) { self.init( - relativeX: relativePosition.xF, relativeY: relativePosition.yF, relativeZ: relativePosition.zF, - absoluteX: absolutePositionX, absoluteY: absolutePositionY, + x: position.xF, y: position.yF, z: position.zF, + frameX: frameCoordX, frameY: frameCoordY, textureU: textureU, textureV: textureV, ) } } -extension TessellatedVertex3DTexture { +extension Vertex3DTextureTessellated { /// The X-coordinate position in the plane - var relativeX: Float { relativePosition.x } + var x: Float { position.x } /// The Y-coordinate position in the plane - var relativeY: Float { relativePosition.y } + var y: Float { position.y } /// The Z-coordinate position in the plane - var relativeZ: Float { relativePosition.z } + var z: Float { position.z } /// The X-coordinate position in the world - var absoluteX: Float { absolutePosition.x } + var frameCoordX: Float { frameCoord.x } /// The Y-coordinate position in the world - var absoluteY: Float { absolutePosition.y } + 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 TessellatedVertex3DTexture: CustomDebugStringConvertible { +extension Vertex3DTextureTessellated: CustomDebugStringConvertible { public var debugDescription: String { - "" + "" } } diff --git a/ios/graphics/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index 83e26ea07..4389ba15d 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -289,7 +289,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .polygonPatternFadeInGroupShader, .polygonStripedGroupShader, .colorShader, .maskShader: - return Vertex4F.descriptor + return Vertex3D.descriptor case .rasterShader, .clearStencilShader, .alphaShader, @@ -300,10 +300,10 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .roundColorShader, .elevationInterpolation: return Vertex3DTexture.descriptor - case .quadTessellatedShader: - return TessellatedVertex3DTexture.descriptor case .maskTessellatedShader: - return TessellatedVertex4F.descriptor + return Vertex3DTessellated.descriptor + case .quadTessellatedShader: + return Vertex3DTextureTessellated.descriptor default: return Vertex.descriptor } diff --git a/ios/graphics/Shader/Metal/BaseShader.metal b/ios/graphics/Shader/Metal/BaseShader.metal index 22df58c55..0384e6e71 100644 --- a/ios/graphics/Shader/Metal/BaseShader.metal +++ b/ios/graphics/Shader/Metal/BaseShader.metal @@ -62,7 +62,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 9d384cc49..dfd5c0131 100644 --- a/ios/graphics/Shader/Metal/DataStructures.metal +++ b/ios/graphics/Shader/Metal/DataStructures.metal @@ -16,26 +16,26 @@ struct VertexIn { float2 uv [[attribute(1)]]; }; -struct Vertex3DTextureIn { +// 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)]]; - float2 uv [[attribute(1)]]; }; -struct TessellatedVertex3DTextureIn { - float4 relativePosition [[attribute(0)]]; - float2 absolutePosition [[attribute(1)]]; - float2 uv [[attribute(2)]]; +struct Vertex3DTessellatedIn { + float4 position [[attribute(0)]]; + float2 frameCoord [[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 Vertex3DTextureIn { float4 position [[attribute(0)]]; + float2 uv [[attribute(1)]]; }; -struct TessellatedVertex4FIn { - float4 relativePosition [[attribute(0)]]; - float2 absolutePosition [[attribute(1)]]; +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 index 09670da0a..f7a4d1040 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -35,7 +35,7 @@ float4 transform(float2 coordinate, float4 origin) { } [[patch(quad, 4)]] vertex VertexOut -quadTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], +quadTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], const float2 positionInPatch [[position_in_patch]], constant float4x4 &vpMatrix [[buffer(1)]], constant float4x4 &mMatrix [[buffer(2)]], @@ -43,18 +43,18 @@ quadTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], +polygonTessellationVertexShader(const patch_control_point controlPoints [[stage_in]], const float3 positionInPatch [[position_in_patch]], constant float4x4 &vpMatrix [[buffer(1)]], constant float4x4 &mMatrix [[buffer(2)]], @@ -76,16 +76,16 @@ polygonTessellationVertexShader(const patch_control_point constant float4 &origin [[buffer(4)]], constant bool &is3d [[buffer(5)]]) { - TessellatedVertex4FIn vA = controlPoints[0]; - TessellatedVertex4FIn vB = controlPoints[1]; - TessellatedVertex4FIn vC = controlPoints[2]; + Vertex3DTessellatedIn vA = controlPoints[0]; + Vertex3DTessellatedIn vB = controlPoints[1]; + Vertex3DTessellatedIn vC = controlPoints[2]; - float4 vertexRelativePosition = baryinterp(vA.relativePosition, vB.relativePosition, vC.relativePosition, positionInPatch); - float2 vertexAbsolutePosition = baryinterp(vA.absolutePosition, vB.absolutePosition, vC.absolutePosition, positionInPatch); + float4 vertexPosition = baryinterp(vA.position, vB.position, vC.position, positionInPatch); + float2 vertexFrameCoord = baryinterp(vA.frameCoord, vB.frameCoord, vC.frameCoord, positionInPatch); - float4 position = vertexRelativePosition; + float4 position = vertexPosition; if (is3d) { - float4 bent = transform(vertexAbsolutePosition, origin) - originOffset; + float4 bent = transform(vertexFrameCoord, origin) - originOffset; float blend = saturate(length(originOffset) * BlendScale - BlendOffset); position = mix(position, bent, blend); } From a4b7f0149cc2f2a349d49f3091c378710b335cbb Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 10 Dec 2025 13:39:36 +0100 Subject: [PATCH 13/24] Refactor is3d and subdivisionFactor handling --- .../objects/GraphicsObjectFactoryOpenGl.cpp | 4 +- .../cpp/graphics/objects/Polygon2dOpenGl.cpp | 4 +- .../cpp/graphics/objects/Polygon2dOpenGl.h | 4 +- .../objects/Polygon2dTessellatedOpenGl.cpp | 15 +++++-- .../objects/Polygon2dTessellatedOpenGl.h | 7 ++- .../main/cpp/graphics/objects/Quad2dOpenGl.h | 3 +- .../objects/Quad2dTessellatedOpenGl.cpp | 18 +++++++- .../objects/Quad2dTessellatedOpenGl.h | 3 +- .../graphics/objects/Polygon2dInterface.kt | 16 +++++-- .../objects/NativePolygon2dInterface.cpp | 24 ++++++++-- .../objects/NativePolygon2dInterface.h | 6 ++- bridging/ios/MCPolygon2dInterface+Private.mm | 20 +++++++-- bridging/ios/MCPolygon2dInterface.h | 4 +- .../graphics/objects/graphicsobjects.djinni | 3 +- ios/graphics/Model/GraphicsFactory.swift | 4 +- ios/graphics/Model/Polygon/Polygon2d.swift | 4 +- .../Model/Polygon/Polygon2dTessellated.swift | 40 ++++++++++------- ios/graphics/Model/Quad/Quad2d.swift | 3 +- .../Model/Quad/Quad2dTessellated.swift | 45 +++++++------------ shared/public/Polygon2dInterface.h | 4 +- .../layers/objects/Polygon2dLayerObject.cpp | 3 +- .../map/layers/objects/PolygonMaskObject.cpp | 7 ++- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 2 +- .../vector/Tiled2dMapVectorLayerConstants.h | 2 +- 24 files changed, 162 insertions(+), 83 deletions(-) diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp index 071ab36be..abb4e173a 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp @@ -39,7 +39,7 @@ GraphicsObjectFactoryOpenGl::createPolygon(const std::shared_ptr<::ShaderProgram std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonTessellated(const std::shared_ptr<::ShaderProgramInterface> &shader) { - return std::make_shared(enforceGlShader(shader), false); // ? + return std::make_shared(enforceGlShader(shader)); } std::shared_ptr @@ -71,7 +71,7 @@ std::shared_ptr GraphicsObjectFactoryOpenGl::createPolygonMa 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), is3D); + return std::make_shared(enforceGlShader(shader)); } std::shared_ptr GraphicsObjectFactoryOpenGl::createText(const std::shared_ptr<::ShaderProgramInterface> &shader) { diff --git a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp index c7c708580..b6d7c50bf 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, int32_t subdivisionFactor) { +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; diff --git a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.h index f2325fc8c..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, int32_t subdivisionFactor) 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 index fe1e26d4d..ae1674b42 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -12,8 +12,8 @@ #include "OpenGlHelper.h" #include -Polygon2dTessellatedOpenGl::Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader, bool is3D) - : shaderProgram(shader), is3d(is3D) {} +Polygon2dTessellatedOpenGl::Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader) + : shaderProgram(shader) {} std::shared_ptr Polygon2dTessellatedOpenGl::asGraphicsObject() { return shared_from_this(); } @@ -21,7 +21,14 @@ std::shared_ptr Polygon2dTessellatedOpenGl::asMaskingObj bool Polygon2dTessellatedOpenGl::isReady() { return ready; } -void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, int32_t subdivisionFactor) { +void Polygon2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { + if (factor != subdivisionFactor) { + subdivisionFactor = factor; + ready = false; // necessary? ask Christoph + } +} + +void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, const ::SharedBytes & indices_, const ::Vec3D & origin, bool is3d) { std::lock_guard lock(dataMutex); ready = false; dataReady = false; @@ -30,7 +37,7 @@ void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, co vertices.resize(vertices_.elementCount); polygonOrigin = origin; - this->subdivisionFactor = subdivisionFactor; + this->is3d = is3d; if(indices_.elementCount > 0) { std::memcpy(indices.data(), (void *)indices_.address, indices_.elementCount * indices_.bytesPerElement); diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h index 560f13d8d..d24bdbe63 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h @@ -24,7 +24,7 @@ class Polygon2dTessellatedOpenGl : public GraphicsObjectInterface, public Polygon2dInterface, public std::enable_shared_from_this { public: - Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader, bool is3D); + Polygon2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader); ~Polygon2dTessellatedOpenGl(){}; @@ -42,7 +42,9 @@ class Polygon2dTessellatedOpenGl : 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, int32_t subdivision) 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; @@ -78,6 +80,7 @@ class Polygon2dTessellatedOpenGl : public GraphicsObjectInterface, std::vector indices; bool glDataBuffersGenerated = false; Vec3D polygonOrigin = Vec3D(0.0, 0.0, 0.0); + bool is3d = false; int32_t subdivisionFactor = 0; 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 index 18570a405..f64dfdbae 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -50,7 +50,7 @@ void Quad2dTessellatedOpenGl::setFrame(const Quad3dD &frame, const RectD &textur void Quad2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { if (factor != subdivisionFactor) { subdivisionFactor = factor; - ready = false; + ready = false; // necessary? ask Christoph } } @@ -86,53 +86,69 @@ void Quad2dTessellatedOpenGl::computeGeometry(bool texCoordsOnly) { 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), }; diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h index 21b42f30c..9d623182b 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.h @@ -96,7 +96,9 @@ class Quad2dTessellatedOpenGl : public GraphicsObjectInterface, 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; @@ -104,7 +106,6 @@ class Quad2dTessellatedOpenGl : 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/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface.kt b/bridging/android/java/io/openmobilemaps/mapscore/shared/graphics/objects/Polygon2dInterface.kt index 71817743e..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, subdivisionFactor: Int) + 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, subdivisionFactor: Int) { + 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, subdivisionFactor) + 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, subdivisionFactor: Int) + 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/jni/graphics/objects/NativePolygon2dInterface.cpp b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp index 718ddca31..2429cbb03 100644 --- a/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp +++ b/bridging/android/jni/graphics/objects/NativePolygon2dInterface.cpp @@ -18,7 +18,7 @@ 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, int32_t c_subdivisionFactor) { +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(); @@ -26,7 +26,15 @@ void NativePolygon2dInterface::JavaProxy::setVertices(const ::SharedBytes & c_ve ::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::I32::fromCpp(jniEnv, c_subdivisionFactor))); + ::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() { @@ -53,14 +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, jint j_subdivisionFactor) +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::I32::toCpp(jniEnv, j_subdivisionFactor)); + ::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 3e6c23537..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, int32_t subdivisionFactor) 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;I)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/ios/MCPolygon2dInterface+Private.mm b/bridging/ios/MCPolygon2dInterface+Private.mm index 913b3baf1..d06addd9b 100644 --- a/bridging/ios/MCPolygon2dInterface+Private.mm +++ b/bridging/ios/MCPolygon2dInterface+Private.mm @@ -38,12 +38,18 @@ - (id)initWithCpp:(const std::shared_ptr<::Polygon2dInterface>&)cppRef - (void)setVertices:(nonnull MCSharedBytes *)vertices indices:(nonnull MCSharedBytes *)indices origin:(nonnull MCVec3D *)origin - subdivisionFactor:(int32_t)subdivisionFactor { + is3d:(BOOL)is3d { try { _cppRefHandle.get()->setVertices(::djinni_generated::SharedBytes::toCpp(vertices), ::djinni_generated::SharedBytes::toCpp(indices), ::djinni_generated::Vec3D::toCpp(origin), - ::djinni::I32::toCpp(subdivisionFactor)); + ::djinni::Bool::toCpp(is3d)); + } DJINNI_TRANSLATE_EXCEPTIONS() +} + +- (void)setSubdivisionFactor:(int32_t)factor { + try { + _cppRefHandle.get()->setSubdivisionFactor(::djinni::I32::toCpp(factor)); } DJINNI_TRANSLATE_EXCEPTIONS() } @@ -70,13 +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, int32_t c_subdivisionFactor) 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)) - subdivisionFactor:(::djinni::I32::fromCpp(c_subdivisionFactor))]; + 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 14a3f5b6c..d5ba2f9e5 100644 --- a/bridging/ios/MCPolygon2dInterface.h +++ b/bridging/ios/MCPolygon2dInterface.h @@ -13,7 +13,9 @@ - (void)setVertices:(nonnull MCSharedBytes *)vertices indices:(nonnull MCSharedBytes *)indices origin:(nonnull MCVec3D *)origin - subdivisionFactor:(int32_t)subdivisionFactor; + is3d:(BOOL)is3d; + +- (void)setSubdivisionFactor:(int32_t)factor; - (nullable id)asGraphicsObject; diff --git a/djinni/graphics/objects/graphicsobjects.djinni b/djinni/graphics/objects/graphicsobjects.djinni index aaca8ddc4..96367150e 100644 --- a/djinni/graphics/objects/graphicsobjects.djinni +++ b/djinni/graphics/objects/graphicsobjects.djinni @@ -133,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, subdivisionFactor: i32); + 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/ios/graphics/Model/GraphicsFactory.swift b/ios/graphics/Model/GraphicsFactory.swift index 323018ab8..3c199f658 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -26,7 +26,7 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { func createPolygonMaskTessellated(_ is3d: Bool) -> (any MCPolygon2dInterface)? { let shader = MaskShader(shader: .maskTessellatedShader) - return Polygon2dTessellated(shader: shader, metalContext: .current, is3d: is3d) + return Polygon2dTessellated(shader: shader, metalContext: .current) } func createPolygonGroup(_ shader: MCShaderProgramInterface?) -> MCPolygonGroup2dInterface? { @@ -71,7 +71,7 @@ class GraphicsFactory: MCGraphicsObjectFactoryInterface { func createPolygonTessellated(_ shader: MCShaderProgramInterface?) -> MCPolygon2dInterface? { guard let shader else { fatalError("No Shader provided") } - return Polygon2dTessellated(shader: shader, metalContext: .current, is3d: false) //? + return Polygon2dTessellated(shader: shader, metalContext: .current) } func createText(_ shader: MCShaderProgramInterface?) -> MCTextInterface? { diff --git a/ios/graphics/Model/Polygon/Polygon2d.swift b/ios/graphics/Model/Polygon/Polygon2d.swift index 6af340daf..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, subdivisionFactor: Int32 + _ 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 index 7dbf31db1..d2f25563a 100644 --- a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -24,21 +24,21 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var originBuffers: MultiBuffer private var is3d = false + private var subdivisionFactor: Int32 = -1 private var stencilState: MTLDepthStencilState? private var renderPassStencilState: MTLDepthStencilState? - init(shader: MCShaderProgramInterface, metalContext: MetalContext, is3d: Bool) { + init(shader: MCShaderProgramInterface, metalContext: MetalContext) { self.shader = shader originBuffers = .init(device: metalContext.device) - self.is3d = is3d // move is3d to set vertices or make own setter? if so also make own subdivision setter super .init( device: metalContext.device, sampler: metalContext.samplerLibrary.value( Sampler.magLinear.rawValue)!, label: "Polygon2dTessellated") - + setSubdivisionFactor(0) // ensure tessellationFactorBuffer creation } override func render( @@ -233,17 +233,31 @@ extension Polygon2dTessellated: MCMaskingObjectInterface { } extension Polygon2dTessellated: MCPolygon2dInterface { + func setSubdivisionFactor(_ factor: Int32) { + lock.withCritical { + if self.subdivisionFactor != factor { + self.subdivisionFactor = factor + + let factorH = Half(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, subdivisionFactor: Int32 // move is3d and subdivisonfactor to own setter? + _ vertices: MCSharedBytes, indices: MCSharedBytes, origin: MCVec3D, is3d: Bool ) { - let factor = Half(subdivisionFactor).bits; - - var tessellationFactors = MTLTriangleTessellationFactorsHalf( - edgeTessellationFactor: (factor, factor, factor), - insideTessellationFactor: factor - ); - 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 { @@ -252,10 +266,6 @@ extension Polygon2dTessellated: MCPolygon2dInterface { self.indicesCount = 0 } self.originOffset = origin - self.tessellationFactorsBuffer.copyOrCreate( - bytes: &tessellationFactors, - length: MemoryLayout.stride, - device: device) } } 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 index efcf56d69..65f65725a 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -21,6 +21,7 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var originBuffers: MultiBuffer private var is3d = false + private var subdivisionFactor: Int32 = -1 private var texture: MTLTexture? @@ -31,8 +32,6 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var renderAsMask = false - private var subdivisionFactor: Int32 = 0 - private var frame: MCQuad3dD? private var textureCoordinates: MCRectD? @@ -54,6 +53,7 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { sampler: metalContext.samplerLibrary.value( Sampler.magLinear.rawValue)!, label: label) + setSubdivisionFactor(0) // ensure tessellationFactorBuffer creation } private func setupStencilStates() { @@ -195,7 +195,7 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { /* WIREFRAME DEBUG */ //encoder.setTriangleFillMode(.lines) - + encoder.drawPatches( numberOfPatchControlPoints: 4, patchStart: 0, @@ -253,35 +253,28 @@ extension Quad2dTessellated: MCQuad2dInterface { } func setSubdivisionFactor(_ factor: Int32) { - let (optFrame, optTextureCoordinates) = lock.withCritical { - () -> (MCQuad3dD?, MCRectD?) in + lock.withCritical { if self.subdivisionFactor != factor { self.subdivisionFactor = factor - return (frame, textureCoordinates) - } else { - return (nil, nil) + + 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) } } - if let frame = optFrame, - let textureCoordinates = optTextureCoordinates - { - setFrame( - frame, textureCoordinates: textureCoordinates, - origin: self.originOffset, is3d: is3d) - } } func setFrame( - _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, - is3d: Bool // maybe move subdivision factor also here? or both own setter? same as in polygon 2d tessellation + _ frame: MCQuad3dD, textureCoordinates: MCRectD, origin: MCVec3D, is3d: Bool ) { - let factor = Half(pow(2, Float(lock.withCritical { subdivisionFactor }))).bits; - - var tessellationFactors = MTLQuadTessellationFactorsHalf( - edgeTessellationFactor: (factor, factor, factor, factor), - insideTessellationFactor: (factor, factor) - ); - var vertices: [Vertex3DTextureTessellated] = [] func transform(_ coordinate: MCVec3D) -> MCVec3D { @@ -341,10 +334,6 @@ extension Quad2dTessellated: MCQuad2dInterface { bytes: vertices, length: MemoryLayout.stride * vertices.count, device: device) - self.tessellationFactorsBuffer.copyOrCreate( - bytes: &tessellationFactors, - length: MemoryLayout.stride, - device: device) } } diff --git a/shared/public/Polygon2dInterface.h b/shared/public/Polygon2dInterface.h index c5c32ae22..f5cd01f61 100644 --- a/shared/public/Polygon2dInterface.h +++ b/shared/public/Polygon2dInterface.h @@ -15,7 +15,9 @@ class Polygon2dInterface { public: virtual ~Polygon2dInterface() = default; - virtual void setVertices(const ::SharedBytes & vertices, const ::SharedBytes & indices, const ::Vec3D & origin, int32_t subdivisionFactor) = 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/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index cc276c0b9..dbffe86a7 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -124,7 +124,8 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons 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), 0); + //polygon->setSubdivisionFactor(0); // use it when switching to tessellated polygon + polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); } 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 7a1d5fa36..49ae313e7 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -24,7 +24,7 @@ PolygonMaskObject::PolygonMaskObject(const std::shared_ptr &conversionHelper, bool is3D) : conversionHelper(conversionHelper) - , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) //is3d here? + , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) , is3D(is3D) {} void PolygonMaskObject::setPositions(const std::vector &positions, @@ -90,12 +90,14 @@ 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 + // Frame Coord vertices.push_back(v.x); vertices.push_back(v.y); } @@ -103,7 +105,8 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, 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, (int32_t)(subdivisionFactor.value_or(1.0f))); + polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(1.0f))); + polygon->setVertices(attr, ind, origin, is3D); } std::shared_ptr PolygonMaskObject::getPolygonObject() { return polygon; } diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index 65be4cf57..95d855eda 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -318,7 +318,7 @@ std::vector sortedTileInfos(currentTileInfos.begin(), } } if (is3D) { - tileObject->getQuadObject()->setSubdivisionFactor( //todo here! + tileObject->getQuadObject()->setSubdivisionFactor( std::clamp(subdivisionFactor + tile.tessellationFactor, 0, 5)); } tileObject->setRectCoord(tile.tileInfo.tileInfo.bounds); diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index 9744eec8a..5b660a0e5 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -11,5 +11,5 @@ #pragma once #define POLYGON_SUBDIVISION_FACTOR 10.0 -#define POLYGON_MASK_SUBDIVISION_FACTOR 20.0 +#define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 // also good enough? From 392b6192d3272ea8320879b7f94eb38b4d3927cb Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 11 Dec 2025 08:48:53 +0100 Subject: [PATCH 14/24] Remove comments and add wireframe macro --- .../objects/GraphicsObjectFactoryOpenGl.cpp | 3 +-- .../objects/GraphicsObjectFactoryOpenGl.h | 1 - .../cpp/graphics/objects/Polygon2dOpenGl.cpp | 13 +++++---- .../objects/Polygon2dTessellatedOpenGl.cpp | 14 +++++----- .../objects/Quad2dTessellatedOpenGl.cpp | 4 +-- .../shader/TessellatedColorShaderOpenGl.cpp | 14 +++++----- .../shader/TessellatedRasterShaderOpenGl.cpp | 27 ++++++++++++------- ios/graphics/Model/GraphicsFactory.swift | 1 - ios/graphics/Shader/Metal/BaseShader.metal | 2 -- .../layers/objects/Polygon2dLayerObject.cpp | 2 +- .../vector/Tiled2dMapVectorLayerConstants.h | 1 + 11 files changed, 47 insertions(+), 35 deletions(-) diff --git a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp index abb4e173a..26dace960 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.cpp @@ -57,7 +57,6 @@ GraphicsObjectFactoryOpenGl::createPolygonPatternGroup(const std::shared_ptr<::S return std::make_shared(enforceGlShader(shader)); } -/* Deprecated? */ std::shared_ptr GraphicsObjectFactoryOpenGl::createQuadMask(bool is3D) { return std::make_shared(std::make_shared(is3D)); } @@ -102,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 a44398427..b7726939e 100644 --- a/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h +++ b/android/src/main/cpp/graphics/objects/GraphicsObjectFactoryOpenGl.h @@ -29,7 +29,6 @@ class GraphicsObjectFactoryOpenGl : public GraphicsObjectFactoryInterface { std::shared_ptr createPolygonPatternGroup(const std::shared_ptr<::ShaderProgramInterface> &shader) override; - /* Deprecated? */ std::shared_ptr createQuadMask(bool is3D) override; std::shared_ptr createPolygonMask(bool is3D) override; diff --git a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp index b6d7c50bf..db325bdb5 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dOpenGl.cpp @@ -32,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); } @@ -46,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(); @@ -120,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); @@ -173,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/Polygon2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp index ae1674b42..3dc8a64fa 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -24,7 +24,6 @@ bool Polygon2dTessellatedOpenGl::isReady() { return ready; } void Polygon2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { if (factor != subdivisionFactor) { subdivisionFactor = factor; - ready = false; // necessary? ask Christoph } } @@ -39,11 +38,11 @@ void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, co this->is3d = is3d; - 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); } @@ -53,8 +52,9 @@ void Polygon2dTessellatedOpenGl::setVertices(const ::SharedBytes & vertices_, co void Polygon2dTessellatedOpenGl::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(); @@ -134,8 +134,9 @@ void Polygon2dTessellatedOpenGl::render(const std::shared_ptr<::RenderingContext 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); @@ -194,8 +195,9 @@ void Polygon2dTessellatedOpenGl::drawPolygon(const std::shared_ptr<::RenderingCo 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) + if (!ready) { return; + } std::shared_ptr openGlContext = std::static_pointer_cast(context); diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp index f64dfdbae..7a92bac4f 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -50,7 +50,6 @@ void Quad2dTessellatedOpenGl::setFrame(const Quad3dD &frame, const RectD &textur void Quad2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { if (factor != subdivisionFactor) { subdivisionFactor = factor; - ready = false; // necessary? ask Christoph } } @@ -298,8 +297,9 @@ void Quad2dTessellatedOpenGl::render(const std::shared_ptr<::RenderingContextInt 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()) + if (!ready || (usesTextureCoords && !textureCoordsReady) || !shaderProgram->isRenderable()) { return; + } GLuint stencilMask = 0; GLuint validTarget = 0; diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp index 94d61c354..bda1217d3 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -10,6 +10,7 @@ #include "TessellatedColorShaderOpenGl.h" #include "OpenGlContext.h" +#include "Tiled2dMapVectorLayerConstants.h" TessellatedColorShaderOpenGl::TessellatedColorShaderOpenGl(bool projectOntoUnitSphere) : ColorShaderOpenGl(projectOntoUnitSphere ? "UBMAP_TessellatedColorShaderUnitSphereOpenGl" : "UBMAP_TessellatedColorShaderOpenGl") @@ -22,8 +23,9 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); int evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); - /* WIREFRAME DEBUG */ - //int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); +#if TESSELLATION_WIREFRAME_MODE + int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); +#endif int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); @@ -37,9 +39,10 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); - /* WIREFRAME DEBUG */ - //glAttachShader(program, geometryShader); - //glDeleteShader(geometryShader); +#if TESSELLATION_WIREFRAME_MODE + glAttachShader(program, geometryShader); + glDeleteShader(geometryShader); +#endif glLinkProgram(program); @@ -142,7 +145,6 @@ std::string TessellatedColorShaderOpenGl::getEvaluationShader() { ); } -/* WIREFRAME DEBUG */ std::string TessellatedColorShaderOpenGl::getGeometryShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(triangles) in; diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index ebb535f19..af6f0385a 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -10,6 +10,7 @@ #include "TessellatedRasterShaderOpenGl.h" #include "OpenGlContext.h" +#include "Tiled2dMapVectorLayerConstants.h" TessellatedRasterShaderOpenGl::TessellatedRasterShaderOpenGl(bool projectOntoUnitSphere) : RasterShaderOpenGl(projectOntoUnitSphere ? "UBMAP_TessellatedRasterShaderUnitSphereOpenGl" : "UBMAP_TessellatedRasterShaderOpenGl") @@ -22,8 +23,9 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); int evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); - /* WIREFRAME DEBUG */ - //int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); +#if TESSELLATION_WIREFRAME_MODE + int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); +#endif int fragmentShader = loadShader(GL_FRAGMENT_SHADER, getFragmentShader()); @@ -37,9 +39,10 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); - /* WIREFRAME DEBUG */ - //glAttachShader(program, geometryShader); - //glDeleteShader(geometryShader); +#if TESSELLATION_WIREFRAME_MODE + glAttachShader(program, geometryShader); + glDeleteShader(geometryShader); +#endif glLinkProgram(program); @@ -108,8 +111,11 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { in vec2 e_framecoord[]; in vec2 e_texcoord[]; - out vec2 v_texcoord; // out vec2 g_texcoord; /* WIREFRAME DEBUG */ - +#if TESSELLATION_WIREFRAME_MODE + out vec2 g_texcoord; +#else + out vec2 v_texcoord; +#endif const float BlendScale = 1000.0; const float BlendOffset = 0.01; @@ -160,12 +166,15 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { } gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vec4(position.xyz, 1.0)) + uOriginOffset); - v_texcoord = texCoord; // g_texcoord = texCoord; /* WIREFRAME DEBUG */ +#if TESSELLATION_WIREFRAME_MODE + g_texcoord = texCoord; +#else + v_texcoord = texCoord; +#endif } ); } -/* WIREFRAME DEBUG */ std::string TessellatedRasterShaderOpenGl::getGeometryShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(triangles) in; diff --git a/ios/graphics/Model/GraphicsFactory.swift b/ios/graphics/Model/GraphicsFactory.swift index 3c199f658..9897467f5 100644 --- a/ios/graphics/Model/GraphicsFactory.swift +++ b/ios/graphics/Model/GraphicsFactory.swift @@ -13,7 +13,6 @@ import MapCoreSharedModule class GraphicsFactory: MCGraphicsObjectFactoryInterface { - /* Deprecated? */ func createQuadMask(_ is3d: Bool) -> (any MCQuad2dInterface)? { let shader = ColorShader(shader: .colorShader) return Quad2d(shader: shader, metalContext: .current) diff --git a/ios/graphics/Shader/Metal/BaseShader.metal b/ios/graphics/Shader/Metal/BaseShader.metal index 0384e6e71..d31bf7568 100644 --- a/ios/graphics/Shader/Metal/BaseShader.metal +++ b/ios/graphics/Shader/Metal/BaseShader.metal @@ -27,8 +27,6 @@ baseVertexShader(const Vertex3DTextureIn vertexIn [[stage_in]], return out; } -// deprecated? (baseVertexShader and baseVertexShaderModel identical) - vertex VertexOut baseVertexShaderModel(const Vertex3DTextureIn vertexIn [[stage_in]], constant float4x4 &vpMatrix [[buffer(1)]], diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index dbffe86a7..4df95948e 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -99,7 +99,7 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons 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); // here, do on gpu + PolygonHelper::subdivision(vecVertices, indices, threshold); } for (const auto& v : vecVertices) { diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index 5b660a0e5..d7c3ec50a 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -13,3 +13,4 @@ #define POLYGON_SUBDIVISION_FACTOR 10.0 #define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 // also good enough? +#define TESSELLATION_WIREFRAME_MODE 0 From dbabf12a4fb05cad2d30ce868a842e50925c5736 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 11 Dec 2025 13:41:56 +0100 Subject: [PATCH 15/24] Port polygon layer to tessellation --- .../graphics/shader/ShaderFactoryOpenGl.cpp | 5 +++++ .../cpp/graphics/shader/ShaderFactoryOpenGl.h | 2 ++ .../graphics/shader/ShaderFactoryInterface.kt | 8 ++++++++ .../shader/NativeShaderFactoryInterface.cpp | 18 ++++++++++++++++++ .../shader/NativeShaderFactoryInterface.h | 2 ++ .../ios/MCShaderFactoryInterface+Private.mm | 14 ++++++++++++++ bridging/ios/MCShaderFactoryInterface.h | 2 ++ djinni/graphics/shader/shader.djinni | 1 + ios/graphics/Pipelines/PipelineLibrary.swift | 10 ++++++++-- ios/graphics/Shader/ShaderFactory.swift | 4 ++++ shared/public/ShaderFactoryInterface.h | 2 ++ .../layers/objects/Polygon2dLayerObject.cpp | 18 ++++++++++-------- shared/src/map/layers/polygon/PolygonLayer.cpp | 4 ++-- 13 files changed, 78 insertions(+), 12 deletions(-) diff --git a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp index c8841483e..55bc2f788 100644 --- a/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/ShaderFactoryOpenGl.cpp @@ -25,6 +25,7 @@ #include "SphereEffectShaderOpenGl.h" #include "SkySphereShaderOpenGl.h" #include "TessellatedRasterShaderOpenGl.h" +#include "TessellatedColorShaderOpenGl.h" #include "ElevationInterpolationShaderOpenGl.h" std::shared_ptr ShaderFactoryOpenGl::createAlphaShader() { @@ -79,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 d89c463c8..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; 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 34f6b4754..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 @@ -135,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) diff --git a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.cpp index 285423237..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); @@ -340,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 { diff --git a/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h b/bridging/android/jni/graphics/shader/NativeShaderFactoryInterface.h index 2e74b59f4..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; @@ -75,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;") }; diff --git a/bridging/ios/MCShaderFactoryInterface+Private.mm b/bridging/ios/MCShaderFactoryInterface+Private.mm index 77df92c45..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(); @@ -311,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 { diff --git a/bridging/ios/MCShaderFactoryInterface.h b/bridging/ios/MCShaderFactoryInterface.h index 84540e4fd..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; diff --git a/djinni/graphics/shader/shader.djinni b/djinni/graphics/shader/shader.djinni index 30dd5ef7d..5890b7d05 100644 --- a/djinni/graphics/shader/shader.djinni +++ b/djinni/graphics/shader/shader.djinni @@ -41,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; diff --git a/ios/graphics/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index 4389ba15d..54d97fd24 100644 --- a/ios/graphics/Pipelines/PipelineLibrary.swift +++ b/ios/graphics/Pipelines/PipelineLibrary.swift @@ -154,6 +154,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { case maskShader case maskTessellatedShader case colorShader + case polygonTessellatedShader case roundColorShader case clearStencilShader case textShader @@ -185,6 +186,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { 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" @@ -227,6 +229,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { 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" @@ -260,6 +263,7 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { 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" @@ -300,7 +304,8 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { .roundColorShader, .elevationInterpolation: return Vertex3DTexture.descriptor - case .maskTessellatedShader: + case .maskTessellatedShader, + .polygonTessellatedShader: return Vertex3DTessellated.descriptor case .quadTessellatedShader: return Vertex3DTextureTessellated.descriptor @@ -313,7 +318,8 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { switch self { case .quadTessellatedShader: return MCTessellationMode.QUAD - case .maskTessellatedShader: + case .maskTessellatedShader, + .polygonTessellatedShader: return MCTessellationMode.TRIANGLE default: return MCTessellationMode.NONE diff --git a/ios/graphics/Shader/ShaderFactory.swift b/ios/graphics/Shader/ShaderFactory.swift index 1161b9a75..9a9c4a345 100644 --- a/ios/graphics/Shader/ShaderFactory.swift +++ b/ios/graphics/Shader/ShaderFactory.swift @@ -87,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/shared/public/ShaderFactoryInterface.h b/shared/public/ShaderFactoryInterface.h index 50493e9f4..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; diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index 4df95948e..f0d29f0ed 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -96,13 +96,9 @@ 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; - 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); - } - for (const auto& v : vecVertices) { + + // Position if(is3D) { double sinX, sinY, cosX, cosY; lut::sincos(v.x, sinX, cosX); @@ -116,15 +112,21 @@ 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 + + // Frame Coord + vertices.push_back(v.x); + vertices.push_back(v.y); } 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->setSubdivisionFactor(0); // use it when switching to tessellated polygon + + int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 0; // correct? debug wireframe + + polygon->setSubdivisionFactor(subdivisionFactor); polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); } diff --git a/shared/src/map/layers/polygon/PolygonLayer.cpp b/shared/src/map/layers/polygon/PolygonLayer.cpp index 4c47daa36..00fbda7b8 100644 --- a/shared/src/map/layers/polygon/PolygonLayer.cpp +++ b/shared/src/map/layers/polygon/PolygonLayer.cpp @@ -123,8 +123,8 @@ void PolygonLayer::addAll(const std::vector &polygons) { std::lock_guard lock(polygonsMutex); for (const auto &polygon : polygons) { - auto shader = mapInterface->is3d() ? shaderFactory->createUnitSphereColorShader() : shaderFactory->createColorShader(); - auto polygonGraphicsObject = objectFactory->createPolygon(shader->asShaderProgramInterface()); + auto shader = shaderFactory->createPolygonTessellatedShader(mapInterface->is3d()); + auto polygonGraphicsObject = objectFactory->createPolygonTessellated(shader->asShaderProgramInterface()); auto polygonObject = std::make_shared(mapInterface->getCoordinateConverterHelper(), polygonGraphicsObject, shader, is3d); From e87eb01cc14224f1953d9d4c25701899df8241f5 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 11 Dec 2025 14:45:52 +0100 Subject: [PATCH 16/24] Optimize tessellation shaders with reordering --- .../shader/TessellatedColorShaderOpenGl.cpp | 10 ++++---- .../shader/TessellatedRasterShaderOpenGl.cpp | 22 ++++++++--------- .../Shader/Metal/TessellatedShader.metal | 24 ++++++++----------- .../layers/objects/Polygon2dLayerObject.cpp | 2 +- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp index bda1217d3..aed79dad7 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -129,12 +129,12 @@ std::string TessellatedColorShaderOpenGl::getEvaluationShader() { vec4 p2 = gl_in[2].gl_Position; vec4 position = baryinterp(p0, p1, p2, bary); - vec2 f0 = e_framecoord[0]; - vec2 f1 = e_framecoord[1]; - vec2 f2 = e_framecoord[2]; - vec2 frameCoord = baryinterp(f0, f1, f2, 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); diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index af6f0385a..a04914728 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -147,11 +147,17 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { vec4 p11 = gl_in[3].gl_Position; vec4 position = bilerp(p00, p01, p10, p11, uv); - 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); + 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]; @@ -159,12 +165,6 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { vec2 t11 = e_texcoord[3]; vec2 texCoord = bilerp(t00, t01, t10, t11, uv); - if (uIs3d) { - 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 TESSELLATION_WIREFRAME_MODE g_texcoord = texCoord; diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index f7a4d1040..1d6271b5c 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -47,21 +47,19 @@ quadTessellationVertexShader(const patch_control_point Vertex3DTessellatedIn vB = controlPoints[1]; Vertex3DTessellatedIn vC = controlPoints[2]; - float4 vertexPosition = baryinterp(vA.position, vB.position, vC.position, positionInPatch); - float2 vertexFrameCoord = baryinterp(vA.frameCoord, vB.frameCoord, vC.frameCoord, positionInPatch); - - float4 position = vertexPosition; + float4 position = baryinterp(vA.position, vB.position, vC.position, positionInPatch); if (is3d) { - float4 bent = transform(vertexFrameCoord, origin) - originOffset; + 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); } diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index f0d29f0ed..eae9b5add 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -124,7 +124,7 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons 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)); - int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 0; // correct? debug wireframe + int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 0; polygon->setSubdivisionFactor(subdivisionFactor); polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); From 95bf9539b8324303e1b2f7dd6b7b6bf4693562d1 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 17 Dec 2025 10:41:53 +0100 Subject: [PATCH 17/24] Optimize tessellation shaders with halfs --- .../Shader/Metal/TessellatedShader.metal | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/ios/graphics/Shader/Metal/TessellatedShader.metal b/ios/graphics/Shader/Metal/TessellatedShader.metal index 1d6271b5c..8967ecd27 100644 --- a/ios/graphics/Shader/Metal/TessellatedShader.metal +++ b/ios/graphics/Shader/Metal/TessellatedShader.metal @@ -16,18 +16,35 @@ const constant float BlendScale = 1000; const constant float BlendOffset = 0.01; template -T bilerp(T c00, T c01, T c10, T c11, float2 uv) { +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 -T baryinterp(T c0, T c1, T c2, float3 bary) { +inline T baryinterp(T c0, T c1, T c2, float3 bary) { return c0 * bary[0] + c1 * bary[1] + c2 * bary[2]; } -float4 transform(float2 coordinate, float4 origin) { +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; @@ -47,15 +64,16 @@ quadTessellationVertexShader(const patch_control_point Date: Wed, 17 Dec 2025 14:27:58 +0100 Subject: [PATCH 18/24] Split tessellation and legacy path by compile time flag --- .../layers/objects/Polygon2dLayerObject.cpp | 24 ++++++++++++++----- .../map/layers/objects/PolygonMaskObject.cpp | 18 +++++++++++++- .../src/map/layers/polygon/PolygonLayer.cpp | 9 +++++-- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 17 +++++++++---- .../vector/Tiled2dMapVectorLayerConstants.h | 6 ++++- ...2dMapVectorSourceRasterTileDataManager.cpp | 12 +++++++++- ...2dMapVectorSourceVectorTileDataManager.cpp | 10 ++++++++ .../raster/Tiled2dMapVectorRasterTile.cpp | 15 +++++++++--- 8 files changed, 93 insertions(+), 18 deletions(-) diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index eae9b5add..efad6f3a3 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,6 +97,14 @@ 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 TESSELLATION_ACTIVATED + 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 @@ -112,22 +121,25 @@ 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 TESSELLATION_ACTIVATED // 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), is3D); +#ifdef TESSELLATION_ACTIVATED int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 0; - polygon->setSubdivisionFactor(subdivisionFactor); - polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); +#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 49ae313e7..e4f2d2bd0 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 TESSELLATION_ACTIVATED , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) +#else + , polygon(graphicsObjectFactory->createPolygonMask(is3D)) +#endif , is3D(is3D) {} void PolygonMaskObject::setPositions(const std::vector &positions, @@ -81,6 +86,12 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } +#ifndef TESSELLATION_ACTIVATED + if(subdivisionFactor) { + PolygonHelper::subdivision(vecVertices, indices, *subdivisionFactor); + } +#endif + for (const auto& v : vecVertices) { double rx = origin.x; double ry = origin.y; @@ -97,16 +108,21 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, #ifdef __APPLE__ vertices.push_back(0.0f); #endif + + #ifdef TESSELLATION_ACTIVATED // 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, is3D); +#ifdef TESSELLATION_ACTIVATED polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(1.0f))); - polygon->setVertices(attr, ind, origin, is3D); +#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 00fbda7b8..bbc839a82 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,14 @@ void PolygonLayer::addAll(const std::vector &polygons) { std::lock_guard lock(polygonsMutex); for (const auto &polygon : polygons) { + #ifdef TESSELLATION_ACTIVATED auto shader = shaderFactory->createPolygonTessellatedShader(mapInterface->is3d()); auto polygonGraphicsObject = objectFactory->createPolygonTessellated(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 95d855eda..137b7d437 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,12 +303,20 @@ std::vector sortedTileInfos(currentTileInfos.begin(), quad->setMinMagFilter(textureFilterType); tileObject = std::make_shared(quad, mapInterface, is3D); } else { - auto rShader = shaderFactory->createQuadTessellatedShader(); - rShader->asShaderProgramInterface()->setBlendMode(blendMode); - auto quad = graphicsFactory->createQuadTessellated(rShader->asShaderProgramInterface()); + + #ifdef TESSELLATION_ACTIVATED + auto rasterShader = shaderFactory->createQuadTessellatedShader(); + auto quad = graphicsFactory->createQuadTessellated(rasterShader->asShaderProgramInterface()); + #else + auto rasterShader = is3D ? shaderFactory->createUnitSphereRasterShader() : shaderFactory->createRasterShader(); + auto quad = graphicsFactory->createQuad(rasterShader->asShaderProgramInterface()); + #endif + + rasterShader->asShaderProgramInterface()->setBlendMode(blendMode); quad->setMinMagFilter(textureFilterType); + tileObject = std::make_shared( - quad, rShader, mapInterface, is3D); + quad, rasterShader, mapInterface, is3D); if (zoomInfo.numDrawPreviousLayers == 0 || !animationsEnabled || zoomInfo.maskTile || is3D) { tileObject->setStyle(style); } else { diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index d7c3ec50a..267ddcc08 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -11,6 +11,10 @@ #pragma once #define POLYGON_SUBDIVISION_FACTOR 10.0 -#define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 // also good enough? +#define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 + +#if defined(__APPLE__) || defined(__ANDROID__) + #define TESSELLATION_ACTIVATED +#endif #define TESSELLATION_WIREFRAME_MODE 0 diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index ff005e621..67a27d8db 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -107,12 +107,22 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); + #ifdef TESSELLATION_ACTIVATED std::optional subdivisionFactor = is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) : std::nullopt; 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) / + POLYGON_MASK_SUBDIVISION_FACTOR, (M_PI * 2.0) / 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 f2d5f72d0..c1df19862 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -115,11 +115,21 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); + #ifdef TESSELLATION_ACTIVATED std::optional subdivisionFactor = is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) : std::nullopt; 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) / + POLYGON_MASK_SUBDIVISION_FACTOR, (M_PI * 2.0) / 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/raster/Tiled2dMapVectorRasterTile.cpp b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp index 662aaefc4..ba1ba22b4 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,20 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptrgetShaderFactory()->createQuadTessellatedShader(); - shader->asShaderProgramInterface()->setBlendMode(description->style.getBlendMode(EvaluationContext(0.0, dpFactor, std::make_shared(), featureStateManager))); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); -#if DEBUG + #else + auto shader = pMapInterface->is3d() ? pMapInterface->getShaderFactory()->createUnitSphereRasterShader() : pMapInterface->getShaderFactory()->createRasterShader(); + auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface()); + #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); From 1d89e9dbb121201a989b029c7ef68c0be8d88278 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 17 Dec 2025 16:51:34 +0100 Subject: [PATCH 19/24] Minor improvements in android backend --- .../cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp | 1 + .../main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h | 2 +- android/src/main/cpp/graphics/objects/Quad2dOpenGl.cpp | 3 ++- .../main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp | 4 +++- .../cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp | 6 +++--- .../cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp | 6 +++--- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp index 3dc8a64fa..99835903f 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -22,6 +22,7 @@ std::shared_ptr Polygon2dTessellatedOpenGl::asMaskingObj bool Polygon2dTessellatedOpenGl::isReady() { return ready; } void Polygon2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { + std::lock_guard lock(dataMutex); if (factor != subdivisionFactor) { subdivisionFactor = factor; } diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h index d24bdbe63..afc7816a1 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h @@ -1,4 +1,4 @@ - /* +/* * Copyright (c) 2021 Ubique Innovation AG * * This Source Code Form is subject to the terms of the Mozilla Public 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/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp index 7a92bac4f..ca287c813 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -12,6 +12,7 @@ #include "TextureHolderInterface.h" #include "TextureFilterType.h" #include +#include "Logger.h" Quad2dTessellatedOpenGl::Quad2dTessellatedOpenGl(const std::shared_ptr<::BaseShaderProgramOpenGl> &shader) : shaderProgram(shader) {} @@ -48,6 +49,7 @@ void Quad2dTessellatedOpenGl::setFrame(const Quad3dD &frame, const RectD &textur } void Quad2dTessellatedOpenGl::setSubdivisionFactor(int32_t factor) { + std::lock_guard lock(dataMutex); if (factor != subdivisionFactor) { subdivisionFactor = factor; } @@ -292,7 +294,7 @@ void Quad2dTessellatedOpenGl::renderAsMask(const std::shared_ptr<::RenderingCont render(context, renderPass, vpMatrix, mMatrix, origin, false, screenPixelAsRealMeterFactor, isScreenSpaceCoords); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } -#include "Logger.h" + 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) { diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp index aed79dad7..686d89051 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -21,7 +21,7 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin int vertexShader = loadShader(GL_VERTEX_SHADER, getVertexShader()); int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); - int evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + int evaluationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); #if TESSELLATION_WIREFRAME_MODE int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); @@ -34,8 +34,8 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin glDeleteShader(vertexShader); glAttachShader(program, controlShader); glDeleteShader(controlShader); - glAttachShader(program, evalutationShader); - glDeleteShader(evalutationShader); + glAttachShader(program, evaluationShader); + glDeleteShader(evaluationShader); glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp index a04914728..616c5fa92 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -21,7 +21,7 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi int vertexShader = loadShader(GL_VERTEX_SHADER, getVertexShader()); int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); - int evalutationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); + int evaluationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); #if TESSELLATION_WIREFRAME_MODE int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); @@ -34,8 +34,8 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi glDeleteShader(vertexShader); glAttachShader(program, controlShader); glDeleteShader(controlShader); - glAttachShader(program, evalutationShader); - glDeleteShader(evalutationShader); + glAttachShader(program, evaluationShader); + glDeleteShader(evaluationShader); glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); From dfd1d3bc2ad7a66456d1a099f24321ce793aba41 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 21 Jan 2026 16:38:59 +0100 Subject: [PATCH 20/24] Prevent tessellation on ios simulator --- .../shader/TessellatedColorShaderOpenGl.cpp | 2 ++ .../shader/TessellatedColorShaderOpenGl.h | 4 ++- .../shader/TessellatedRasterShaderOpenGl.cpp | 2 ++ .../shader/TessellatedRasterShaderOpenGl.h | 4 ++- ios/graphics/Pipelines/PipelineLibrary.swift | 31 +++++++++++++------ .../vector/Tiled2dMapVectorLayerConstants.h | 9 +++++- 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp index 686d89051..cf22ac39e 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -145,6 +145,7 @@ std::string TessellatedColorShaderOpenGl::getEvaluationShader() { ); } +#if TESSELLATION_WIREFRAME_MODE std::string TessellatedColorShaderOpenGl::getGeometryShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(triangles) in; @@ -167,3 +168,4 @@ std::string TessellatedColorShaderOpenGl::getGeometryShader() { } ); } +#endif diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h index 9b191c2de..1636c7ad6 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h @@ -12,6 +12,7 @@ #include "ColorShaderOpenGl.h" #include "RenderingContextInterface.h" +#include "Tiled2dMapVectorLayerConstants.h" class TessellatedColorShaderOpenGl : public ColorShaderOpenGl { public: @@ -26,7 +27,8 @@ class TessellatedColorShaderOpenGl : public ColorShaderOpenGl { virtual std::string getEvaluationShader(); - /* WIREFRAME DEBUG */ +#if TESSELLATION_WIREFRAME_MODE 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 index 616c5fa92..95577bb00 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -175,6 +175,7 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { ); } +#if TESSELLATION_WIREFRAME_MODE std::string TessellatedRasterShaderOpenGl::getGeometryShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(triangles) in; @@ -202,3 +203,4 @@ std::string TessellatedRasterShaderOpenGl::getGeometryShader() { } ); } +#endif diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h index 6a2de497b..dc11ab37b 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h @@ -12,6 +12,7 @@ #include "RasterShaderOpenGl.h" #include "RenderingContextInterface.h" +#include "Tiled2dMapVectorLayerConstants.h" class TessellatedRasterShaderOpenGl : public RasterShaderOpenGl { public: @@ -26,7 +27,8 @@ class TessellatedRasterShaderOpenGl : public RasterShaderOpenGl { virtual std::string getEvaluationShader(); - /* WIREFRAME DEBUG */ +#if TESSELLATION_WIREFRAME_MODE virtual std::string getGeometryShader(); +#endif }; diff --git a/ios/graphics/Pipelines/PipelineLibrary.swift b/ios/graphics/Pipelines/PipelineLibrary.swift index 54d97fd24..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( @@ -129,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 } } @@ -207,7 +212,15 @@ public enum PipelineType: String, CaseIterable, Codable, Sendable { var vertexShaderUsesModelMatrix: Bool { switch self { - case .rasterShader, .quadTessellatedShader, .roundColorShader, .unitSphereRoundColorShader, .alphaShader, .unitSphereAlphaShader, .sphereEffectShader, .skySphereShader, .elevationInterpolation: + case .rasterShader, + .quadTessellatedShader, + .roundColorShader, + .unitSphereRoundColorShader, + .alphaShader, + .unitSphereAlphaShader, + .sphereEffectShader, + .skySphereShader, + .elevationInterpolation: return true default: return false diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index 267ddcc08..68be75137 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -13,8 +13,15 @@ #define POLYGON_SUBDIVISION_FACTOR 10.0 #define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 -#if defined(__APPLE__) || defined(__ANDROID__) +#if defined(__ANDROID__) #define TESSELLATION_ACTIVATED #endif +#if defined(__APPLE__) + #include + #if !TARGET_OS_SIMULATOR + #define TESSELLATION_ACTIVATED + #endif +#endif + #define TESSELLATION_WIREFRAME_MODE 0 From 8083aad1c75948395cb5aab686100f899e3ab039 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Thu, 22 Jan 2026 14:42:49 +0100 Subject: [PATCH 21/24] Rename tessellation constants --- .../objects/Polygon2dTessellatedOpenGl.h | 20 +++++++++---------- .../shader/TessellatedColorShaderOpenGl.cpp | 6 +++--- .../shader/TessellatedColorShaderOpenGl.h | 2 +- .../shader/TessellatedRasterShaderOpenGl.cpp | 10 +++++----- .../shader/TessellatedRasterShaderOpenGl.h | 2 +- .../layers/objects/Polygon2dLayerObject.cpp | 8 ++++---- .../map/layers/objects/PolygonMaskObject.cpp | 8 ++++---- .../src/map/layers/polygon/PolygonLayer.cpp | 2 +- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 2 +- .../vector/Tiled2dMapVectorLayerConstants.h | 6 +++--- ...2dMapVectorSourceRasterTileDataManager.cpp | 2 +- ...2dMapVectorSourceVectorTileDataManager.cpp | 2 +- .../raster/Tiled2dMapVectorRasterTile.cpp | 2 +- 13 files changed, 36 insertions(+), 36 deletions(-) diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h index afc7816a1..d5af2fb3d 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.h @@ -66,17 +66,17 @@ class Polygon2dTessellatedOpenGl : public GraphicsObjectInterface, std::string programName; int program = 0; - int mMatrixHandle{}; - int originOffsetHandle{}; - int subdivisionFactorHandle{}; - int originHandle{}; - int is3dHandle{}; - int positionHandle{}; - int frameCoordHandle{}; - GLuint vao{}; - GLuint vertexBuffer{}; + int mMatrixHandle; + int originOffsetHandle; + int subdivisionFactorHandle; + int originHandle; + int is3dHandle; + int positionHandle; + int frameCoordHandle; + GLuint vao; + GLuint vertexBuffer; std::vector vertices; - GLuint indexBuffer{}; + GLuint indexBuffer; std::vector indices; bool glDataBuffersGenerated = false; Vec3D polygonOrigin = Vec3D(0.0, 0.0, 0.0); diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp index cf22ac39e..2f5bbc2af 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.cpp @@ -23,7 +23,7 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); int evaluationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); #endif @@ -39,7 +39,7 @@ void TessellatedColorShaderOpenGl::setupProgram(const std::shared_ptr<::Renderin glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME glAttachShader(program, geometryShader); glDeleteShader(geometryShader); #endif @@ -145,7 +145,7 @@ std::string TessellatedColorShaderOpenGl::getEvaluationShader() { ); } -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME std::string TessellatedColorShaderOpenGl::getGeometryShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(triangles) in; diff --git a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h index 1636c7ad6..84562b649 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/TessellatedColorShaderOpenGl.h @@ -27,7 +27,7 @@ class TessellatedColorShaderOpenGl : public ColorShaderOpenGl { virtual std::string getEvaluationShader(); -#if TESSELLATION_WIREFRAME_MODE +#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 index 95577bb00..75600df58 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.cpp @@ -23,7 +23,7 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi int controlShader = loadShader(GL_TESS_CONTROL_SHADER, getControlShader()); int evaluationShader = loadShader(GL_TESS_EVALUATION_SHADER, getEvaluationShader()); -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME int geometryShader = loadShader(GL_GEOMETRY_SHADER, getGeometryShader()); #endif @@ -39,7 +39,7 @@ void TessellatedRasterShaderOpenGl::setupProgram(const std::shared_ptr<::Renderi glAttachShader(program, fragmentShader); glDeleteShader(fragmentShader); -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME glAttachShader(program, geometryShader); glDeleteShader(geometryShader); #endif @@ -111,7 +111,7 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { in vec2 e_framecoord[]; in vec2 e_texcoord[]; -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME out vec2 g_texcoord; #else out vec2 v_texcoord; @@ -166,7 +166,7 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { vec2 texCoord = bilerp(t00, t01, t10, t11, uv); gl_Position = uFrameUniforms.vpMatrix * ((umMatrix * vec4(position.xyz, 1.0)) + uOriginOffset); -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME g_texcoord = texCoord; #else v_texcoord = texCoord; @@ -175,7 +175,7 @@ std::string TessellatedRasterShaderOpenGl::getEvaluationShader() { ); } -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME std::string TessellatedRasterShaderOpenGl::getGeometryShader() { return OMMVersionedGlesShaderCodeWithFrameUBO(320 es, layout(triangles) in; diff --git a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h index dc11ab37b..dac389648 100644 --- a/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h +++ b/android/src/main/cpp/graphics/shader/TessellatedRasterShaderOpenGl.h @@ -27,7 +27,7 @@ class TessellatedRasterShaderOpenGl : public RasterShaderOpenGl { virtual std::string getEvaluationShader(); -#if TESSELLATION_WIREFRAME_MODE +#if HARDWARE_TESSELLATION_WIREFRAME virtual std::string getGeometryShader(); #endif }; diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index efad6f3a3..f2392bb94 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -97,7 +97,7 @@ 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 TESSELLATION_ACTIVATED +#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); @@ -125,7 +125,7 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons vertices.push_back(0.0f); #endif - #ifdef TESSELLATION_ACTIVATED + #ifdef HARDWARE_TESSELLATION_SUPPORTED // Frame Coord vertices.push_back(v.x); vertices.push_back(v.y); @@ -136,8 +136,8 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons auto ind = SharedBytes((int64_t)indices.data(), (int32_t)indices.size(), (int32_t)sizeof(uint16_t)); polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); -#ifdef TESSELLATION_ACTIVATED - int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 0; +#ifdef HARDWARE_TESSELLATION_SUPPORTED + int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 1; polygon->setSubdivisionFactor(subdivisionFactor); #endif } diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index e4f2d2bd0..d5a78e0cf 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -25,7 +25,7 @@ PolygonMaskObject::PolygonMaskObject(const std::shared_ptr &conversionHelper, bool is3D) : conversionHelper(conversionHelper) -#ifdef TESSELLATION_ACTIVATED +#ifdef HARDWARE_TESSELLATION_SUPPORTED , polygon(graphicsObjectFactory->createPolygonMaskTessellated(is3D)) #else , polygon(graphicsObjectFactory->createPolygonMask(is3D)) @@ -86,7 +86,7 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, } } -#ifndef TESSELLATION_ACTIVATED +#ifndef HARDWARE_TESSELLATION_SUPPORTED if(subdivisionFactor) { PolygonHelper::subdivision(vecVertices, indices, *subdivisionFactor); } @@ -109,7 +109,7 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, vertices.push_back(0.0f); #endif - #ifdef TESSELLATION_ACTIVATED + #ifdef HARDWARE_TESSELLATION_SUPPORTED // Frame Coord vertices.push_back(v.x); vertices.push_back(v.y); @@ -120,7 +120,7 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, auto ind = SharedBytes((int64_t)indices.data(), (int32_t)indices.size(), (int32_t)sizeof(uint16_t)); polygon->setVertices(attr, ind, origin, is3D); -#ifdef TESSELLATION_ACTIVATED +#ifdef HARDWARE_TESSELLATION_SUPPORTED polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(1.0f))); #endif } diff --git a/shared/src/map/layers/polygon/PolygonLayer.cpp b/shared/src/map/layers/polygon/PolygonLayer.cpp index bbc839a82..8fdfc51e8 100644 --- a/shared/src/map/layers/polygon/PolygonLayer.cpp +++ b/shared/src/map/layers/polygon/PolygonLayer.cpp @@ -123,7 +123,7 @@ void PolygonLayer::addAll(const std::vector &polygons) { std::lock_guard lock(polygonsMutex); for (const auto &polygon : polygons) { - #ifdef TESSELLATION_ACTIVATED + #ifdef HARDWARE_TESSELLATION_SUPPORTED auto shader = shaderFactory->createPolygonTessellatedShader(mapInterface->is3d()); auto polygonGraphicsObject = objectFactory->createPolygonTessellated(shader->asShaderProgramInterface()); #else diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index 137b7d437..c42d7331a 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -304,7 +304,7 @@ std::vector sortedTileInfos(currentTileInfos.begin(), tileObject = std::make_shared(quad, mapInterface, is3D); } else { - #ifdef TESSELLATION_ACTIVATED + #ifdef HARDWARE_TESSELLATION_SUPPORTED auto rasterShader = shaderFactory->createQuadTessellatedShader(); auto quad = graphicsFactory->createQuadTessellated(rasterShader->asShaderProgramInterface()); #else diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index 68be75137..046e515e3 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -14,14 +14,14 @@ #define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 #if defined(__ANDROID__) - #define TESSELLATION_ACTIVATED + #define HARDWARE_TESSELLATION_SUPPORTED #endif #if defined(__APPLE__) #include #if !TARGET_OS_SIMULATOR - #define TESSELLATION_ACTIVATED + #define HARDWARE_TESSELLATION_SUPPORTED #endif #endif -#define TESSELLATION_WIREFRAME_MODE 0 +#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 67a27d8db..00385ce53 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -107,7 +107,7 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); - #ifdef TESSELLATION_ACTIVATED + #ifdef HARDWARE_TESSELLATION_SUPPORTED std::optional subdivisionFactor = is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) : std::nullopt; diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp index c1df19862..5556e0e61 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -115,7 +115,7 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); - #ifdef TESSELLATION_ACTIVATED + #ifdef HARDWARE_TESSELLATION_SUPPORTED std::optional subdivisionFactor = is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) : std::nullopt; 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 ba1ba22b4..1dd244bf3 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -30,7 +30,7 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptrgetShaderFactory()->createQuadTessellatedShader(); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); #else From a605587024fb342d0216ae2fb72b6163b8bb1a59 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Mon, 26 Jan 2026 14:52:32 +0100 Subject: [PATCH 22/24] Consistent subdivision factor space --- .../objects/Polygon2dTessellatedOpenGl.cpp | 2 +- .../objects/Quad2dTessellatedOpenGl.cpp | 2 +- .../Model/Polygon/Polygon2dTessellated.swift | 17 ++++++++++++++--- ios/graphics/Model/Quad/Quad2dTessellated.swift | 16 ++++++++++++++-- shared/src/map/layers/icon/IconLayer.cpp | 5 ++--- .../map/layers/objects/Polygon2dLayerObject.cpp | 2 +- .../map/layers/objects/PolygonMaskObject.cpp | 2 +- .../vector/Tiled2dMapVectorLayerConstants.h | 4 ++-- ...ed2dMapVectorSourceRasterTileDataManager.cpp | 14 +++++++------- ...ed2dMapVectorSourceVectorTileDataManager.cpp | 13 +++++++------ .../polygon/Tiled2dMapVectorPolygonTile.cpp | 5 ++++- 11 files changed, 54 insertions(+), 28 deletions(-) diff --git a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp index 99835903f..afcfb2bd1 100644 --- a/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Polygon2dTessellatedOpenGl.cpp @@ -180,7 +180,7 @@ void Polygon2dTessellatedOpenGl::drawPolygon(const std::shared_ptr<::RenderingCo glPatchParameteri(GL_PATCH_VERTICES, 3); - glUniform1i(subdivisionFactorHandle, subdivisionFactor); + glUniform1i(subdivisionFactorHandle, pow(2, subdivisionFactor)); glUniform4f(originHandle, origin.x, origin.y, origin.z, 0.0); diff --git a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp index ca287c813..fde1c4f91 100644 --- a/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp +++ b/android/src/main/cpp/graphics/objects/Quad2dTessellatedOpenGl.cpp @@ -337,7 +337,7 @@ void Quad2dTessellatedOpenGl::render(const std::shared_ptr<::RenderingContextInt glPatchParameteri(GL_PATCH_VERTICES, 4); - glUniform1i(subdivisionFactorHandle, 1 << subdivisionFactor); + glUniform1i(subdivisionFactorHandle, pow(2, subdivisionFactor)); glUniform4f(originHandle, origin.x, origin.y, origin.z, 0.0); diff --git a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift index d2f25563a..9e2f7912e 100644 --- a/ios/graphics/Model/Polygon/Polygon2dTessellated.swift +++ b/ios/graphics/Model/Polygon/Polygon2dTessellated.swift @@ -24,7 +24,7 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var originBuffers: MultiBuffer private var is3d = false - private var subdivisionFactor: Int32 = -1 + private var subdivisionFactor: Int32 = 0 private var stencilState: MTLDepthStencilState? private var renderPassStencilState: MTLDepthStencilState? @@ -38,7 +38,18 @@ final class Polygon2dTessellated: BaseGraphicsObject, @unchecked Sendable { sampler: metalContext.samplerLibrary.value( Sampler.magLinear.rawValue)!, label: "Polygon2dTessellated") - setSubdivisionFactor(0) // ensure tessellationFactorBuffer creation + + 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( @@ -238,7 +249,7 @@ extension Polygon2dTessellated: MCPolygon2dInterface { if self.subdivisionFactor != factor { self.subdivisionFactor = factor - let factorH = Half(self.subdivisionFactor).bits; + let factorH = Half(pow(2, Float(self.subdivisionFactor))).bits; var tessellationFactors = MTLTriangleTessellationFactorsHalf( edgeTessellationFactor: (factorH, factorH, factorH), diff --git a/ios/graphics/Model/Quad/Quad2dTessellated.swift b/ios/graphics/Model/Quad/Quad2dTessellated.swift index 65f65725a..919a3c33a 100644 --- a/ios/graphics/Model/Quad/Quad2dTessellated.swift +++ b/ios/graphics/Model/Quad/Quad2dTessellated.swift @@ -21,7 +21,7 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { private var originBuffers: MultiBuffer private var is3d = false - private var subdivisionFactor: Int32 = -1 + private var subdivisionFactor: Int32 = 0 private var texture: MTLTexture? @@ -53,7 +53,19 @@ final class Quad2dTessellated: BaseGraphicsObject, @unchecked Sendable { sampler: metalContext.samplerLibrary.value( Sampler.magLinear.rawValue)!, label: label) - setSubdivisionFactor(0) // ensure tessellationFactorBuffer creation + + + 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() { diff --git a/shared/src/map/layers/icon/IconLayer.cpp b/shared/src/map/layers/icon/IconLayer.cpp index 595a2a2a2..7358508b2 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 f2392bb94..cb6c2a648 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -137,7 +137,7 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); #ifdef HARDWARE_TESSELLATION_SUPPORTED - int32_t subdivisionFactor = is3D ? std::pow(2, SUBDIVISION_FACTOR_3D_DEFAULT) : 1; + int32_t subdivisionFactor = is3D ? SUBDIVISION_FACTOR_3D_DEFAULT : 0; polygon->setSubdivisionFactor(subdivisionFactor); #endif } diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index d5a78e0cf..fe8cfad59 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -121,7 +121,7 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, polygon->setVertices(attr, ind, origin, is3D); #ifdef HARDWARE_TESSELLATION_SUPPORTED - polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(1.0f))); + polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(0.0f))); #endif } diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h index 046e515e3..7f9040667 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayerConstants.h @@ -10,8 +10,8 @@ #pragma once -#define POLYGON_SUBDIVISION_FACTOR 10.0 -#define POLYGON_MASK_SUBDIVISION_FACTOR 10.0 +#define POLYGON_SUBDIVISION_FACTOR 4 +#define POLYGON_MASK_SUBDIVISION_FACTOR 4 #if defined(__ANDROID__) #define HARDWARE_TESSELLATION_SUPPORTED diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp index 00385ce53..5f592c39a 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceRasterTileDataManager.cpp @@ -108,18 +108,18 @@ void Tiled2dMapVectorSourceRasterTileDataManager::onRasterTilesUpdated(const std Vec3D origin(rx, ry, rz); #ifdef HARDWARE_TESSELLATION_SUPPORTED - std::optional subdivisionFactor = - is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) - : std::nullopt; - + 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) / - POLYGON_MASK_SUBDIVISION_FACTOR, (M_PI * 2.0) / POLYGON_MASK_SUBDIVISION_FACTOR); + 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 diff --git a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp index 5556e0e61..8f9e896d4 100644 --- a/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/sourcemanagers/Tiled2dMapVectorSourceVectorTileDataManager.cpp @@ -116,16 +116,17 @@ void Tiled2dMapVectorSourceVectorTileDataManager::onVectorTilesUpdated(const std Vec3D origin(rx, ry, rz); #ifdef HARDWARE_TESSELLATION_SUPPORTED - std::optional subdivisionFactor = - is3D ? std::optional(float(POLYGON_MASK_SUBDIVISION_FACTOR)) - : std::nullopt; - + 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) / - POLYGON_MASK_SUBDIVISION_FACTOR, (M_PI * 2.0) / POLYGON_MASK_SUBDIVISION_FACTOR); + 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); 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); } From 8f22cb0d91a74dcab4f21977a6feddea87b01bb7 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 28 Jan 2026 11:45:17 +0100 Subject: [PATCH 23/24] Fix IconLayer subdivision error --- shared/src/map/layers/icon/IconLayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/map/layers/icon/IconLayer.cpp b/shared/src/map/layers/icon/IconLayer.cpp index 7358508b2..26aca12f5 100644 --- a/shared/src/map/layers/icon/IconLayer.cpp +++ b/shared/src/map/layers/icon/IconLayer.cpp @@ -155,8 +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()); - int32_t subdivisionFactor = is3D ? SUBDIVISION_FACTOR_3D_DEFAULT : 0; - quadObject->setSubdivisionFactor(subdivisionFactor); + //int32_t subdivisionFactor = is3D ? SUBDIVISION_FACTOR_3D_DEFAULT : 0; + //quadObject->setSubdivisionFactor(subdivisionFactor); #if DEBUG quadObject->asGraphicsObject()->setDebugLabel("IconLayerID:" + icon->getIdentifier()); From 7ce8fcf8de1a91ba80a716fa72905e887e6425c9 Mon Sep 17 00:00:00 2001 From: Noah Bussinger Date: Wed, 28 Jan 2026 16:31:21 +0100 Subject: [PATCH 24/24] Disable tessellation in 2d cases with hardware support --- .../layers/objects/Polygon2dLayerObject.cpp | 14 +++++++++----- .../map/layers/objects/PolygonMaskObject.cpp | 18 +++++++++++------- shared/src/map/layers/polygon/PolygonLayer.cpp | 6 ++++-- .../tiled/raster/Tiled2dMapRasterLayer.cpp | 4 ++-- .../raster/Tiled2dMapVectorRasterTile.cpp | 5 +++-- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp index cb6c2a648..97599e261 100644 --- a/shared/src/map/layers/objects/Polygon2dLayerObject.cpp +++ b/shared/src/map/layers/objects/Polygon2dLayerObject.cpp @@ -126,9 +126,11 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons #endif #ifdef HARDWARE_TESSELLATION_SUPPORTED - // Frame Coord - vertices.push_back(v.x); - vertices.push_back(v.y); + if(is3D) { + // Frame Coord + vertices.push_back(v.x); + vertices.push_back(v.y); + } #endif } @@ -137,8 +139,10 @@ void Polygon2dLayerObject::setPolygons(const std::vector &polygons polygon->setVertices(attr, ind, Vec3D(rx, ry, rz), is3D); #ifdef HARDWARE_TESSELLATION_SUPPORTED - int32_t subdivisionFactor = is3D ? SUBDIVISION_FACTOR_3D_DEFAULT : 0; - polygon->setSubdivisionFactor(subdivisionFactor); + if(is3D) { + int32_t subdivisionFactor = SUBDIVISION_FACTOR_3D_DEFAULT; + polygon->setSubdivisionFactor(subdivisionFactor); + } #endif } diff --git a/shared/src/map/layers/objects/PolygonMaskObject.cpp b/shared/src/map/layers/objects/PolygonMaskObject.cpp index fe8cfad59..bf9aa8b79 100644 --- a/shared/src/map/layers/objects/PolygonMaskObject.cpp +++ b/shared/src/map/layers/objects/PolygonMaskObject.cpp @@ -26,7 +26,7 @@ PolygonMaskObject::PolygonMaskObject(const std::shared_ptrcreatePolygonMaskTessellated(is3D)) + , polygon(is3D ? graphicsObjectFactory->createPolygonMaskTessellated(is3D) : graphicsObjectFactory->createPolygonMask(is3D)) #else , polygon(graphicsObjectFactory->createPolygonMask(is3D)) #endif @@ -80,14 +80,14 @@ 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); } } } #ifndef HARDWARE_TESSELLATION_SUPPORTED - if(subdivisionFactor) { + if (subdivisionFactor) { PolygonHelper::subdivision(vecVertices, indices, *subdivisionFactor); } #endif @@ -110,9 +110,11 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, #endif #ifdef HARDWARE_TESSELLATION_SUPPORTED - // Frame Coord - vertices.push_back(v.x); - vertices.push_back(v.y); + if (subdivisionFactor) { + // Frame Coord + vertices.push_back(v.x); + vertices.push_back(v.y); + } #endif } @@ -121,7 +123,9 @@ void PolygonMaskObject::setPolygons(const std::vector<::PolygonCoord> &polygons, polygon->setVertices(attr, ind, origin, is3D); #ifdef HARDWARE_TESSELLATION_SUPPORTED - polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(0.0f))); + if (subdivisionFactor) { + polygon->setSubdivisionFactor((int32_t)(subdivisionFactor.value_or(0.0f))); + } #endif } diff --git a/shared/src/map/layers/polygon/PolygonLayer.cpp b/shared/src/map/layers/polygon/PolygonLayer.cpp index 8fdfc51e8..530780c01 100644 --- a/shared/src/map/layers/polygon/PolygonLayer.cpp +++ b/shared/src/map/layers/polygon/PolygonLayer.cpp @@ -124,8 +124,10 @@ void PolygonLayer::addAll(const std::vector &polygons) { for (const auto &polygon : polygons) { #ifdef HARDWARE_TESSELLATION_SUPPORTED - auto shader = shaderFactory->createPolygonTessellatedShader(mapInterface->is3d()); - auto polygonGraphicsObject = objectFactory->createPolygonTessellated(shader->asShaderProgramInterface()); + 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()); diff --git a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp index c42d7331a..f3ad923a5 100644 --- a/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp +++ b/shared/src/map/layers/tiled/raster/Tiled2dMapRasterLayer.cpp @@ -305,8 +305,8 @@ std::vector sortedTileInfos(currentTileInfos.begin(), } else { #ifdef HARDWARE_TESSELLATION_SUPPORTED - auto rasterShader = shaderFactory->createQuadTessellatedShader(); - auto quad = graphicsFactory->createQuadTessellated(rasterShader->asShaderProgramInterface()); + 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(); auto quad = graphicsFactory->createQuad(rasterShader->asShaderProgramInterface()); 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 1dd244bf3..923a6dbaf 100644 --- a/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp +++ b/shared/src/map/layers/tiled/vector/tiles/raster/Tiled2dMapVectorRasterTile.cpp @@ -31,8 +31,9 @@ Tiled2dMapVectorRasterTile::Tiled2dMapVectorRasterTile(const std::weak_ptrgetShaderFactory()->createQuadTessellatedShader(); - auto quad = pMapInterface->getGraphicsObjectFactory()->createQuadTessellated(shader->asShaderProgramInterface()); + auto shader = pMapInterface->is3d() ? 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(); auto quad = pMapInterface->getGraphicsObjectFactory()->createQuad(shader->asShaderProgramInterface());