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,28 @@
|
|
|
1
|
+
import { GLTFUtils } from '../../../libs/glTF/GLTFUtils.js';
|
|
2
|
+
|
|
3
|
+
export class I3DMParser {
|
|
4
|
+
|
|
5
|
+
static parse(context, loader) {
|
|
6
|
+
const bodyBytes = new Uint8Array(
|
|
7
|
+
context.options.buffer,
|
|
8
|
+
context.batchTableEnd,
|
|
9
|
+
context.header.byteLength - context.batchTableEnd
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
let promise = null;
|
|
13
|
+
if (context.header.gltfFormat === 1) {
|
|
14
|
+
promise = Promise.resolve(bodyBytes);
|
|
15
|
+
} else {
|
|
16
|
+
const externalUri = GLTFUtils.resolveURL(GLTFUtils.decodeText(bodyBytes), context.path);
|
|
17
|
+
promise = loader.loadFile(externalUri, 'arraybuffer').then(buffer => new Uint8Array(buffer));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return promise.then(glbBytes => {
|
|
21
|
+
const glbData = GLTFUtils.parseGLB(glbBytes.slice().buffer);
|
|
22
|
+
|
|
23
|
+
context.gltf = glbData.gltf;
|
|
24
|
+
context.buffers = glbData.buffers;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Vector3, Matrix4, Quaternion, Attribute, Buffer } from 't3d';
|
|
2
|
+
|
|
3
|
+
export class I3DMRootParser {
|
|
4
|
+
|
|
5
|
+
static parse(context, loader) {
|
|
6
|
+
const { featureTable, root, options } = context;
|
|
7
|
+
|
|
8
|
+
const INSTANCES_LENGTH = featureTable.getData('INSTANCES_LENGTH');
|
|
9
|
+
const POSITION = featureTable.getData('POSITION', INSTANCES_LENGTH, 'FLOAT', 'VEC3');
|
|
10
|
+
const NORMAL_UP = featureTable.getData('NORMAL_UP', INSTANCES_LENGTH, 'FLOAT', 'VEC3');
|
|
11
|
+
const NORMAL_RIGHT = featureTable.getData('NORMAL_RIGHT', INSTANCES_LENGTH, 'FLOAT', 'VEC3');
|
|
12
|
+
const SCALE = featureTable.getData('SCALE', INSTANCES_LENGTH, 'FLOAT', 'SCALAR');
|
|
13
|
+
const SCALE_NON_UNIFORM = featureTable.getData('SCALE_NON_UNIFORM', INSTANCES_LENGTH, 'FLOAT', 'VEC3');
|
|
14
|
+
|
|
15
|
+
// check unsupported features
|
|
16
|
+
|
|
17
|
+
[
|
|
18
|
+
// Global Properties
|
|
19
|
+
'QUANTIZED_VOLUME_OFFSET',
|
|
20
|
+
'QUANTIZED_VOLUME_SCALE',
|
|
21
|
+
'EAST_NORTH_UP',
|
|
22
|
+
|
|
23
|
+
// Per-instance Properties
|
|
24
|
+
'POSITION_QUANTIZED',
|
|
25
|
+
'NORMAL_UP_OCT32P',
|
|
26
|
+
'NORMAL_RIGHT_OCT32P'
|
|
27
|
+
].forEach(feature => {
|
|
28
|
+
if (feature in featureTable.header) {
|
|
29
|
+
console.warn(`I3DMLoader: Unsupported FeatureTable feature "${feature}" detected.`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// set instance matrix for all geometries
|
|
34
|
+
|
|
35
|
+
const averageVector = new Vector3();
|
|
36
|
+
for (let i = 0; i < INSTANCES_LENGTH; i++) {
|
|
37
|
+
averageVector.x += POSITION[i * 3 + 0] / INSTANCES_LENGTH;
|
|
38
|
+
averageVector.y += POSITION[i * 3 + 1] / INSTANCES_LENGTH;
|
|
39
|
+
averageVector.z += POSITION[i * 3 + 2] / INSTANCES_LENGTH;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const instances = [];
|
|
43
|
+
|
|
44
|
+
root.traverse(child => {
|
|
45
|
+
if (child.isMesh) {
|
|
46
|
+
const { geometry } = child;
|
|
47
|
+
geometry.instanceCount = INSTANCES_LENGTH;
|
|
48
|
+
|
|
49
|
+
const instanceMatrix = new Attribute(new Buffer(new Float32Array(INSTANCES_LENGTH * 16), 16), 16);
|
|
50
|
+
instanceMatrix.divisor = 1;
|
|
51
|
+
geometry.addAttribute('instanceMatrix', instanceMatrix);
|
|
52
|
+
|
|
53
|
+
// Center the instance around an average point to avoid jitter at large scales.
|
|
54
|
+
// Transform the average vector by matrix world so we can account for any existing
|
|
55
|
+
// transforms of the instanced mesh.
|
|
56
|
+
child.updateMatrix(true);
|
|
57
|
+
child.position.copy(averageVector).applyMatrix4(child.worldMatrix);
|
|
58
|
+
|
|
59
|
+
instances.push(child);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < INSTANCES_LENGTH; i++) {
|
|
64
|
+
// position
|
|
65
|
+
tempPos.fromArray(POSITION, i * 3).sub(averageVector);
|
|
66
|
+
|
|
67
|
+
// rotation
|
|
68
|
+
if (NORMAL_UP) {
|
|
69
|
+
tempUp.fromArray(NORMAL_UP, i * 3);
|
|
70
|
+
tempRight.fromArray(NORMAL_RIGHT, i * 3);
|
|
71
|
+
tempFwd.crossVectors(tempRight, tempUp).normalize();
|
|
72
|
+
|
|
73
|
+
tempMat.set(
|
|
74
|
+
tempRight.x, tempUp.x, tempFwd.x, 0,
|
|
75
|
+
tempRight.y, tempUp.y, tempFwd.y, 0,
|
|
76
|
+
tempRight.z, tempUp.z, tempFwd.z, 0,
|
|
77
|
+
0, 0, 0, 1
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
tempQuat.setFromRotationMatrix(tempMat);
|
|
81
|
+
} else {
|
|
82
|
+
tempQuat.set(0, 0, 0, 1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// scale
|
|
86
|
+
if (SCALE) {
|
|
87
|
+
tempSca.set(SCALE[i], SCALE[i], SCALE[i]);
|
|
88
|
+
} else if (SCALE_NON_UNIFORM) {
|
|
89
|
+
tempSca.fromArray(SCALE_NON_UNIFORM, i * 3);
|
|
90
|
+
} else {
|
|
91
|
+
tempSca.set(1, 1, 1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// TODO instance matrix should be applied to model root
|
|
95
|
+
tempMat.transform(tempPos, tempSca, tempQuat).multiply(options.adjustmentTransform);
|
|
96
|
+
|
|
97
|
+
for (let j = 0, l = instances.length; j < l; j++) {
|
|
98
|
+
const { geometry } = instances[j];
|
|
99
|
+
const instanceArray = geometry.getAttribute('instanceMatrix').buffer.array;
|
|
100
|
+
tempMat.toArray(instanceArray, i * 16);
|
|
101
|
+
geometry.version++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// fix rtc center
|
|
106
|
+
|
|
107
|
+
const rtcCenter = featureTable.getData('RTC_CENTER');
|
|
108
|
+
if (rtcCenter) {
|
|
109
|
+
root.position.x += rtcCenter[0];
|
|
110
|
+
root.position.y += rtcCenter[1];
|
|
111
|
+
root.position.z += rtcCenter[2];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const tempFwd = new Vector3();
|
|
118
|
+
const tempUp = new Vector3();
|
|
119
|
+
const tempRight = new Vector3();
|
|
120
|
+
const tempPos = new Vector3();
|
|
121
|
+
const tempQuat = new Quaternion();
|
|
122
|
+
const tempSca = new Vector3();
|
|
123
|
+
const tempMat = new Matrix4();
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Color3,
|
|
3
|
+
// PBRMaterial,
|
|
4
|
+
DRAW_SIDE,
|
|
5
|
+
TEXEL_ENCODING_TYPE
|
|
6
|
+
} from 't3d';
|
|
7
|
+
// import { KHR_materials_unlit as _KHR_materials_unlit } from '../../../libs/glTF/extensions/KHR_materials_unlit.js';
|
|
8
|
+
import { KHR_materials_pbrSpecularGlossiness as _KHR_materials_pbrSpecularGlossiness } from '../../../libs/glTF/extensions/KHR_materials_pbrSpecularGlossiness.js';
|
|
9
|
+
import { KHR_materials_clearcoat as _KHR_materials_clearcoat } from '../../../libs/glTF/extensions/KHR_materials_clearcoat.js';
|
|
10
|
+
import { KHR_texture_transform } from '../../../libs/glTF/extensions/KHR_texture_transform.js';
|
|
11
|
+
import { ALPHA_MODES } from '../../../libs/glTF/Constants.js';
|
|
12
|
+
|
|
13
|
+
import { InstancedPBRMaterial } from '../../../materials/InstancedPBRMaterial.js'; // @parser-modification - instanced materials
|
|
14
|
+
import { InstancedBasicMaterial } from '../../../materials/InstancedBasicMaterial.js'; // @parser-modification - instanced materials
|
|
15
|
+
|
|
16
|
+
export class MaterialParser {
|
|
17
|
+
|
|
18
|
+
static parse(context) {
|
|
19
|
+
const { gltf, textures } = context;
|
|
20
|
+
|
|
21
|
+
if (!gltf.materials) return;
|
|
22
|
+
|
|
23
|
+
const materials = [];
|
|
24
|
+
for (let i = 0; i < gltf.materials.length; i++) {
|
|
25
|
+
const {
|
|
26
|
+
extensions = {},
|
|
27
|
+
pbrMetallicRoughness,
|
|
28
|
+
normalTexture,
|
|
29
|
+
occlusionTexture,
|
|
30
|
+
emissiveTexture,
|
|
31
|
+
emissiveFactor,
|
|
32
|
+
alphaMode,
|
|
33
|
+
alphaCutoff,
|
|
34
|
+
doubleSided,
|
|
35
|
+
name = ''
|
|
36
|
+
} = gltf.materials[i];
|
|
37
|
+
|
|
38
|
+
const { KHR_materials_unlit, KHR_materials_pbrSpecularGlossiness, KHR_materials_clearcoat } = extensions;
|
|
39
|
+
|
|
40
|
+
let material = null;
|
|
41
|
+
if (KHR_materials_unlit) {
|
|
42
|
+
material = new InstancedBasicMaterial(); // @parser-modification - instanced materials
|
|
43
|
+
} else if (KHR_materials_pbrSpecularGlossiness) {
|
|
44
|
+
// TODO - InstancedPBR2Material
|
|
45
|
+
material = new InstancedPBRMaterial(); // @parser-modification - instanced materials
|
|
46
|
+
material.specular = new Color3(0x111111);
|
|
47
|
+
_KHR_materials_pbrSpecularGlossiness.parseParams(material, KHR_materials_pbrSpecularGlossiness, textures);
|
|
48
|
+
} else if (KHR_materials_clearcoat) {
|
|
49
|
+
material = new InstancedPBRMaterial(); // @parser-modification - instanced materials
|
|
50
|
+
_KHR_materials_clearcoat.parseParams(material, KHR_materials_clearcoat, textures);
|
|
51
|
+
} else {
|
|
52
|
+
material = new InstancedPBRMaterial(); // @parser-modification - instanced materials
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
material.name = name;
|
|
56
|
+
|
|
57
|
+
if (pbrMetallicRoughness) {
|
|
58
|
+
const { baseColorFactor, baseColorTexture, metallicFactor, roughnessFactor, metallicRoughnessTexture } = pbrMetallicRoughness;
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(baseColorFactor)) {
|
|
61
|
+
material.diffuse.fromArray(baseColorFactor);
|
|
62
|
+
material.opacity = (baseColorFactor[3] !== undefined) ? baseColorFactor[3] : 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (baseColorTexture) {
|
|
66
|
+
material.diffuseMap = textures[baseColorTexture.index];
|
|
67
|
+
material.diffuseMapCoord = baseColorTexture.texCoord || 0;
|
|
68
|
+
if (material.diffuseMap) {
|
|
69
|
+
material.diffuseMap.encoding = TEXEL_ENCODING_TYPE.SRGB;
|
|
70
|
+
parseTextureTransform(material, 'diffuseMap', baseColorTexture.extensions);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!KHR_materials_unlit && !KHR_materials_pbrSpecularGlossiness) {
|
|
75
|
+
material.metalness = metallicFactor !== undefined ? metallicFactor : 1;
|
|
76
|
+
material.roughness = roughnessFactor !== undefined ? roughnessFactor : 1;
|
|
77
|
+
if (metallicRoughnessTexture) {
|
|
78
|
+
material.metalnessMap = textures[metallicRoughnessTexture.index];
|
|
79
|
+
material.roughnessMap = textures[metallicRoughnessTexture.index];
|
|
80
|
+
// parseTextureTransform(material, 'metalnessMap', metallicRoughnessTexture.extensions);
|
|
81
|
+
// parseTextureTransform(material, 'roughnessMap', metallicRoughnessTexture.extensions);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (emissiveFactor) {
|
|
87
|
+
material.emissive.fromArray(emissiveFactor);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (emissiveTexture) {
|
|
91
|
+
material.emissiveMap = textures[emissiveTexture.index];
|
|
92
|
+
material.emissiveMapCoord = emissiveTexture.texCoord || 0;
|
|
93
|
+
if (material.emissiveMap) {
|
|
94
|
+
material.emissiveMap.encoding = TEXEL_ENCODING_TYPE.SRGB;
|
|
95
|
+
parseTextureTransform(material, 'emissiveMap', emissiveTexture.extensions);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (occlusionTexture) {
|
|
100
|
+
material.aoMap = textures[occlusionTexture.index];
|
|
101
|
+
material.aoMapCoord = occlusionTexture.texCoord || 0;
|
|
102
|
+
if (occlusionTexture.strength !== undefined) {
|
|
103
|
+
material.aoMapIntensity = occlusionTexture.strength;
|
|
104
|
+
}
|
|
105
|
+
if (material.aoMap) {
|
|
106
|
+
parseTextureTransform(material, 'aoMap', occlusionTexture.extensions);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!KHR_materials_unlit) {
|
|
111
|
+
if (normalTexture) {
|
|
112
|
+
material.normalMap = textures[normalTexture.index];
|
|
113
|
+
|
|
114
|
+
material.normalScale.set(1, -1);
|
|
115
|
+
if (normalTexture.scale !== undefined) {
|
|
116
|
+
// fix flip y for normal map
|
|
117
|
+
// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
|
|
118
|
+
material.normalScale.set(normalTexture.scale, -normalTexture.scale);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (material.normalMap) {
|
|
122
|
+
// parseTextureTransform(material, 'normalMap', normalTexture.extensions);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
material.side = doubleSided === true ? DRAW_SIDE.DOUBLE : DRAW_SIDE.FRONT;
|
|
128
|
+
|
|
129
|
+
if (alphaMode === ALPHA_MODES.BLEND) {
|
|
130
|
+
material.transparent = true;
|
|
131
|
+
} else {
|
|
132
|
+
material.transparent = false;
|
|
133
|
+
|
|
134
|
+
if (alphaMode === ALPHA_MODES.MASK) {
|
|
135
|
+
material.alphaTest = alphaCutoff !== undefined ? alphaCutoff : 0.5;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
materials[i] = material;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
context.materials = materials;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function parseTextureTransform(material, key, extensions = {}) {
|
|
148
|
+
const extension = extensions.KHR_texture_transform;
|
|
149
|
+
if (extension) {
|
|
150
|
+
KHR_texture_transform.transform(material[key + 'Transform'], extension);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { Geometry, /* PBRMaterial, */ VERTEX_COLOR, SHADING_TYPE, PointsMaterial, Material, BasicMaterial } from 't3d';
|
|
2
|
+
import { GLTFUtils } from '../../../libs/glTF/GLTFUtils.js';
|
|
3
|
+
import { ATTRIBUTES, ACCESSOR_COMPONENT_TYPES, WEBGL_DRAW_MODES } from '../../../libs/glTF/Constants.js';
|
|
4
|
+
import { KHR_draco_mesh_compression as _KHR_draco_mesh_compression } from '../../../libs/glTF/extensions/KHR_draco_mesh_compression.js';
|
|
5
|
+
|
|
6
|
+
import { InstancedPBRMaterial } from '../../../materials/InstancedPBRMaterial.js'; // @parser-modification - instanced materials
|
|
7
|
+
|
|
8
|
+
export class PrimitiveParser {
|
|
9
|
+
|
|
10
|
+
static parse(context, loader) {
|
|
11
|
+
const { gltf, accessors, materials, bufferViews } = context;
|
|
12
|
+
|
|
13
|
+
if (!gltf.meshes) return;
|
|
14
|
+
|
|
15
|
+
const materialCache = new Map();
|
|
16
|
+
const geometryPromiseCache = new Map();
|
|
17
|
+
|
|
18
|
+
const meshPromises = [];
|
|
19
|
+
for (let i = 0; i < gltf.meshes.length; i++) {
|
|
20
|
+
const gltfMesh = gltf.meshes[i];
|
|
21
|
+
|
|
22
|
+
const primitivePromises = [];
|
|
23
|
+
for (let j = 0; j < gltfMesh.primitives.length; j++) {
|
|
24
|
+
const gltfPrimitive = gltfMesh.primitives[j];
|
|
25
|
+
const {
|
|
26
|
+
extensions = {},
|
|
27
|
+
mode,
|
|
28
|
+
material
|
|
29
|
+
} = gltfPrimitive;
|
|
30
|
+
const { KHR_draco_mesh_compression } = extensions;
|
|
31
|
+
|
|
32
|
+
let geometryPromise;
|
|
33
|
+
|
|
34
|
+
const geometryKey = createGeometryKey(gltfPrimitive);
|
|
35
|
+
if (geometryPromiseCache.has(geometryKey)) {
|
|
36
|
+
geometryPromise = geometryPromiseCache.get(geometryKey);
|
|
37
|
+
} else {
|
|
38
|
+
if (KHR_draco_mesh_compression) {
|
|
39
|
+
geometryPromise = _KHR_draco_mesh_compression.getGeometry(KHR_draco_mesh_compression, bufferViews, gltfPrimitive.attributes, gltf.accessors, loader.getDRACOLoader());
|
|
40
|
+
} else {
|
|
41
|
+
geometryPromise = Promise.resolve(new Geometry());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
geometryPromise = geometryPromise.then(geometry => {
|
|
45
|
+
parseGeometryFromGLTFPrimitive(geometry, gltfPrimitive, gltf, accessors);
|
|
46
|
+
return geometry;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
geometryPromiseCache.set(geometryKey, geometryPromise);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const primitivePromise = geometryPromise.then(geometry => {
|
|
53
|
+
const primitive = {
|
|
54
|
+
mode,
|
|
55
|
+
geometry,
|
|
56
|
+
material: material === undefined ? new InstancedPBRMaterial() : materials[material], // @parser-modification - instanced materials
|
|
57
|
+
weights: (Object.keys(geometry.morphAttributes).length > 0 && gltfMesh.weights) ? gltfMesh.weights.slice(0) : undefined,
|
|
58
|
+
skinned: gltfMesh.isSkinned
|
|
59
|
+
};
|
|
60
|
+
assignFinalMaterial(primitive, materialCache);
|
|
61
|
+
return primitive;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
primitivePromises.push(primitivePromise);
|
|
65
|
+
}
|
|
66
|
+
meshPromises.push(Promise.all(primitivePromises));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
materialCache.clear();
|
|
70
|
+
geometryPromiseCache.clear();
|
|
71
|
+
|
|
72
|
+
return Promise.all(meshPromises).then(primitives => {
|
|
73
|
+
context.primitives = primitives;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function parseGeometryFromGLTFPrimitive(geometry, gltfPrimitive, gltf, accessors) {
|
|
80
|
+
const { attributes, indices, targets } = gltfPrimitive;
|
|
81
|
+
|
|
82
|
+
// set attributes
|
|
83
|
+
|
|
84
|
+
for (const attributeSemantic in attributes) {
|
|
85
|
+
const accessorIdx = attributes[attributeSemantic];
|
|
86
|
+
|
|
87
|
+
const attributeName = ATTRIBUTES[attributeSemantic] === undefined ? attributeSemantic : ATTRIBUTES[attributeSemantic];
|
|
88
|
+
// Skip attributes already provided by e.g. Draco extension.
|
|
89
|
+
if (attributeName in geometry.attributes) continue;
|
|
90
|
+
|
|
91
|
+
geometry.addAttribute(attributeName, accessors[accessorIdx]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// set index
|
|
95
|
+
|
|
96
|
+
if (indices !== undefined && !geometry.index) {
|
|
97
|
+
geometry.setIndex(accessors[indices]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// compute bounds
|
|
101
|
+
|
|
102
|
+
const { boundingBox, boundingSphere } = geometry;
|
|
103
|
+
if (attributes.POSITION !== undefined) {
|
|
104
|
+
const accessorIdx = attributes.POSITION;
|
|
105
|
+
const accessor = gltf.accessors[accessorIdx];
|
|
106
|
+
|
|
107
|
+
if (accessor.min && accessor.max) {
|
|
108
|
+
boundingBox.min.fromArray(accessor.min);
|
|
109
|
+
boundingBox.max.fromArray(accessor.max);
|
|
110
|
+
|
|
111
|
+
if (accessor.normalized) {
|
|
112
|
+
const boxScale = GLTFUtils.getNormalizedComponentScale(ACCESSOR_COMPONENT_TYPES[accessor.componentType]);
|
|
113
|
+
boundingBox.min.multiplyScalar(boxScale);
|
|
114
|
+
boundingBox.max.multiplyScalar(boxScale);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
geometry.computeBoundingBox();
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
geometry.computeBoundingBox();
|
|
121
|
+
}
|
|
122
|
+
boundingBox.getCenter(boundingSphere.center);
|
|
123
|
+
boundingSphere.radius = boundingBox.min.distanceTo(boundingBox.max) / 2;
|
|
124
|
+
|
|
125
|
+
// set morph targets
|
|
126
|
+
|
|
127
|
+
if (targets) {
|
|
128
|
+
let hasMorphPosition = false;
|
|
129
|
+
let hasMorphNormal = false;
|
|
130
|
+
|
|
131
|
+
for (let i = 0, il = targets.length; i < il; i++) {
|
|
132
|
+
const target = targets[i];
|
|
133
|
+
|
|
134
|
+
if (target.POSITION !== undefined) hasMorphPosition = true;
|
|
135
|
+
if (target.NORMAL !== undefined) hasMorphNormal = true;
|
|
136
|
+
|
|
137
|
+
if (hasMorphPosition && hasMorphNormal) break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (hasMorphPosition || hasMorphNormal) {
|
|
141
|
+
const morphPositions = [];
|
|
142
|
+
const morphNormals = [];
|
|
143
|
+
|
|
144
|
+
for (let i = 0, il = targets.length; i < il; i++) {
|
|
145
|
+
const target = targets[i];
|
|
146
|
+
|
|
147
|
+
if (hasMorphPosition) {
|
|
148
|
+
morphPositions.push(target.POSITION !== undefined ? accessors[target.POSITION] : geometry.attributes[ATTRIBUTES.POSITION]);
|
|
149
|
+
}
|
|
150
|
+
if (hasMorphNormal) {
|
|
151
|
+
morphNormals.push(target.NORMAL !== undefined ? accessors[target.NORMAL] : geometry.attributes[ATTRIBUTES.NORMAL]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (hasMorphPosition) {
|
|
156
|
+
geometry.morphAttributes.position = morphPositions;
|
|
157
|
+
}
|
|
158
|
+
if (hasMorphNormal) {
|
|
159
|
+
geometry.morphAttributes.normal = morphNormals;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return geometry;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function assignFinalMaterial(primitive, materialCache) {
|
|
168
|
+
let { geometry, material, skinned, mode } = primitive;
|
|
169
|
+
|
|
170
|
+
// If the material will be modified later on, clone it now.
|
|
171
|
+
const useVertexTangents = geometry.attributes[ATTRIBUTES.TANGENT] !== undefined;
|
|
172
|
+
const useVertexColors = geometry.attributes[ATTRIBUTES.COLOR_0] !== undefined;
|
|
173
|
+
const useFlatShading = geometry.attributes[ATTRIBUTES.NORMAL] === undefined;
|
|
174
|
+
const useSkinning = skinned;
|
|
175
|
+
|
|
176
|
+
if (mode === WEBGL_DRAW_MODES.POINTS) {
|
|
177
|
+
const cacheKey = 'PointsMaterial:' + material.id;
|
|
178
|
+
let pointsMaterial = materialCache.get(cacheKey);
|
|
179
|
+
if (!pointsMaterial) {
|
|
180
|
+
pointsMaterial = new PointsMaterial();
|
|
181
|
+
Material.prototype.copy.call(pointsMaterial, material);
|
|
182
|
+
pointsMaterial.diffuse.copy(material.diffuse);
|
|
183
|
+
pointsMaterial.diffuseMap = material.map;
|
|
184
|
+
pointsMaterial.drawMode = mode;
|
|
185
|
+
pointsMaterial.acceptLight = false; // PointsMaterial doesn't support lights yet
|
|
186
|
+
materialCache.set(cacheKey, pointsMaterial);
|
|
187
|
+
}
|
|
188
|
+
material = pointsMaterial;
|
|
189
|
+
} else if (mode === WEBGL_DRAW_MODES.LINES || mode === WEBGL_DRAW_MODES.LINE_STRIP || mode === WEBGL_DRAW_MODES.LINE_LOOP) {
|
|
190
|
+
const cacheKey = 'BasicMaterial:' + material.id;
|
|
191
|
+
let basicMaterial = materialCache.get(cacheKey);
|
|
192
|
+
if (!basicMaterial) {
|
|
193
|
+
basicMaterial = new BasicMaterial();
|
|
194
|
+
basicMaterial.envMap = undefined; // force close env map
|
|
195
|
+
basicMaterial.diffuse.copy(material.diffuse);
|
|
196
|
+
basicMaterial.diffuseMap = material.diffuseMap;
|
|
197
|
+
basicMaterial.drawMode = mode;
|
|
198
|
+
materialCache.set(cacheKey, basicMaterial);
|
|
199
|
+
}
|
|
200
|
+
material = basicMaterial;
|
|
201
|
+
} else if (mode === WEBGL_DRAW_MODES.TRIANGLE_STRIP) {
|
|
202
|
+
// TODO
|
|
203
|
+
console.warn('TRIANGLE_STRIP will be removed later.');
|
|
204
|
+
material.drawMode = WEBGL_DRAW_MODES.TRIANGLE_STRIP;
|
|
205
|
+
} else if (mode === WEBGL_DRAW_MODES.TRIANGLE_FAN) {
|
|
206
|
+
// TODO
|
|
207
|
+
console.warn('TRIANGLE_FAN will be removed later.');
|
|
208
|
+
material.drawMode = WEBGL_DRAW_MODES.TRIANGLE_FAN;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (useVertexTangents || useVertexColors || useFlatShading || useSkinning) {
|
|
212
|
+
let cacheKey = 'ClonedMaterial:' + material.id + ':';
|
|
213
|
+
|
|
214
|
+
if (useVertexTangents) cacheKey += 'vertex-tangents:';
|
|
215
|
+
if (useVertexColors) {
|
|
216
|
+
if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 3) {
|
|
217
|
+
cacheKey += 'vertex-colors-rgb:';
|
|
218
|
+
} else if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 4) {
|
|
219
|
+
cacheKey += 'vertex-colors-rgba:';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (useFlatShading) cacheKey += 'flat-shading:';
|
|
223
|
+
|
|
224
|
+
let cachedMaterial = materialCache.get(cacheKey);
|
|
225
|
+
|
|
226
|
+
if (!cachedMaterial) {
|
|
227
|
+
cachedMaterial = material.clone();
|
|
228
|
+
|
|
229
|
+
if (useVertexTangents) {
|
|
230
|
+
cachedMaterial.vertexTangents = true;
|
|
231
|
+
|
|
232
|
+
// revert flip y fix for tangents
|
|
233
|
+
// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
|
|
234
|
+
if (cachedMaterial.normalMap) {
|
|
235
|
+
cachedMaterial.normalScale.y *= -1;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (useVertexColors) {
|
|
240
|
+
if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 3) {
|
|
241
|
+
cachedMaterial.vertexColors = VERTEX_COLOR.RGB;
|
|
242
|
+
} else if (geometry.attributes[ATTRIBUTES.COLOR_0].size === 4) {
|
|
243
|
+
cachedMaterial.vertexColors = VERTEX_COLOR.RGBA;
|
|
244
|
+
} else {
|
|
245
|
+
console.warn('Illegal vertex color size: ' + geometry.attributes[ATTRIBUTES.COLOR_0].size);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (useFlatShading) {
|
|
250
|
+
cachedMaterial.shading = SHADING_TYPE.FLAT_SHADING;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
material = cachedMaterial;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
primitive.material = material;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function createGeometryKey(primitive) {
|
|
261
|
+
const dracoExtension = primitive.extensions && primitive.extensions.KHR_draco_mesh_compression;
|
|
262
|
+
let geometryKey;
|
|
263
|
+
|
|
264
|
+
if (dracoExtension) {
|
|
265
|
+
geometryKey = 'draco:' + dracoExtension.bufferView
|
|
266
|
+
+ ':' + dracoExtension.indices
|
|
267
|
+
+ ':' + createAttributesKey(dracoExtension.attributes);
|
|
268
|
+
} else {
|
|
269
|
+
geometryKey = primitive.indices + ':' + createAttributesKey(primitive.attributes) + ':' + primitive.mode;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (primitive.targets) {
|
|
273
|
+
for (let i = 0, il = primitive.targets.length; i < il; i++) {
|
|
274
|
+
geometryKey += ':' + createAttributesKey(primitive.targets[i]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return geometryKey;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function createAttributesKey(attributes) {
|
|
282
|
+
let attributesKey = '';
|
|
283
|
+
|
|
284
|
+
const keys = Object.keys(attributes).sort();
|
|
285
|
+
|
|
286
|
+
for (let i = 0, il = keys.length; i < il; i++) {
|
|
287
|
+
attributesKey += keys[i] + ':' + attributes[keys[i]] + ';';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return attributesKey;
|
|
291
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Mesh, Geometry, PointsMaterial, Attribute, Buffer, VERTEX_COLOR } from 't3d';
|
|
2
|
+
|
|
3
|
+
export class PNTSRootParser {
|
|
4
|
+
|
|
5
|
+
static parse(context, loader) {
|
|
6
|
+
const { featureTable } = context;
|
|
7
|
+
|
|
8
|
+
const POINTS_LENGTH = featureTable.getData('POINTS_LENGTH');
|
|
9
|
+
const POSITION = featureTable.getData('POSITION', POINTS_LENGTH, 'FLOAT', 'VEC3');
|
|
10
|
+
const RGB = featureTable.getData('RGB', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC3');
|
|
11
|
+
const RGBA = featureTable.getData('RGBA', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC4');
|
|
12
|
+
|
|
13
|
+
// check unsupported features
|
|
14
|
+
|
|
15
|
+
[
|
|
16
|
+
// Global Properties
|
|
17
|
+
'QUANTIZED_VOLUME_OFFSET',
|
|
18
|
+
'QUANTIZED_VOLUME_SCALE',
|
|
19
|
+
'CONSTANT_RGBA',
|
|
20
|
+
'BATCH_LENGTH',
|
|
21
|
+
|
|
22
|
+
// Per-point Properties
|
|
23
|
+
'POSITION_QUANTIZED',
|
|
24
|
+
'RGB565',
|
|
25
|
+
'NORMAL',
|
|
26
|
+
'NORMAL_OCT16P',
|
|
27
|
+
'BATCH_ID'
|
|
28
|
+
].forEach(feature => {
|
|
29
|
+
if (feature in featureTable.header) {
|
|
30
|
+
console.warn(`PNTSLoader: Unsupported FeatureTable feature "${feature}" detected.`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// generate root
|
|
35
|
+
|
|
36
|
+
const geometry = new Geometry();
|
|
37
|
+
geometry.addAttribute('a_Position', new Attribute(new Buffer(POSITION, 3), 3, 0, true));
|
|
38
|
+
geometry.computeBoundingBox();
|
|
39
|
+
geometry.computeBoundingSphere();
|
|
40
|
+
|
|
41
|
+
const material = new PointsMaterial();
|
|
42
|
+
material.size = 2;
|
|
43
|
+
material.sizeAttenuation = false;
|
|
44
|
+
|
|
45
|
+
if (RGB !== null) {
|
|
46
|
+
geometry.addAttribute('a_Color', new Attribute(new Buffer(RGB, 3), 3, 0, true));
|
|
47
|
+
material.vertexColors = VERTEX_COLOR.RGB;
|
|
48
|
+
} else if (RGBA !== null) {
|
|
49
|
+
geometry.addAttribute('a_Color', new Attribute(new Buffer(RGBA, 4), 4, 0, true));
|
|
50
|
+
material.vertexColors = VERTEX_COLOR.RGBA;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const root = new Mesh(geometry, material);
|
|
54
|
+
|
|
55
|
+
// output mesh to root
|
|
56
|
+
|
|
57
|
+
context.root = root;
|
|
58
|
+
|
|
59
|
+
// fix rtc center
|
|
60
|
+
|
|
61
|
+
const rtcCenter = featureTable.getData('RTC_CENTER');
|
|
62
|
+
if (rtcCenter) {
|
|
63
|
+
root.position.x += rtcCenter[0];
|
|
64
|
+
root.position.y += rtcCenter[1];
|
|
65
|
+
root.position.z += rtcCenter[2];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
}
|
package/src/main.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './Tiles3D.js';
|
|
2
|
+
|
|
3
|
+
export * from './math/OBB.js';
|
|
4
|
+
|
|
5
|
+
export * from './materials/InstancedBasicMaterial.js';
|
|
6
|
+
export * from './materials/InstancedPBRMaterial.js';
|
|
7
|
+
|
|
8
|
+
export * from './loaders/B3DMLoader.js';
|
|
9
|
+
export * from './loaders/CMPTLoader.js';
|
|
10
|
+
export * from './loaders/TileGLTFLoader.js';
|
|
11
|
+
export * from './loaders/I3DMLoader.js';
|
|
12
|
+
export * from './loaders/PNTSLoader.js';
|
|
13
|
+
|
|
14
|
+
export { LoadParser as DebugLoadParser } from './loaders/parsers/LoadParser.js';
|