chorama 0.0.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/.configs/tsconfig.lib.json +43 -0
- package/.configs/tsconfig.website.json +34 -0
- package/.github/workflows/static.yml +88 -0
- package/.vscode/launch.json +29 -0
- package/.vscode/tasks.json +19 -0
- package/README.md +127 -0
- package/assets/images/disappointed.jpg +0 -0
- package/assets/images/skybox/grimmnight_back.png +0 -0
- package/assets/images/skybox/grimmnight_bottom.png +0 -0
- package/assets/images/skybox/grimmnight_front.png +0 -0
- package/assets/images/skybox/grimmnight_left.png +0 -0
- package/assets/images/skybox/grimmnight_right.png +0 -0
- package/assets/images/skybox/grimmnight_top.png +0 -0
- package/assets/images/skybox/miramar_back.png +0 -0
- package/assets/images/skybox/miramar_bottom.png +0 -0
- package/assets/images/skybox/miramar_front.png +0 -0
- package/assets/images/skybox/miramar_left.png +0 -0
- package/assets/images/skybox/miramar_right.png +0 -0
- package/assets/images/skybox/miramar_top.png +0 -0
- package/assets/images/uv.jpg +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LensesMat_BaseColor.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LensesMat_Normal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_MetalPartsMat_Normal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_RubberWoodMat_Normal.png +0 -0
- package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png +0 -0
- package/assets/models/gltf/flight_helmet/index.bin +0 -0
- package/assets/models/gltf/flight_helmet/index.gltf +705 -0
- package/assets/models/gltf/object.gltf +23 -0
- package/assets/models/gltf/pirate_girl/index.bin +0 -0
- package/assets/models/gltf/pirate_girl/index.gltf +2082 -0
- package/assets/models/obj/pirate_girl/pirate_girl.obj +18459 -0
- package/assets/models/obj/pirate_girl/pirate_girl.png +0 -0
- package/astro.config.mjs +45 -0
- package/content/guide/api-map.md +89 -0
- package/content/guide/camera-and-controls.md +98 -0
- package/content/guide/first-scene.md +176 -0
- package/content/guide/index.md +72 -0
- package/content/guide/installation.md +179 -0
- package/content/guide/materials-and-lighting.md +138 -0
- package/content/guide/plugins-and-render-pipeline.md +124 -0
- package/content/guide/render-targets-and-views.md +147 -0
- package/content/guide/scene-graph-and-transforms.md +113 -0
- package/content/guide/textures-and-assets.md +120 -0
- package/content/guide/troubleshooting.md +49 -0
- package/env.d.ts +19 -0
- package/examples/addons/rendergraph_gui.js +580 -0
- package/examples/camera/orthographic.js +120 -0
- package/examples/camera/perspective.js +138 -0
- package/examples/lights/directional.js +397 -0
- package/examples/lights/multiple_spot_lights.js +304 -0
- package/examples/lights/point.js +337 -0
- package/examples/lights/spot.js +366 -0
- package/examples/loader/gltf_material.js +111 -0
- package/examples/loader/gltfloader.js +78 -0
- package/examples/loader/objloader.js +95 -0
- package/examples/material/cullface.js +111 -0
- package/examples/material/materials.js +126 -0
- package/examples/material/standard/basic.js +164 -0
- package/examples/mesh/circle.js +117 -0
- package/examples/mesh/cuboid.js +151 -0
- package/examples/mesh/cylinder.js +139 -0
- package/examples/mesh/geometries.js +108 -0
- package/examples/mesh/meshTopology.js +103 -0
- package/examples/mesh/plane.js +117 -0
- package/examples/mesh/skinning.js +136 -0
- package/examples/mesh/uvsphere.js +113 -0
- package/examples/other/rotatingCube.js +93 -0
- package/examples/other/rotatingSphere.js +96 -0
- package/examples/rendertarget/basic_canvas.js +130 -0
- package/examples/rendertarget/depth_texture.js +130 -0
- package/examples/rendertarget/image_target.js +140 -0
- package/examples/rendertarget/multiple_views.js +158 -0
- package/examples/rendertarget/render_masks.js +173 -0
- package/examples/rendertarget/split_screen.js +123 -0
- package/examples/rendertarget/split_view.js +137 -0
- package/examples/skybox/skybox.js +111 -0
- package/examples/texture/arrays.js +156 -0
- package/examples/texture/textureWrap.js +118 -0
- package/examples/transform/propagation.js +92 -0
- package/package.json +55 -0
- package/rollup.config.js +66 -0
- package/scripts/stage-chorama.mjs +29 -0
- package/src/caches/cache.js +420 -0
- package/src/caches/index.js +2 -0
- package/src/caches/uniformbuffers.js +104 -0
- package/src/cameracontrols/index.js +258 -0
- package/src/constants/index.js +3 -0
- package/src/constants/mesh.js +197 -0
- package/src/constants/others.js +218 -0
- package/src/constants/texture.js +183 -0
- package/src/core/constants.js +14 -0
- package/src/core/extensions.js +42 -0
- package/src/core/index.js +7 -0
- package/src/core/layouts/index.js +4 -0
- package/src/core/layouts/meshvertex.js +60 -0
- package/src/core/layouts/uniform.js +21 -0
- package/src/core/layouts/uniformbuffer.js +15 -0
- package/src/core/layouts/vertexbuffer.js +43 -0
- package/src/core/limits.js +247 -0
- package/src/core/resources/blendparams.js +89 -0
- package/src/core/resources/framebuffer.js +127 -0
- package/src/core/resources/gpubuffer.js +32 -0
- package/src/core/resources/gpumesh.js +43 -0
- package/src/core/resources/gputexture.js +73 -0
- package/src/core/resources/index.js +5 -0
- package/src/core/shader.js +62 -0
- package/src/core/webgl/bindgroup.js +89 -0
- package/src/core/webgl/descriptors.js +104 -0
- package/src/core/webgl/index.js +5 -0
- package/src/core/webgl/renderpassencoder.js +96 -0
- package/src/core/webgl/renderpipeline.js +54 -0
- package/src/core/webgl/utils.js +371 -0
- package/src/core/webgl/webglrenderdevice.js +235 -0
- package/src/function.js +358 -0
- package/src/index.js +15 -0
- package/src/loader/gltf.js +2172 -0
- package/src/loader/index.js +3 -0
- package/src/loader/loader.js +174 -0
- package/src/loader/obj.js +188 -0
- package/src/loader/texture.js +85 -0
- package/src/loader/utils.js +16 -0
- package/src/material/basic.js +75 -0
- package/src/material/depth.js +73 -0
- package/src/material/index.js +8 -0
- package/src/material/lambert.js +73 -0
- package/src/material/material.js +106 -0
- package/src/material/normal.js +30 -0
- package/src/material/phong.js +86 -0
- package/src/material/raw.js +52 -0
- package/src/material/standard.js +221 -0
- package/src/math/index.js +3 -0
- package/src/math/transform.js +38 -0
- package/src/mesh/attribute/attribute.js +79 -0
- package/src/mesh/attribute/index.js +1 -0
- package/src/mesh/attributedata/index.js +1 -0
- package/src/mesh/attributedata/separate.js +180 -0
- package/src/mesh/builders/base.js +41 -0
- package/src/mesh/builders/circle.js +63 -0
- package/src/mesh/builders/cuboid.js +135 -0
- package/src/mesh/builders/cylinder.js +131 -0
- package/src/mesh/builders/index.js +7 -0
- package/src/mesh/builders/plane.js +73 -0
- package/src/mesh/builders/utils.js +20 -0
- package/src/mesh/builders/uvsphere.js +80 -0
- package/src/mesh/builders/wireframe.js +62 -0
- package/src/mesh/index.js +4 -0
- package/src/mesh/mesh.js +149 -0
- package/src/objects/bone.js +17 -0
- package/src/objects/camera/camera.js +56 -0
- package/src/objects/camera/index.js +2 -0
- package/src/objects/camera/projection.js +203 -0
- package/src/objects/debug/index.js +1 -0
- package/src/objects/debug/skeleton.js +28 -0
- package/src/objects/index.js +7 -0
- package/src/objects/light/ambient.js +20 -0
- package/src/objects/light/directional.js +29 -0
- package/src/objects/light/index.js +5 -0
- package/src/objects/light/point.js +32 -0
- package/src/objects/light/shadow/index.js +1 -0
- package/src/objects/light/shadow/shadow.js +67 -0
- package/src/objects/light/spot.js +56 -0
- package/src/objects/mesh.js +141 -0
- package/src/objects/object3d.js +167 -0
- package/src/objects/skybox.js +38 -0
- package/src/plugins/camera/camera.js +19 -0
- package/src/plugins/camera/index.js +2 -0
- package/src/plugins/camera/nodes/cameraview.js +46 -0
- package/src/plugins/camera/nodes/index.js +2 -0
- package/src/plugins/camera/nodes/opaquepass.js +79 -0
- package/src/plugins/index.js +6 -0
- package/src/plugins/light/index.js +2 -0
- package/src/plugins/light/light.js +23 -0
- package/src/plugins/light/nodes/index.js +1 -0
- package/src/plugins/light/nodes/light.js +127 -0
- package/src/plugins/meshmaterial/index.js +3 -0
- package/src/plugins/meshmaterial/meshmaterial.js +381 -0
- package/src/plugins/meshmaterial/nodes/index.js +1 -0
- package/src/plugins/meshmaterial/nodes/meshmaterial.js +50 -0
- package/src/plugins/meshmaterial/resources/index.js +1 -0
- package/src/plugins/meshmaterial/resources/meshmaterialpipelines.js +50 -0
- package/src/plugins/shadow/index.js +3 -0
- package/src/plugins/shadow/nodes/index.js +3 -0
- package/src/plugins/shadow/nodes/shadow.js +272 -0
- package/src/plugins/shadow/nodes/shadowOccluder.js +112 -0
- package/src/plugins/shadow/nodes/shadowOpaquePass.js +73 -0
- package/src/plugins/shadow/resources/ShadowMap.js +99 -0
- package/src/plugins/shadow/resources/index.js +2 -0
- package/src/plugins/shadow/resources/shadowpipelines.js +25 -0
- package/src/plugins/shadow/shadow.js +31 -0
- package/src/plugins/skeletonhelper/index.js +1 -0
- package/src/plugins/skeletonhelper/skeletonhelper.js +160 -0
- package/src/plugins/skybox/index.js +3 -0
- package/src/plugins/skybox/nodes/index.js +1 -0
- package/src/plugins/skybox/nodes/skybox.js +143 -0
- package/src/plugins/skybox/resources/index.js +2 -0
- package/src/plugins/skybox/resources/skyboxmesh.js +14 -0
- package/src/plugins/skybox/resources/skyboxpipeline.js +6 -0
- package/src/plugins/skybox/skybox.js +137 -0
- package/src/renderer/core/index.js +179 -0
- package/src/renderer/graph/index.js +3 -0
- package/src/renderer/graph/nodes.js +34 -0
- package/src/renderer/graph/rendergraph.js +182 -0
- package/src/renderer/index.js +5 -0
- package/src/renderer/plugin.js +36 -0
- package/src/renderer/renderer.js +179 -0
- package/src/renderer/views.js +28 -0
- package/src/rendertarget/canvastarget.js +30 -0
- package/src/rendertarget/image.js +132 -0
- package/src/rendertarget/index.js +3 -0
- package/src/rendertarget/rendertarget.js +89 -0
- package/src/shader/basicFragment.glsl +30 -0
- package/src/shader/basicVertex.glsl +87 -0
- package/src/shader/common/color.glsl +7 -0
- package/src/shader/common/common.glsl +25 -0
- package/src/shader/common/index.js +4 -0
- package/src/shader/common/light.glsl +437 -0
- package/src/shader/common/math.glsl +12 -0
- package/src/shader/debug/index.js +2 -0
- package/src/shader/debug/skeletonFragment.glsl +8 -0
- package/src/shader/debug/skeletonVertex.glsl +27 -0
- package/src/shader/depthFragment.glsl +37 -0
- package/src/shader/index.js +11 -0
- package/src/shader/lambertFragment.glsl +126 -0
- package/src/shader/normalFragment.glsl +25 -0
- package/src/shader/phongFragment.glsl +140 -0
- package/src/shader/skyboxFragment.glsl +16 -0
- package/src/shader/skyboxVertex.glsl +20 -0
- package/src/shader/standardFragment.glsl +274 -0
- package/src/texture/index.js +2 -0
- package/src/texture/sampler.js +111 -0
- package/src/texture/texture.js +234 -0
- package/src/utils/index.js +115 -0
- package/tsconfig.json +11 -0
- package/website/config/index.ts +1 -0
- package/website/config/navigation.ts +53 -0
- package/website/content.config.ts +92 -0
- package/website/layouts/DocLayout.astro +501 -0
- package/website/layouts/Example.astro +91 -0
- package/website/pages/examples/[...slug].astro +77 -0
- package/website/pages/examples/index.astro +98 -0
- package/website/pages/examples/samples/[...slug].astro +17 -0
- package/website/pages/guide/[slug].astro +30 -0
- package/website/pages/guide/index.astro +21 -0
- package/website/pages/index.astro +9 -0
- package/website/plugins/remark-link-base.js +23 -0
- package/website/utils/url.ts +30 -0
|
@@ -0,0 +1,2172 @@
|
|
|
1
|
+
/**@import { LoadSettings } from './loader.js' */
|
|
2
|
+
import { Attribute, Mesh } from '../mesh/index.js';
|
|
3
|
+
import { StandardMaterial } from '../material/index.js';
|
|
4
|
+
import { MeshMaterial3D, Object3D, Skin } from '../objects/index.js';
|
|
5
|
+
import { Loader } from './loader.js';
|
|
6
|
+
import { arrayBufferToJSON } from './utils.js';
|
|
7
|
+
import { Bone3D } from '../objects/bone.js';
|
|
8
|
+
import { Affine3 } from '../math/index.js';
|
|
9
|
+
import { SeparateAttributeData } from '../mesh/attributedata/separate.js';
|
|
10
|
+
import { TextureLoader } from './texture.js';
|
|
11
|
+
import { Texture, Sampler } from '../texture/index.js';
|
|
12
|
+
import { assert } from '../utils/index.js';
|
|
13
|
+
import { GlDataType, TextureFilter, TextureWrap, VertexFormat } from '../constants/index.js';
|
|
14
|
+
|
|
15
|
+
const defaultMaterial = new StandardMaterial()
|
|
16
|
+
/**
|
|
17
|
+
* @extends {Loader<Object3D, GLTFLoadSettings>}
|
|
18
|
+
*/
|
|
19
|
+
export class GLTFLoader extends Loader {
|
|
20
|
+
textureLoader
|
|
21
|
+
constructor({ textureLoader = new TextureLoader() } = {}) {
|
|
22
|
+
super(Object3D)
|
|
23
|
+
this.textureLoader = textureLoader
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @override
|
|
27
|
+
* @param {ArrayBuffer[]} buffers
|
|
28
|
+
* @param {Object3D} destination
|
|
29
|
+
* @param {GLTFLoadSettings} settings
|
|
30
|
+
*/
|
|
31
|
+
async parse(buffers, destination, settings) {
|
|
32
|
+
const buffer = buffers[0]
|
|
33
|
+
const path = settings.paths[0]
|
|
34
|
+
if (!buffer || !path) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**@type {Map<number, Object3D>} */
|
|
39
|
+
const entityMap = new Map()
|
|
40
|
+
const baseUrl = new URL(path, location.href).href
|
|
41
|
+
const gltf = await loadGLTF(buffer, baseUrl)
|
|
42
|
+
const scene = gltf.scenes[gltf.scene]
|
|
43
|
+
|
|
44
|
+
if (!scene) {
|
|
45
|
+
throw "No root scene defined"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const images = await Promise.all(gltf.images.map(async (gltfTexture) => {
|
|
49
|
+
if (gltfTexture.uri) {
|
|
50
|
+
return this.textureLoader.asyncLoad({
|
|
51
|
+
paths: [new URL(gltfTexture.uri, baseUrl).href]
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
throw "Unsupported gltf image setting"
|
|
56
|
+
}))
|
|
57
|
+
const samplers = gltf.samplers.map((gltfSampler) => {
|
|
58
|
+
const sampler = new Sampler()
|
|
59
|
+
sampler.magnificationFilter = gltfSampler.magFilter || TextureFilter.Nearest
|
|
60
|
+
sampler.wrapS = gltfSampler.wrapS || TextureWrap.Clamp
|
|
61
|
+
sampler.wrapT = gltfSampler.wrapT || TextureWrap.Clamp
|
|
62
|
+
|
|
63
|
+
switch (gltfSampler.minFilter) {
|
|
64
|
+
case WebGL2RenderingContext.NEAREST:
|
|
65
|
+
sampler.minificationFilter = TextureFilter.Nearest
|
|
66
|
+
sampler.mipmapFilter = undefined
|
|
67
|
+
break;
|
|
68
|
+
case WebGL2RenderingContext.LINEAR:
|
|
69
|
+
sampler.minificationFilter = TextureFilter.Linear
|
|
70
|
+
sampler.mipmapFilter = undefined
|
|
71
|
+
break;
|
|
72
|
+
case WebGL2RenderingContext.NEAREST_MIPMAP_NEAREST:
|
|
73
|
+
sampler.minificationFilter = TextureFilter.Nearest
|
|
74
|
+
sampler.mipmapFilter = TextureFilter.Nearest
|
|
75
|
+
break;
|
|
76
|
+
case WebGL2RenderingContext.NEAREST_MIPMAP_LINEAR:
|
|
77
|
+
sampler.minificationFilter = TextureFilter.Nearest
|
|
78
|
+
sampler.mipmapFilter = TextureFilter.Linear
|
|
79
|
+
break;
|
|
80
|
+
case WebGL2RenderingContext.LINEAR_MIPMAP_NEAREST:
|
|
81
|
+
sampler.minificationFilter = TextureFilter.Linear
|
|
82
|
+
sampler.mipmapFilter = TextureFilter.Nearest
|
|
83
|
+
break;
|
|
84
|
+
case WebGL2RenderingContext.LINEAR_MIPMAP_LINEAR:
|
|
85
|
+
sampler.minificationFilter = TextureFilter.Linear
|
|
86
|
+
sampler.mipmapFilter = TextureFilter.Linear
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
throw 'GLTF: Invalid minification sampler';
|
|
90
|
+
}
|
|
91
|
+
return sampler
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @type {[Texture, Sampler | undefined][]}
|
|
96
|
+
*/
|
|
97
|
+
const textures = gltf.textures.map((gltfTextures) => {
|
|
98
|
+
const image = images[gltfTextures.source]
|
|
99
|
+
assert(image, "GLTF texture does not have an image source")
|
|
100
|
+
|
|
101
|
+
if (gltfTextures.sampler !== undefined) {
|
|
102
|
+
const sampler = samplers[gltfTextures.sampler]
|
|
103
|
+
if (sampler?.mipmapFilter !== undefined) {
|
|
104
|
+
image.generateMipmaps = true
|
|
105
|
+
}
|
|
106
|
+
return [image, sampler]
|
|
107
|
+
}
|
|
108
|
+
return [image, undefined]
|
|
109
|
+
})
|
|
110
|
+
const materials = gltf.materials.map((gltfMaterial) => {
|
|
111
|
+
const {
|
|
112
|
+
emissiveFactor,
|
|
113
|
+
emissiveTexture,
|
|
114
|
+
normalTexture,
|
|
115
|
+
occlusionTexture,
|
|
116
|
+
pbrMetallicRoughness
|
|
117
|
+
} = gltfMaterial
|
|
118
|
+
const {
|
|
119
|
+
baseColorFactor,
|
|
120
|
+
baseColorTexture,
|
|
121
|
+
metallicFactor,
|
|
122
|
+
roughnessFactor,
|
|
123
|
+
metallicRoughnessTexture
|
|
124
|
+
} = pbrMetallicRoughness
|
|
125
|
+
const material = new StandardMaterial()
|
|
126
|
+
|
|
127
|
+
material.color.set(
|
|
128
|
+
baseColorFactor[0],
|
|
129
|
+
baseColorFactor[1],
|
|
130
|
+
baseColorFactor[2],
|
|
131
|
+
baseColorFactor[3]
|
|
132
|
+
)
|
|
133
|
+
material.emissiveColor.set(
|
|
134
|
+
emissiveFactor[0],
|
|
135
|
+
emissiveFactor[1],
|
|
136
|
+
emissiveFactor[2]
|
|
137
|
+
)
|
|
138
|
+
material.metallic = metallicFactor
|
|
139
|
+
material.roughness = roughnessFactor
|
|
140
|
+
|
|
141
|
+
if (baseColorTexture) {
|
|
142
|
+
const texture = textures[baseColorTexture.index]
|
|
143
|
+
|
|
144
|
+
if (texture) {
|
|
145
|
+
material.mainTexture = texture[0]
|
|
146
|
+
material.mainSampler = texture[1]
|
|
147
|
+
} else {
|
|
148
|
+
console.warn("gltf: invalid color texture on material");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (normalTexture) {
|
|
153
|
+
const texture = textures[normalTexture.index]
|
|
154
|
+
|
|
155
|
+
if (texture) {
|
|
156
|
+
material.normalTexture = texture[0]
|
|
157
|
+
material.normalSampler = texture[1]
|
|
158
|
+
} else {
|
|
159
|
+
console.warn("gltf: invalid normal texture on material");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (occlusionTexture) {
|
|
164
|
+
const texture = textures[occlusionTexture.index]
|
|
165
|
+
|
|
166
|
+
if (texture) {
|
|
167
|
+
material.occlusionStrength = occlusionTexture.strength
|
|
168
|
+
material.occlusionTexture = texture[0]
|
|
169
|
+
material.occlusionSampler = texture[1]
|
|
170
|
+
} else {
|
|
171
|
+
console.warn("gltf: invalid occlusion texture on material");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (emissiveTexture) {
|
|
176
|
+
const texture = textures[emissiveTexture.index]
|
|
177
|
+
|
|
178
|
+
if (texture) {
|
|
179
|
+
material.emissiveTexture = texture[0]
|
|
180
|
+
material.emissiveSampler = texture[1]
|
|
181
|
+
} else {
|
|
182
|
+
console.warn("gltf: invalid emissive texture on material");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (metallicRoughnessTexture) {
|
|
187
|
+
const texture = textures[metallicRoughnessTexture.index]
|
|
188
|
+
|
|
189
|
+
if (texture) {
|
|
190
|
+
material.metallicTexture = texture[0]
|
|
191
|
+
material.metallicSampler = texture[1]
|
|
192
|
+
material.roughnessTexture = texture[0]
|
|
193
|
+
material.roughnessSampler = texture[1]
|
|
194
|
+
} else {
|
|
195
|
+
console.warn("gltf: invalid metallic-rougness texture on material");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return material
|
|
200
|
+
})
|
|
201
|
+
const geometries = gltf.meshes.map((data) => {
|
|
202
|
+
return parseGeometry(data, gltf)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
gltf.nodes.forEach((node, index) => {
|
|
206
|
+
const object = parseObject(index, node, gltf, geometries, materials)
|
|
207
|
+
|
|
208
|
+
if (object) {
|
|
209
|
+
entityMap.set(index, object)
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
gltf.nodes.forEach((node, index) => {
|
|
214
|
+
const parent = entityMap.get(index)
|
|
215
|
+
|
|
216
|
+
if (!parent) {
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for (const child of node.children) {
|
|
221
|
+
const childEntity = entityMap.get(child)
|
|
222
|
+
|
|
223
|
+
if (childEntity) {
|
|
224
|
+
parent.add(childEntity)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
scene.nodes.forEach((node) => {
|
|
230
|
+
entityMap.get(node)?.update()
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const skins = gltf.skins.map((skin) => {
|
|
234
|
+
return parseSkin(skin, gltf, entityMap)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
entityMap.forEach((entity, index) => {
|
|
238
|
+
const node = /**@type {GLTFNode} */ (gltf.nodes[index])
|
|
239
|
+
if (node.skin !== undefined) {
|
|
240
|
+
entity.traverseBFS((mesh) => {
|
|
241
|
+
if (mesh instanceof MeshMaterial3D) {
|
|
242
|
+
mesh.skin = skins[/**@type {number} */ (node.skin)]
|
|
243
|
+
}
|
|
244
|
+
return true
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const sceneEntities = scene.nodes.map((node) => {
|
|
250
|
+
return /**@type {Object3D} */ (entityMap.get(node))
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
sceneEntities.forEach((object) => {
|
|
254
|
+
object.traverseDFS((innerObject) => {
|
|
255
|
+
innerObject.update()
|
|
256
|
+
return true
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
destination.add(...sceneEntities)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @override
|
|
266
|
+
*/
|
|
267
|
+
default() {
|
|
268
|
+
return new Object3D()
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @param {ArrayBuffer} data
|
|
274
|
+
* @param {string} baseUrl
|
|
275
|
+
*/
|
|
276
|
+
async function loadGLTF(data, baseUrl) {
|
|
277
|
+
const json = arrayBufferToJSON(data)
|
|
278
|
+
const { buffers: urlBuffers } = json
|
|
279
|
+
const buffers = urlBuffers instanceof Array ? await loadBuffers(baseUrl, urlBuffers) : []
|
|
280
|
+
const gltf = GLTF.deserialize(json)
|
|
281
|
+
gltf.buffers = buffers
|
|
282
|
+
|
|
283
|
+
return gltf
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @param {string} base
|
|
288
|
+
* @param {{uri: string;}[]} uris
|
|
289
|
+
*/
|
|
290
|
+
async function loadBuffers(base, uris) {
|
|
291
|
+
|
|
292
|
+
return Promise.all(
|
|
293
|
+
uris.map(async (buffer) => {
|
|
294
|
+
const url = buffer.uri.startsWith('data') ?
|
|
295
|
+
buffer.uri :
|
|
296
|
+
new URL(buffer.uri, base).href
|
|
297
|
+
const response = await fetch(url);
|
|
298
|
+
if (!response.ok) throw new Error(`Failed to fetch buffer`);
|
|
299
|
+
return await response.arrayBuffer();
|
|
300
|
+
})
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* @typedef {LoadSettings} GLTFLoadSettings
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
class GLTF {
|
|
308
|
+
/**
|
|
309
|
+
* @type {number}
|
|
310
|
+
*/
|
|
311
|
+
scene = 0
|
|
312
|
+
/**
|
|
313
|
+
* @type {GLTFScene[]}
|
|
314
|
+
*/
|
|
315
|
+
scenes = []
|
|
316
|
+
/**
|
|
317
|
+
* @type {GLTFNode[]}
|
|
318
|
+
*/
|
|
319
|
+
nodes = []
|
|
320
|
+
/**
|
|
321
|
+
* @type {GLTFImage[]}
|
|
322
|
+
*/
|
|
323
|
+
images = []
|
|
324
|
+
/**
|
|
325
|
+
* @type {GLFTSampler[]}
|
|
326
|
+
*/
|
|
327
|
+
samplers = []
|
|
328
|
+
/**
|
|
329
|
+
* @type {GLFTTexture[]}
|
|
330
|
+
*/
|
|
331
|
+
textures = []
|
|
332
|
+
/**
|
|
333
|
+
* @type {GLTFMesh[]}
|
|
334
|
+
*/
|
|
335
|
+
meshes = []
|
|
336
|
+
/**
|
|
337
|
+
* @type {GLTFMaterial[]}
|
|
338
|
+
*/
|
|
339
|
+
materials = []
|
|
340
|
+
/**
|
|
341
|
+
* @type {GLTFSkin[]}
|
|
342
|
+
*/
|
|
343
|
+
skins = []
|
|
344
|
+
/**
|
|
345
|
+
* @type {ArrayBuffer[]}
|
|
346
|
+
*/
|
|
347
|
+
buffers = []
|
|
348
|
+
/**
|
|
349
|
+
* @type {GLTFBufferView[]}
|
|
350
|
+
*/
|
|
351
|
+
bufferViews = []
|
|
352
|
+
/**
|
|
353
|
+
* @type {GLTFAccessor[]}
|
|
354
|
+
*/
|
|
355
|
+
accessors = []
|
|
356
|
+
/**
|
|
357
|
+
* @type {GLTFMetaData}
|
|
358
|
+
*/
|
|
359
|
+
metaData
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {GLTFMetaData} meta
|
|
363
|
+
*/
|
|
364
|
+
constructor(meta) {
|
|
365
|
+
this.metaData = meta
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* @param {any} data
|
|
369
|
+
*/
|
|
370
|
+
static deserialize(data) {
|
|
371
|
+
const {
|
|
372
|
+
scene,
|
|
373
|
+
scenes,
|
|
374
|
+
nodes,
|
|
375
|
+
meshes,
|
|
376
|
+
images,
|
|
377
|
+
textures,
|
|
378
|
+
samplers,
|
|
379
|
+
materials,
|
|
380
|
+
bufferViews,
|
|
381
|
+
accessors,
|
|
382
|
+
asset,
|
|
383
|
+
skins
|
|
384
|
+
} = data
|
|
385
|
+
|
|
386
|
+
if (
|
|
387
|
+
!(asset instanceof Object) ||
|
|
388
|
+
!(scenes instanceof Array) ||
|
|
389
|
+
!(nodes instanceof Array) ||
|
|
390
|
+
!(meshes instanceof Array) ||
|
|
391
|
+
!(bufferViews instanceof Array) ||
|
|
392
|
+
!(accessors instanceof Array)
|
|
393
|
+
) {
|
|
394
|
+
throw new Error("Invalid gltf json")
|
|
395
|
+
}
|
|
396
|
+
const gltf = new GLTF(GLTFMetaData.deserialize(asset))
|
|
397
|
+
|
|
398
|
+
if (typeof scene === "number") {
|
|
399
|
+
gltf.scene = scene
|
|
400
|
+
} else {
|
|
401
|
+
gltf.scene = 0
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
gltf.scenes = scenes.map((/**@type {any}*/d) => GLTFScene.deserialize(d))
|
|
405
|
+
gltf.nodes = nodes.map((/**@type {any}*/d) => GLTFNode.deserialize(d))
|
|
406
|
+
gltf.meshes = meshes.map((/**@type {any}*/d) => GLTFMesh.deserialize(d))
|
|
407
|
+
gltf.bufferViews = bufferViews.map((/**@type {any}*/d) => GLTFBufferView.deserialize(d))
|
|
408
|
+
gltf.accessors = accessors.map((/**@type {any}*/d) => GLTFAccessor.deserialize(d))
|
|
409
|
+
|
|
410
|
+
if (materials instanceof Array) {
|
|
411
|
+
gltf.materials = materials.map((/**@type {any}*/d) => GLTFMaterial.deserialize(d))
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (images instanceof Array) {
|
|
415
|
+
gltf.images = images.map((/**@type {any}*/d) => GLTFImage.deserialize(d))
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (samplers instanceof Array) {
|
|
419
|
+
gltf.samplers = samplers.map((/**@type {any}*/d) => GLFTSampler.deserialize(d))
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (textures instanceof Array) {
|
|
423
|
+
gltf.textures = textures.map((/**@type {any}*/d) => GLFTTexture.deserialize(d))
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (skins instanceof Array) {
|
|
427
|
+
gltf.skins = skins.map(a => GLTFSkin.deserialize(a))
|
|
428
|
+
} else {
|
|
429
|
+
gltf.skins = []
|
|
430
|
+
}
|
|
431
|
+
return gltf
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
class GLTFScene {
|
|
436
|
+
/**
|
|
437
|
+
* @type {string}
|
|
438
|
+
*/
|
|
439
|
+
name = ''
|
|
440
|
+
/**
|
|
441
|
+
* @type {Record<string,any>}
|
|
442
|
+
*/
|
|
443
|
+
extensions = {}
|
|
444
|
+
/**
|
|
445
|
+
* @type {Record<string,any>}
|
|
446
|
+
*/
|
|
447
|
+
extras = []
|
|
448
|
+
/**
|
|
449
|
+
* @type {number[]}
|
|
450
|
+
*/
|
|
451
|
+
nodes = []
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @param {any} data
|
|
455
|
+
*/
|
|
456
|
+
static deserialize(data) {
|
|
457
|
+
const { nodes, name, extensions, extras } = data
|
|
458
|
+
const scene = new GLTFScene()
|
|
459
|
+
|
|
460
|
+
if (nodes instanceof Array) {
|
|
461
|
+
scene.nodes = nodes
|
|
462
|
+
.filter((node) => typeof node == "number")
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (typeof name === "string") {
|
|
466
|
+
scene.name = name
|
|
467
|
+
} else {
|
|
468
|
+
scene.name = ''
|
|
469
|
+
}
|
|
470
|
+
if (extensions instanceof Object) {
|
|
471
|
+
scene.extensions = extensions
|
|
472
|
+
} else {
|
|
473
|
+
scene.extensions = {}
|
|
474
|
+
}
|
|
475
|
+
if (extras instanceof Object) {
|
|
476
|
+
scene.extras = extras
|
|
477
|
+
} else {
|
|
478
|
+
scene.extras = {}
|
|
479
|
+
}
|
|
480
|
+
return scene
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
class GLTFNode {
|
|
485
|
+
/**
|
|
486
|
+
* @type {string}
|
|
487
|
+
*/
|
|
488
|
+
name = ''
|
|
489
|
+
/**
|
|
490
|
+
* @type {Record<string,any>}
|
|
491
|
+
*/
|
|
492
|
+
extensions = {}
|
|
493
|
+
/**
|
|
494
|
+
* @type {Record<string,any>}
|
|
495
|
+
*/
|
|
496
|
+
extras = []
|
|
497
|
+
/**
|
|
498
|
+
* @type {number | undefined}
|
|
499
|
+
*/
|
|
500
|
+
mesh
|
|
501
|
+
/**
|
|
502
|
+
* @type {number | undefined}
|
|
503
|
+
*/
|
|
504
|
+
skin
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @type {number | undefined}
|
|
508
|
+
*/
|
|
509
|
+
camera
|
|
510
|
+
/**
|
|
511
|
+
* @type {number[] | undefined}
|
|
512
|
+
*/
|
|
513
|
+
weights
|
|
514
|
+
/**
|
|
515
|
+
* @type {number[]}
|
|
516
|
+
*/
|
|
517
|
+
children = []
|
|
518
|
+
/**
|
|
519
|
+
* @type {TRSTransform | MatrixTransform | undefined}
|
|
520
|
+
*/
|
|
521
|
+
transform
|
|
522
|
+
/**
|
|
523
|
+
* @param {any} data
|
|
524
|
+
*/
|
|
525
|
+
static deserialize(data) {
|
|
526
|
+
const {
|
|
527
|
+
mesh, matrix, translation,
|
|
528
|
+
rotation, scale, weights,
|
|
529
|
+
children, skin, camera,
|
|
530
|
+
name, extensions, extras
|
|
531
|
+
} = data
|
|
532
|
+
const node = new GLTFNode()
|
|
533
|
+
|
|
534
|
+
if (typeof mesh === "number") {
|
|
535
|
+
node.mesh = mesh
|
|
536
|
+
}
|
|
537
|
+
if (typeof skin === 'number') {
|
|
538
|
+
node.skin = skin
|
|
539
|
+
}
|
|
540
|
+
if (typeof camera === 'number') {
|
|
541
|
+
node.camera = camera
|
|
542
|
+
}
|
|
543
|
+
if (weights instanceof Array) {
|
|
544
|
+
node.weights = weights.filter(w => typeof w === "number")
|
|
545
|
+
}
|
|
546
|
+
if (matrix) {
|
|
547
|
+
node.transform = MatrixTransform.deserialize(matrix)
|
|
548
|
+
}
|
|
549
|
+
if (translation || rotation || scale) {
|
|
550
|
+
const ntranslation = translation || [0, 0, 0]
|
|
551
|
+
const nrotation = rotation || [0, 0, 0, 1]
|
|
552
|
+
const nscale = scale || [1, 1, 1]
|
|
553
|
+
node.transform = TRSTransform.deserialize(
|
|
554
|
+
ntranslation,
|
|
555
|
+
nrotation,
|
|
556
|
+
nscale
|
|
557
|
+
)
|
|
558
|
+
} else if (matrix) {
|
|
559
|
+
node.transform = MatrixTransform.deserialize(matrix)
|
|
560
|
+
} else {
|
|
561
|
+
node.transform = new TRSTransform()
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (children instanceof Array) {
|
|
565
|
+
node.children = children.filter(c => typeof c === "number")
|
|
566
|
+
} else {
|
|
567
|
+
node.children = []
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (typeof name === 'string') {
|
|
571
|
+
node.name = name
|
|
572
|
+
} else {
|
|
573
|
+
node.name = ""
|
|
574
|
+
}
|
|
575
|
+
if (extensions instanceof Object) {
|
|
576
|
+
node.extensions = extensions
|
|
577
|
+
} else {
|
|
578
|
+
node.extensions = {}
|
|
579
|
+
}
|
|
580
|
+
if (extras instanceof Object) {
|
|
581
|
+
node.extras = extras
|
|
582
|
+
} else {
|
|
583
|
+
node.extras = {}
|
|
584
|
+
}
|
|
585
|
+
return node
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
class GLTFMesh {
|
|
590
|
+
/**
|
|
591
|
+
* @type {string}
|
|
592
|
+
*/
|
|
593
|
+
name = ''
|
|
594
|
+
/**
|
|
595
|
+
* @type {Record<string,any>}
|
|
596
|
+
*/
|
|
597
|
+
extensions = {}
|
|
598
|
+
/**
|
|
599
|
+
* @type {Record<string,any>}
|
|
600
|
+
*/
|
|
601
|
+
extras = {}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* @type {GLTFPrimitive[]}
|
|
605
|
+
*/
|
|
606
|
+
primitives = []
|
|
607
|
+
/**
|
|
608
|
+
* @type {number[]}
|
|
609
|
+
*/
|
|
610
|
+
weights = []
|
|
611
|
+
/**
|
|
612
|
+
* @param {any} data
|
|
613
|
+
*/
|
|
614
|
+
static deserialize(data) {
|
|
615
|
+
const { primitives, weights, name, extensions, extras } = data
|
|
616
|
+
const mesh = new GLTFMesh()
|
|
617
|
+
|
|
618
|
+
if (primitives instanceof Array) {
|
|
619
|
+
mesh.primitives = primitives.map((p) => GLTFPrimitive.deserialize(p))
|
|
620
|
+
} else {
|
|
621
|
+
mesh.primitives = []
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (weights instanceof Array) {
|
|
625
|
+
mesh.weights = weights.filter(weight => typeof weight === "number")
|
|
626
|
+
} else {
|
|
627
|
+
mesh.weights = []
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (typeof name === "string") {
|
|
631
|
+
mesh.name = name
|
|
632
|
+
} else {
|
|
633
|
+
mesh.name = ''
|
|
634
|
+
}
|
|
635
|
+
if (extensions instanceof Object) {
|
|
636
|
+
mesh.extensions = extensions
|
|
637
|
+
} else {
|
|
638
|
+
mesh.extensions = {}
|
|
639
|
+
}
|
|
640
|
+
if (extras instanceof Object) {
|
|
641
|
+
mesh.extras = extras
|
|
642
|
+
} else {
|
|
643
|
+
mesh.extras = {}
|
|
644
|
+
}
|
|
645
|
+
return mesh
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
class GLTFMaterial {
|
|
650
|
+
/**
|
|
651
|
+
* @type {string}
|
|
652
|
+
*/
|
|
653
|
+
name = ''
|
|
654
|
+
/**
|
|
655
|
+
* @type {Record<string,any>}
|
|
656
|
+
*/
|
|
657
|
+
extensions = {}
|
|
658
|
+
/**
|
|
659
|
+
* @type {Record<string,any>}
|
|
660
|
+
*/
|
|
661
|
+
extras = {}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* @type {GLTFPBRetallicRoughness}
|
|
665
|
+
*/
|
|
666
|
+
pbrMetallicRoughness
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* @type {GLFTTextureInfo | undefined}
|
|
670
|
+
*/
|
|
671
|
+
normalTexture
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* @type {GLFTTextureInfo | undefined}
|
|
675
|
+
*/
|
|
676
|
+
occlusionTexture
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* @type {GLFTTextureInfo | undefined}
|
|
680
|
+
*/
|
|
681
|
+
emissiveTexture
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* @type {[number, number, number]}
|
|
685
|
+
*/
|
|
686
|
+
emissiveFactor = [0, 0, 0]
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* @type {GLFTAlphaMode}
|
|
690
|
+
*/
|
|
691
|
+
alphaMode = GLFTAlphaMode.Opaque
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* @type {number}
|
|
695
|
+
*/
|
|
696
|
+
alphaCutoff = 0.5
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* @type {boolean}
|
|
700
|
+
*/
|
|
701
|
+
doubleSide = false
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* @param {GLTFPBRetallicRoughness} metallicRoughness
|
|
705
|
+
*/
|
|
706
|
+
constructor(metallicRoughness) {
|
|
707
|
+
this.pbrMetallicRoughness = metallicRoughness
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* @param {any} data
|
|
711
|
+
*/
|
|
712
|
+
static deserialize(data) {
|
|
713
|
+
const {
|
|
714
|
+
pbrMetallicRoughness,
|
|
715
|
+
normalTexture,
|
|
716
|
+
occlusionTexture,
|
|
717
|
+
emissiveTexture,
|
|
718
|
+
emissiveFactor,
|
|
719
|
+
alphaMode,
|
|
720
|
+
alphaCutoff,
|
|
721
|
+
doubleSide,
|
|
722
|
+
name,
|
|
723
|
+
extensions,
|
|
724
|
+
extras
|
|
725
|
+
} = data
|
|
726
|
+
const pbr = pbrMetallicRoughness ? GLTFPBRetallicRoughness.deserialize(pbrMetallicRoughness) : new GLTFPBRetallicRoughness()
|
|
727
|
+
const result = new GLTFMaterial(pbr)
|
|
728
|
+
|
|
729
|
+
if (normalTexture instanceof Object) {
|
|
730
|
+
result.normalTexture = GLFTTextureInfo.deserialize(normalTexture)
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (occlusionTexture instanceof Object) {
|
|
734
|
+
result.occlusionTexture = GLFTTextureInfo.deserialize(occlusionTexture)
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (emissiveTexture instanceof Object) {
|
|
738
|
+
result.emissiveTexture = GLFTTextureInfo.deserialize(emissiveTexture)
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (emissiveFactor instanceof Array && emissiveFactor.length === 3) {
|
|
742
|
+
result.emissiveFactor = /**@type {[number, number, number]}*/(emissiveFactor)
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (typeof alphaCutoff === 'number') {
|
|
746
|
+
result.alphaCutoff = alphaCutoff
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (typeof alphaMode === 'string') {
|
|
750
|
+
result.alphaMode = alphaMode
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (typeof doubleSide === 'boolean') {
|
|
754
|
+
result.doubleSide = doubleSide
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (typeof name === "string") {
|
|
758
|
+
result.name = name
|
|
759
|
+
} else {
|
|
760
|
+
result.name = ''
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (extensions instanceof Object) {
|
|
764
|
+
result.extensions = extensions
|
|
765
|
+
} else {
|
|
766
|
+
result.extensions = {}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (extras instanceof Object) {
|
|
770
|
+
result.extras = extras
|
|
771
|
+
} else {
|
|
772
|
+
result.extras = {}
|
|
773
|
+
}
|
|
774
|
+
return result
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
class GLTFImage {
|
|
779
|
+
/**
|
|
780
|
+
* @type {string | undefined}
|
|
781
|
+
*/
|
|
782
|
+
uri
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* @type {number | undefined}
|
|
786
|
+
*/
|
|
787
|
+
bufferView
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* @type {string | undefined}
|
|
791
|
+
*/
|
|
792
|
+
mimeType
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* @type {string}
|
|
797
|
+
*/
|
|
798
|
+
name = ''
|
|
799
|
+
/**
|
|
800
|
+
* @type {Record<string,any>}
|
|
801
|
+
*/
|
|
802
|
+
extensions = {}
|
|
803
|
+
/**
|
|
804
|
+
* @type {Record<string,any>}
|
|
805
|
+
*/
|
|
806
|
+
extras = []
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* @param {any} data
|
|
810
|
+
*/
|
|
811
|
+
static deserialize(data) {
|
|
812
|
+
const { uri, bufferView, mimeType, name, extensions, extras } = data
|
|
813
|
+
const result = new GLTFImage()
|
|
814
|
+
|
|
815
|
+
if (typeof uri === 'string') {
|
|
816
|
+
result.uri = uri
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (typeof bufferView === 'number') {
|
|
820
|
+
result.bufferView = bufferView
|
|
821
|
+
}
|
|
822
|
+
if (typeof mimeType === 'string') {
|
|
823
|
+
result.mimeType = mimeType
|
|
824
|
+
}
|
|
825
|
+
if (typeof name === 'string') {
|
|
826
|
+
result.name = name
|
|
827
|
+
}
|
|
828
|
+
if (extensions instanceof Object) {
|
|
829
|
+
result.extensions = extensions
|
|
830
|
+
} else {
|
|
831
|
+
result.extensions = {}
|
|
832
|
+
}
|
|
833
|
+
if (extras instanceof Object) {
|
|
834
|
+
result.extras = extras
|
|
835
|
+
} else {
|
|
836
|
+
result.extras = {}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return result
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
class GLFTSampler {
|
|
844
|
+
/**
|
|
845
|
+
* @type {number | undefined}
|
|
846
|
+
*/
|
|
847
|
+
magFilter
|
|
848
|
+
/**
|
|
849
|
+
* @type {number | undefined}
|
|
850
|
+
*/
|
|
851
|
+
minFilter
|
|
852
|
+
/**
|
|
853
|
+
* @type {number | undefined}
|
|
854
|
+
*/
|
|
855
|
+
wrapS
|
|
856
|
+
/**
|
|
857
|
+
* @type {number | undefined}
|
|
858
|
+
*/
|
|
859
|
+
wrapT
|
|
860
|
+
/**
|
|
861
|
+
* @type {string}
|
|
862
|
+
*/
|
|
863
|
+
name = ''
|
|
864
|
+
/**
|
|
865
|
+
* @type {Record<string,any>}
|
|
866
|
+
*/
|
|
867
|
+
extensions = {}
|
|
868
|
+
/**
|
|
869
|
+
* @type {Record<string,any>}
|
|
870
|
+
*/
|
|
871
|
+
extras = []
|
|
872
|
+
/**
|
|
873
|
+
* @param {any} data
|
|
874
|
+
*/
|
|
875
|
+
static deserialize(data) {
|
|
876
|
+
const { magFilter, minFilter, wrapS, wrapT, name, extensions, extras } = data
|
|
877
|
+
const result = new GLFTSampler()
|
|
878
|
+
|
|
879
|
+
if (typeof magFilter === 'number') {
|
|
880
|
+
result.magFilter = magFilter
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (typeof minFilter === 'number') {
|
|
884
|
+
result.minFilter = minFilter
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (typeof wrapS === 'number') {
|
|
888
|
+
result.wrapS = wrapS
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (typeof wrapT === 'number') {
|
|
892
|
+
result.wrapT = wrapT
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (typeof name === 'string') {
|
|
896
|
+
result.name = name
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (extensions instanceof Object) {
|
|
900
|
+
result.extensions = extensions
|
|
901
|
+
} else {
|
|
902
|
+
result.extensions = {}
|
|
903
|
+
}
|
|
904
|
+
if (extras instanceof Object) {
|
|
905
|
+
result.extras = extras
|
|
906
|
+
} else {
|
|
907
|
+
result.extras = {}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return result
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
class GLFTTexture {
|
|
915
|
+
/**
|
|
916
|
+
* @type {number | undefined}
|
|
917
|
+
*/
|
|
918
|
+
sampler
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* @type {number}
|
|
922
|
+
*/
|
|
923
|
+
source
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* @type {string}
|
|
927
|
+
*/
|
|
928
|
+
name = ''
|
|
929
|
+
/**
|
|
930
|
+
* @type {Record<string,any>}
|
|
931
|
+
*/
|
|
932
|
+
extensions = {}
|
|
933
|
+
/**
|
|
934
|
+
* @type {Record<string,any>}
|
|
935
|
+
*/
|
|
936
|
+
extras = []
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* @param {number} source
|
|
940
|
+
*/
|
|
941
|
+
constructor(source) {
|
|
942
|
+
this.source = source
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* @param {any} data
|
|
946
|
+
*/
|
|
947
|
+
static deserialize(data) {
|
|
948
|
+
const { source, sampler, name, extensions, extras } = data
|
|
949
|
+
|
|
950
|
+
if (typeof source !== 'number') {
|
|
951
|
+
throw 'No source for the texture.'
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const result = new GLFTTexture(source)
|
|
955
|
+
|
|
956
|
+
if (typeof sampler === 'number') {
|
|
957
|
+
result.sampler = sampler
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (typeof name === 'string') {
|
|
961
|
+
result.name = name
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (extensions instanceof Object) {
|
|
965
|
+
result.extensions = extensions
|
|
966
|
+
} else {
|
|
967
|
+
result.extensions = {}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
if (extras instanceof Object) {
|
|
971
|
+
result.extras = extras
|
|
972
|
+
} else {
|
|
973
|
+
result.extras = {}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return result
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
class GLTFSkin {
|
|
981
|
+
/**
|
|
982
|
+
* @type {number}
|
|
983
|
+
*/
|
|
984
|
+
inverseBindMatrices
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* @type {number | undefined}
|
|
988
|
+
*/
|
|
989
|
+
skeleton
|
|
990
|
+
/**
|
|
991
|
+
* @type {number[]}
|
|
992
|
+
*/
|
|
993
|
+
joints
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* @param {number[]} joints
|
|
997
|
+
* @param {number} inverseBindMatrices
|
|
998
|
+
*/
|
|
999
|
+
constructor(joints, inverseBindMatrices) {
|
|
1000
|
+
this.inverseBindMatrices = inverseBindMatrices
|
|
1001
|
+
this.joints = joints
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
|
|
1006
|
+
* @param {any} data
|
|
1007
|
+
*/
|
|
1008
|
+
static deserialize(data) {
|
|
1009
|
+
const { joints, inverseBindMatrices, skeleton } = data
|
|
1010
|
+
|
|
1011
|
+
if (
|
|
1012
|
+
!(joints instanceof Array) ||
|
|
1013
|
+
typeof inverseBindMatrices !== 'number'
|
|
1014
|
+
) {
|
|
1015
|
+
throw 'Invalid skin'
|
|
1016
|
+
}
|
|
1017
|
+
const object = new GLTFSkin(
|
|
1018
|
+
joints.filter(e => typeof e === 'number'),
|
|
1019
|
+
inverseBindMatrices
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
if (typeof skeleton === 'number') {
|
|
1023
|
+
object.skeleton = skeleton
|
|
1024
|
+
}
|
|
1025
|
+
return object
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
class GLTFBufferView {
|
|
1030
|
+
/**
|
|
1031
|
+
* @type {string}
|
|
1032
|
+
*/
|
|
1033
|
+
name = ''
|
|
1034
|
+
/**
|
|
1035
|
+
* @type {Record<string,any>}
|
|
1036
|
+
*/
|
|
1037
|
+
extensions = {}
|
|
1038
|
+
/**
|
|
1039
|
+
* @type {Record<string,any>}
|
|
1040
|
+
*/
|
|
1041
|
+
extras = []
|
|
1042
|
+
/**
|
|
1043
|
+
* @type {number}
|
|
1044
|
+
*/
|
|
1045
|
+
buffer
|
|
1046
|
+
/**
|
|
1047
|
+
* @type {number}
|
|
1048
|
+
*/
|
|
1049
|
+
offset = 0
|
|
1050
|
+
/**
|
|
1051
|
+
* @type {number}
|
|
1052
|
+
*/
|
|
1053
|
+
length
|
|
1054
|
+
/**
|
|
1055
|
+
* @type {number}
|
|
1056
|
+
*/
|
|
1057
|
+
stride = 0
|
|
1058
|
+
/**
|
|
1059
|
+
* @type {number | undefined}
|
|
1060
|
+
*/
|
|
1061
|
+
target
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* @param {number} buffer
|
|
1065
|
+
* @param {number} length
|
|
1066
|
+
*/
|
|
1067
|
+
constructor(buffer, length) {
|
|
1068
|
+
this.buffer = buffer
|
|
1069
|
+
this.length = length
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* @param {any} data
|
|
1074
|
+
*/
|
|
1075
|
+
static deserialize(data) {
|
|
1076
|
+
const {
|
|
1077
|
+
buffer,
|
|
1078
|
+
byteOffset,
|
|
1079
|
+
byteLength,
|
|
1080
|
+
target,
|
|
1081
|
+
byteStride,
|
|
1082
|
+
name,
|
|
1083
|
+
extensions,
|
|
1084
|
+
extras
|
|
1085
|
+
} = data
|
|
1086
|
+
|
|
1087
|
+
if (
|
|
1088
|
+
typeof buffer !== "number" ||
|
|
1089
|
+
typeof byteLength !== "number"
|
|
1090
|
+
) {
|
|
1091
|
+
throw "Invalid buffer view provided"
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
const struct = new GLTFBufferView(buffer, byteLength)
|
|
1095
|
+
|
|
1096
|
+
if (typeof byteOffset === "number") {
|
|
1097
|
+
struct.offset = byteOffset
|
|
1098
|
+
} else {
|
|
1099
|
+
struct.offset = 0
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (typeof byteStride === "number") {
|
|
1103
|
+
struct.stride = byteStride
|
|
1104
|
+
} else {
|
|
1105
|
+
struct.stride = 0
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if (typeof target === "number") {
|
|
1109
|
+
struct.target = target
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (typeof name === 'string') {
|
|
1113
|
+
struct.name = name
|
|
1114
|
+
} else {
|
|
1115
|
+
struct.name = ""
|
|
1116
|
+
}
|
|
1117
|
+
if (extensions instanceof Object) {
|
|
1118
|
+
struct.extensions = extensions
|
|
1119
|
+
} else {
|
|
1120
|
+
struct.extensions = {}
|
|
1121
|
+
}
|
|
1122
|
+
if (extras instanceof Object) {
|
|
1123
|
+
struct.extras = extras
|
|
1124
|
+
} else {
|
|
1125
|
+
struct.extras = {}
|
|
1126
|
+
}
|
|
1127
|
+
return struct
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
class GLTFAccessor {
|
|
1132
|
+
/**
|
|
1133
|
+
* @type {string}
|
|
1134
|
+
*/
|
|
1135
|
+
name = ''
|
|
1136
|
+
/**
|
|
1137
|
+
* @type {Record<string,any>}
|
|
1138
|
+
*/
|
|
1139
|
+
extensions = {}
|
|
1140
|
+
/**
|
|
1141
|
+
* @type {Record<string,any>}
|
|
1142
|
+
*/
|
|
1143
|
+
extras = {}
|
|
1144
|
+
/**
|
|
1145
|
+
* @type {boolean}
|
|
1146
|
+
*/
|
|
1147
|
+
normalized = false
|
|
1148
|
+
/**
|
|
1149
|
+
* @type {number}
|
|
1150
|
+
*/
|
|
1151
|
+
view = 0
|
|
1152
|
+
/**
|
|
1153
|
+
* @type {number}
|
|
1154
|
+
*/
|
|
1155
|
+
offset = 0
|
|
1156
|
+
/**
|
|
1157
|
+
* @type {GLTFComponentType}
|
|
1158
|
+
*/
|
|
1159
|
+
componentType
|
|
1160
|
+
/**
|
|
1161
|
+
* @type {number}
|
|
1162
|
+
*/
|
|
1163
|
+
count
|
|
1164
|
+
/**
|
|
1165
|
+
* @type {GLTFAccessorType}
|
|
1166
|
+
*/
|
|
1167
|
+
type
|
|
1168
|
+
/**
|
|
1169
|
+
* @type {number[] | undefined}
|
|
1170
|
+
*/
|
|
1171
|
+
max
|
|
1172
|
+
/**
|
|
1173
|
+
* @type {number[] | undefined}
|
|
1174
|
+
*/
|
|
1175
|
+
min
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* @param {number} type
|
|
1179
|
+
* @param {number} componentType
|
|
1180
|
+
* @param {number} count
|
|
1181
|
+
*/
|
|
1182
|
+
constructor(type, componentType, count) {
|
|
1183
|
+
this.type = type
|
|
1184
|
+
this.componentType = componentType
|
|
1185
|
+
this.count = count
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* @param {any} data
|
|
1189
|
+
*/
|
|
1190
|
+
static deserialize(data) {
|
|
1191
|
+
const {
|
|
1192
|
+
bufferView,
|
|
1193
|
+
byteOffset,
|
|
1194
|
+
componentType,
|
|
1195
|
+
count,
|
|
1196
|
+
type,
|
|
1197
|
+
max,
|
|
1198
|
+
min,
|
|
1199
|
+
normalized,
|
|
1200
|
+
sparse,
|
|
1201
|
+
name,
|
|
1202
|
+
extensions,
|
|
1203
|
+
extras
|
|
1204
|
+
} = data
|
|
1205
|
+
|
|
1206
|
+
if (
|
|
1207
|
+
typeof componentType !== "number" ||
|
|
1208
|
+
typeof type !== "string" ||
|
|
1209
|
+
typeof count !== "number"
|
|
1210
|
+
) {
|
|
1211
|
+
throw "Invalid accessor"
|
|
1212
|
+
}
|
|
1213
|
+
const struct = new GLTFAccessor(
|
|
1214
|
+
mapAccessorType(type),
|
|
1215
|
+
mapComponentType(componentType),
|
|
1216
|
+
count
|
|
1217
|
+
)
|
|
1218
|
+
|
|
1219
|
+
if (typeof bufferView === "number") {
|
|
1220
|
+
struct.view = bufferView
|
|
1221
|
+
} else {
|
|
1222
|
+
if (sparse instanceof Object) {
|
|
1223
|
+
throw "Sparse accessors are not yet supported"
|
|
1224
|
+
} else {
|
|
1225
|
+
throw "No buffer view to index into for accessor"
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
if (typeof byteOffset === "number") {
|
|
1230
|
+
struct.offset = byteOffset
|
|
1231
|
+
} else {
|
|
1232
|
+
struct.offset = 0
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
if (typeof normalized === "boolean") {
|
|
1236
|
+
struct.normalized = normalized
|
|
1237
|
+
} else {
|
|
1238
|
+
struct.normalized = false
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
if (max instanceof Array) {
|
|
1242
|
+
struct.max = max
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (min instanceof Array) {
|
|
1246
|
+
struct.min = min
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
if (typeof name === 'string') {
|
|
1250
|
+
struct.name = name
|
|
1251
|
+
} else {
|
|
1252
|
+
struct.name = ""
|
|
1253
|
+
}
|
|
1254
|
+
if (extensions instanceof Object) {
|
|
1255
|
+
struct.extensions = extensions
|
|
1256
|
+
} else {
|
|
1257
|
+
struct.extensions = {}
|
|
1258
|
+
}
|
|
1259
|
+
if (extras instanceof Object) {
|
|
1260
|
+
struct.extras = extras
|
|
1261
|
+
} else {
|
|
1262
|
+
struct.extras = {}
|
|
1263
|
+
}
|
|
1264
|
+
return struct
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
class GLTFMetaData {
|
|
1269
|
+
/**
|
|
1270
|
+
* @type {string}
|
|
1271
|
+
*/
|
|
1272
|
+
version
|
|
1273
|
+
/**
|
|
1274
|
+
* @type {string | undefined}
|
|
1275
|
+
*/
|
|
1276
|
+
generator
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* @param {string} version
|
|
1280
|
+
*/
|
|
1281
|
+
constructor(version) {
|
|
1282
|
+
this.version = version
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* @param {any} data
|
|
1286
|
+
*/
|
|
1287
|
+
static deserialize(data) {
|
|
1288
|
+
const { version, generator } = data
|
|
1289
|
+
|
|
1290
|
+
if (typeof version !== "string") {
|
|
1291
|
+
throw "GLTF asset version is required"
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
const meta = new GLTFMetaData(version)
|
|
1295
|
+
if (typeof generator === "string") {
|
|
1296
|
+
meta.generator = generator
|
|
1297
|
+
}
|
|
1298
|
+
return meta
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
class GLTFPrimitive {
|
|
1303
|
+
/**
|
|
1304
|
+
* @type {Map<GLTFAttributeName,number>}
|
|
1305
|
+
*/
|
|
1306
|
+
attributes = new Map()
|
|
1307
|
+
/**
|
|
1308
|
+
* @type {number | undefined}
|
|
1309
|
+
*/
|
|
1310
|
+
indices
|
|
1311
|
+
/**
|
|
1312
|
+
* @type {number | undefined}
|
|
1313
|
+
*/
|
|
1314
|
+
material
|
|
1315
|
+
/**
|
|
1316
|
+
* @type {GLTFPrimitiveMode}
|
|
1317
|
+
*/
|
|
1318
|
+
mode = GLTFPrimitiveMode.Triangles
|
|
1319
|
+
/**
|
|
1320
|
+
* @param {any} data
|
|
1321
|
+
*/
|
|
1322
|
+
static deserialize(data) {
|
|
1323
|
+
const { attributes, material, indices, mode } = data
|
|
1324
|
+
const primitive = new GLTFPrimitive()
|
|
1325
|
+
|
|
1326
|
+
if (typeof indices === "number") {
|
|
1327
|
+
primitive.indices = indices
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
if (typeof material === "number") {
|
|
1331
|
+
primitive.material = material
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if (attributes instanceof Object) {
|
|
1335
|
+
for (const key in attributes) {
|
|
1336
|
+
const value = attributes[key]
|
|
1337
|
+
if (typeof value === "number") {
|
|
1338
|
+
primitive.attributes.set(key, value)
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (typeof mode === "number") {
|
|
1343
|
+
primitive.mode = mapPrimitiveMode(mode)
|
|
1344
|
+
} else {
|
|
1345
|
+
primitive.mode = GLTFPrimitiveMode.Triangles
|
|
1346
|
+
}
|
|
1347
|
+
return primitive
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
class GLTFPBRetallicRoughness {
|
|
1352
|
+
/**
|
|
1353
|
+
* @type {Record<string,any>}
|
|
1354
|
+
*/
|
|
1355
|
+
extensions = {}
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* @type {Record<string,any>}
|
|
1359
|
+
*/
|
|
1360
|
+
extras = {}
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* @type {[number, number, number, number]}
|
|
1364
|
+
*/
|
|
1365
|
+
baseColorFactor = [1, 1, 1, 1]
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* @type { GLFTTextureInfo | undefined }
|
|
1369
|
+
*/
|
|
1370
|
+
baseColorTexture
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* @type {number}
|
|
1374
|
+
*/
|
|
1375
|
+
metallicFactor = 1
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* @type {number}
|
|
1379
|
+
*/
|
|
1380
|
+
roughnessFactor = 1
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* @type {GLFTTextureInfo | undefined}
|
|
1384
|
+
*/
|
|
1385
|
+
metallicRoughnessTexture
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* @param {any} data
|
|
1389
|
+
*/
|
|
1390
|
+
static deserialize(data) {
|
|
1391
|
+
const {
|
|
1392
|
+
baseColorFactor,
|
|
1393
|
+
baseColorTexture,
|
|
1394
|
+
metallicFactor,
|
|
1395
|
+
roughnessFactor,
|
|
1396
|
+
metallicRoughnessTexture,
|
|
1397
|
+
extensions,
|
|
1398
|
+
extras
|
|
1399
|
+
} = data
|
|
1400
|
+
|
|
1401
|
+
const result = new GLTFPBRetallicRoughness()
|
|
1402
|
+
|
|
1403
|
+
if (typeof metallicFactor === 'number') {
|
|
1404
|
+
result.metallicFactor = metallicFactor
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if (typeof roughnessFactor === 'number') {
|
|
1408
|
+
result.roughnessFactor = roughnessFactor
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
if (baseColorFactor instanceof Array && baseColorFactor.length === 4) [
|
|
1412
|
+
result.baseColorFactor = /**@type {[number,number,number,number]}*/(baseColorFactor)
|
|
1413
|
+
]
|
|
1414
|
+
|
|
1415
|
+
if (baseColorTexture) {
|
|
1416
|
+
result.baseColorTexture = GLFTTextureInfo.deserialize(baseColorTexture)
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
if (metallicRoughnessTexture) {
|
|
1420
|
+
result.metallicRoughnessTexture = GLFTTextureInfo.deserialize(metallicRoughnessTexture)
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
if (extensions instanceof Object) {
|
|
1424
|
+
result.extensions = extensions
|
|
1425
|
+
} else {
|
|
1426
|
+
result.extensions = {}
|
|
1427
|
+
}
|
|
1428
|
+
if (extras instanceof Object) {
|
|
1429
|
+
result.extras = extras
|
|
1430
|
+
} else {
|
|
1431
|
+
result.extras = {}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
return result
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
class GLFTTextureInfo {
|
|
1439
|
+
/**
|
|
1440
|
+
* @type {number}
|
|
1441
|
+
*/
|
|
1442
|
+
index
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* @type {number}
|
|
1446
|
+
*/
|
|
1447
|
+
texCoord = 0
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* @type {number}
|
|
1451
|
+
*/
|
|
1452
|
+
scale = 1
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* @type {number}
|
|
1456
|
+
*/
|
|
1457
|
+
strength = 1
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* @type {Record<string, any>}
|
|
1461
|
+
*/
|
|
1462
|
+
extensions = {}
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* @type {Record<string, any>}
|
|
1466
|
+
*/
|
|
1467
|
+
extras = {}
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* @param {number} index
|
|
1471
|
+
*/
|
|
1472
|
+
constructor(index) {
|
|
1473
|
+
this.index = index
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* @param {any} data
|
|
1477
|
+
*/
|
|
1478
|
+
static deserialize(data) {
|
|
1479
|
+
const { index, texCoord, scale, strength, extensions, extras } = data
|
|
1480
|
+
|
|
1481
|
+
if (typeof index !== 'number') {
|
|
1482
|
+
throw 'Texture info index is not type of number'
|
|
1483
|
+
}
|
|
1484
|
+
const result = new GLFTTextureInfo(index)
|
|
1485
|
+
|
|
1486
|
+
if (typeof texCoord === 'number') [
|
|
1487
|
+
result.texCoord = texCoord
|
|
1488
|
+
]
|
|
1489
|
+
|
|
1490
|
+
if (typeof scale === 'number') [
|
|
1491
|
+
result.scale = scale
|
|
1492
|
+
]
|
|
1493
|
+
|
|
1494
|
+
if (typeof strength === 'number') [
|
|
1495
|
+
result.strength = strength
|
|
1496
|
+
]
|
|
1497
|
+
|
|
1498
|
+
if (extensions instanceof Object) {
|
|
1499
|
+
result.extensions = extensions
|
|
1500
|
+
} else {
|
|
1501
|
+
result.extensions = {}
|
|
1502
|
+
}
|
|
1503
|
+
if (extras instanceof Object) {
|
|
1504
|
+
result.extras = extras
|
|
1505
|
+
} else {
|
|
1506
|
+
result.extras = {}
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
return result
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Enum of GLTF's semantic mesh attribute names.
|
|
1515
|
+
* @enum {string}
|
|
1516
|
+
*/
|
|
1517
|
+
export const GLTFAttributeName = /**@type {const}*/({
|
|
1518
|
+
/** Vertex positions */
|
|
1519
|
+
Position: "POSITION",
|
|
1520
|
+
/** Vertex normals */
|
|
1521
|
+
Normal: "NORMAL",
|
|
1522
|
+
/** Vertex tangents */
|
|
1523
|
+
Tangent: "TANGENT",
|
|
1524
|
+
/** Vertex texture coordinates set 0 */
|
|
1525
|
+
TexCoord0: "TEXCOORD_0",
|
|
1526
|
+
/** Vertex texture coordinates set 1 */
|
|
1527
|
+
TexCoord1: "TEXCOORD_1",
|
|
1528
|
+
/** Vertex colors set 0 */
|
|
1529
|
+
Color0: "COLOR_0",
|
|
1530
|
+
/** Joint indices for skinning set 0 */
|
|
1531
|
+
Joints0: "JOINTS_0",
|
|
1532
|
+
/** Joint weights for skinning set 0 */
|
|
1533
|
+
Weights0: "WEIGHTS_0"
|
|
1534
|
+
})
|
|
1535
|
+
|
|
1536
|
+
/**
|
|
1537
|
+
* @enum {number}
|
|
1538
|
+
*/
|
|
1539
|
+
export const GLTFComponentType = /**@type {const}*/({
|
|
1540
|
+
Byte: 0x1400,
|
|
1541
|
+
UnsignedByte: 0x1401,
|
|
1542
|
+
Short: 0x1402,
|
|
1543
|
+
UnsignedShort: 0x1403,
|
|
1544
|
+
Int: 0x1404,
|
|
1545
|
+
UnsignedInt: 0x1405,
|
|
1546
|
+
Float: 0x1406
|
|
1547
|
+
})
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* @enum {number}
|
|
1551
|
+
*/
|
|
1552
|
+
export const GLTFAccessorType = /**@type {const}*/({
|
|
1553
|
+
Scalar: 0,
|
|
1554
|
+
Vec2: 1,
|
|
1555
|
+
Vec3: 2,
|
|
1556
|
+
Vec4: 3,
|
|
1557
|
+
Mat2: 4,
|
|
1558
|
+
Mat3: 5,
|
|
1559
|
+
Mat4: 6
|
|
1560
|
+
})
|
|
1561
|
+
|
|
1562
|
+
/**
|
|
1563
|
+
* @enum {number}
|
|
1564
|
+
*/
|
|
1565
|
+
export const GLTFPrimitiveMode = /**@type {const}*/({
|
|
1566
|
+
Points: 0,
|
|
1567
|
+
Lines: 1,
|
|
1568
|
+
LineLoop: 2,
|
|
1569
|
+
LineStrip: 3,
|
|
1570
|
+
Triangles: 4,
|
|
1571
|
+
TriangleStrip: 5,
|
|
1572
|
+
TriangleFan: 6
|
|
1573
|
+
})
|
|
1574
|
+
|
|
1575
|
+
/**
|
|
1576
|
+
* @enum {string}
|
|
1577
|
+
*/
|
|
1578
|
+
export const GLFTAlphaMode = /**@type {const}*/({
|
|
1579
|
+
Opaque: "OPAQUE",
|
|
1580
|
+
Mask: "MASK",
|
|
1581
|
+
Blend: "BLEND"
|
|
1582
|
+
})
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* Maps a raw glTF accessor type string to a valid AccessorType enum value.
|
|
1586
|
+
* @param {string} typeString - Raw type from the glTF JSON (e.g. "VEC3", "MAT4").
|
|
1587
|
+
* @returns {GLTFAccessorType} One of the AccessorType values.
|
|
1588
|
+
* @throws {Error} If the type is not recognized.
|
|
1589
|
+
*/
|
|
1590
|
+
function mapAccessorType(typeString) {
|
|
1591
|
+
switch (typeString.toUpperCase()) {
|
|
1592
|
+
case "SCALAR": return GLTFAccessorType.Scalar;
|
|
1593
|
+
case "VEC2": return GLTFAccessorType.Vec2;
|
|
1594
|
+
case "VEC3": return GLTFAccessorType.Vec3;
|
|
1595
|
+
case "VEC4": return GLTFAccessorType.Vec4;
|
|
1596
|
+
case "MAT2": return GLTFAccessorType.Mat2;
|
|
1597
|
+
case "MAT3": return GLTFAccessorType.Mat3;
|
|
1598
|
+
case "MAT4": return GLTFAccessorType.Mat4;
|
|
1599
|
+
default:
|
|
1600
|
+
throw new Error(`Unknown accessor type: ${typeString}`);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
* Maps a raw glTF accessor component type string to a valid AccessorComponentType enum value.
|
|
1606
|
+
* @param {number} value - Raw type from the glTF JSON (e.g. "VEC3", "MAT4").
|
|
1607
|
+
* @returns {GLTFAccessorType} One of the AccessorType values.
|
|
1608
|
+
* @throws {Error} If the component type is not recognized.
|
|
1609
|
+
*/
|
|
1610
|
+
function mapComponentType(value) {
|
|
1611
|
+
switch (value) {
|
|
1612
|
+
case 0x1400: return GLTFComponentType.Byte;
|
|
1613
|
+
case 0x1401: return GLTFComponentType.UnsignedByte;
|
|
1614
|
+
case 0x1402: return GLTFComponentType.Short;
|
|
1615
|
+
case 0x1403: return GLTFComponentType.UnsignedShort;
|
|
1616
|
+
case 0x1404: return GLTFComponentType.Int;
|
|
1617
|
+
case 0x1405: return GLTFComponentType.UnsignedInt;
|
|
1618
|
+
case 0x1406: return GLTFComponentType.Float;
|
|
1619
|
+
default:
|
|
1620
|
+
throw new Error(`Unknown accessor component type: ${value}`);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* @param {number} mode
|
|
1626
|
+
*/
|
|
1627
|
+
export function mapPrimitiveMode(mode) {
|
|
1628
|
+
switch (mode) {
|
|
1629
|
+
case 0: return GLTFPrimitiveMode.Points;
|
|
1630
|
+
case 1: return GLTFPrimitiveMode.Lines;
|
|
1631
|
+
case 2: return GLTFPrimitiveMode.LineLoop;
|
|
1632
|
+
case 3: return GLTFPrimitiveMode.LineStrip;
|
|
1633
|
+
case 4: return GLTFPrimitiveMode.Triangles;
|
|
1634
|
+
case 5: return GLTFPrimitiveMode.TriangleStrip;
|
|
1635
|
+
case 6: return GLTFPrimitiveMode.TriangleFan;
|
|
1636
|
+
default: throw "Unrecognized primitive mode: " + mode
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
class TRSTransform {
|
|
1641
|
+
/**
|
|
1642
|
+
* @type {[number,number,number]}
|
|
1643
|
+
*/
|
|
1644
|
+
translation
|
|
1645
|
+
/**
|
|
1646
|
+
* @type {[number,number,number,number]}
|
|
1647
|
+
*/
|
|
1648
|
+
rotation
|
|
1649
|
+
/**
|
|
1650
|
+
* @type {[number,number,number]}
|
|
1651
|
+
*/
|
|
1652
|
+
scale
|
|
1653
|
+
/**
|
|
1654
|
+
* @param {[number, number, number]} [translation]
|
|
1655
|
+
* @param {[number, number, number, number]} [rotation]
|
|
1656
|
+
* @param {[number, number, number]} [scale]
|
|
1657
|
+
*/
|
|
1658
|
+
constructor(
|
|
1659
|
+
translation = [0, 0, 0],
|
|
1660
|
+
rotation = [0, 0, 0, 1],
|
|
1661
|
+
scale = [1, 1, 1]
|
|
1662
|
+
) {
|
|
1663
|
+
this.translation = translation
|
|
1664
|
+
this.rotation = rotation
|
|
1665
|
+
this.scale = scale
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* @param {any} translation
|
|
1669
|
+
* @param {any} rotation
|
|
1670
|
+
* @param {any} scale
|
|
1671
|
+
*/
|
|
1672
|
+
static deserialize(translation, rotation, scale) {
|
|
1673
|
+
const transform = new TRSTransform()
|
|
1674
|
+
|
|
1675
|
+
if (
|
|
1676
|
+
!(translation instanceof Array) ||
|
|
1677
|
+
!(rotation instanceof Array) ||
|
|
1678
|
+
!(scale instanceof Array)
|
|
1679
|
+
) {
|
|
1680
|
+
throw "Invalid transform"
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
//@ts-ignore
|
|
1684
|
+
transform.translation = translation.filter((data) => typeof data === "number")
|
|
1685
|
+
//@ts-ignore
|
|
1686
|
+
transform.rotation = rotation.filter((data) => typeof data === "number")
|
|
1687
|
+
//@ts-ignore
|
|
1688
|
+
transform.scale = scale.filter((data) => typeof data === "number")
|
|
1689
|
+
return transform
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
class MatrixTransform {
|
|
1694
|
+
/**
|
|
1695
|
+
* @type {MatrixArray}
|
|
1696
|
+
*/
|
|
1697
|
+
value
|
|
1698
|
+
|
|
1699
|
+
/**
|
|
1700
|
+
* @param {MatrixArray} value
|
|
1701
|
+
*/
|
|
1702
|
+
constructor(value) {
|
|
1703
|
+
this.value = value
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* @param {any} data
|
|
1707
|
+
*/
|
|
1708
|
+
static deserialize(data) {
|
|
1709
|
+
|
|
1710
|
+
if (
|
|
1711
|
+
!(data instanceof Array)
|
|
1712
|
+
) {
|
|
1713
|
+
throw "Invalid transform"
|
|
1714
|
+
}
|
|
1715
|
+
const t = data.filter((data) => typeof data === "number")
|
|
1716
|
+
|
|
1717
|
+
if (t.length !== 16) {
|
|
1718
|
+
throw "invalid matrix transform"
|
|
1719
|
+
}
|
|
1720
|
+
const transform = new MatrixTransform(/**@type {MatrixArray}*/(t))
|
|
1721
|
+
|
|
1722
|
+
return transform
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
*
|
|
1728
|
+
* @param {number} index
|
|
1729
|
+
* @param {GLTF} gltf
|
|
1730
|
+
* @returns {[DataView,GLTFAccessor]}
|
|
1731
|
+
*/
|
|
1732
|
+
function getAccessorData(index, gltf) {
|
|
1733
|
+
const { accessors, bufferViews, buffers } = gltf
|
|
1734
|
+
const accessor = accessors[index]
|
|
1735
|
+
if (!accessor) throw "Invalid access to accessors"
|
|
1736
|
+
|
|
1737
|
+
const view = bufferViews[accessor.view]
|
|
1738
|
+
|
|
1739
|
+
if (!view) throw "Invalid access to buffer views"
|
|
1740
|
+
|
|
1741
|
+
const buffer = buffers[view.buffer]
|
|
1742
|
+
|
|
1743
|
+
if (!buffer) throw "Invalid access to buffer"
|
|
1744
|
+
|
|
1745
|
+
const byteLength = accessor.count * getComponentSize(accessor.componentType) * getElementSize(accessor.type)
|
|
1746
|
+
|
|
1747
|
+
return [
|
|
1748
|
+
new DataView(buffer, view.offset + accessor.offset, byteLength),
|
|
1749
|
+
accessor
|
|
1750
|
+
]
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
/**
|
|
1754
|
+
* @param {GLTFAccessor} accessor
|
|
1755
|
+
* @param {DataView} view
|
|
1756
|
+
*/
|
|
1757
|
+
function mapToIndices(accessor, view) {
|
|
1758
|
+
switch (accessor.componentType) {
|
|
1759
|
+
case GLTFComponentType.UnsignedByte:
|
|
1760
|
+
return new Uint8Array(
|
|
1761
|
+
view.buffer,
|
|
1762
|
+
view.byteOffset,
|
|
1763
|
+
view.byteLength / Uint8Array.BYTES_PER_ELEMENT
|
|
1764
|
+
)
|
|
1765
|
+
case GLTFComponentType.UnsignedShort:
|
|
1766
|
+
return new Uint16Array(
|
|
1767
|
+
view.buffer,
|
|
1768
|
+
view.byteOffset,
|
|
1769
|
+
view.byteLength / Uint16Array.BYTES_PER_ELEMENT
|
|
1770
|
+
)
|
|
1771
|
+
case GLTFComponentType.UnsignedInt:
|
|
1772
|
+
return new Uint32Array(
|
|
1773
|
+
view.buffer,
|
|
1774
|
+
view.byteOffset,
|
|
1775
|
+
view.byteLength / Uint32Array.BYTES_PER_ELEMENT
|
|
1776
|
+
)
|
|
1777
|
+
default:
|
|
1778
|
+
throw "Indices provided on a mesh is not valid"
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
/**
|
|
1783
|
+
* @param {string} name
|
|
1784
|
+
* @param {GLTFAccessor} accessor
|
|
1785
|
+
* @param {DataView} buffer
|
|
1786
|
+
* @returns {[string, DataView] | undefined}
|
|
1787
|
+
*/
|
|
1788
|
+
function mapAccessorTypeToAttribute(name, accessor, buffer) {
|
|
1789
|
+
const { componentType: type } = accessor
|
|
1790
|
+
switch (name) {
|
|
1791
|
+
case GLTFAttributeName.Position:
|
|
1792
|
+
if (type !== mapToGLTFComponentType(Attribute.Position.format))
|
|
1793
|
+
throw "Attribute types do not match"
|
|
1794
|
+
return [Attribute.Position.name, buffer]
|
|
1795
|
+
case GLTFAttributeName.TexCoord0:
|
|
1796
|
+
if (type !== mapToGLTFComponentType(Attribute.UV.format))
|
|
1797
|
+
throw "Attribute types do not match"
|
|
1798
|
+
return [Attribute.UV.name, buffer]
|
|
1799
|
+
case GLTFAttributeName.TexCoord1:
|
|
1800
|
+
if (type !== mapToGLTFComponentType(Attribute.UVB.format))
|
|
1801
|
+
throw "Attribute types do not match"
|
|
1802
|
+
return [Attribute.UVB.name, buffer]
|
|
1803
|
+
case GLTFAttributeName.Normal:
|
|
1804
|
+
if (type !== mapToGLTFComponentType(Attribute.Normal.format))
|
|
1805
|
+
throw "Attribute types do not match"
|
|
1806
|
+
return [Attribute.Normal.name, buffer]
|
|
1807
|
+
case GLTFAttributeName.Tangent:
|
|
1808
|
+
if (type !== mapToGLTFComponentType(Attribute.Tangent.format))
|
|
1809
|
+
throw "Attribute types do not match"
|
|
1810
|
+
return [Attribute.Tangent.name, buffer]
|
|
1811
|
+
case GLTFAttributeName.Color0:
|
|
1812
|
+
if (type !== mapToGLTFComponentType(Attribute.Color.format))
|
|
1813
|
+
throw "Attribute types do not match"
|
|
1814
|
+
return [Attribute.Color.name, buffer]
|
|
1815
|
+
case GLTFAttributeName.Weights0:
|
|
1816
|
+
if (type !== mapToGLTFComponentType(Attribute.JointWeight.format))
|
|
1817
|
+
throw "Attribute types do not match"
|
|
1818
|
+
return [Attribute.JointWeight.name, buffer]
|
|
1819
|
+
case GLTFAttributeName.Joints0:
|
|
1820
|
+
if (type === mapToGLTFComponentType(Attribute.JointIndex.format)) {
|
|
1821
|
+
return [Attribute.JointIndex.name, buffer]
|
|
1822
|
+
} else if (type === GlDataType.UnsignedByte) {
|
|
1823
|
+
const newBuffer = widenTypedArray(
|
|
1824
|
+
new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength),
|
|
1825
|
+
Uint16Array
|
|
1826
|
+
)
|
|
1827
|
+
return [Attribute.JointIndex.name, new DataView(newBuffer.buffer)]
|
|
1828
|
+
} else {
|
|
1829
|
+
throw "Attribute types do not match"
|
|
1830
|
+
}
|
|
1831
|
+
default:
|
|
1832
|
+
return undefined;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* @param {GLTFComponentType} componentType
|
|
1839
|
+
*/
|
|
1840
|
+
function getComponentSize(componentType) {
|
|
1841
|
+
switch (componentType) {
|
|
1842
|
+
case GLTFComponentType.Byte: // 0x1400
|
|
1843
|
+
return 1;
|
|
1844
|
+
case GLTFComponentType.UnsignedByte: // 0x1401
|
|
1845
|
+
return 1;
|
|
1846
|
+
case GLTFComponentType.Short: // 0x1402
|
|
1847
|
+
return 2;
|
|
1848
|
+
case GLTFComponentType.UnsignedShort: // 0x1403
|
|
1849
|
+
return 2;
|
|
1850
|
+
case GLTFComponentType.Int: // 0x1404
|
|
1851
|
+
return 4;
|
|
1852
|
+
case GLTFComponentType.UnsignedInt: // 0x1405
|
|
1853
|
+
return 4;
|
|
1854
|
+
case GLTFComponentType.Float: // 0x1406
|
|
1855
|
+
return 4;
|
|
1856
|
+
default:
|
|
1857
|
+
return 0; // Return 0 if componentType is unknown
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* @param {GLTFAccessorType} type
|
|
1864
|
+
*/
|
|
1865
|
+
function getElementSize(type) {
|
|
1866
|
+
switch (type) {
|
|
1867
|
+
case GLTFAccessorType.Scalar:
|
|
1868
|
+
return 1;
|
|
1869
|
+
case GLTFAccessorType.Vec2:
|
|
1870
|
+
return 2;
|
|
1871
|
+
case GLTFAccessorType.Vec3:
|
|
1872
|
+
return 3;
|
|
1873
|
+
case GLTFAccessorType.Vec4:
|
|
1874
|
+
return 4;
|
|
1875
|
+
case GLTFAccessorType.Mat2:
|
|
1876
|
+
return 4;
|
|
1877
|
+
case GLTFAccessorType.Mat3:
|
|
1878
|
+
return 9;
|
|
1879
|
+
case GLTFAccessorType.Mat4:
|
|
1880
|
+
return 16;
|
|
1881
|
+
default:
|
|
1882
|
+
return 0;;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/**
|
|
1887
|
+
* @param {number} mesh
|
|
1888
|
+
* @param {GLTFMesh[]} meshes
|
|
1889
|
+
* @param {[Mesh,number | undefined][][]} geometries
|
|
1890
|
+
* @param {StandardMaterial[]} materials
|
|
1891
|
+
*/
|
|
1892
|
+
function parseMeshObject(mesh, meshes, geometries, materials) {
|
|
1893
|
+
const meshData = meshes[mesh]
|
|
1894
|
+
const geometry = geometries[mesh]
|
|
1895
|
+
|
|
1896
|
+
if (!meshData || !geometry) {
|
|
1897
|
+
throw "Invalid mesh index on node"
|
|
1898
|
+
}
|
|
1899
|
+
if (geometry.length === 1) {
|
|
1900
|
+
const item = /**@type {[Mesh,number | undefined]}*/(geometry[0])
|
|
1901
|
+
const material = item[1] !== undefined ? materials[item[1]] : defaultMaterial
|
|
1902
|
+
|
|
1903
|
+
return new MeshMaterial3D(item[0], material || defaultMaterial)
|
|
1904
|
+
}
|
|
1905
|
+
const root = new Object3D()
|
|
1906
|
+
for (let i = 0; i < geometry.length; i++) {
|
|
1907
|
+
const item = /**@type {[Mesh,number | undefined]}*/(geometry[i])
|
|
1908
|
+
const material = item[1] !== undefined ? materials[item[1]] : defaultMaterial
|
|
1909
|
+
const object = new MeshMaterial3D(item[0], material || defaultMaterial)
|
|
1910
|
+
|
|
1911
|
+
root.add(object)
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
return root
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* @param {GLTFMesh} gltfMesh
|
|
1919
|
+
* @param {GLTF} gltf
|
|
1920
|
+
* @returns {[Mesh, number | undefined][]}
|
|
1921
|
+
*/
|
|
1922
|
+
function parseGeometry(gltfMesh, gltf) {
|
|
1923
|
+
/**@type {[Mesh, number | undefined][]}*/
|
|
1924
|
+
const results = []
|
|
1925
|
+
for (let i = 0; i < gltfMesh.primitives.length; i++) {
|
|
1926
|
+
const primitive = /**@type {GLTFPrimitive} */ (gltfMesh.primitives[i])
|
|
1927
|
+
const attributes = new SeparateAttributeData()
|
|
1928
|
+
const mesh = new Mesh(attributes)
|
|
1929
|
+
if (primitive.indices !== undefined) {
|
|
1930
|
+
const [dataView, accessor] = getAccessorData(
|
|
1931
|
+
primitive.indices,
|
|
1932
|
+
gltf
|
|
1933
|
+
)
|
|
1934
|
+
mesh.indices = mapToIndices(accessor, dataView)
|
|
1935
|
+
}
|
|
1936
|
+
for (const [name, location] of primitive.attributes) {
|
|
1937
|
+
const [buffer, accessor] = getAccessorData(
|
|
1938
|
+
location,
|
|
1939
|
+
gltf
|
|
1940
|
+
)
|
|
1941
|
+
const attribute = mapAccessorTypeToAttribute(name, accessor, buffer)
|
|
1942
|
+
if (!attribute) continue
|
|
1943
|
+
const [attributeName, attributeBuffer] = attribute
|
|
1944
|
+
attributes.set(
|
|
1945
|
+
attributeName,
|
|
1946
|
+
attributeBuffer
|
|
1947
|
+
)
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
mesh.normalizeJointWeights()
|
|
1951
|
+
results.push([mesh, primitive.material])
|
|
1952
|
+
}
|
|
1953
|
+
return results
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* @param {GLTFSkin} gltfSkin
|
|
1958
|
+
* @param {GLTF} gltf
|
|
1959
|
+
* @param {Map<number, Object3D>} entityMap
|
|
1960
|
+
* @returns {Skin}
|
|
1961
|
+
*/
|
|
1962
|
+
function parseSkin(gltfSkin, gltf, entityMap) {
|
|
1963
|
+
const [bindPoseData] = getAccessorData(gltfSkin.inverseBindMatrices, gltf)
|
|
1964
|
+
const bindPose = convertToInverseBindPose(bindPoseData)
|
|
1965
|
+
const skin = new Skin()
|
|
1966
|
+
|
|
1967
|
+
if (bindPose.length !== gltfSkin.joints.length) {
|
|
1968
|
+
console.warn(
|
|
1969
|
+
"GLTF skin inverse bind matrix count does not match joint count",
|
|
1970
|
+
bindPose.length,
|
|
1971
|
+
gltfSkin.joints.length
|
|
1972
|
+
)
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
skin.inverseBindPose = bindPose
|
|
1976
|
+
skin.bones = gltfSkin.joints.map(joint => {
|
|
1977
|
+
const entity = entityMap.get(joint)
|
|
1978
|
+
if (!(entity instanceof Bone3D)) {
|
|
1979
|
+
throw "One of the bones is not a `Bone3D`"
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
return entity
|
|
1983
|
+
})
|
|
1984
|
+
|
|
1985
|
+
return skin
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
*
|
|
1990
|
+
* @param {DataView} poseData
|
|
1991
|
+
* @returns {Affine3[]}
|
|
1992
|
+
*/
|
|
1993
|
+
function convertToInverseBindPose(poseData) {
|
|
1994
|
+
const results = []
|
|
1995
|
+
const data = new Float32Array(
|
|
1996
|
+
poseData.buffer,
|
|
1997
|
+
poseData.byteOffset,
|
|
1998
|
+
poseData.byteLength / Float32Array.BYTES_PER_ELEMENT
|
|
1999
|
+
)
|
|
2000
|
+
|
|
2001
|
+
for (let offset = 0; offset < data.length; offset += 16) {
|
|
2002
|
+
// glTF stores matrices in column-major order. Affine3 expects the same
|
|
2003
|
+
// 3x4 affine components, with translation coming from the last column.
|
|
2004
|
+
const affine = new Affine3(
|
|
2005
|
+
data[offset + 0], data[offset + 4], data[offset + 8], data[offset + 12],
|
|
2006
|
+
data[offset + 1], data[offset + 5], data[offset + 9], data[offset + 13],
|
|
2007
|
+
data[offset + 2], data[offset + 6], data[offset + 10], data[offset + 14],
|
|
2008
|
+
)
|
|
2009
|
+
|
|
2010
|
+
results.push(affine)
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
return results
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* @param {number} index
|
|
2018
|
+
* @param {GLTFNode} node
|
|
2019
|
+
* @param {GLTF} gltf
|
|
2020
|
+
* @param {[Mesh,number | undefined][][]} geometries
|
|
2021
|
+
* @param {StandardMaterial[]} materials
|
|
2022
|
+
*/
|
|
2023
|
+
function parseObject(index, node, gltf, geometries, materials) {
|
|
2024
|
+
const { mesh, transform, name } = node
|
|
2025
|
+
|
|
2026
|
+
let object
|
|
2027
|
+
if (mesh !== undefined) {
|
|
2028
|
+
object = parseMeshObject(mesh, gltf.meshes, geometries, materials)
|
|
2029
|
+
} else {
|
|
2030
|
+
const bone = parseBone(index, gltf)
|
|
2031
|
+
|
|
2032
|
+
if (bone) {
|
|
2033
|
+
object = bone
|
|
2034
|
+
} else {
|
|
2035
|
+
object = new Object3D()
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
if (transform) {
|
|
2040
|
+
transferTransform(object, transform)
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
object.name = name
|
|
2044
|
+
|
|
2045
|
+
return object
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
/**
|
|
2049
|
+
* @param {number} index
|
|
2050
|
+
* @param {GLTF} gltf
|
|
2051
|
+
* @returns {Bone3D | undefined}
|
|
2052
|
+
*/
|
|
2053
|
+
function parseBone(index, gltf) {
|
|
2054
|
+
for (const skin of gltf.skins) {
|
|
2055
|
+
const boneIndex = skin.joints.findIndex((v) => v === index)
|
|
2056
|
+
if (boneIndex !== -1) {
|
|
2057
|
+
const bone = new Bone3D()
|
|
2058
|
+
bone.index = boneIndex
|
|
2059
|
+
return bone
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
return undefined
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
/**
|
|
2066
|
+
* @param {Object3D} object
|
|
2067
|
+
* @param {TRSTransform | MatrixTransform} transform
|
|
2068
|
+
*/
|
|
2069
|
+
function transferTransform(object, transform) {
|
|
2070
|
+
if (transform instanceof TRSTransform) {
|
|
2071
|
+
object.transform.position.x = transform.translation[0]
|
|
2072
|
+
object.transform.position.y = transform.translation[1]
|
|
2073
|
+
object.transform.position.z = transform.translation[2]
|
|
2074
|
+
object.transform.orientation.x = transform.rotation[0]
|
|
2075
|
+
object.transform.orientation.y = transform.rotation[1]
|
|
2076
|
+
object.transform.orientation.z = transform.rotation[2]
|
|
2077
|
+
object.transform.orientation.w = transform.rotation[3]
|
|
2078
|
+
object.transform.scale.x = transform.scale[0]
|
|
2079
|
+
object.transform.scale.y = transform.scale[1]
|
|
2080
|
+
object.transform.scale.z = transform.scale[2]
|
|
2081
|
+
}
|
|
2082
|
+
if (transform instanceof MatrixTransform) {
|
|
2083
|
+
throw "matrix transform not yet supported"
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
/**
|
|
2088
|
+
*
|
|
2089
|
+
* @param {TypedArray} from
|
|
2090
|
+
* @param {new (...args:any[])=>TypedArray} to
|
|
2091
|
+
* @returns
|
|
2092
|
+
*/
|
|
2093
|
+
function widenTypedArray(from, to) {
|
|
2094
|
+
const result = new to(from.length);
|
|
2095
|
+
for (let i = 0; i < from.length; i++) {
|
|
2096
|
+
result[i] = /**@type {bigint | number}*/(from[i])
|
|
2097
|
+
}
|
|
2098
|
+
return result;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
/**
|
|
2102
|
+
* @param {VertexFormat} vertexFormat
|
|
2103
|
+
*/
|
|
2104
|
+
function mapToGLTFComponentType(vertexFormat) {
|
|
2105
|
+
switch (vertexFormat) {
|
|
2106
|
+
case VertexFormat.Uint8:
|
|
2107
|
+
case VertexFormat.Uint8x2:
|
|
2108
|
+
case VertexFormat.Uint8x3:
|
|
2109
|
+
case VertexFormat.Uint8x4:
|
|
2110
|
+
case VertexFormat.Unorm8:
|
|
2111
|
+
case VertexFormat.Unorm8x2:
|
|
2112
|
+
case VertexFormat.Unorm8x3:
|
|
2113
|
+
case VertexFormat.Unorm8x4:
|
|
2114
|
+
return GLTFComponentType.UnsignedByte;
|
|
2115
|
+
case VertexFormat.Snorm8:
|
|
2116
|
+
case VertexFormat.Snorm8x2:
|
|
2117
|
+
case VertexFormat.Snorm8x3:
|
|
2118
|
+
case VertexFormat.Snorm8x4:
|
|
2119
|
+
case VertexFormat.Sint8:
|
|
2120
|
+
case VertexFormat.Sint8x2:
|
|
2121
|
+
case VertexFormat.Sint8x3:
|
|
2122
|
+
case VertexFormat.Sint8x4:
|
|
2123
|
+
return GLTFComponentType.Byte;
|
|
2124
|
+
case VertexFormat.Uint16:
|
|
2125
|
+
case VertexFormat.Uint16x2:
|
|
2126
|
+
case VertexFormat.Uint16x3:
|
|
2127
|
+
case VertexFormat.Uint16x4:
|
|
2128
|
+
case VertexFormat.Unorm16:
|
|
2129
|
+
case VertexFormat.Unorm16x2:
|
|
2130
|
+
case VertexFormat.Unorm16x3:
|
|
2131
|
+
case VertexFormat.Unorm16x4:
|
|
2132
|
+
return GLTFComponentType.UnsignedShort;
|
|
2133
|
+
case VertexFormat.Snorm16:
|
|
2134
|
+
case VertexFormat.Snorm16x2:
|
|
2135
|
+
case VertexFormat.Snorm16x3:
|
|
2136
|
+
case VertexFormat.Snorm16x4:
|
|
2137
|
+
case VertexFormat.Sint16:
|
|
2138
|
+
case VertexFormat.Sint16x2:
|
|
2139
|
+
case VertexFormat.Sint16x3:
|
|
2140
|
+
case VertexFormat.Sint16x4:
|
|
2141
|
+
return GLTFComponentType.Short;
|
|
2142
|
+
// 32-bit unsigned ints
|
|
2143
|
+
case VertexFormat.Uint32:
|
|
2144
|
+
case VertexFormat.Uint32x2:
|
|
2145
|
+
case VertexFormat.Uint32x3:
|
|
2146
|
+
case VertexFormat.Uint32x4:
|
|
2147
|
+
return GLTFComponentType.UnsignedInt;
|
|
2148
|
+
|
|
2149
|
+
// 32-bit signed ints
|
|
2150
|
+
case VertexFormat.Sint32:
|
|
2151
|
+
case VertexFormat.Sint32x2:
|
|
2152
|
+
case VertexFormat.Sint32x3:
|
|
2153
|
+
case VertexFormat.Sint32x4:
|
|
2154
|
+
return GLTFComponentType.Int;
|
|
2155
|
+
case VertexFormat.Float32:
|
|
2156
|
+
case VertexFormat.Float32x2:
|
|
2157
|
+
case VertexFormat.Float32x3:
|
|
2158
|
+
case VertexFormat.Float32x4:
|
|
2159
|
+
return GLTFComponentType.Float
|
|
2160
|
+
default:
|
|
2161
|
+
throw new Error('Unsupported vertex format: ' + vertexFormat);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
|
|
2166
|
+
/**
|
|
2167
|
+
*@typedef {Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array} TypedArray
|
|
2168
|
+
*/
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
* @typedef {[number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]} MatrixArray
|
|
2172
|
+
*/
|