t3d-3dtiles 0.1.0

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.
Files changed (83) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +41 -0
  3. package/build/t3d.3dtiles.js +5418 -0
  4. package/build/t3d.3dtiles.min.js +2 -0
  5. package/build/t3d.3dtiles.module.js +6183 -0
  6. package/examples/jsm/CesiumIon.js +30 -0
  7. package/examples/jsm/Tiles3DDebugger.js +219 -0
  8. package/examples/jsm/Tiles3DUtils.js +32 -0
  9. package/examples/jsm/helpers/LineSegmentHelper.js +74 -0
  10. package/examples/jsm/helpers/OBBHelper.js +48 -0
  11. package/examples/jsm/helpers/Tiles3DHelper.js +135 -0
  12. package/examples/jsm/math/Intersects.js +100 -0
  13. package/examples/jsm/math/LineSegment.js +234 -0
  14. package/package.json +57 -0
  15. package/src/Tiles3D.js +402 -0
  16. package/src/libs/ImageBitmapLoader.js +56 -0
  17. package/src/libs/glTF/Constants.js +85 -0
  18. package/src/libs/glTF/GLTFLoader.js +169 -0
  19. package/src/libs/glTF/GLTFResource.js +28 -0
  20. package/src/libs/glTF/GLTFUtils.js +131 -0
  21. package/src/libs/glTF/extensions/EXT_meshopt_compression.js +35 -0
  22. package/src/libs/glTF/extensions/KHR_draco_mesh_compression.js +54 -0
  23. package/src/libs/glTF/extensions/KHR_lights_punctual.js +53 -0
  24. package/src/libs/glTF/extensions/KHR_materials_clearcoat.js +42 -0
  25. package/src/libs/glTF/extensions/KHR_materials_pbrSpecularGlossiness.js +53 -0
  26. package/src/libs/glTF/extensions/KHR_materials_unlit.js +13 -0
  27. package/src/libs/glTF/extensions/KHR_texture_basisu.js +14 -0
  28. package/src/libs/glTF/extensions/KHR_texture_transform.js +31 -0
  29. package/src/libs/glTF/parsers/AccessorParser.js +99 -0
  30. package/src/libs/glTF/parsers/AnimationParser.js +118 -0
  31. package/src/libs/glTF/parsers/BufferParser.js +35 -0
  32. package/src/libs/glTF/parsers/BufferViewParser.js +27 -0
  33. package/src/libs/glTF/parsers/ImageParser.js +97 -0
  34. package/src/libs/glTF/parsers/IndexParser.js +22 -0
  35. package/src/libs/glTF/parsers/MaterialParser.js +146 -0
  36. package/src/libs/glTF/parsers/NodeParser.js +145 -0
  37. package/src/libs/glTF/parsers/PrimitiveParser.js +289 -0
  38. package/src/libs/glTF/parsers/ReferenceParser.js +67 -0
  39. package/src/libs/glTF/parsers/SceneParser.js +41 -0
  40. package/src/libs/glTF/parsers/SkinParser.js +53 -0
  41. package/src/libs/glTF/parsers/TextureParser.js +71 -0
  42. package/src/libs/glTF/parsers/Validator.js +16 -0
  43. package/src/loaders/B3DMLoader.js +49 -0
  44. package/src/loaders/CMPTLoader.js +48 -0
  45. package/src/loaders/I3DMLoader.js +49 -0
  46. package/src/loaders/PNTSLoader.js +20 -0
  47. package/src/loaders/TileGLTFLoader.js +17 -0
  48. package/src/loaders/parsers/HeaderParser.js +104 -0
  49. package/src/loaders/parsers/LoadParser.js +22 -0
  50. package/src/loaders/parsers/TableParser.js +66 -0
  51. package/src/loaders/parsers/b3dm/B3DMParser.js +18 -0
  52. package/src/loaders/parsers/b3dm/B3DMRootParser.js +22 -0
  53. package/src/loaders/parsers/b3dm/MaterialParser.js +156 -0
  54. package/src/loaders/parsers/cmpt/CMPTParser.js +37 -0
  55. package/src/loaders/parsers/cmpt/CMPTRootParser.js +44 -0
  56. package/src/loaders/parsers/gltf/IndexParser.js +24 -0
  57. package/src/loaders/parsers/i3dm/I3DMParser.js +28 -0
  58. package/src/loaders/parsers/i3dm/I3DMRootParser.js +123 -0
  59. package/src/loaders/parsers/i3dm/MaterialParser.js +152 -0
  60. package/src/loaders/parsers/i3dm/PrimitiveParser.js +291 -0
  61. package/src/loaders/parsers/pnts/PNTSRootParser.js +69 -0
  62. package/src/main.js +14 -0
  63. package/src/materials/InstancedBasicMaterial.js +32 -0
  64. package/src/materials/InstancedPBRMaterial.js +32 -0
  65. package/src/materials/InstancedShaderChunks.js +23 -0
  66. package/src/math/Ellipsoid.js +41 -0
  67. package/src/math/EllipsoidRegion.js +160 -0
  68. package/src/math/FastFrustum.js +49 -0
  69. package/src/math/GeoUtils.js +31 -0
  70. package/src/math/OBB.js +395 -0
  71. package/src/math/TileBoundingVolume.js +172 -0
  72. package/src/math/TileOBB.js +103 -0
  73. package/src/utils/BatchTable.js +14 -0
  74. package/src/utils/CameraList.js +131 -0
  75. package/src/utils/FeatureTable.js +107 -0
  76. package/src/utils/IntersectionUtils.js +136 -0
  77. package/src/utils/LRUCache.js +159 -0
  78. package/src/utils/ModelLoader.js +150 -0
  79. package/src/utils/PriorityQueue.js +102 -0
  80. package/src/utils/RequestState.js +17 -0
  81. package/src/utils/TilesLoader.js +386 -0
  82. package/src/utils/TilesScheduler.js +375 -0
  83. package/src/utils/Utils.js +43 -0
