Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decode 3D texture coordinates during upgrade #146

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions src/tools/migration/GltfUpgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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;
}
}
6 changes: 4 additions & 2 deletions src/tools/migration/TileFormatsMigrationB3dm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ export class TileFormatsMigrationB3dm {
primitive,
batchIdToFeatureIdAccessor
);
if (propertyTable) {
featureId.setPropertyTable(propertyTable);
if (featureId) {
if (propertyTable) {
featureId.setPropertyTable(propertyTable);
}
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions src/tools/migration/TileTableDataToMeshFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -42,7 +42,7 @@ export class TileTableDataToMeshFeatures {
document: Document,
primitive: Primitive,
batchIdToFeatureIdAccessor: Map<Accessor, Accessor>
): FeatureId {
): FeatureId | undefined {
let batchIdAttribute = primitive.getAttribute("_BATCHID");
if (!batchIdAttribute) {
batchIdAttribute = primitive.getAttribute("BATCHID");
Expand All @@ -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;
}
}

Expand Down
Loading