@loaders.gl/gltf 4.3.0-alpha.7 → 4.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dist.dev.js +383 -66
- package/dist/dist.min.js +1 -1
- package/dist/glb-loader.d.ts.map +1 -1
- package/dist/glb-writer.js +1 -1
- package/dist/gltf-loader.d.ts.map +1 -1
- package/dist/gltf-writer.d.ts.map +1 -1
- package/dist/gltf-writer.js +4 -2
- package/dist/index.cjs +266 -16
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/api/gltf-extensions.d.ts +4 -1
- package/dist/lib/api/gltf-extensions.d.ts.map +1 -1
- package/dist/lib/api/gltf-extensions.js +11 -0
- package/dist/lib/api/gltf-scenegraph.d.ts.map +1 -1
- package/dist/lib/api/gltf-scenegraph.js +1 -2
- package/dist/lib/encoders/encode-gltf.d.ts +3 -1
- package/dist/lib/encoders/encode-gltf.d.ts.map +1 -1
- package/dist/lib/encoders/encode-gltf.js +3 -3
- package/dist/lib/extensions/EXT_mesh_features.d.ts +17 -1
- package/dist/lib/extensions/EXT_mesh_features.d.ts.map +1 -1
- package/dist/lib/extensions/EXT_mesh_features.js +109 -0
- package/dist/lib/extensions/EXT_structural_metadata.d.ts +19 -0
- package/dist/lib/extensions/EXT_structural_metadata.d.ts.map +1 -1
- package/dist/lib/extensions/EXT_structural_metadata.js +168 -0
- package/dist/lib/extensions/KHR_draco_mesh_compression.d.ts.map +1 -1
- package/dist/lib/utils/version.js +1 -1
- package/package.json +8 -8
- package/src/glb-writer.ts +1 -1
- package/src/gltf-writer.ts +8 -4
- package/src/index.ts +6 -0
- package/src/lib/api/gltf-extensions.ts +15 -1
- package/src/lib/api/gltf-scenegraph.ts +1 -3
- package/src/lib/encoders/encode-gltf.ts +11 -4
- package/src/lib/extensions/EXT_mesh_features.ts +130 -0
- package/src/lib/extensions/EXT_structural_metadata.ts +232 -1
- package/src/lib/extensions/KHR_draco_mesh_compression.ts +1 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
2
|
import {GLTF} from '../types/gltf-json-schema';
|
|
3
3
|
import type {GLTFLoaderOptions} from '../../gltf-loader';
|
|
4
|
+
import {GLTFWriterOptions} from '../../gltf-writer';
|
|
4
5
|
|
|
5
6
|
// GLTF 1.0 extensions (decode only)
|
|
6
7
|
// import * as KHR_binary_gltf from './KHR_draco_mesh_compression';
|
|
@@ -33,7 +34,7 @@ type GLTFExtensionPlugin = {
|
|
|
33
34
|
options: GLTFLoaderOptions,
|
|
34
35
|
context
|
|
35
36
|
) => Promise<void>;
|
|
36
|
-
encode?: (gltfData: {json: GLTF}, options:
|
|
37
|
+
encode?: (gltfData: {json: GLTF}, options: GLTFWriterOptions) => void;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
/**
|
|
@@ -61,6 +62,11 @@ export const EXTENSIONS: GLTFExtensionPlugin[] = [
|
|
|
61
62
|
EXT_feature_metadata
|
|
62
63
|
];
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* List of extensions processed by the GLTFWriter
|
|
67
|
+
*/
|
|
68
|
+
const EXTENSIONS_ENCODING: GLTFExtensionPlugin[] = [EXT_structural_metadata, EXT_mesh_features];
|
|
69
|
+
|
|
64
70
|
/** Call before any resource loading starts */
|
|
65
71
|
export function preprocessExtensions(gltf, options: GLTFLoaderOptions = {}, context?) {
|
|
66
72
|
const extensions = EXTENSIONS.filter((extension) => useExtension(extension.name, options));
|
|
@@ -79,6 +85,14 @@ export async function decodeExtensions(gltf, options: GLTFLoaderOptions = {}, co
|
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
/** Call before resource writing */
|
|
89
|
+
export function encodeExtensions(gltf, options: GLTFWriterOptions = {}) {
|
|
90
|
+
for (const extension of EXTENSIONS_ENCODING) {
|
|
91
|
+
gltf = extension.encode?.(gltf, options) ?? gltf;
|
|
92
|
+
}
|
|
93
|
+
return gltf;
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
function useExtension(extensionName: string, options: GLTFLoaderOptions) {
|
|
83
97
|
const excludes = options?.gltf?.excludeExtensions || {};
|
|
84
98
|
const exclude = extensionName in excludes && !excludes[extensionName];
|
|
@@ -557,9 +557,6 @@ export class GLTFScenegraph {
|
|
|
557
557
|
|
|
558
558
|
/** Pack the binary chunk */
|
|
559
559
|
createBinaryChunk(): void {
|
|
560
|
-
// Encoder expects this array undefined or empty
|
|
561
|
-
this.gltf.buffers = [];
|
|
562
|
-
|
|
563
560
|
// Allocate total array
|
|
564
561
|
const totalByteLength = this.byteLength;
|
|
565
562
|
const arrayBuffer = new ArrayBuffer(totalByteLength);
|
|
@@ -583,6 +580,7 @@ export class GLTFScenegraph {
|
|
|
583
580
|
|
|
584
581
|
// Put arrayBuffer to sourceBuffers for possible additional writing data in the chunk
|
|
585
582
|
this.sourceBuffers = [arrayBuffer];
|
|
583
|
+
this.gltf.buffers = [{arrayBuffer, byteOffset: 0, byteLength: arrayBuffer.byteLength}];
|
|
586
584
|
}
|
|
587
585
|
|
|
588
586
|
// PRIVATE
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {encodeGLBSync} from './encode-glb';
|
|
2
|
+
import {GLTFWriterOptions} from '../../gltf-writer';
|
|
3
|
+
import {GLTFWithBuffers} from '@loaders.gl/gltf';
|
|
2
4
|
|
|
3
5
|
export type GLTFEncodeOptions = Record<string, any>;
|
|
4
6
|
|
|
@@ -19,16 +21,21 @@ export type GLTFEncodeOptions = Record<string, any>;
|
|
|
19
21
|
* @param options
|
|
20
22
|
* @returns
|
|
21
23
|
*/
|
|
22
|
-
export function encodeGLTFSync(
|
|
23
|
-
|
|
24
|
+
export function encodeGLTFSync(
|
|
25
|
+
gltf: GLTFWithBuffers,
|
|
26
|
+
arrayBuffer: DataView | null,
|
|
27
|
+
byteOffset: number,
|
|
28
|
+
options: GLTFWriterOptions
|
|
29
|
+
) {
|
|
30
|
+
validateGltf(gltf);
|
|
24
31
|
|
|
25
32
|
// TODO: Copy buffers to binary
|
|
26
33
|
|
|
27
34
|
return encodeGLBSync(gltf, arrayBuffer, byteOffset, options);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
function
|
|
31
|
-
if (gltf.buffers && gltf.buffers.length >
|
|
37
|
+
function validateGltf(gltf) {
|
|
38
|
+
if (gltf.buffers && gltf.buffers.length > 1) {
|
|
32
39
|
throw new Error('encodeGLTF: multiple buffers not yet implemented');
|
|
33
40
|
}
|
|
34
41
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type {NumericArray} from '@loaders.gl/loader-utils';
|
|
5
5
|
import type {GLTF, GLTFMeshPrimitive} from '../types/gltf-json-schema';
|
|
6
6
|
import {GLTFLoaderOptions} from '../../gltf-loader';
|
|
7
|
+
import {GLTFWriterOptions} from '../../gltf-writer';
|
|
7
8
|
import type {
|
|
8
9
|
GLTF_EXT_mesh_features,
|
|
9
10
|
GLTF_EXT_mesh_features_featureId
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
|
|
12
13
|
import {GLTFScenegraph} from '../api/gltf-scenegraph';
|
|
13
14
|
import {getPrimitiveTextureData} from './utils/3d-tiles-utils';
|
|
15
|
+
import {getComponentTypeFromArray} from '../gltf-utils/gltf-utils';
|
|
14
16
|
|
|
15
17
|
const EXT_MESH_FEATURES_NAME = 'EXT_mesh_features';
|
|
16
18
|
export const name = EXT_MESH_FEATURES_NAME;
|
|
@@ -20,6 +22,13 @@ export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions)
|
|
|
20
22
|
decodeExtMeshFeatures(scenegraph, options);
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
export function encode(gltfData: {json: GLTF}, options: GLTFWriterOptions): {json: GLTF} {
|
|
26
|
+
const scenegraph = new GLTFScenegraph(gltfData);
|
|
27
|
+
encodeExtMeshFeatures(scenegraph, options);
|
|
28
|
+
scenegraph.createBinaryChunk();
|
|
29
|
+
return scenegraph.gltf;
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
/**
|
|
24
33
|
* Decodes feature metadata from extension.
|
|
25
34
|
* @param {GLTFScenegraph} scenegraph - Instance of the class for structured access to GLTF data.
|
|
@@ -91,3 +100,124 @@ function processMeshPrimitiveFeatures(
|
|
|
91
100
|
featureId.data = featureIdData;
|
|
92
101
|
}
|
|
93
102
|
}
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
Encoding data
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
function encodeExtMeshFeatures(scenegraph: GLTFScenegraph, options: GLTFWriterOptions) {
|
|
109
|
+
const meshes = scenegraph.gltf.json.meshes;
|
|
110
|
+
if (!meshes) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Iterate through all meshes/primitives.
|
|
115
|
+
for (const mesh of meshes) {
|
|
116
|
+
for (const primitive of mesh.primitives) {
|
|
117
|
+
encodeExtMeshFeaturesForPrimitive(scenegraph, primitive);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Creates ExtMeshFeatures, creates a featureId containing feature ids provided.
|
|
124
|
+
* @param scenegraph - Instance of the class for structured access to GLTF data.
|
|
125
|
+
* @param primitive - target primitive instance that will contain the extension
|
|
126
|
+
* @param featureIdArray - Array of feature id
|
|
127
|
+
* @param propertyTableIndex - index of the property table created by the ExtStructuralMetadata (optional).
|
|
128
|
+
*/
|
|
129
|
+
export function createExtMeshFeatures(
|
|
130
|
+
scenegraph: GLTFScenegraph,
|
|
131
|
+
primitive: GLTFMeshPrimitive,
|
|
132
|
+
featureIdArray: NumericArray,
|
|
133
|
+
propertyTableIndex?: number
|
|
134
|
+
) {
|
|
135
|
+
if (!primitive.extensions) {
|
|
136
|
+
primitive.extensions = {};
|
|
137
|
+
}
|
|
138
|
+
let extension = primitive.extensions[EXT_MESH_FEATURES_NAME] as GLTF_EXT_mesh_features;
|
|
139
|
+
if (!extension) {
|
|
140
|
+
extension = {featureIds: []};
|
|
141
|
+
primitive.extensions[EXT_MESH_FEATURES_NAME] = extension;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const {featureIds} = extension;
|
|
145
|
+
const featureId: GLTF_EXT_mesh_features_featureId = {
|
|
146
|
+
featureCount: featureIdArray.length,
|
|
147
|
+
propertyTable: propertyTableIndex,
|
|
148
|
+
data: featureIdArray
|
|
149
|
+
};
|
|
150
|
+
featureIds.push(featureId);
|
|
151
|
+
|
|
152
|
+
scenegraph.addObjectExtension(primitive, EXT_MESH_FEATURES_NAME, extension);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Encodes a feature ID set to extension.
|
|
157
|
+
* @param scenegraph - Instance of the class for structured access to GLTF data.
|
|
158
|
+
* @param primitive - Primitive that the data encoded belongs to.
|
|
159
|
+
* @see https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features
|
|
160
|
+
*/
|
|
161
|
+
function encodeExtMeshFeaturesForPrimitive(
|
|
162
|
+
scenegraph: GLTFScenegraph,
|
|
163
|
+
primitive: GLTFMeshPrimitive
|
|
164
|
+
) {
|
|
165
|
+
const extension = primitive.extensions?.[EXT_MESH_FEATURES_NAME] as GLTF_EXT_mesh_features;
|
|
166
|
+
if (!extension) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const featureIds: GLTF_EXT_mesh_features_featureId[] = extension.featureIds;
|
|
170
|
+
featureIds.forEach((featureId, elementIndex) => {
|
|
171
|
+
if (featureId.data) {
|
|
172
|
+
const {accessorKey, index} = createAccessorKey(primitive.attributes);
|
|
173
|
+
const typedArray = new Uint32Array(featureId.data as NumericArray);
|
|
174
|
+
|
|
175
|
+
// Clean up featureId object.
|
|
176
|
+
// Everything that could come from the original extension in case of round-trip decode/encode operations should be deleted.
|
|
177
|
+
// We need make sure the featureId object is clean.
|
|
178
|
+
featureIds[elementIndex] = {
|
|
179
|
+
featureCount: typedArray.length,
|
|
180
|
+
propertyTable: featureId.propertyTable,
|
|
181
|
+
attribute: index
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
scenegraph.gltf.buffers.push({
|
|
185
|
+
arrayBuffer: typedArray.buffer,
|
|
186
|
+
byteOffset: typedArray.byteOffset,
|
|
187
|
+
byteLength: typedArray.byteLength
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const bufferViewIndex = scenegraph.addBufferView(typedArray);
|
|
191
|
+
const accessorIndex = scenegraph.addAccessor(bufferViewIndex, {
|
|
192
|
+
size: 1,
|
|
193
|
+
componentType: getComponentTypeFromArray(typedArray),
|
|
194
|
+
count: typedArray.length
|
|
195
|
+
});
|
|
196
|
+
primitive.attributes[accessorKey] = accessorIndex;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Creates an accessor key for the attribute array provided.
|
|
203
|
+
* The generated key has a suffix (number) that is the next consequtive in the list of existing accessors.
|
|
204
|
+
* @param attributes - attribute array
|
|
205
|
+
* @returns accessor key and the key suffix (number) used in the key.
|
|
206
|
+
*/
|
|
207
|
+
function createAccessorKey(attributes: {[k: string]: number}) {
|
|
208
|
+
const prefix = '_FEATURE_ID_';
|
|
209
|
+
// Search for all "_FEATURE_ID_n" attribures in the primitive provided if any.
|
|
210
|
+
// If there are some, e.g. "_FEATURE_ID_0", "_FEATURE_ID_1",
|
|
211
|
+
// we will add a new one with the name "_FEATURE_ID_2"
|
|
212
|
+
const attrs = Object.keys(attributes).filter((item) => item.indexOf(prefix) === 0);
|
|
213
|
+
let max = -1;
|
|
214
|
+
for (const a of attrs) {
|
|
215
|
+
const n = Number(a.substring(prefix.length));
|
|
216
|
+
if (n > max) {
|
|
217
|
+
max = n;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
max++;
|
|
221
|
+
const accessorKey = `${prefix}${max}`;
|
|
222
|
+
return {accessorKey, index: max};
|
|
223
|
+
}
|
|
@@ -12,9 +12,11 @@ import type {
|
|
|
12
12
|
GLTF_EXT_structural_metadata_GLTF,
|
|
13
13
|
GLTF_EXT_structural_metadata_PropertyTexture,
|
|
14
14
|
GLTF_EXT_structural_metadata_PropertyTable_Property,
|
|
15
|
-
GLTF_EXT_structural_metadata_Primitive
|
|
15
|
+
GLTF_EXT_structural_metadata_Primitive,
|
|
16
|
+
GLTF_EXT_structural_metadata_Class
|
|
16
17
|
} from '../types/gltf-ext-structural-metadata-schema';
|
|
17
18
|
import type {GLTFLoaderOptions} from '../../gltf-loader';
|
|
19
|
+
import {GLTFWriterOptions} from '../../gltf-writer';
|
|
18
20
|
|
|
19
21
|
import {GLTFScenegraph} from '../api/gltf-scenegraph';
|
|
20
22
|
import {
|
|
@@ -37,6 +39,13 @@ export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions)
|
|
|
37
39
|
decodeExtStructuralMetadata(scenegraph, options);
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
export function encode(gltfData: {json: GLTF}, options: GLTFWriterOptions) {
|
|
43
|
+
const scenegraph = new GLTFScenegraph(gltfData);
|
|
44
|
+
encodeExtStructuralMetadata(scenegraph, options);
|
|
45
|
+
scenegraph.createBinaryChunk();
|
|
46
|
+
return scenegraph.gltf;
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
/*
|
|
41
50
|
// Example of the extension.
|
|
42
51
|
// See more info at https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata
|
|
@@ -701,3 +710,225 @@ function getEnumByValue(
|
|
|
701
710
|
|
|
702
711
|
return null;
|
|
703
712
|
}
|
|
713
|
+
|
|
714
|
+
/*
|
|
715
|
+
Encoding data
|
|
716
|
+
*/
|
|
717
|
+
|
|
718
|
+
export interface PropertyAttribute {
|
|
719
|
+
name: string;
|
|
720
|
+
elementType: string;
|
|
721
|
+
componentType?: string;
|
|
722
|
+
values: number[] | string[];
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const SCHEMA_CLASS_ID_DEFAULT = 'schemaClassId';
|
|
726
|
+
|
|
727
|
+
function encodeExtStructuralMetadata(scenegraph: GLTFScenegraph, options: GLTFWriterOptions) {
|
|
728
|
+
const extension: GLTF_EXT_structural_metadata_GLTF | null = scenegraph.getExtension(
|
|
729
|
+
EXT_STRUCTURAL_METADATA_NAME
|
|
730
|
+
);
|
|
731
|
+
if (!extension) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (extension.propertyTables) {
|
|
735
|
+
for (const table of extension.propertyTables) {
|
|
736
|
+
const classId = table.class;
|
|
737
|
+
const schemaClass = extension.schema?.classes?.[classId];
|
|
738
|
+
if (table.properties && schemaClass) {
|
|
739
|
+
encodeProperties(table, schemaClass, scenegraph);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function encodeProperties(
|
|
746
|
+
table: GLTF_EXT_structural_metadata_PropertyTable,
|
|
747
|
+
schemaClass: GLTF_EXT_structural_metadata_Class,
|
|
748
|
+
scenegraph: GLTFScenegraph
|
|
749
|
+
) {
|
|
750
|
+
for (const propertyName in table.properties) {
|
|
751
|
+
const data = table.properties[propertyName].data;
|
|
752
|
+
if (data) {
|
|
753
|
+
const classProperty = schemaClass.properties[propertyName];
|
|
754
|
+
if (classProperty) {
|
|
755
|
+
const tableProperty = createPropertyTableProperty(
|
|
756
|
+
data as number[] | string[],
|
|
757
|
+
classProperty,
|
|
758
|
+
scenegraph
|
|
759
|
+
);
|
|
760
|
+
// Override table property that came with "data"
|
|
761
|
+
table.properties[propertyName] = tableProperty;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Creates ExtStructuralMetadata, creates the schema and creates a property table containing feature data provided.
|
|
769
|
+
* @param scenegraph - Instance of the class for structured access to GLTF data.
|
|
770
|
+
* @param propertyAttributes - property attributes
|
|
771
|
+
* @param classId - classId to use for encoding metadata.
|
|
772
|
+
* @returns Index of the table created.
|
|
773
|
+
*/
|
|
774
|
+
export function createExtStructuralMetadata(
|
|
775
|
+
scenegraph: GLTFScenegraph,
|
|
776
|
+
propertyAttributes: PropertyAttribute[],
|
|
777
|
+
classId: string = SCHEMA_CLASS_ID_DEFAULT
|
|
778
|
+
): number {
|
|
779
|
+
let extension: GLTF_EXT_structural_metadata_GLTF | null = scenegraph.getExtension(
|
|
780
|
+
EXT_STRUCTURAL_METADATA_NAME
|
|
781
|
+
);
|
|
782
|
+
if (!extension) {
|
|
783
|
+
extension = scenegraph.addExtension(EXT_STRUCTURAL_METADATA_NAME);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
extension.schema = createSchema(propertyAttributes, classId, extension.schema);
|
|
787
|
+
const table = createPropertyTable(propertyAttributes, classId, extension.schema);
|
|
788
|
+
if (!extension.propertyTables) {
|
|
789
|
+
extension.propertyTables = [];
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
return extension.propertyTables.push(table) - 1; // index of the table
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function createSchema(
|
|
796
|
+
propertyAttributes: PropertyAttribute[],
|
|
797
|
+
classId: string,
|
|
798
|
+
schemaToUpdate?: GLTF_EXT_structural_metadata_Schema
|
|
799
|
+
): GLTF_EXT_structural_metadata_Schema {
|
|
800
|
+
const schema: GLTF_EXT_structural_metadata_Schema = schemaToUpdate ?? {
|
|
801
|
+
id: 'schema_id'
|
|
802
|
+
};
|
|
803
|
+
const schemaClass: GLTF_EXT_structural_metadata_Class = {
|
|
804
|
+
properties: {}
|
|
805
|
+
};
|
|
806
|
+
for (const attribute of propertyAttributes) {
|
|
807
|
+
const classProperty: GLTF_EXT_structural_metadata_ClassProperty = {
|
|
808
|
+
type: attribute.elementType,
|
|
809
|
+
componentType: attribute.componentType
|
|
810
|
+
};
|
|
811
|
+
schemaClass.properties[attribute.name] = classProperty;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
schema.classes = {};
|
|
815
|
+
schema.classes[classId] = schemaClass;
|
|
816
|
+
return schema;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function createPropertyTable(
|
|
820
|
+
propertyAttributes: PropertyAttribute[],
|
|
821
|
+
classId: string,
|
|
822
|
+
schema: GLTF_EXT_structural_metadata_Schema
|
|
823
|
+
): GLTF_EXT_structural_metadata_PropertyTable {
|
|
824
|
+
const table: GLTF_EXT_structural_metadata_PropertyTable = {
|
|
825
|
+
class: classId,
|
|
826
|
+
count: 0
|
|
827
|
+
};
|
|
828
|
+
// count is a number of rows in the table
|
|
829
|
+
let count = 0;
|
|
830
|
+
const schemaClass = schema.classes?.[classId];
|
|
831
|
+
|
|
832
|
+
for (const attribute of propertyAttributes) {
|
|
833
|
+
if (count === 0) {
|
|
834
|
+
count = attribute.values.length;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// The number of elements in all propertyAttributes must be the same
|
|
838
|
+
if (count !== attribute.values.length && attribute.values.length) {
|
|
839
|
+
throw new Error('Illegal values in attributes');
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
const classProperty = schemaClass?.properties[attribute.name];
|
|
843
|
+
if (classProperty) {
|
|
844
|
+
// const tableProperty = createPropertyTableProperty(attribute, classProperty, scenegraph);
|
|
845
|
+
if (!table.properties) {
|
|
846
|
+
table.properties = {};
|
|
847
|
+
}
|
|
848
|
+
// values is a required field. Its real value will be set while encoding data
|
|
849
|
+
table.properties[attribute.name] = {values: 0, data: attribute.values};
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
table.count = count;
|
|
854
|
+
return table;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function createPropertyTableProperty(
|
|
858
|
+
// attribute: PropertyAttribute,
|
|
859
|
+
values: number[] | string[],
|
|
860
|
+
classProperty: GLTF_EXT_structural_metadata_ClassProperty,
|
|
861
|
+
scenegraph: GLTFScenegraph
|
|
862
|
+
): GLTF_EXT_structural_metadata_PropertyTable_Property {
|
|
863
|
+
const prop: GLTF_EXT_structural_metadata_PropertyTable_Property = {values: 0};
|
|
864
|
+
|
|
865
|
+
if (classProperty.type === 'STRING') {
|
|
866
|
+
const {stringData, stringOffsets} = createPropertyDataString(values as string[]);
|
|
867
|
+
prop.stringOffsets = createBufferView(stringOffsets, scenegraph);
|
|
868
|
+
prop.values = createBufferView(stringData, scenegraph);
|
|
869
|
+
} else if (classProperty.type === 'SCALAR' && classProperty.componentType) {
|
|
870
|
+
const data = createPropertyDataScalar(values as number[], classProperty.componentType);
|
|
871
|
+
prop.values = createBufferView(data, scenegraph);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return prop;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const COMPONENT_TYPE_TO_ARRAY_CONSTRUCTOR = {
|
|
878
|
+
INT8: Int8Array,
|
|
879
|
+
UINT8: Uint8Array,
|
|
880
|
+
INT16: Int16Array,
|
|
881
|
+
UINT16: Uint16Array,
|
|
882
|
+
INT32: Int32Array,
|
|
883
|
+
UINT32: Uint32Array,
|
|
884
|
+
INT64: Int32Array,
|
|
885
|
+
UINT64: Uint32Array,
|
|
886
|
+
FLOAT32: Float32Array,
|
|
887
|
+
FLOAT64: Float64Array
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
function createPropertyDataScalar(array: number[], componentType: string): TypedArray {
|
|
891
|
+
const numberArray: number[] = [];
|
|
892
|
+
for (const value of array) {
|
|
893
|
+
numberArray.push(Number(value));
|
|
894
|
+
}
|
|
895
|
+
const Construct = COMPONENT_TYPE_TO_ARRAY_CONSTRUCTOR[componentType];
|
|
896
|
+
if (!Construct) {
|
|
897
|
+
throw new Error('Illegal component type');
|
|
898
|
+
}
|
|
899
|
+
return new Construct(numberArray);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function createPropertyDataString(strings: string[]): {
|
|
903
|
+
stringData: TypedArray;
|
|
904
|
+
stringOffsets: TypedArray;
|
|
905
|
+
} {
|
|
906
|
+
const utf8Encode = new TextEncoder();
|
|
907
|
+
const arr: Uint8Array[] = [];
|
|
908
|
+
let len = 0;
|
|
909
|
+
for (const str of strings) {
|
|
910
|
+
const uint8Array = utf8Encode.encode(str);
|
|
911
|
+
len += uint8Array.length;
|
|
912
|
+
arr.push(uint8Array);
|
|
913
|
+
}
|
|
914
|
+
const strArray = new Uint8Array(len);
|
|
915
|
+
const strOffsets: number[] = [];
|
|
916
|
+
let offset = 0;
|
|
917
|
+
for (const str of arr) {
|
|
918
|
+
strArray.set(str, offset);
|
|
919
|
+
strOffsets.push(offset);
|
|
920
|
+
offset += str.length;
|
|
921
|
+
}
|
|
922
|
+
strOffsets.push(offset); // The last offset represents the byte offset after the last string.
|
|
923
|
+
const stringOffsetsTypedArray = new Uint32Array(strOffsets); // Its length = len+1
|
|
924
|
+
return {stringData: strArray, stringOffsets: stringOffsetsTypedArray};
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function createBufferView(typedArray: TypedArray, scenegraph: GLTFScenegraph): number {
|
|
928
|
+
scenegraph.gltf.buffers.push({
|
|
929
|
+
arrayBuffer: typedArray.buffer,
|
|
930
|
+
byteOffset: typedArray.byteOffset,
|
|
931
|
+
byteLength: typedArray.byteLength
|
|
932
|
+
});
|
|
933
|
+
return scenegraph.addBufferView(typedArray);
|
|
934
|
+
}
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
import type {LoaderContext} from '@loaders.gl/loader-utils';
|
|
6
6
|
import {sliceArrayBuffer, parseFromContext} from '@loaders.gl/loader-utils';
|
|
7
7
|
|
|
8
|
-
import {DracoLoader} from '@loaders.gl/draco';
|
|
9
|
-
import {DracoLoaderOptions} from '@loaders.gl/draco';
|
|
8
|
+
import {DracoLoader, DracoLoaderOptions} from '@loaders.gl/draco';
|
|
10
9
|
|
|
11
10
|
import type {
|
|
12
11
|
GLTF,
|