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,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
|
+
}
|