diff --git a/CHANGES.md b/CHANGES.md index 0853ab2f..aa1b7452 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ##### Fixes :wrench: - Textures and UV coordinates from glTFs are now flipped to properly comply with Unity's U-right, V-up convention. +- Vertex tangents are now properly imported when present in glTFs. ## v1.18.1 - 2025-10-01 diff --git a/CONTRIBUTING.md.meta b/CONTRIBUTING.md.meta new file mode 100644 index 00000000..4c20f83c --- /dev/null +++ b/CONTRIBUTING.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3fd6df72dd74e4b4a9a1096d82b3e1b5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/native~/Runtime/src/UnityPrepareRendererResources.cpp b/native~/Runtime/src/UnityPrepareRendererResources.cpp index 19f8d26e..1ef9c51d 100644 --- a/native~/Runtime/src/UnityPrepareRendererResources.cpp +++ b/native~/Runtime/src/UnityPrepareRendererResources.cpp @@ -369,6 +369,15 @@ void loadPrimitive( computeFlatNormals = hasNormals = true; } + bool hasTangents = false; + auto tangentAccessorIt = primitive.attributes.find("TANGENT"); + AccessorView tangentView; + if (tangentAcccessorIt != primitive.attributes.end()) { + tangentView = + AccessorView(gltf, tangentAcccessorIt->second); + hasTangents = tangentView.status() == AccessorViewStatus::Valid; + } + // Check if we need to upgrade to a large index type to accommodate the // larger number of vertices we need for flat normals. if (computeFlatNormals && indexFormat == IndexFormat::UInt16 && @@ -450,6 +459,15 @@ void loadPrimitive( ++numberOfAttributes; } + if (hasTangents) { + assert(numberOfAttributes < MAX_ATTRIBUTES); + descriptor[numberOfAttributes].attribute = VertexAttribute::Tangent; + descriptor[numberOfAttributes].format = VertexAttributeFormat::Float32; + descriptor[numberOfAttributes].dimension = 4; + descriptor[numberOfAttributes].stream = streamIndex; + ++numberOfAttributes; + } + // Add the COLOR_0 attribute, if it exists. auto colorAccessorIt = primitive.attributes.find("COLOR_0"); bool hasVertexColors = @@ -554,9 +572,9 @@ void loadPrimitive( attributes.Item(i, descriptor[i]); } - int32_t vertexCount = computeFlatNormals - ? indexCount - : static_cast(positionView.size()); + const int32_t vertexCount = computeFlatNormals + ? indexCount + : static_cast(positionView.size()); meshData.SetVertexBufferParams(vertexCount, attributes); NativeArray1 nativeVertexBuffer = @@ -571,21 +589,30 @@ void loadPrimitive( // The vertex layout will be as follows: // 1. position // 2. normals (skip if N/A) - // 3. vertex colors (skip if N/A) - // 4. texcoords (first all TEXCOORD_i, then all _CESIUMOVERLAY_i) + // 3. tangents (skip if N/A) + // 4. vertex colors (skip if N/A) + // 5. texcoords (first all TEXCOORD_i, then all _CESIUMOVERLAY_i) size_t stride = sizeof(Vector3); - size_t normalByteOffset, colorByteOffset; + size_t normalByteOffset; if (hasNormals) { normalByteOffset = stride; stride += sizeof(Vector3); } + + if (hasTangents) { + stride += sizeof(Vector4); + } + + size_t colorByteOffset; if (hasVertexColors) { colorByteOffset = stride; stride += sizeof(uint32_t); } + stride += numTexCoords * sizeof(Vector2); + const bool duplicateVertices = computeFlatNormals; if (computeFlatNormals) { ::computeFlatNormals( pWritePos + normalByteOffset, @@ -593,52 +620,45 @@ void loadPrimitive( indices, indexCount, positionView); - for (int64_t i = 0; i < vertexCount; ++i) { - TIndex vertexIndex = indices[i]; - *reinterpret_cast(pWritePos) = positionView[vertexIndex]; - // skip position and normal - pWritePos += 2 * 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) { - 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); - } - } - } else { - for (int64_t i = 0; i < vertexCount; ++i) { - *reinterpret_cast(pWritePos) = positionView[i]; + } + + for (int64_t i = 0; i < vertexCount; ++i) { + const TIndex vertexIndex = duplicateVertices ? indices[i] : i; + *reinterpret_cast(pWritePos) = positionView[vertexIndex]; + pWritePos += sizeof(Vector3); + + if (computeFlatNormals) { + // skip computed normal + pWritePos += sizeof(Vector3); + } else if (hasNormals) { + *reinterpret_cast(pWritePos) = normalView[vertexIndex]; 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) { - 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); - } + } + + if (hasTangents) { + *reinterpret_cast(pWritePos) = tangentView[vertexIndex]; + pWritePos += sizeof(Vector4); + } + + // 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) { + 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); } } // Fill in vertex colors separately, if they exist. if (hasVertexColors) { - // Color comes after position and normal. + // Color comes after position, normal and tangent createAccessorView( gltf, colorAccessorIt->second, @@ -650,7 +670,7 @@ void loadPrimitive( indices}); } - if (computeFlatNormals) { + if (duplicateVertices) { // rewrite indices for (TIndex i = 0; i < indexCount; i++) { indices[i] = i;