From 38cf809279fc601ce0de8b36cb5fde0fc88cfa36 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 13 Aug 2024 18:23:09 +0200 Subject: [PATCH 1/2] Draft for decoding 3D texture coordinates --- src/tools/migration/GltfUpgrade.ts | 118 +++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/src/tools/migration/GltfUpgrade.ts b/src/tools/migration/GltfUpgrade.ts index 409f4206..f4d6fb35 100644 --- a/src/tools/migration/GltfUpgrade.ts +++ b/src/tools/migration/GltfUpgrade.ts @@ -42,6 +42,7 @@ export class GltfUpgrade { // oct-encoded (2D) normals into 3D if (gltfVersion < 2.0) { await document.transform(GltfUpgrade.octDecode2DNormals); + await document.transform(GltfUpgrade.decode3DTexCoords); document.setLogger(new Logger(Logger.Verbosity.WARN)); await document.transform(prune()); } @@ -166,4 +167,121 @@ export class GltfUpgrade { v[2] *= invLen; return v; } + + /** + * Check each mesh primitive in the given document, to see if it + * contains a VEC3/BYTE or VEC3/SHORT accessor for the TEXCOORD_X. + * If it does, then this accessor will be replaced by one that + * contains the decoded 2D texture coordinates. + * + * (Note that the old accessors might become unused by that. + * The document should afterwards be cleaned up with + * glTF-Transform 'prune()') + * + * @param document - The glTF-Transform document + */ + private static decode3DTexCoords(document: Document) { + const root = document.getRoot(); + const meshes = root.listMeshes(); + let decodedAccessorsCounter = 0; + for (const mesh of meshes) { + const primitives = mesh.listPrimitives(); + for (const primitive of primitives) { + const semantics = primitive.listSemantics(); + for (const semantic of semantics) { + if (semantic.startsWith("TEXCOORD_")) { + const texcoordAccessor = primitive.getAttribute(semantic); + if (texcoordAccessor) { + const type = texcoordAccessor.getType(); + const componentType = texcoordAccessor.getComponentType(); + const GL_BYTE = 5120; + const GL_SHORT = 5122; + if (type === "VEC3" && componentType === GL_BYTE) { + logger.debug("Decoding (VEC3/BYTE) texture coordinates..."); + const decodedTexCoordsAccessor = + GltfUpgrade.decodeTexCoordAccessor( + document, + texcoordAccessor, + 255.0 + ); + primitive.setAttribute(semantic, decodedTexCoordsAccessor); + decodedAccessorsCounter++; + } else if (type === "VEC3" && componentType === GL_SHORT) { + logger.debug("Decoding (VEC3/SHORT) texture coordinates..."); + const decodedTexCoordsAccessor = + GltfUpgrade.decodeTexCoordAccessor( + document, + texcoordAccessor, + 65535.0 + ); + primitive.setAttribute(semantic, decodedTexCoordsAccessor); + decodedAccessorsCounter++; + } + } + } + } + } + } + if (decodedAccessorsCounter > 0) { + logger.info( + `Decoded ${decodedAccessorsCounter} texture coordinate accessors to 2D` + ); + } + } + + /** + * Decode the encoded (3D) texture coordinates from the given accessor, and + * return the result as a new accessor. + * + * @param document - The glTF-Transform document + * @param encodedAccessor - The (VEC3) accessor containing the + * encoded 3D texture coordinate data + * @param range - The decoding range: 255 for BYTE, 65535 for SHORT + * @returns The decoded (VEC2/FLOAT) accessor. + */ + private static decodeTexCoordAccessor( + document: Document, + encodedAccessor: Accessor, + range: number + ) { + const decodedData: number[] = []; + + const count = encodedAccessor.getCount(); + for (let i = 0; i < count; i++) { + const encoded = [0, 0, 0]; + encodedAccessor.getElement(i, encoded); + const decoded = GltfUpgrade.decodeTexCoord(encoded, range); + decodedData.push(...decoded); + } + + const decodedAccessor = document.createAccessor(); + decodedAccessor.setType("VEC2"); + decodedAccessor.setArray(new Float32Array(decodedData)); + return decodedAccessor; + } + + /** + * Decode the given 3D texture coordinates from the given value + * range into 2D texture coordinates + * + * @param encoded - The encoded coordinates as a 3-element array + * @param range - The decoding range: 255 for BYTE, 65535 for SHORT + * @returns The decoded texture coordinates as a 2-element array + */ + private static decodeTexCoord(encoded: number[], range: number): number[] { + // Note: The deconding of 3D texture coordinates into 2D that + // took place in some shaders in glTF 1.0 was implemented + // like this: + // const float uvMultiplier = 0.0000305185; // 1/32767 + // v_texcoord0 = a_texcoord0.xy * uvMultiplier * (a_texcoord0.z+32767.0); + // This is an attempt to emulate this, involving some guesses: + + const halfRange = Math.floor(range / 2); + const uvMultiplier = 1.0 / halfRange; + const zFactor = encoded[2] + halfRange; + const x = encoded[0] * uvMultiplier * zFactor; + const y = encoded[1] * uvMultiplier * zFactor; + const result = [x, y]; + return result; + } } From 445ffa964ab915183877cd2167c2036003459f8e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 13 Aug 2024 18:23:27 +0200 Subject: [PATCH 2/2] Preliminarily ignore missing BATCHID --- src/tools/migration/TileFormatsMigrationB3dm.ts | 6 ++++-- src/tools/migration/TileTableDataToMeshFeatures.ts | 13 ++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/tools/migration/TileFormatsMigrationB3dm.ts b/src/tools/migration/TileFormatsMigrationB3dm.ts index f7e60cdf..3b0b456d 100644 --- a/src/tools/migration/TileFormatsMigrationB3dm.ts +++ b/src/tools/migration/TileFormatsMigrationB3dm.ts @@ -90,8 +90,10 @@ export class TileFormatsMigrationB3dm { primitive, batchIdToFeatureIdAccessor ); - if (propertyTable) { - featureId.setPropertyTable(propertyTable); + if (featureId) { + if (propertyTable) { + featureId.setPropertyTable(propertyTable); + } } } } diff --git a/src/tools/migration/TileTableDataToMeshFeatures.ts b/src/tools/migration/TileTableDataToMeshFeatures.ts index b9de9eeb..18cf7795 100644 --- a/src/tools/migration/TileTableDataToMeshFeatures.ts +++ b/src/tools/migration/TileTableDataToMeshFeatures.ts @@ -23,7 +23,7 @@ export class TileTableDataToMeshFeatures { * extension that is associated with this primitive, storing * the former batch ID attribute as a new `_FEATURE_ID_0` attribute. * - * Note that this will remove the former batch ID attributes + * Note that this will set the former batch ID attributes * in the given primitive to `null`, but it will not dispose * the corresponding accessors. These have to be disposed * after all primitives have been processed. @@ -42,7 +42,7 @@ export class TileTableDataToMeshFeatures { document: Document, primitive: Primitive, batchIdToFeatureIdAccessor: Map - ): FeatureId { + ): FeatureId | undefined { let batchIdAttribute = primitive.getAttribute("_BATCHID"); if (!batchIdAttribute) { batchIdAttribute = primitive.getAttribute("BATCHID"); @@ -52,9 +52,12 @@ export class TileTableDataToMeshFeatures { "should be _BATCHID, starting with an underscore" ); } else { - throw new TileFormatError( - "The primitive did not contain a _BATCHID attribute" - ); + // XXX Preliminarily ignore this + //throw new TileFormatError( + // "The primitive did not contain a _BATCHID attribute" + //); + logger.warn("The primitive did not contain a _BATCHID attribute"); + return undefined; } }