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.
- package/LICENSE +28 -0
- package/README.md +41 -0
- package/build/t3d.3dtiles.js +5418 -0
- package/build/t3d.3dtiles.min.js +2 -0
- package/build/t3d.3dtiles.module.js +6183 -0
- package/examples/jsm/CesiumIon.js +30 -0
- package/examples/jsm/Tiles3DDebugger.js +219 -0
- package/examples/jsm/Tiles3DUtils.js +32 -0
- package/examples/jsm/helpers/LineSegmentHelper.js +74 -0
- package/examples/jsm/helpers/OBBHelper.js +48 -0
- package/examples/jsm/helpers/Tiles3DHelper.js +135 -0
- package/examples/jsm/math/Intersects.js +100 -0
- package/examples/jsm/math/LineSegment.js +234 -0
- package/package.json +57 -0
- package/src/Tiles3D.js +402 -0
- package/src/libs/ImageBitmapLoader.js +56 -0
- package/src/libs/glTF/Constants.js +85 -0
- package/src/libs/glTF/GLTFLoader.js +169 -0
- package/src/libs/glTF/GLTFResource.js +28 -0
- package/src/libs/glTF/GLTFUtils.js +131 -0
- package/src/libs/glTF/extensions/EXT_meshopt_compression.js +35 -0
- package/src/libs/glTF/extensions/KHR_draco_mesh_compression.js +54 -0
- package/src/libs/glTF/extensions/KHR_lights_punctual.js +53 -0
- package/src/libs/glTF/extensions/KHR_materials_clearcoat.js +42 -0
- package/src/libs/glTF/extensions/KHR_materials_pbrSpecularGlossiness.js +53 -0
- package/src/libs/glTF/extensions/KHR_materials_unlit.js +13 -0
- package/src/libs/glTF/extensions/KHR_texture_basisu.js +14 -0
- package/src/libs/glTF/extensions/KHR_texture_transform.js +31 -0
- package/src/libs/glTF/parsers/AccessorParser.js +99 -0
- package/src/libs/glTF/parsers/AnimationParser.js +118 -0
- package/src/libs/glTF/parsers/BufferParser.js +35 -0
- package/src/libs/glTF/parsers/BufferViewParser.js +27 -0
- package/src/libs/glTF/parsers/ImageParser.js +97 -0
- package/src/libs/glTF/parsers/IndexParser.js +22 -0
- package/src/libs/glTF/parsers/MaterialParser.js +146 -0
- package/src/libs/glTF/parsers/NodeParser.js +145 -0
- package/src/libs/glTF/parsers/PrimitiveParser.js +289 -0
- package/src/libs/glTF/parsers/ReferenceParser.js +67 -0
- package/src/libs/glTF/parsers/SceneParser.js +41 -0
- package/src/libs/glTF/parsers/SkinParser.js +53 -0
- package/src/libs/glTF/parsers/TextureParser.js +71 -0
- package/src/libs/glTF/parsers/Validator.js +16 -0
- package/src/loaders/B3DMLoader.js +49 -0
- package/src/loaders/CMPTLoader.js +48 -0
- package/src/loaders/I3DMLoader.js +49 -0
- package/src/loaders/PNTSLoader.js +20 -0
- package/src/loaders/TileGLTFLoader.js +17 -0
- package/src/loaders/parsers/HeaderParser.js +104 -0
- package/src/loaders/parsers/LoadParser.js +22 -0
- package/src/loaders/parsers/TableParser.js +66 -0
- package/src/loaders/parsers/b3dm/B3DMParser.js +18 -0
- package/src/loaders/parsers/b3dm/B3DMRootParser.js +22 -0
- package/src/loaders/parsers/b3dm/MaterialParser.js +156 -0
- package/src/loaders/parsers/cmpt/CMPTParser.js +37 -0
- package/src/loaders/parsers/cmpt/CMPTRootParser.js +44 -0
- package/src/loaders/parsers/gltf/IndexParser.js +24 -0
- package/src/loaders/parsers/i3dm/I3DMParser.js +28 -0
- package/src/loaders/parsers/i3dm/I3DMRootParser.js +123 -0
- package/src/loaders/parsers/i3dm/MaterialParser.js +152 -0
- package/src/loaders/parsers/i3dm/PrimitiveParser.js +291 -0
- package/src/loaders/parsers/pnts/PNTSRootParser.js +69 -0
- package/src/main.js +14 -0
- package/src/materials/InstancedBasicMaterial.js +32 -0
- package/src/materials/InstancedPBRMaterial.js +32 -0
- package/src/materials/InstancedShaderChunks.js +23 -0
- package/src/math/Ellipsoid.js +41 -0
- package/src/math/EllipsoidRegion.js +160 -0
- package/src/math/FastFrustum.js +49 -0
- package/src/math/GeoUtils.js +31 -0
- package/src/math/OBB.js +395 -0
- package/src/math/TileBoundingVolume.js +172 -0
- package/src/math/TileOBB.js +103 -0
- package/src/utils/BatchTable.js +14 -0
- package/src/utils/CameraList.js +131 -0
- package/src/utils/FeatureTable.js +107 -0
- package/src/utils/IntersectionUtils.js +136 -0
- package/src/utils/LRUCache.js +159 -0
- package/src/utils/ModelLoader.js +150 -0
- package/src/utils/PriorityQueue.js +102 -0
- package/src/utils/RequestState.js +17 -0
- package/src/utils/TilesLoader.js +386 -0
- package/src/utils/TilesScheduler.js +375 -0
- package/src/utils/Utils.js +43 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TEXEL_ENCODING_TYPE, PBR2Material } from 't3d';
|
|
2
|
+
import { KHR_texture_transform } from './KHR_texture_transform.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* KHR_materials_pbrSpecularGlossiness extension
|
|
6
|
+
* https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
|
|
7
|
+
*/
|
|
8
|
+
export class KHR_materials_pbrSpecularGlossiness {
|
|
9
|
+
|
|
10
|
+
static getMaterial() {
|
|
11
|
+
return new PBR2Material();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static parseParams(material, params, textures) {
|
|
15
|
+
const { diffuseFactor, diffuseTexture, specularFactor, glossinessFactor, specularGlossinessTexture } = params;
|
|
16
|
+
|
|
17
|
+
if (Array.isArray(diffuseFactor)) {
|
|
18
|
+
material.diffuse.fromArray(diffuseFactor);
|
|
19
|
+
material.opacity = diffuseFactor[3] || 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (diffuseTexture) {
|
|
23
|
+
material.diffuseMap = textures[diffuseTexture.index];
|
|
24
|
+
material.diffuseMapCoord = diffuseTexture.texCoord || 0;
|
|
25
|
+
if (material.diffuseMap) {
|
|
26
|
+
material.diffuseMap.encoding = TEXEL_ENCODING_TYPE.SRGB;
|
|
27
|
+
}
|
|
28
|
+
parseTextureTransform(material, 'diffuseMap', diffuseTexture.extensions);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
material.glossiness = glossinessFactor !== undefined ? glossinessFactor : 1.0;
|
|
32
|
+
|
|
33
|
+
if (Array.isArray(specularFactor)) {
|
|
34
|
+
material.specular.fromArray(specularFactor);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (specularGlossinessTexture) {
|
|
38
|
+
material.glossinessMap = textures[specularGlossinessTexture.index];
|
|
39
|
+
material.specularMap = textures[specularGlossinessTexture.index];
|
|
40
|
+
// material does not yet support the transform of glossinessMap and specularMap.
|
|
41
|
+
// parseTextureTransform(material, 'glossinessMap', specularGlossinessTexture.extensions);
|
|
42
|
+
// parseTextureTransform(material, 'specularMap', specularGlossinessTexture.extensions);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseTextureTransform(material, key, extensions = {}) {
|
|
49
|
+
const extension = extensions.KHR_texture_transform;
|
|
50
|
+
if (extension) {
|
|
51
|
+
material[key] = KHR_texture_transform.transform(material[key + 'Transform'], extension);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BasicMaterial } from 't3d';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* KHR_materials_unlit extension
|
|
5
|
+
* https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
|
|
6
|
+
*/
|
|
7
|
+
export class KHR_materials_unlit {
|
|
8
|
+
|
|
9
|
+
static getMaterial() {
|
|
10
|
+
return new BasicMaterial();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BasisU Texture Extension
|
|
3
|
+
*
|
|
4
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
|
|
5
|
+
*/
|
|
6
|
+
export class KHR_texture_basisu {
|
|
7
|
+
|
|
8
|
+
static loadTextureData(url, ktx2Loader) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
ktx2Loader.load(url, resolve, undefined, reject);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KHR_texture_transform extension
|
|
3
|
+
* https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
|
|
4
|
+
*/
|
|
5
|
+
export class KHR_texture_transform {
|
|
6
|
+
|
|
7
|
+
static transform(matrix, transform) {
|
|
8
|
+
let offsetX = 0, offsetY = 0, repeatX = 1, repeatY = 1, rotation = 0;
|
|
9
|
+
|
|
10
|
+
if (transform.offset !== undefined) {
|
|
11
|
+
offsetX = transform.offset[0];
|
|
12
|
+
offsetY = transform.offset[1];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (transform.rotation !== undefined) {
|
|
16
|
+
rotation = transform.rotation;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (transform.scale !== undefined) {
|
|
20
|
+
repeatX = transform.scale[0];
|
|
21
|
+
repeatY = transform.scale[1];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
matrix.setUvTransform(offsetX, offsetY, repeatX, repeatY, rotation, 0, 0);
|
|
25
|
+
|
|
26
|
+
if (transform.texCoord !== undefined) {
|
|
27
|
+
console.warn('Custom UV sets in KHR_texture_transform extension not yet supported.');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Attribute, Buffer } from 't3d';
|
|
2
|
+
import { ACCESSOR_COMPONENT_TYPES, ACCESSOR_TYPE_SIZES } from '../Constants.js';
|
|
3
|
+
|
|
4
|
+
export class AccessorParser {
|
|
5
|
+
|
|
6
|
+
static parse(context) {
|
|
7
|
+
const { bufferViews, gltf } = context;
|
|
8
|
+
|
|
9
|
+
if (!gltf.accessors) return;
|
|
10
|
+
|
|
11
|
+
const interleavedBufferCache = new Map();
|
|
12
|
+
|
|
13
|
+
const accessors = gltf.accessors.map(accessor => {
|
|
14
|
+
const { bufferView: bufferViewIndex, type, componentType, count, byteOffset = 0, normalized = false, sparse } = accessor;
|
|
15
|
+
|
|
16
|
+
if (bufferViewIndex === undefined && sparse === undefined) {
|
|
17
|
+
// Ignore empty accessors, which may be used to declare runtime
|
|
18
|
+
// information about attributes coming from another source (e.g. Draco compression extension).
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Get buffer view infos
|
|
23
|
+
const bufferView = bufferViewIndex !== undefined ? bufferViews[bufferViewIndex] : null;
|
|
24
|
+
const byteStride = bufferViewIndex !== undefined ? gltf.bufferViews[bufferViewIndex].byteStride : undefined;
|
|
25
|
+
|
|
26
|
+
// Get accessor infos
|
|
27
|
+
const itemSize = ACCESSOR_TYPE_SIZES[type];
|
|
28
|
+
const TypedArray = ACCESSOR_COMPONENT_TYPES[componentType];
|
|
29
|
+
const elementBytes = TypedArray.BYTES_PER_ELEMENT;
|
|
30
|
+
const itemBytes = elementBytes * itemSize; // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
|
|
31
|
+
|
|
32
|
+
let array, attribute;
|
|
33
|
+
|
|
34
|
+
if (byteStride && byteStride !== itemBytes) { // The buffer is interleaved
|
|
35
|
+
// Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer
|
|
36
|
+
// This makes sure that IBA.count reflects accessor.count properly
|
|
37
|
+
const ibSlice = Math.floor(byteOffset / byteStride);
|
|
38
|
+
const ibCacheKey = 'Buffer:' + bufferViewIndex + ':' + componentType + ':' + ibSlice + ':' + count;
|
|
39
|
+
let ib = interleavedBufferCache.get(ibCacheKey);
|
|
40
|
+
|
|
41
|
+
if (!ib) {
|
|
42
|
+
// Use the full buffer if it's interleaved.
|
|
43
|
+
array = new TypedArray(bufferView, ibSlice * byteStride, count * byteStride / elementBytes);
|
|
44
|
+
|
|
45
|
+
// Integer parameters to IB/IBA are in array elements, not bytes.
|
|
46
|
+
ib = new Buffer(array, byteStride / elementBytes);
|
|
47
|
+
|
|
48
|
+
interleavedBufferCache.set(ibCacheKey, ib);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
attribute = new Attribute(ib, itemSize, (byteOffset % byteStride) / elementBytes, normalized);
|
|
52
|
+
} else {
|
|
53
|
+
if (bufferView === null) {
|
|
54
|
+
array = new TypedArray(count * itemSize);
|
|
55
|
+
} else {
|
|
56
|
+
array = new TypedArray(bufferView, byteOffset, count * itemSize);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
attribute = new Attribute(new Buffer(array, itemSize), itemSize, 0, normalized);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
|
|
63
|
+
if (sparse) {
|
|
64
|
+
const itemSizeIndices = ACCESSOR_TYPE_SIZES.SCALAR;
|
|
65
|
+
const TypedArrayIndices = ACCESSOR_COMPONENT_TYPES[sparse.indices.componentType];
|
|
66
|
+
|
|
67
|
+
const byteOffsetIndices = sparse.indices.byteOffset || 0;
|
|
68
|
+
const byteOffsetValues = sparse.values.byteOffset || 0;
|
|
69
|
+
|
|
70
|
+
const sparseIndices = new TypedArrayIndices(bufferViews[sparse.indices.bufferView], byteOffsetIndices, sparse.count * itemSizeIndices);
|
|
71
|
+
const sparseValues = new TypedArray(bufferViews[sparse.values.bufferView], byteOffsetValues, sparse.count * itemSize);
|
|
72
|
+
|
|
73
|
+
if (bufferView !== null) {
|
|
74
|
+
// Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
|
|
75
|
+
attribute = new Attribute(attribute.buffer.clone(), attribute.size, attribute.offset, attribute.normalized);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const buffer = attribute.buffer;
|
|
79
|
+
|
|
80
|
+
for (let i = 0, il = sparseIndices.length; i < il; i++) {
|
|
81
|
+
const index = sparseIndices[i];
|
|
82
|
+
|
|
83
|
+
buffer.array[index * attribute.size] = sparseValues[i * itemSize];
|
|
84
|
+
if (itemSize >= 2) buffer.array[index * attribute.size + 1] = sparseValues[i * itemSize + 1];
|
|
85
|
+
if (itemSize >= 3) buffer.array[index * attribute.size + 2] = sparseValues[i * itemSize + 2];
|
|
86
|
+
if (itemSize >= 4) buffer.array[index * attribute.size + 3] = sparseValues[i * itemSize + 3];
|
|
87
|
+
if (itemSize >= 5) throw new Error('Unsupported itemSize in sparse Attribute.');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return attribute;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
interleavedBufferCache.clear();
|
|
95
|
+
|
|
96
|
+
context.accessors = accessors;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
QuaternionKeyframeTrack,
|
|
3
|
+
NumberKeyframeTrack,
|
|
4
|
+
VectorKeyframeTrack,
|
|
5
|
+
KeyframeClip,
|
|
6
|
+
StepInterpolant,
|
|
7
|
+
LinearInterpolant,
|
|
8
|
+
CubicSplineInterpolant,
|
|
9
|
+
QuaternionLinearInterpolant,
|
|
10
|
+
QuaternionCubicSplineInterpolant
|
|
11
|
+
} from 't3d';
|
|
12
|
+
import { GLTFUtils } from '../GLTFUtils.js';
|
|
13
|
+
import { PATH_PROPERTIES } from '../Constants.js';
|
|
14
|
+
|
|
15
|
+
export class AnimationParser {
|
|
16
|
+
|
|
17
|
+
static parse(context) {
|
|
18
|
+
const { gltf, nodes, accessors } = context;
|
|
19
|
+
const { animations } = gltf;
|
|
20
|
+
|
|
21
|
+
if (!animations) return;
|
|
22
|
+
|
|
23
|
+
const animationClips = animations.map((gltfAnimation, index) => {
|
|
24
|
+
const { channels, samplers, name = `animation_${index}` } = gltfAnimation;
|
|
25
|
+
|
|
26
|
+
const tracks = [];
|
|
27
|
+
let duration = 0;
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < channels.length; i++) {
|
|
30
|
+
const gltfChannel = channels[i];
|
|
31
|
+
const gltfSampler = samplers[gltfChannel.sampler];
|
|
32
|
+
if (gltfSampler) {
|
|
33
|
+
const target = gltfChannel.target;
|
|
34
|
+
const name = target.node !== undefined ? target.node : target.id; // Note: target.id is deprecated.
|
|
35
|
+
const inputAccessor = accessors[gltfSampler.input];
|
|
36
|
+
const outputAccessor = accessors[gltfSampler.output];
|
|
37
|
+
|
|
38
|
+
const node = nodes[name];
|
|
39
|
+
|
|
40
|
+
if (!node) continue;
|
|
41
|
+
|
|
42
|
+
node.updateMatrix();
|
|
43
|
+
node.matrixAutoUpdate = true;
|
|
44
|
+
|
|
45
|
+
let TypedKeyframeTrack;
|
|
46
|
+
|
|
47
|
+
switch (PATH_PROPERTIES[target.path]) {
|
|
48
|
+
case PATH_PROPERTIES.rotation:
|
|
49
|
+
TypedKeyframeTrack = QuaternionKeyframeTrack;
|
|
50
|
+
break;
|
|
51
|
+
case PATH_PROPERTIES.weights:
|
|
52
|
+
TypedKeyframeTrack = NumberKeyframeTrack;
|
|
53
|
+
break;
|
|
54
|
+
case PATH_PROPERTIES.position:
|
|
55
|
+
case PATH_PROPERTIES.scale:
|
|
56
|
+
default:
|
|
57
|
+
TypedKeyframeTrack = VectorKeyframeTrack;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!TypedKeyframeTrack) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const input = new inputAccessor.buffer.array.constructor(inputAccessor.buffer.array);
|
|
66
|
+
const output = new Float32Array(outputAccessor.buffer.array);
|
|
67
|
+
|
|
68
|
+
if (outputAccessor.normalized) {
|
|
69
|
+
const scale = GLTFUtils.getNormalizedComponentScale(outputAccessor.buffer.array.constructor);
|
|
70
|
+
for (let j = 0, jl = output.length; j < jl; j++) {
|
|
71
|
+
output[j] *= scale;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const targetNodes = [];
|
|
76
|
+
if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) {
|
|
77
|
+
// Node may be a Object3D (glTF mesh with several primitives) or a Mesh.
|
|
78
|
+
node.traverse(function(object) {
|
|
79
|
+
if (object.isMesh && object.morphTargetInfluences) {
|
|
80
|
+
targetNodes.push(object);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
targetNodes.push(node);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (let j = 0, jl = targetNodes.length; j < jl; j++) {
|
|
88
|
+
const interpolant = getInterpolant(gltfSampler.interpolation, TypedKeyframeTrack === QuaternionKeyframeTrack);
|
|
89
|
+
const track = new TypedKeyframeTrack(targetNodes[j], PATH_PROPERTIES[target.path], input, output, interpolant);
|
|
90
|
+
tracks.push(track);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const maxTime = input[input.length - 1];
|
|
94
|
+
if (duration < maxTime) {
|
|
95
|
+
duration = maxTime;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return new KeyframeClip(name, tracks, duration);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
context.animations = animationClips;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getInterpolant(type, quaternion) {
|
|
109
|
+
switch (type) {
|
|
110
|
+
case 'STEP':
|
|
111
|
+
return StepInterpolant;
|
|
112
|
+
case 'CUBICSPLINE':
|
|
113
|
+
return quaternion ? QuaternionCubicSplineInterpolant : CubicSplineInterpolant;
|
|
114
|
+
case 'LINEAR':
|
|
115
|
+
default:
|
|
116
|
+
return quaternion ? QuaternionLinearInterpolant : LinearInterpolant;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { GLTFUtils } from '../GLTFUtils.js';
|
|
2
|
+
|
|
3
|
+
export class BufferParser {
|
|
4
|
+
|
|
5
|
+
static parse(context, loader) {
|
|
6
|
+
const { gltf, loadItems } = context;
|
|
7
|
+
|
|
8
|
+
if (context.buffers !== null) {
|
|
9
|
+
// buffers have been parsed
|
|
10
|
+
return null;
|
|
11
|
+
} else {
|
|
12
|
+
return Promise.all(
|
|
13
|
+
gltf.buffers.map(buffer => {
|
|
14
|
+
const bufferUrl = GLTFUtils.resolveURL(buffer.uri, context.path);
|
|
15
|
+
if (loader.detailLoadProgress) {
|
|
16
|
+
loadItems.delete(bufferUrl);
|
|
17
|
+
}
|
|
18
|
+
const promise = loader.loadFile(bufferUrl, 'arraybuffer').then(buffer => {
|
|
19
|
+
if (loader.detailLoadProgress) {
|
|
20
|
+
loader.manager.itemEnd(bufferUrl);
|
|
21
|
+
}
|
|
22
|
+
return buffer;
|
|
23
|
+
});
|
|
24
|
+
if (loader.detailLoadProgress) {
|
|
25
|
+
promise.catch(() => loader.manager.itemEnd(bufferUrl));
|
|
26
|
+
}
|
|
27
|
+
return promise;
|
|
28
|
+
})
|
|
29
|
+
).then(buffers => {
|
|
30
|
+
context.buffers = buffers;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { EXT_meshopt_compression as _EXT_meshopt_compression } from '../extensions/EXT_meshopt_compression.js';
|
|
2
|
+
export class BufferViewParser {
|
|
3
|
+
|
|
4
|
+
static parse(context, loader) {
|
|
5
|
+
const { buffers, gltf } = context;
|
|
6
|
+
|
|
7
|
+
if (!gltf.bufferViews) return;
|
|
8
|
+
|
|
9
|
+
return Promise.all(
|
|
10
|
+
gltf.bufferViews.map(bufferView => {
|
|
11
|
+
const { buffer, byteOffset = 0, byteLength = 0 } = bufferView;
|
|
12
|
+
|
|
13
|
+
if (bufferView.extensions) {
|
|
14
|
+
const { EXT_meshopt_compression } = bufferView.extensions;
|
|
15
|
+
if (EXT_meshopt_compression) {
|
|
16
|
+
return _EXT_meshopt_compression.loadBufferView(EXT_meshopt_compression, buffers, loader.getMeshoptDecoder());
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const arrayBuffer = buffers[buffer];
|
|
20
|
+
return arrayBuffer.slice(byteOffset, byteOffset + byteLength);
|
|
21
|
+
})
|
|
22
|
+
).then(bufferViews => {
|
|
23
|
+
context.bufferViews = bufferViews;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { GLTFUtils } from '../GLTFUtils.js';
|
|
2
|
+
import { KHR_texture_basisu as _KHR_texture_basisu } from '../extensions/KHR_texture_basisu.js';
|
|
3
|
+
|
|
4
|
+
export class ImageParser {
|
|
5
|
+
|
|
6
|
+
static parse(context, loader) {
|
|
7
|
+
const { gltf, bufferViews, path, loadItems } = context;
|
|
8
|
+
|
|
9
|
+
if (!gltf.images) return;
|
|
10
|
+
|
|
11
|
+
return Promise.all(
|
|
12
|
+
gltf.images.map((params, index) => {
|
|
13
|
+
const { uri, bufferView: bufferViewIndex, mimeType, name: imageName } = params;
|
|
14
|
+
let isObjectURL = false;
|
|
15
|
+
let sourceUrl = uri || '';
|
|
16
|
+
|
|
17
|
+
if (bufferViewIndex !== undefined) {
|
|
18
|
+
const bufferViewData = bufferViews[bufferViewIndex];
|
|
19
|
+
const blob = new Blob([bufferViewData], { type: mimeType });
|
|
20
|
+
sourceUrl = URL.createObjectURL(blob);
|
|
21
|
+
isObjectURL = true;
|
|
22
|
+
}
|
|
23
|
+
const imageUrl = GLTFUtils.resolveURL(sourceUrl, path);
|
|
24
|
+
|
|
25
|
+
if (loader.detailLoadProgress) {
|
|
26
|
+
loadItems.delete(imageUrl);
|
|
27
|
+
}
|
|
28
|
+
let promise;
|
|
29
|
+
if (mimeType && mimeType.includes('ktx2')) {
|
|
30
|
+
promise = _KHR_texture_basisu.loadTextureData(imageUrl, loader.getKTX2Loader()).then(transcodeResult => {
|
|
31
|
+
if (loader.detailLoadProgress) {
|
|
32
|
+
if (isObjectURL) {
|
|
33
|
+
loader.manager.itemEnd(GLTFUtils.resolveURL('blob<' + index + '>', path));
|
|
34
|
+
} else {
|
|
35
|
+
loader.manager.itemEnd(imageUrl);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return transcodeResult;
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
const param = { loader, imageUrl, imageName, isObjectURL, sourceUrl, index, path };
|
|
42
|
+
if (mimeType && (mimeType.includes('avif') || mimeType.includes('webp'))) {
|
|
43
|
+
promise = detectSupport(mimeType).then(isSupported => {
|
|
44
|
+
if (isSupported) return loadImage(param);
|
|
45
|
+
throw new Error('GLTFLoader: WebP or AVIF required by asset but unsupported.');
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
return loadImage(param);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (loader.detailLoadProgress) {
|
|
52
|
+
promise.catch(() => loader.manager.itemEnd(imageUrl));
|
|
53
|
+
}
|
|
54
|
+
return promise;
|
|
55
|
+
})
|
|
56
|
+
).then(images => {
|
|
57
|
+
context.images = images;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function detectSupport(mimeType) {
|
|
64
|
+
const isSupported = new Promise(resolve => {
|
|
65
|
+
// Lossy test image.
|
|
66
|
+
const image = new Image();
|
|
67
|
+
if (mimeType.includes('avif')) {
|
|
68
|
+
image.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEDQgMgkQAAAAB8dSLfI=';
|
|
69
|
+
} else {
|
|
70
|
+
image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
|
|
71
|
+
}
|
|
72
|
+
image.onload = () => {
|
|
73
|
+
resolve(image.height === 1);
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return isSupported;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function loadImage(param) {
|
|
81
|
+
const { loader, imageUrl, imageName, isObjectURL, sourceUrl, index, path } = param;
|
|
82
|
+
const promise = loader.loadImage(imageUrl).then(image => {
|
|
83
|
+
image.__name = imageName;
|
|
84
|
+
if (isObjectURL === true) {
|
|
85
|
+
URL.revokeObjectURL(sourceUrl);
|
|
86
|
+
}
|
|
87
|
+
if (loader.detailLoadProgress) {
|
|
88
|
+
if (isObjectURL) {
|
|
89
|
+
loader.manager.itemEnd(GLTFUtils.resolveURL('blob<' + index + '>', path));
|
|
90
|
+
} else {
|
|
91
|
+
loader.manager.itemEnd(imageUrl);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return image;
|
|
95
|
+
});
|
|
96
|
+
return promise;
|
|
97
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { GLTFUtils } from '../GLTFUtils.js';
|
|
2
|
+
|
|
3
|
+
export class IndexParser {
|
|
4
|
+
|
|
5
|
+
static parse(context, loader) {
|
|
6
|
+
const { url } = context;
|
|
7
|
+
|
|
8
|
+
return loader.loadFile(url, 'arraybuffer').then(data => {
|
|
9
|
+
const magic = GLTFUtils.decodeText(new Uint8Array(data, 0, 4));
|
|
10
|
+
|
|
11
|
+
if (magic === 'glTF') {
|
|
12
|
+
const glbData = GLTFUtils.parseGLB(data);
|
|
13
|
+
context.gltf = glbData.gltf;
|
|
14
|
+
context.buffers = glbData.buffers;
|
|
15
|
+
} else {
|
|
16
|
+
const gltfString = GLTFUtils.decodeText(new Uint8Array(data));
|
|
17
|
+
context.gltf = JSON.parse(gltfString);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PBRMaterial,
|
|
3
|
+
DRAW_SIDE,
|
|
4
|
+
TEXEL_ENCODING_TYPE
|
|
5
|
+
} from 't3d';
|
|
6
|
+
import { KHR_materials_unlit as _KHR_materials_unlit } from '../extensions/KHR_materials_unlit.js';
|
|
7
|
+
import { KHR_materials_pbrSpecularGlossiness as _KHR_materials_pbrSpecularGlossiness } from '../extensions/KHR_materials_pbrSpecularGlossiness.js';
|
|
8
|
+
import { KHR_materials_clearcoat as _KHR_materials_clearcoat } from '../extensions/KHR_materials_clearcoat.js';
|
|
9
|
+
import { KHR_texture_transform } from '../extensions/KHR_texture_transform.js';
|
|
10
|
+
import { ALPHA_MODES } from '../Constants.js';
|
|
11
|
+
|
|
12
|
+
export class MaterialParser {
|
|
13
|
+
|
|
14
|
+
static parse(context) {
|
|
15
|
+
const { gltf, textures } = context;
|
|
16
|
+
|
|
17
|
+
if (!gltf.materials) return;
|
|
18
|
+
|
|
19
|
+
const materials = [];
|
|
20
|
+
for (let i = 0; i < gltf.materials.length; i++) {
|
|
21
|
+
const {
|
|
22
|
+
extensions = {},
|
|
23
|
+
pbrMetallicRoughness,
|
|
24
|
+
normalTexture,
|
|
25
|
+
occlusionTexture,
|
|
26
|
+
emissiveTexture,
|
|
27
|
+
emissiveFactor,
|
|
28
|
+
alphaMode,
|
|
29
|
+
alphaCutoff,
|
|
30
|
+
doubleSided,
|
|
31
|
+
name = ''
|
|
32
|
+
} = gltf.materials[i];
|
|
33
|
+
|
|
34
|
+
const { KHR_materials_unlit, KHR_materials_pbrSpecularGlossiness, KHR_materials_clearcoat } = extensions;
|
|
35
|
+
|
|
36
|
+
let material = null;
|
|
37
|
+
if (KHR_materials_unlit) {
|
|
38
|
+
material = _KHR_materials_unlit.getMaterial();
|
|
39
|
+
} else if (KHR_materials_pbrSpecularGlossiness) {
|
|
40
|
+
material = _KHR_materials_pbrSpecularGlossiness.getMaterial();
|
|
41
|
+
_KHR_materials_pbrSpecularGlossiness.parseParams(material, KHR_materials_pbrSpecularGlossiness, textures);
|
|
42
|
+
} else if (KHR_materials_clearcoat) {
|
|
43
|
+
material = _KHR_materials_clearcoat.getMaterial();
|
|
44
|
+
_KHR_materials_clearcoat.parseParams(material, KHR_materials_clearcoat, textures);
|
|
45
|
+
} else {
|
|
46
|
+
material = new PBRMaterial();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
material.name = name;
|
|
50
|
+
|
|
51
|
+
if (pbrMetallicRoughness) {
|
|
52
|
+
const { baseColorFactor, baseColorTexture, metallicFactor, roughnessFactor, metallicRoughnessTexture } = pbrMetallicRoughness;
|
|
53
|
+
|
|
54
|
+
if (Array.isArray(baseColorFactor)) {
|
|
55
|
+
material.diffuse.fromArray(baseColorFactor);
|
|
56
|
+
material.opacity = (baseColorFactor[3] !== undefined) ? baseColorFactor[3] : 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (baseColorTexture) {
|
|
60
|
+
material.diffuseMap = textures[baseColorTexture.index];
|
|
61
|
+
material.diffuseMapCoord = baseColorTexture.texCoord || 0;
|
|
62
|
+
if (material.diffuseMap) {
|
|
63
|
+
material.diffuseMap.encoding = TEXEL_ENCODING_TYPE.SRGB;
|
|
64
|
+
parseTextureTransform(material, 'diffuseMap', baseColorTexture.extensions);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!KHR_materials_unlit && !KHR_materials_pbrSpecularGlossiness) {
|
|
69
|
+
material.metalness = metallicFactor !== undefined ? metallicFactor : 1;
|
|
70
|
+
material.roughness = roughnessFactor !== undefined ? roughnessFactor : 1;
|
|
71
|
+
if (metallicRoughnessTexture) {
|
|
72
|
+
material.metalnessMap = textures[metallicRoughnessTexture.index];
|
|
73
|
+
material.roughnessMap = textures[metallicRoughnessTexture.index];
|
|
74
|
+
// parseTextureTransform(material, 'metalnessMap', metallicRoughnessTexture.extensions);
|
|
75
|
+
// parseTextureTransform(material, 'roughnessMap', metallicRoughnessTexture.extensions);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (emissiveFactor) {
|
|
81
|
+
material.emissive.fromArray(emissiveFactor);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (emissiveTexture) {
|
|
85
|
+
material.emissiveMap = textures[emissiveTexture.index];
|
|
86
|
+
material.emissiveMapCoord = emissiveTexture.texCoord || 0;
|
|
87
|
+
if (material.emissiveMap) {
|
|
88
|
+
material.emissiveMap.encoding = TEXEL_ENCODING_TYPE.SRGB;
|
|
89
|
+
parseTextureTransform(material, 'emissiveMap', emissiveTexture.extensions);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (occlusionTexture) {
|
|
94
|
+
material.aoMap = textures[occlusionTexture.index];
|
|
95
|
+
material.aoMapCoord = occlusionTexture.texCoord || 0;
|
|
96
|
+
if (occlusionTexture.strength !== undefined) {
|
|
97
|
+
material.aoMapIntensity = occlusionTexture.strength;
|
|
98
|
+
}
|
|
99
|
+
if (material.aoMap) {
|
|
100
|
+
parseTextureTransform(material, 'aoMap', occlusionTexture.extensions);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!KHR_materials_unlit) {
|
|
105
|
+
if (normalTexture) {
|
|
106
|
+
material.normalMap = textures[normalTexture.index];
|
|
107
|
+
|
|
108
|
+
material.normalScale.set(1, -1);
|
|
109
|
+
if (normalTexture.scale !== undefined) {
|
|
110
|
+
// fix flip y for normal map
|
|
111
|
+
// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
|
|
112
|
+
material.normalScale.set(normalTexture.scale, -normalTexture.scale);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (material.normalMap) {
|
|
116
|
+
// parseTextureTransform(material, 'normalMap', normalTexture.extensions);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
material.side = doubleSided === true ? DRAW_SIDE.DOUBLE : DRAW_SIDE.FRONT;
|
|
122
|
+
|
|
123
|
+
if (alphaMode === ALPHA_MODES.BLEND) {
|
|
124
|
+
material.transparent = true;
|
|
125
|
+
} else {
|
|
126
|
+
material.transparent = false;
|
|
127
|
+
|
|
128
|
+
if (alphaMode === ALPHA_MODES.MASK) {
|
|
129
|
+
material.alphaTest = alphaCutoff !== undefined ? alphaCutoff : 0.5;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
materials[i] = material;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
context.materials = materials;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseTextureTransform(material, key, extensions = {}) {
|
|
142
|
+
const extension = extensions.KHR_texture_transform;
|
|
143
|
+
if (extension) {
|
|
144
|
+
KHR_texture_transform.transform(material[key + 'Transform'], extension);
|
|
145
|
+
}
|
|
146
|
+
}
|