diff --git a/.gitignore b/.gitignore index af8e1747..73bb4bcc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ CesiumForUnityNativeBindings/native/ *.user .DS_STORE .idea +obj.meta +bin.meta Reinterop.dll Reinterop.deps.json Reinterop.deps.json.meta diff --git a/CHANGES.md b/CHANGES.md index 9b9a5249..0853ab2f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +## ? - ? + +##### Fixes :wrench: + +- Textures and UV coordinates from glTFs are now flipped to properly comply with Unity's U-right, V-up convention. + ## v1.18.1 - 2025-10-01 This release updates [cesium-native](https://github.com/CesiumGS/cesium-native) from v0.51.0 to v0.52.1. See the [changelog](https://github.com/CesiumGS/cesium-native/blob/main/CHANGES.md) for a complete list of changes in cesium-native. diff --git a/native~/Runtime/src/TextureLoader.cpp b/native~/Runtime/src/TextureLoader.cpp index 9ae20be8..33281ce3 100644 --- a/native~/Runtime/src/TextureLoader.cpp +++ b/native~/Runtime/src/TextureLoader.cpp @@ -69,6 +69,29 @@ getUncompressedPixelFormat(const CesiumGltf::ImageAsset& image) { } } +/** + * Copy image data while flipping the data vertically. According to the glTF 2.0 + * spec, glTF stores textures in x-right, y-down coordinates. + * However, Unity uses x-right, y-up coordinates, so we need to flip the + * textures and UV coordinates. See loadPrimitive() in + * UnityPrepareRenderResources.cpp for the corresponding UV flip. + **/ +void copyAndFlipTexture( + std::uint8_t* pDst, + const std::byte* pSrc, + const size_t dataLength, + const size_t stride) { + assert( + (dataLength % stride == 0) && + "Image data size is not an even multiple of the given row pitch."); + + const int32_t height = static_cast(dataLength / stride); + for (int32_t i = height - 1; i >= 0; --i) { + memcpy(pDst, pSrc + i * stride, stride); + pDst += stride; + } +} + } // namespace UnityEngine::Texture @@ -100,7 +123,12 @@ TextureLoader::loadTexture(const CesiumGltf::ImageAsset& image, bool sRGB) { if (image.mipPositions.empty()) { // No mipmaps, copy the whole thing and then let Unity generate mipmaps on a // worker thread. - std::memcpy(pixels, image.pixelData.data(), image.pixelData.size()); + const size_t stride = image.pixelData.size() / image.height; + copyAndFlipTexture( + pixels, + image.pixelData.data(), + image.pixelData.size(), + stride); result.Apply(false, true); } else { // Copy the mipmaps explicitly. @@ -108,12 +136,17 @@ TextureLoader::loadTexture(const CesiumGltf::ImageAsset& image, bool sRGB) { const std::byte* pReadBuffer = image.pixelData.data(); for (const ImageAssetMipPosition& mip : image.mipPositions) { - size_t start = mip.byteOffset; - size_t end = mip.byteOffset + mip.byteSize; - if (start >= textureLength || end > textureLength) - continue; // invalid mip spec, ignore it - - std::memcpy(pWritePosition, pReadBuffer + start, mip.byteSize); + const size_t start = mip.byteOffset; + const size_t end = mip.byteOffset + mip.byteSize; + if (start >= textureLength || end > textureLength) { + // Invalid mip, skip this level. + continue; + } + copyAndFlipTexture( + pWritePosition, + pReadBuffer + start, + mip.byteSize, + mip.rowPitch); pWritePosition += mip.byteSize; } diff --git a/native~/Runtime/src/UnityPrepareRendererResources.cpp b/native~/Runtime/src/UnityPrepareRendererResources.cpp index dbeff78c..19f8d26e 100644 --- a/native~/Runtime/src/UnityPrepareRendererResources.cpp +++ b/native~/Runtime/src/UnityPrepareRendererResources.cpp @@ -605,8 +605,10 @@ void loadPrimitive( } for (uint32_t texCoordIndex = 0; texCoordIndex < numTexCoords; ++texCoordIndex) { - *reinterpret_cast(pWritePos) = - texCoordViews[texCoordIndex][vertexIndex]; + Vector2 texCoord = texCoordViews[texCoordIndex][vertexIndex]; + // Flip Y to comply with Unity's V-up coordinate convention + texCoord.y = 1 - texCoord.y; + *reinterpret_cast(pWritePos) = texCoord; pWritePos += sizeof(Vector2); } } @@ -614,22 +616,21 @@ void loadPrimitive( for (int64_t i = 0; i < vertexCount; ++i) { *reinterpret_cast(pWritePos) = positionView[i]; pWritePos += sizeof(Vector3); - if (hasNormals) { *reinterpret_cast(pWritePos) = normalView[i]; pWritePos += sizeof(Vector3); } - // Skip the slot allocated for vertex colors, we will fill them in // bulk later. if (hasVertexColors) { pWritePos += sizeof(uint32_t); } - for (uint32_t texCoordIndex = 0; texCoordIndex < numTexCoords; ++texCoordIndex) { - *reinterpret_cast(pWritePos) = - texCoordViews[texCoordIndex][i]; + Vector2 texCoord = texCoordViews[texCoordIndex][i]; + // Flip Y to comply with Unity's V-up coordinate convention + texCoord.y = 1 - texCoord.y; + *reinterpret_cast(pWritePos) = texCoord; pWritePos += sizeof(Vector2); } } diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index 9f6ae299..00e16452 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit 9f6ae299e2709f866db52c4be29b6c31e10718c8 +Subproject commit 00e164521e9783284930bb4a067fa7b0b0ce4349