Skip to content

Commit

Permalink
First pass of validation of EXT_mesh_features - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
javagl committed Sep 20, 2023
1 parent 6de58b6 commit e2df7a5
Show file tree
Hide file tree
Showing 8 changed files with 1,286 additions and 22 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
"3d-tiles-validator": "./build/main"
},
"dependencies": {
"3d-tiles-tools": "^0.3.0",
"3d-tiles-tools": "file:../3d-tiles-tools",
"cesium": "^1.97.0",
"gltf-validator": "^2.0.0-dev.3.9",
"minimatch": "^5.1.0",
"node-stream-zip": "^1.10.1",
"sharp": "^0.32.1",
"yargs": "^17.5.1"
},
"devDependencies": {
Expand Down
67 changes: 67 additions & 0 deletions src/issues/GltfExtensionValidationIssues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,71 @@ export class GltfExtensionValidationIssues {
const issue = new ValidationIssue(type, path, message, severity);
return issue;
}

/**
* Indicates that the extension referred to an element in the
* glTF assset that did not match the requirements of the
* extension specification. (For example, a 'texCoord' that
* referred to a VEC3 accessor). Further details should be
* encoded in the 'message'.
*
* @param path - The path for the `ValidationIssue`
* @param message - The message for the `ValidationIssue`
* @returns The `ValidationIssue`
*/
static INVALID_GLTF_STRUCTURE(path: string, message: string) {
const type = "INVALID_GLTF_STRUCTURE";
const severity = ValidationIssueSeverity.ERROR;
const issue = new ValidationIssue(type, path, message, severity);
return issue;
}

/**
* Indicates that the feature ID texture 'channels' property
* had a structure that did not match the actual image data,
* meaning that the `channels` array contained an element
* that was not smaller than the number of actual channels
* in the image.
*
* @param path - The path for the `ValidationIssue`
* @param message - The message for the `ValidationIssue`
* @returns The `ValidationIssue`
*/
static TEXTURE_CHANNELS_OUT_OF_RANGE(path: string, message: string) {
const type = "TEXTURE_CHANNELS_OUT_OF_RANGE";
const severity = ValidationIssueSeverity.ERROR;
const issue = new ValidationIssue(type, path, message, severity);
return issue;
}

/**
* Indicates that the featureCount of a feature ID did not
* match the actual number of IDs
*
* @param path - The path for the `ValidationIssue`
* @param message - The message for the `ValidationIssue`
* @returns The `ValidationIssue`
*/
static FEATURE_COUNT_MISMATCH(path: string, message: string) {
const type = "FEATURE_COUNT_MISMATCH";
const severity = ValidationIssueSeverity.ERROR;
const issue = new ValidationIssue(type, path, message, severity);
return issue;
}

/**
* Indicates that a feature ID texture sampler used a filtering
* method (`minFilter` or `magFilter`) that was not `undefined`
* and not 9728 (`NEAREST`).
*
* @param path - The path for the `ValidationIssue`
* @param message - The message for the `ValidationIssue`
* @returns The `ValidationIssue`
*/
static INVALID_FILTER_MODE(path: string, message: string) {
const type = "INVALID_FILTER_MODE";
const severity = ValidationIssueSeverity.ERROR;
const issue = new ValidationIssue(type, path, message, severity);
return issue;
}
}
142 changes: 142 additions & 0 deletions src/validation/gltfExtensions/Accessors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { BinaryBufferData } from "3d-tiles-tools";

/**
* Utility methods related to glTF accessors.
*
* NOTE: These methods are only intended for the use in the
* glTF extension validation. They make assumptions about
* the validity of the glTF asset (as established by the
* glTF Validator), and the structure of the glTF asset
* (as established by the extension validator).
*
* @internal
*/
export class Accessors {
/**
* Read the values of the given accessor into an array of numbers.
*
* This assumes that the accessor has the type SCALAR, and is
* valid (in terms of its bufferView etc), as validated with
* the glTF Validator.
*
* @param accessor - The glTF accessor
* @param gltf - The glTF object
* @param binaryBufferData - The binary buffer data of the glTF
* @returns The accessor values (or undefined if the input
* glTF data was invalid)
*/
static readScalarAccessorValues(
accessor: any,
gltf: any,
binaryBufferData: BinaryBufferData
): number[] | undefined {
const bufferViewIndex = accessor.bufferView;
const bufferViews = gltf.bufferViews || [];
const bufferView = bufferViews[bufferViewIndex];
const componentType = accessor.componentType;
let byteStride = bufferView.byteStride;
if (byteStride === undefined) {
byteStride = Accessors.sizeForComponentType(componentType);
if (byteStride === undefined) {
// Invalid component type, should have been
// detected by the glTF-Validator
return undefined;
}
}
const bufferViewData = binaryBufferData.bufferViewsData[bufferViewIndex];
const count = accessor.count;
const byteOffset = accessor.byteOffset;
const byteLength = count * byteStride;
const accessorData = bufferViewData.subarray(
byteOffset,
byteOffset + byteLength
);

const values = [];
for (let i = 0; i < count; i++) {
const value = Accessors.readValue(
accessorData,
i,
byteStride,
componentType
);
if (value === undefined) {
// Invalid component type, should have been
// detected by the glTF-Validator
return undefined;
}
values.push(value);
}
return values;
}

/**
* Returns the size in bytes for the given accessor component type,
* or undefined if the given component type is not valid.
*
* @param componentType - The component type
* @returns The size in bytes
*/
private static sizeForComponentType(
componentType: number
): number | undefined {
const BYTE = 5120;
const UNSIGNED_BYTE = 5121;
const SHORT = 5122;
const UNSIGNED_SHORT = 5123;
const UNSIGNED_INT = 5125;
const FLOAT = 5126;
switch (componentType) {
case BYTE:
case UNSIGNED_BYTE:
return 1;
case SHORT:
case UNSIGNED_SHORT:
return 2;
case UNSIGNED_INT:
case FLOAT:
return 4;
}
return undefined;
}

/**
* Read a single numeric value from a buffer that contains
* the accessor data
*
* @param buffer - The buffer
* @param index - The index
* @param byteStide - The byte stride
* @param componentType - The component type
* @returns The value
*/
private static readValue(
buffer: Buffer,
index: number,
byteStide: number,
componentType: number
): number | undefined {
const BYTE = 5120;
const UNSIGNED_BYTE = 5121;
const SHORT = 5122;
const UNSIGNED_SHORT = 5123;
const UNSIGNED_INT = 5125;
const FLOAT = 5126;
const byteOffset = index * byteStide;
switch (componentType) {
case BYTE:
return buffer.readInt8(byteOffset);
case UNSIGNED_BYTE:
return buffer.readUint8(byteOffset);
case SHORT:
return buffer.readInt16LE(byteOffset);
case UNSIGNED_SHORT:
return buffer.readUInt16LE(byteOffset);
case UNSIGNED_INT:
return buffer.readUInt32LE(byteOffset);
case FLOAT:
return buffer.readFloatLE(byteOffset);
}
return undefined;
}
}
Loading

0 comments on commit e2df7a5

Please sign in to comment.