@@ -0,0 +1,145 @@
1
+ import { Bone, Camera, Object3D, Mesh, SkinnedMesh, Vector4 } from 't3d';
2
+ import { KHR_lights_punctual as _KHR_lights_punctual } from '../extensions/KHR_lights_punctual.js';
3
+
4
+ export class NodeParser {
5
+
6
+ static parse(context) {
7
+ const {
8
+ gltf: { nodes: gltfNodes, cameras: gltfCameras, extensions: gltfExtensions }
9
+ } = context;
10
+
11
+ if (!gltfNodes) return;
12
+
13
+ const cameras = [];
14
+ const lights = [];
15
+ const nodes = gltfNodes.map(gltfNode => {
16
+ const {
17
+ matrix, translation, rotation, scale,
18
+ camera: cameraID, mesh: meshID,
19
+ extensions = {}
20
+ } = gltfNode;
21
+ const { KHR_lights_punctual } = extensions;
22
+
23
+ let node = null;
24
+
25
+ if (gltfNode.isBone) {
26
+ // .isBone isn't in glTF spec. Marked in IndexParser
27
+ node = new Bone();
28
+ } else if (meshID !== undefined) {
29
+ node = createMesh(context, gltfNode);
30
+ } else if (cameraID !== undefined) {
31
+ node = createCamera(gltfCameras[cameraID]);
32
+ cameras.push(node);
33
+ } else if (KHR_lights_punctual) {
34
+ const lightIndex = KHR_lights_punctual.light;
35
+ const gltfLights = gltfExtensions.KHR_lights_punctual.lights;
36
+ node = _KHR_lights_punctual.getLight(gltfLights[lightIndex]);
37
+ lights.push(node);
38
+ } else {
39
+ node = new Object3D();
40
+ }
41
+
42
+ node.name = gltfNode.name || '';
43
+ if (!!node.name && node.children.length > 0) {
44
+ for (let i = 0; i < node.children.length; i++) {
45
+ node.children[i].name = node.name + '_' + i;
46
+ }
47
+ }
48
+
49
+ if (matrix !== undefined) {
50
+ node.matrix.fromArray(matrix);
51
+ node.matrix.decompose(node.position, node.quaternion, node.scale);
52
+ } else {
53
+ if (translation !== undefined) {
54
+ node.position.fromArray(translation);
55
+ }
56
+
57
+ if (rotation !== undefined) {
58
+ node.quaternion.fromArray(rotation);
59
+ }
60
+
61
+ if (scale !== undefined) {
62
+ node.scale.fromArray(scale);
63
+ }
64
+ }
65
+
66
+ return node;
67
+ });
68
+
69
+ context.nodes = nodes;
70
+ context.cameras = cameras;
71
+ context.lights = lights;
72
+ }
73
+
74
+ }
75
+
76
+ function createCamera(cameraDef) {
77
+ const { orthographic, perspective, type } = cameraDef;
78
+
79
+ const camera = new Camera();
80
+
81
+ if (type == 'perspective') {
82
+ const { aspectRatio, yfov, zfar, znear } = perspective;
83
+ camera.setPerspective(yfov, aspectRatio || 1, znear || 1, zfar || 2e6);
84
+ } else if (type == 'orthographic') {
85
+ const { xmag, ymag, zfar, znear } = orthographic;
86
+ // https:// github.com/KhronosGroup/glTF/issues/1663
87
+ camera.setOrtho(-xmag, xmag, -ymag, ymag, znear || 1, zfar || 2e6);
88
+ }
89
+
90
+ return camera;
91
+ }
92
+
93
+ function createMesh(context, gltfNode) {
94
+ const { primitives } = context;
95
+
96
+ const { mesh: meshID, skin: skinID } = gltfNode;
97
+
98
+ const meshes = primitives[meshID].map(primitive => {
99
+ const { geometry, material, weights } = primitive;
100
+
101
+ let mesh;
102
+
103
+ if (skinID !== undefined) {
104
+ mesh = new SkinnedMesh(geometry, material);
105
+
106
+ if (geometry.attributes.skinWeight && !geometry.attributes.skinWeight.normalized) {
107
+ normalizeSkinWeights(geometry.attributes.skinWeight);
108
+ }
109
+ } else {
110
+ mesh = new Mesh(geometry, material);
111
+
112
+ if (weights) {
113
+ mesh.morphTargetInfluences = weights.slice();
114
+ }
115
+ }
116
+
117
+ return mesh;
118
+ });
119
+
120
+ if (meshes.length > 1) {
121
+ const parent = new Object3D();
122
+ meshes.forEach(mesh => parent.add(mesh));
123
+ return parent;
124
+ } else {
125
+ return meshes[0];
126
+ }
127
+ }
128
+
129
+ const _vec4_1 = new Vector4();
130
+
131
+ function normalizeSkinWeights(skinWeight) {
132
+ const offset = skinWeight.offset;
133
+ const buffer = skinWeight.buffer;
134
+ const stride = buffer.stride;
135
+ for (let i = 0, l = buffer.count; i < l; i++) {
136
+ _vec4_1.fromArray(buffer.array, i * stride + offset);
137
+ const scale = 1.0 / _vec4_1.getManhattanLength();
138
+ if (scale !== Infinity) {
139
+ _vec4_1.multiplyScalar(scale);
140
+ } else {
141
+ _vec4_1.set(1, 0, 0, 0); // do something reasonable
142
+ }
143
+ _vec4_1.toArray(buffer.array, i * stride + offset);
144
+ }
145
+ }
@@ -0,0 +1,289 @@
1
+ import { Geometry, PBRMaterial, VERTEX_COLOR, SHADING_TYPE, PointsMaterial, Material, BasicMaterial } from 't3d';
2
+ import { GLTFUtils } from '../GLTFUtils.js';
3
+ import { ATTRIBUTES, ACCESSOR_COMPONENT_TYPES, WEBGL_DRAW_MODES } from '../Constants.js';
4
+ import { KHR_draco_mesh_compression as _KHR_draco_mesh_compression } from '../extensions/KHR_draco_mesh_compression.js';
5
+
6
+ export class PrimitiveParser {
7
+
8
+ static parse(context, loader) {
9
+ const { gltf, accessors, materials, bufferViews } = context;
10
+
11
+ if (!gltf.meshes) return;
12
+
13
+ const materialCache = new Map();
14
+ const geometryPromiseCache = new Map();
15
+
16
+ const meshPromises = [];
17
+ for (let i = 0; i < gltf.meshes.length; i++) {
18
+ const gltfMesh = gltf.meshes[i];
19
+
20
+ const primitivePromises = [];
21
+ for (let j = 0; j < gltfMesh.primitives.length; j++) {
22
+ const gltfPrimitive = gltfMesh.primitives[j];
23
+ const {
24
+ extensions = {},
25
+ mode,
26
+ material
27
+ } = gltfPrimitive;
28
+ const { KHR_draco_mesh_compression } = extensions;
29
+
30
+ let geometryPromise;
31
+
32
+ const geometryKey = createGeometryKey(gltfPrimitive);
33
+ if (geometryPromiseCache.has(geometryKey)) {
34
+ geometryPromise = geometryPromiseCache.get(geometryKey);
35
+ } else {
36
+ if (KHR_draco_mesh_compression) {
37
+ geometryPromise = _KHR_draco_mesh_compression.getGeometry(KHR_draco_mesh_compression, bufferViews, gltfPrimitive.attributes, gltf.accessors, loader.getDRACOLoader());
38
+ } else {
39
+ geometryPromise = Promise.resolve(new Geometry());
40
+ }
41
+
42
+ geometryPromise = geometryPromise.then(geometry => {
43
+ parseGeometryFromGLTFPrimitive(geometry, gltfPrimitive, gltf, accessors);
44
+ return geometry;
45
+ });
46
+
47
+ geometryPromiseCache.set(geometryKey, geometryPromise);
48
+ }
49
+
50
+ const primitivePromise = geometryPromise.then(geometry => {
51
+ const primitive = {
52
+ mode,
53
+ geometry,
54
+ material: material === undefined ? new PBRMaterial() : materials[material],
55
+ weights: (Object.keys(geometry.morphAttributes).length > 0 && gltfMesh.weights) ? gltfMesh.weights.slice(0) : undefined,
56
+ skinned: gltfMesh.isSkinned
57
+ };
58
+ assignFinalMaterial(primitive, materialCache);
59
+ return primitive;
60
+ });
61
+
62
+ primitivePromises.push(primitivePromise);
63
+ }
64
+ meshPromises.push(Promise.all(primitivePromises));
65
+ }
66
+
67
+ materialCache.clear();
68
+ geometryPromiseCache.clear();
69
+
70
+ return Promise.all(meshPromises).then(primitives => {
71
+ context.primitives = primitives;
72
+ });
73
+ }
74
+
75
+ }
76
+
77
+ function parseGeometryFromGLTFPrimitive(geometry, gltfPrimitive, gltf, accessors) {
78
+ const { attributes, indices, targets } = gltfPrimitive;
79
+
80
+ // set attributes
81
+
82
+ for (const attributeSemantic in attributes) {
83
+ const accessorIdx = attributes[attributeSemantic];
84
+
85
+ const attributeName = ATTRIBUTES[attributeSemantic] === undefined ? attributeSemantic : ATTRIBUTES[attributeSemantic];
86
+ // Skip attributes already provided by e.g. Draco extension.
87
+ if (attributeName in geometry.attributes) continue;
88
+
89
+ geometry.addAttribute(attributeName, accessors[accessorIdx]);
90
+ }
91
+
92
+ // set index
93
+
94
+ if (indices !== undefined && !geometry.index) {
95
+ geometry.setIndex(accessors[indices]);
96
+ }
97
+
98
+ // compute bounds
99
+
100
+ const { boundingBox, boundingSphere } = geometry;
101
+ if (attributes.POSITION !== undefined) {
102
+ const accessorIdx = attributes.POSITION;
103
+ const accessor = gltf.accessors[accessorIdx];
104
+
105
+ if (accessor.min && accessor.max) {
106
+ boundingBox.min.fromArray(accessor.min);
107
+ boundingBox.max.fromArray(accessor.max);
108
+
109
+ if (accessor.normalized) {
110
+ const boxScale = GLTFUtils.getNormalizedComponentScale(ACCESSOR_COMPONENT_TYPES[accessor.componentType]);
111
+ boundingBox.min.multiplyScalar(boxScale);
112
+ boundingBox.max.multiplyScalar(boxScale);
113
+ }
114
+ } else {
115
+ geometry.computeBoundingBox();
116
+ }
117
+ } else {
118
+ geometry.computeBoundingBox();
119
+ }
120
+ boundingBox.getCenter(boundingSphere.center);
121
+ boundingSphere.radius = boundingBox.min.distanceTo(boundingBox.max) / 2;
122
+
123
+ // set morph targets
124
+
125
+ if (targets) {
126
+ let hasMorphPosition = false;
127
+ let hasMorphNormal = false;
128
+
129
+ for (let i = 0, il = targets.length; i < il; i++) {
130
+ const target = targets[i];
131
+
132
+ if (target.POSITION !== undefined) hasMorphPosition = true;
133
+ if (target.NORMAL !== undefined) hasMorphNormal = true;
134
+
135
+ if (hasMorphPosition && hasMorphNormal) break;
136
+ }
137
+
138
+ if (hasMorphPosition || hasMorphNormal) {
139
+ const morphPositions = [];
140
+ const morphNormals = [];
141
+
142
+ for (let i = 0, il = targets.length; i < il; i++) {
143
+ const target = targets[i];
144
+
145
+ if (hasMorphPosition) {
146
+ morphPositions.push(target.POSITION !== undefined ? accessors[target.POSITION] : geometry.attributes[ATTRIBUTES.POSITION]);
147
+ }
148
+ if (hasMorphNormal) {
149
+ morphNormals.push(target.NORMAL !== undefined ? accessors[target.NORMAL] : geometry.attributes[ATTRIBUTES.NORMAL]);
150
+ }
151
+ }
152
+
153
+ if (hasMorphPosition) {
154
+ geometry.morphAttributes.position = morphPositions;
155
+ }
156
+ if (hasMorphNormal) {
157
+ geometry.morphAttributes.normal = morphNormals;
158
+ }
159
+ }
160
+ }
161
+
162
+ return geometry;
163
+ }
164
+
165
+ function assignFinalMaterial(primitive, materialCache) {
166
+ let { geometry, material, skinned, mode } = primitive;
167
+
168
+ // If the material will be modified later on, clone it now.
169
+ const useVertexTangents = geometry.attributes[ATTRIBUTES.TANGENT] !== undefined;
170
+ const useVertexColors = geometry.attributes[ATTRIBUTES.COLOR_0] !== undefined;
171
+ const useFlatShading = geometry.attributes[ATTRIBUTES.NORMAL] === undefined;
172
+ const useSkinning = skinned;
173
+
174
+ if (mode === WEBGL_DRAW_MODES.POINTS) {
175
+ const cacheKey = 'PointsMaterial:' + material.id;
176
+ let pointsMaterial = materialCache.get(cacheKey);
177
+ if (!pointsMaterial) {
178
+ pointsMaterial = new PointsMaterial();
179
+ Material.prototype.copy.call(pointsMaterial, material);
180
+ pointsMaterial.diffuse.copy(material.diffuse);
181
+ pointsMaterial.diffuseMap = material.map;
182
+ pointsMaterial.drawMode = mode;
183
+ pointsMaterial.acceptLight = false; // PointsMaterial doesn't support lights yet
184
+ materialCache.set(cacheKey, pointsMaterial);
185
+ }
186
+ material = pointsMaterial;
187
+ } else if (mode === WEBGL_DRAW_MODES.LINES || mode === WEBGL_DRAW_MODES.LINE_STRIP || mode === WEBGL_DRAW_MODES.LINE_LOOP) {
188
+ const cacheKey = 'BasicMaterial:' + material.id;
189
+ let basicMaterial = materialCache.get(cacheKey);
190
+ if (!basicMaterial) {
191
+ basicMaterial = new BasicMaterial();
192
+ basicMaterial.envMap = undefined; // force close env map
193
+ basicMaterial.diffuse.copy(material.diffuse);
194
+ basicMaterial.diffuseMap = material.diffuseMap;
195
+ basicMaterial.drawMode = mode;
196
+ materialCache.set(cacheKey, basicMaterial);
197
+ }
198
+ material = basicMaterial;
199
+ } else if (mode === WEBGL_DRAW_MODES.TRIANGLE_STRIP) {
200
+ // TODO
201
+ console.warn('TRIANGLE_STRIP will be removed later.');
202
+ material.drawMode = WEBGL_DRAW_MODES.TRIANGLE_STRIP;
203
+ } else if (mode === WEBGL_DRAW_MODES.TRIANGLE_FAN) {
204
+ // TODO
205
+ console.warn('TRIANGLE_FAN will be removed later.');
206
+ material.drawMode = WEBGL_DRAW_MODES.TRIANGLE_FAN;
207
+ }
208
+
209
+ if (useVertexTangents || useVertexColors || useFlatShading || useSkinning) {
210
+ let cacheKey = 'ClonedMaterial:' + material.id + ':';
211
+
212
+ if (useVertexTangents) cacheKey += 'vertex-tangents:';
213
+ if (useVertexColors) {
214
+ if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 3) {
215
+ cacheKey += 'vertex-colors-rgb:';
216
+ } else if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 4) {
217
+ cacheKey += 'vertex-colors-rgba:';
218
+ }
219
+ }
220
+ if (useFlatShading) cacheKey += 'flat-shading:';
221
+
222
+ let cachedMaterial = materialCache.get(cacheKey);
223
+
224
+ if (!cachedMaterial) {
225
+ cachedMaterial = material.clone();
226
+
227
+ if (useVertexTangents) {
228
+ cachedMaterial.vertexTangents = true;
229
+
230
+ // revert flip y fix for tangents
231
+ // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
232
+ if (cachedMaterial.normalMap) {
233
+ cachedMaterial.normalScale.y *= -1;
234
+ }
235
+ }
236
+
237
+ if (useVertexColors) {
238
+ if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 3) {
239
+ cachedMaterial.vertexColors = VERTEX_COLOR.RGB;
240
+ } else if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 4) {
241
+ cachedMaterial.vertexColors = VERTEX_COLOR.RGBA;
242
+ } else {
243
+ console.warn('Illegal vertex color size: ' + geometry.attributes[ATTRIBUTES.COLOR_0].size);
244
+ }
245
+ }
246
+
247
+ if (useFlatShading) {
248
+ cachedMaterial.shading = SHADING_TYPE.FLAT_SHADING;
249
+ }
250
+ }
251
+
252
+ material = cachedMaterial;
253
+ }
254
+
255
+ primitive.material = material;
256
+ }
257
+
258
+ function createGeometryKey(primitive) {
259
+ const dracoExtension = primitive.extensions && primitive.extensions.KHR_draco_mesh_compression;
260
+ let geometryKey;
261
+
262
+ if (dracoExtension) {
263
+ geometryKey = 'draco:' + dracoExtension.bufferView
264
+ + ':' + dracoExtension.indices
265
+ + ':' + createAttributesKey(dracoExtension.attributes);
266
+ } else {
267
+ geometryKey = primitive.indices + ':' + createAttributesKey(primitive.attributes) + ':' + primitive.mode;
268
+ }
269
+
270
+ if (primitive.targets) {
271
+ for (let i = 0, il = primitive.targets.length; i < il; i++) {
272
+ geometryKey += ':' + createAttributesKey(primitive.targets[i]);
273
+ }
274
+ }
275
+
276
+ return geometryKey;
277
+ }
278
+
279
+ function createAttributesKey(attributes) {
280
+ let attributesKey = '';
281
+
282
+ const keys = Object.keys(attributes).sort();
283
+
284
+ for (let i = 0, il = keys.length; i < il; i++) {
285
+ attributesKey += keys[i] + ':' + attributes[keys[i]] + ';';
286
+ }
287
+
288
+ return attributesKey;
289
+ }
@@ -0,0 +1,67 @@
1
+ import { GLTFUtils } from '../GLTFUtils.js';
2
+
3
+ // Marks the special nodes/meshes in json for efficient parse.
4
+ export class ReferenceParser {
5
+
6
+ static parse(context, loader) {
7
+ const { gltf, path } = context;
8
+ const { nodes = [], skins = [], meshes = [], buffers, images } = gltf;
9
+
10
+ // Nothing in the node definition indicates whether it is a Bone or an
11
+ // Object3D. Use the skins' joint references to mark bones.
12
+ skins.forEach(skin => {
13
+ const { joints = [] } = skin;
14
+ joints.forEach(joint => {
15
+ nodes[joint].isBone = true;
16
+ });
17
+ });
18
+
19
+ // Nothing in the mesh definition indicates whether it is
20
+ // a SkinnedMesh or Mesh. Use the node's mesh reference
21
+ // to mark SkinnedMesh if node has skin.
22
+ nodes.forEach(node => {
23
+ if (node.mesh !== undefined) {
24
+ if (node.skin !== undefined) {
25
+ meshes[node.mesh].isSkinned = true;
26
+ }
27
+ }
28
+ });
29
+
30
+ // setup loading list for detail load progress
31
+ if (loader.detailLoadProgress) {
32
+ const loadItems = new Set();
33
+
34
+ if (buffers) {
35
+ buffers.forEach(buffer => {
36
+ if (!buffer.uri) {
37
+ // glb or other
38
+ return;
39
+ }
40
+ const bufferUrl = GLTFUtils.resolveURL(buffer.uri, path);
41
+ loadItems.add(bufferUrl);
42
+ });
43
+ }
44
+
45
+ if (images) {
46
+ images.forEach((image, index) => {
47
+ const { uri, bufferView: bufferViewIndex } = image;
48
+
49
+ let imageUrl = uri;
50
+
51
+ if (bufferViewIndex !== undefined) {
52
+ imageUrl = 'blob<' + index + '>'; // fake url for blob image
53
+ }
54
+
55
+ imageUrl = GLTFUtils.resolveURL(imageUrl, path);
56
+
57
+ loadItems.add(imageUrl);
58
+ });
59
+ }
60
+
61
+ loadItems.forEach(item => loader.manager.itemStart(item));
62
+
63
+ context.loadItems = loadItems;
64
+ }
65
+ }
66
+
67
+ }
@@ -0,0 +1,41 @@
1
+ import { Object3D } from 't3d';
2
+
3
+ export class SceneParser {
4
+
5
+ static parse(context) {
6
+ const { gltf, nodes } = context;
7
+
8
+ const roots = gltf.scenes.map(sceneDef => {
9
+ const { name: sceneName = '', nodes: nodeIds = [] } = sceneDef;
10
+
11
+ const group = new Object3D();
12
+ group.name = sceneName;
13
+
14
+ for (let i = 0; i < nodeIds.length; i++) {
15
+ buildNodeHierachy(nodeIds[i], group, gltf.nodes, nodes);
16
+ }
17
+
18
+ return group;
19
+ });
20
+
21
+ context.roots = roots;
22
+ context.root = roots[gltf.scene || 0];
23
+ }
24
+
25
+ }
26
+
27
+ function buildNodeHierachy(nodeId, parentNode, gltfNodes, nodes) {
28
+ const node = nodes[nodeId];
29
+ const nodeDef = gltfNodes[nodeId];
30
+
31
+ parentNode.add(node);
32
+
33
+ if (nodeDef.children) {
34
+ const children = nodeDef.children;
35
+
36
+ for (let i = 0, il = children.length; i < il; i++) {
37
+ const child = children[i];
38
+ buildNodeHierachy(child, node, gltfNodes, nodes);
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,53 @@
1
+ import { Matrix4, Skeleton } from 't3d';
2
+
3
+ export class SkinParser {
4
+
5
+ static parse(context) {
6
+ const { gltf, accessors, nodes } = context;
7
+
8
+ const gltfSkins = gltf.skins;
9
+
10
+ if (!gltfSkins) return;
11
+
12
+ const skins = gltfSkins.map(skin => {
13
+ const { inverseBindMatrices, joints } = skin;
14
+
15
+ const attribute = accessors[inverseBindMatrices];
16
+
17
+ const bones = [];
18
+ const boneInverses = [];
19
+ joints.forEach((jointId, index) => {
20
+ const jointNode = nodes[jointId];
21
+
22
+ if (jointNode) {
23
+ bones.push(jointNode);
24
+
25
+ const boneInverse = new Matrix4();
26
+ if (attribute) {
27
+ boneInverse.fromArray(attribute.buffer.array, index * 16);
28
+ }
29
+ boneInverses.push(boneInverse);
30
+ } else {
31
+ console.warn('Joint ' + jointId + ' could not be found.');
32
+ }
33
+ });
34
+
35
+ return new Skeleton(bones, boneInverses);
36
+ });
37
+
38
+ context.skins = skins;
39
+
40
+ // Bind all skined meshes
41
+ nodes.forEach((node, index) => {
42
+ const { skin: skinID } = gltf.nodes[index];
43
+
44
+ if (skinID !== undefined) {
45
+ node.traverse(function(mesh) {
46
+ if (!mesh.isSkinnedMesh) return;
47
+ mesh.bind(skins[skinID], mesh.worldMatrix); // TODO need updateMatrix ?
48
+ });
49
+ }
50
+ });
51
+ }
52
+
53
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ Texture2D,
3
+ TEXTURE_FILTER,
4
+ TEXTURE_WRAP
5
+ } from 't3d';
6
+ import { WEBGL_WRAPPINGS, WEBGL_FILTERS } from '../Constants.js';
7
+
8
+ export class TextureParser {
9
+
10
+ static parse(context) {
11
+ const { gltf, images } = context;
12
+
13
+ if (!gltf.textures) return;
14
+
15
+ // TODO need to cache textures by source and samplers?
16
+
17
+ return Promise.all(
18
+ gltf.textures.map((params, index) => {
19
+ const { sampler, source = 0, name: textureName } = params;
20
+
21
+ const texture = new Texture2D();
22
+
23
+ if (params.extensions) {
24
+ const { KHR_texture_basisu } = params.extensions;
25
+ if (KHR_texture_basisu) {
26
+ const transcodeResult = images[KHR_texture_basisu.source];
27
+
28
+ const { image, mipmaps, type, format, minFilter, magFilter, generateMipmaps, encoding, premultiplyAlpha } = transcodeResult;
29
+ texture.image = image;
30
+ texture.mipmaps = mipmaps;
31
+ texture.type = type;
32
+ texture.format = format;
33
+ texture.minFilter = minFilter;
34
+ texture.magFilter = magFilter;
35
+ texture.generateMipmaps = generateMipmaps;
36
+ texture.encoding = encoding;
37
+ texture.premultiplyAlpha = premultiplyAlpha;
38
+ } else if (Object.values(params.extensions).length && Object.values(params.extensions)[0].hasOwnProperty('source')) {
39
+ texture.image = images[Object.values(params.extensions)[0].source];
40
+ } else {
41
+ console.error('GLTFLoader: Couldn\'t load texture');
42
+ return null;
43
+ }
44
+ } else {
45
+ texture.image = images[source];
46
+ }
47
+
48
+ texture.version++;
49
+ texture.name = textureName || texture.image.__name || `texture_${index}`;
50
+ texture.flipY = false;
51
+
52
+ const samplers = gltf.samplers || {};
53
+ parseSampler(texture, samplers[sampler]);
54
+
55
+ return texture;
56
+ })
57
+ ).then(textures => {
58
+ context.textures = textures;
59
+ });
60
+ }
61
+
62
+ }
63
+
64
+ function parseSampler(texture, sampler = {}) {
65
+ const { magFilter, minFilter, wrapS, wrapT } = sampler;
66
+
67
+ texture.magFilter = WEBGL_FILTERS[magFilter] || TEXTURE_FILTER.LINEAR;
68
+ texture.minFilter = WEBGL_FILTERS[minFilter] || TEXTURE_FILTER.LINEAR_MIPMAP_LINEAR;
69
+ texture.wrapS = WEBGL_WRAPPINGS[wrapS] || TEXTURE_WRAP.REPEAT;
70
+ texture.wrapT = WEBGL_WRAPPINGS[wrapT] || TEXTURE_WRAP.REPEAT;
71
+ }
@@ -0,0 +1,16 @@
1
+ export class Validator {
2
+
3
+ static parse(context) {
4
+ const {
5
+ gltf: {
6
+ asset: { version }
7
+ }
8
+ } = context;
9
+
10
+ const gltfVersion = Number(version);
11
+ if (!(gltfVersion >= 2 && gltfVersion < 3)) {
12
+ throw 'Only support gltf 2.x.';
13
+ }
14
+ }
15
+
16
+ }