| "@typescript-eslint/eslint-plugin": "^5.59.7", | "@typescript-eslint/eslint-plugin": "^5.59.7", | ||||
| "@typescript-eslint/parser": "^5.59.5", | "@typescript-eslint/parser": "^5.59.5", | ||||
| "clean-package": "^2.2.0", | "clean-package": "^2.2.0", | ||||
| "@types/node": "^22.10.2", | |||||
| "copyfiles": "^2.4.1", | "copyfiles": "^2.4.1", | ||||
| "eslint": "^8.40.0", | "eslint": "^8.40.0", | ||||
| "eslint-import-resolver-typescript": "^3.5.5", | "eslint-import-resolver-typescript": "^3.5.5", | ||||
| "rollup-plugin-license": "^3.0.1", | "rollup-plugin-license": "^3.0.1", | ||||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.153.1006/package.tgz", | "three": "https://github.com/repalash/three.js-modded/releases/download/v0.153.1006/package.tgz", | ||||
| "tslib": "^2.5.0", | "tslib": "^2.5.0", | ||||
| "typedoc": "^0.25.7", | |||||
| "typescript": "^5.3.3", | |||||
| "typedoc": "^0.27.5", | |||||
| "typescript": "^5.7.2", | |||||
| "typescript-plugin-css-modules": "^5.0.1", | "typescript-plugin-css-modules": "^5.0.1", | ||||
| "vite": "^6.0.5", | "vite": "^6.0.5", | ||||
| "vite-plugin-dts": "^4.4.0", | "vite-plugin-dts": "^4.4.0", | ||||
| "@types/webxr": "^0.5.1", | "@types/webxr": "^0.5.1", | ||||
| "@types/wicg-file-system-access": "^2020.9.5", | "@types/wicg-file-system-access": "^2020.9.5", | ||||
| "stats.js": "^0.17.0", | "stats.js": "^0.17.0", | ||||
| "ts-browser-helpers": "^0.14.2", | |||||
| "ts-browser-helpers": "^0.15.0", | |||||
| "uiconfig.js": "^0.0.12" | "uiconfig.js": "^0.0.12" | ||||
| }, | }, | ||||
| "//": { | "//": { |
| @uiFolderContainer('AWS/S3 Client') | @uiFolderContainer('AWS/S3 Client') | ||||
| export class AWSClientPlugin extends AViewerPluginSync<'fileUpload'> { | export class AWSClientPlugin extends AViewerPluginSync<'fileUpload'> { | ||||
| static readonly PluginType = 'AWSClientPlugin1' | static readonly PluginType = 'AWSClientPlugin1' | ||||
| uiConfig?: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| enabled = true | enabled = true | ||||
| private _connected = false | private _connected = false |
| @uiFolderContainer('Basic SVG Renderer') | @uiFolderContainer('Basic SVG Renderer') | ||||
| export class BasicSVGRenderer extends SVGRenderer { | export class BasicSVGRenderer extends SVGRenderer { | ||||
| @uiToggle() | @uiToggle() | ||||
| autoClear: boolean | |||||
| declare autoClear: boolean | |||||
| @uiToggle() | @uiToggle() | ||||
| sortObjects: boolean | |||||
| declare sortObjects: boolean | |||||
| @uiToggle() | @uiToggle() | ||||
| sortElements: boolean | |||||
| declare sortElements: boolean | |||||
| @uiNumber() | @uiNumber() | ||||
| overdraw: number | |||||
| declare overdraw: number | |||||
| @uiDropdown(undefined, ['low', 'high']) | @uiDropdown(undefined, ['low', 'high']) | ||||
| @onChange2(BasicSVGRenderer.prototype._refresh) | @onChange2(BasicSVGRenderer.prototype._refresh) |
| export class GLTFWriter2 extends GLTFExporter.Utils.GLTFWriter { | export class GLTFWriter2 extends GLTFExporter.Utils.GLTFWriter { | ||||
| options: GLTFExporterOptions & { | |||||
| declare options: GLTFExporterOptions & { | |||||
| externalImagesInExtras: boolean, | externalImagesInExtras: boolean, | ||||
| exporterOptions: GLTFExporter2Options | exporterOptions: GLTFExporter2Options | ||||
| } | } |
| */ | */ | ||||
| static async ImportViewerConfig(parser: GLTFParser, viewer: ThreeViewer, resultScenes: Group[], scene?: any): Promise<Partial<ISerializedViewerConfig>> { | static async ImportViewerConfig(parser: GLTFParser, viewer: ThreeViewer, resultScenes: Group[], scene?: any): Promise<Partial<ISerializedViewerConfig>> { | ||||
| if (!scene) { | if (!scene) { | ||||
| const scenes = parser.json.scenes || [] | |||||
| const scenes = (parser.json.scenes as Group[]) || [] | |||||
| if (scenes.length !== 1) { | if (scenes.length !== 1) { | ||||
| for (const scene1 of scenes) { | for (const scene1 of scenes) { | ||||
| await this.ImportViewerConfig(parser, viewer, [resultScenes[scenes.indexOf(scene1)]] || resultScenes, scene1) | |||||
| const i = scenes.indexOf(scene1) | |||||
| await this.ImportViewerConfig(parser, viewer, i >= 0 ? [resultScenes[i]] : resultScenes, scene1) | |||||
| } | } | ||||
| return {} | return {} | ||||
| } | } |
| console.warn('GLTF Export: encryption key not provided, skipping encryption') | console.warn('GLTF Export: encryption key not provided, skipping encryption') | ||||
| return gltf | return gltf | ||||
| } | } | ||||
| const buffer = await aesGcmEncrypt(new Uint8Array(gltf as ArrayBuffer), options.encryptKey) | |||||
| const buffer = await aesGcmEncrypt(new Uint8Array(gltf), options.encryptKey) | |||||
| return makeGLBFile(buffer, { | return makeGLBFile(buffer, { | ||||
| asset: { | asset: { | ||||
| version: '2.0', | version: '2.0', |
| setDirty?(): void | setDirty?(): void | ||||
| source: Source & { | source: Source & { | ||||
| _sourceImgBuffer?: ArrayBuffer // see KTX2LoadPlugin and serializeTextureInExtras | |||||
| _sourceImgBuffer?: ArrayBuffer|Uint8Array // see KTX2LoadPlugin and serializeTextureInExtras | |||||
| } | } | ||||
| _appliedMaterials?: Set<IMaterial> // for internal use only. refers to the materials that this texture is applied to | _appliedMaterials?: Set<IMaterial> // for internal use only. refers to the materials that this texture is applied to |
| return this._controls | return this._controls | ||||
| } | } | ||||
| @uiInput('Name') name: string | |||||
| @uiInput('Name') declare name: string | |||||
| @serialize('camControls') | @serialize('camControls') | ||||
| private _controls?: ICameraControls | private _controls?: ICameraControls | ||||
| @onChange3(PerspectiveCamera2.prototype.setDirty) | @onChange3(PerspectiveCamera2.prototype.setDirty) | ||||
| @uiSlider('Field Of View', [1, 180], 0.001) | @uiSlider('Field Of View', [1, 180], 0.001) | ||||
| @serialize() fov: number | |||||
| @serialize() declare fov: number | |||||
| @onChange3(PerspectiveCamera2.prototype.setDirty) | @onChange3(PerspectiveCamera2.prototype.setDirty) | ||||
| @serialize() focus: number | |||||
| @serialize() declare focus: number | |||||
| @onChange3(PerspectiveCamera2.prototype.setDirty) | @onChange3(PerspectiveCamera2.prototype.setDirty) | ||||
| @uiSlider('FoV Zoom', [0.001, 10], 0.001) | @uiSlider('FoV Zoom', [0.001, 10], 0.001) | ||||
| @serialize() zoom: number | |||||
| @serialize() declare zoom: number | |||||
| @uiVector('Position', undefined, undefined, (that:PerspectiveCamera2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Position', undefined, undefined, (that:PerspectiveCamera2)=>({onChange: ()=>that.setDirty()})) | ||||
| @serialize() readonly position: Vector3 | |||||
| @serialize() declare readonly position: Vector3 | |||||
| /** | /** | ||||
| * The target position of the camera (where the camera looks at). Also syncs with the controls.target, so it's not required to set that separately. | * The target position of the camera (where the camera looks at). Also syncs with the controls.target, so it's not required to set that separately. | ||||
| @onChange((k: string, v: boolean)=>{ | @onChange((k: string, v: boolean)=>{ | ||||
| if (!v) console.warn('Setting camera invisible is not supported', k, v) | if (!v) console.warn('Setting camera invisible is not supported', k, v) | ||||
| }) | }) | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| get isActiveCamera(): boolean { | get isActiveCamera(): boolean { | ||||
| return this.isMainCamera | return this.isMainCamera | ||||
| add: (...object: IObject3D[]) => this | add: (...object: IObject3D[]) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ICameraEvent) => void | dispatchEvent: (event: ICameraEvent) => void | ||||
| parent: IObject3D | null | |||||
| children: IObject3D[] | |||||
| declare parent: IObject3D | null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| assetType = 'light' as const | assetType = 'light' as const | ||||
| setDirty = iLightCommons.setDirty | setDirty = iLightCommons.setDirty | ||||
| refreshUi = iLightCommons.refreshUi | refreshUi = iLightCommons.refreshUi | ||||
| uiConfig: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| readonly isAmbientLight2 = true | readonly isAmbientLight2 = true | ||||
| @uiToggle('Enabled') | @uiToggle('Enabled') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| @uiColor('Color', (that: AmbientLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Color', (that: AmbientLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| color: Color | |||||
| declare color: Color | |||||
| @uiSlider('Intensity', [0, 30], 0.01) | @uiSlider('Intensity', [0, 30], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| intensity: number | |||||
| declare intensity: number | |||||
| constructor(color?: ColorRepresentation, intensity?: number) { | constructor(color?: ColorRepresentation, intensity?: number) { | ||||
| super(color, intensity) | super(color, intensity) | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| assetType = 'light' as const | assetType = 'light' as const | ||||
| setDirty = iLightCommons.setDirty | setDirty = iLightCommons.setDirty | ||||
| refreshUi = iLightCommons.refreshUi | refreshUi = iLightCommons.refreshUi | ||||
| uiConfig: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| readonly isDirectionalLight2 = true | readonly isDirectionalLight2 = true | ||||
| @uiToggle('Enabled') | @uiToggle('Enabled') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| @uiColor('Color', (that: DirectionalLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Color', (that: DirectionalLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| color: Color | |||||
| declare color: Color | |||||
| @uiSlider('Intensity', [0, 30], 0.01) | @uiSlider('Intensity', [0, 30], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| intensity: number | |||||
| declare intensity: number | |||||
| @uiVector('Position', undefined, undefined, (that: DirectionalLight2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Position', undefined, undefined, (that: DirectionalLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| readonly position: Vector3 | |||||
| declare readonly position: Vector3 | |||||
| @uiVector('Rotation', undefined, undefined, (that: DirectionalLight2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Rotation', undefined, undefined, (that: DirectionalLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| readonly rotation: Euler | |||||
| declare readonly rotation: Euler | |||||
| @uiToggle('Cast Shadow') | @uiToggle('Cast Shadow') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| castShadow: boolean | |||||
| declare castShadow: boolean | |||||
| constructor(color?: ColorRepresentation, intensity?: number) { | constructor(color?: ColorRepresentation, intensity?: number) { | ||||
| super(color, intensity) | super(color, intensity) | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| assetType = 'light' as const | assetType = 'light' as const | ||||
| setDirty = iLightCommons.setDirty | setDirty = iLightCommons.setDirty | ||||
| refreshUi = iLightCommons.refreshUi | refreshUi = iLightCommons.refreshUi | ||||
| uiConfig: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| readonly isHemisphereLight2 = true | readonly isHemisphereLight2 = true | ||||
| @uiToggle('Enabled') | @uiToggle('Enabled') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| @uiColor('Sky Color', (that: HemisphereLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Sky Color', (that: HemisphereLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| color: Color | |||||
| declare color: Color | |||||
| @uiColor('Ground Color', (that: HemisphereLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Ground Color', (that: HemisphereLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| groundColor: Color | |||||
| declare groundColor: Color | |||||
| @uiSlider('Intensity', [0, 30], 0.01) | @uiSlider('Intensity', [0, 30], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| intensity: number | |||||
| declare intensity: number | |||||
| @uiVector('Position', undefined, undefined, (that: HemisphereLight2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Position', undefined, undefined, (that: HemisphereLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| readonly position: Vector3 | |||||
| declare readonly position: Vector3 | |||||
| constructor(skyColor?: ColorRepresentation, groundColor?: ColorRepresentation, intensity?: number) { | constructor(skyColor?: ColorRepresentation, groundColor?: ColorRepresentation, intensity?: number) { | ||||
| super(skyColor, groundColor, intensity) | super(skyColor, groundColor, intensity) | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| assetType = 'light' as const | assetType = 'light' as const | ||||
| setDirty = iLightCommons.setDirty | setDirty = iLightCommons.setDirty | ||||
| refreshUi = iLightCommons.refreshUi | refreshUi = iLightCommons.refreshUi | ||||
| uiConfig: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| readonly isPointLight2 = true | readonly isPointLight2 = true | ||||
| @uiToggle('Enabled') | @uiToggle('Enabled') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| @uiColor('Color', (that: PointLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Color', (that: PointLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| color: Color | |||||
| declare color: Color | |||||
| @uiSlider('Intensity', [0, 30], 0.01) | @uiSlider('Intensity', [0, 30], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| intensity: number | |||||
| declare intensity: number | |||||
| @uiNumber('Distance') | @uiNumber('Distance') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| distance: number | |||||
| declare distance: number | |||||
| @uiNumber('Decay') | @uiNumber('Decay') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| decay: number | |||||
| declare decay: number | |||||
| @uiVector('Position', undefined, undefined, (that: PointLight2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Position', undefined, undefined, (that: PointLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| readonly position: Vector3 | |||||
| declare readonly position: Vector3 | |||||
| @uiToggle('Cast Shadow') | @uiToggle('Cast Shadow') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| castShadow: boolean | |||||
| declare castShadow: boolean | |||||
| constructor(color?: ColorRepresentation, intensity?: number, distance?: number, decay?: number) { | constructor(color?: ColorRepresentation, intensity?: number, distance?: number, decay?: number) { | ||||
| super(color, intensity, distance, decay) | super(color, intensity, distance, decay) | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| @uiToggle('Enabled') | @uiToggle('Enabled') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| @uiColor('Color', (that: RectAreaLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Color', (that: RectAreaLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| color: Color | |||||
| declare color: Color | |||||
| @uiSlider('Intensity', [0, 30], 0.01) | @uiSlider('Intensity', [0, 30], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| intensity: number | |||||
| declare intensity: number | |||||
| @uiNumber('Width') | @uiNumber('Width') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| width: number | |||||
| declare width: number | |||||
| @uiNumber('Height') | @uiNumber('Height') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| height: number | |||||
| declare height: number | |||||
| @uiNumber('Power') | @uiNumber('Power') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| power: number | |||||
| declare power: number | |||||
| constructor(color?: ColorRepresentation, intensity?: number, width?: number, height?: number) { | constructor(color?: ColorRepresentation, intensity?: number, width?: number, height?: number) { | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| @uiToggle('Enabled') | @uiToggle('Enabled') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| visible: boolean | |||||
| declare visible: boolean | |||||
| @uiColor('Color', (that: SpotLight2)=>({onChange: ()=>that.setDirty()})) | @uiColor('Color', (that: SpotLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| color: Color | |||||
| declare color: Color | |||||
| @uiSlider('Intensity', [0, 30], 0.01) | @uiSlider('Intensity', [0, 30], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| intensity: number | |||||
| declare intensity: number | |||||
| @uiSlider('Angle', [0, 2], 0.01) | @uiSlider('Angle', [0, 2], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| angle: number | |||||
| declare angle: number | |||||
| @uiSlider('Penumbra', [0, 0.9999], 0.01) | @uiSlider('Penumbra', [0, 0.9999], 0.01) | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| penumbra: number | |||||
| declare penumbra: number | |||||
| @uiInput('Distance') | @uiInput('Distance') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| distance: number | |||||
| declare distance: number | |||||
| @uiInput('Decay') | @uiInput('Decay') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| decay: number | |||||
| declare decay: number | |||||
| @uiVector('Position', undefined, undefined, (that: SpotLight2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Position', undefined, undefined, (that: SpotLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| readonly position: Vector3 | |||||
| declare readonly position: Vector3 | |||||
| @uiVector('Rotation', undefined, undefined, (that: SpotLight2)=>({onChange: ()=>that.setDirty()})) | @uiVector('Rotation', undefined, undefined, (that: SpotLight2)=>({onChange: ()=>that.setDirty()})) | ||||
| readonly rotation: Euler | |||||
| declare readonly rotation: Euler | |||||
| @uiToggle('Cast Shadow') | @uiToggle('Cast Shadow') | ||||
| @onChange3('setDirty') | @onChange3('setDirty') | ||||
| castShadow: boolean | |||||
| declare castShadow: boolean | |||||
| constructor(color?: ColorRepresentation, intensity?: number, distance?: number, | constructor(color?: ColorRepresentation, intensity?: number, distance?: number, | ||||
| angle?: number, | angle?: number, | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| textures: {colorSpace: ColorSpace, id: string}[] = [] | textures: {colorSpace: ColorSpace, id: string}[] = [] | ||||
| userData: IMaterialUserData | |||||
| declare userData: IMaterialUserData | |||||
| constructor(parameters: ShaderMaterialParameters, textureIds: string[], isRawShaderMaterial = false) { | constructor(parameters: ShaderMaterialParameters, textureIds: string[], isRawShaderMaterial = false) { | ||||
| super(parameters, isRawShaderMaterial) | super(parameters, isRawShaderMaterial) |
| public static readonly TYPE = 'LegacyPhongMaterial' // not using .type because it is used by three.js | public static readonly TYPE = 'LegacyPhongMaterial' // not using .type because it is used by three.js | ||||
| assetType = 'material' as const | assetType = 'material' as const | ||||
| userData: IMaterialUserData | |||||
| declare userData: IMaterialUserData | |||||
| public readonly isLegacyPhongMaterial = true | public readonly isLegacyPhongMaterial = true | ||||
| // region UI Config | // region UI Config | ||||
| @uiInput() name: string | |||||
| @uiColor() color: Color | |||||
| @uiToggle() dashed: boolean | |||||
| @uiNumber() dashScale: number | |||||
| @uiNumber() dashSize: number | |||||
| @uiNumber() dashOffset: number | |||||
| @uiNumber() gapSize: number | |||||
| @uiNumber() linewidth: number | |||||
| @uiVector() resolution: Vector2 | |||||
| @uiToggle() alphaToCoverage: boolean | |||||
| @uiToggle() worldUnits: boolean | |||||
| // @uiToggle() fog = true | |||||
| @uiInput() declare name: string | |||||
| @uiColor() declare color: Color | |||||
| @uiToggle() declare dashed: boolean | |||||
| @uiNumber() declare dashScale: number | |||||
| @uiNumber() declare dashSize: number | |||||
| @uiNumber() declare dashOffset: number | |||||
| @uiNumber() declare gapSize: number | |||||
| @uiNumber() declare linewidth: number | |||||
| @uiVector() declare resolution: Vector2 | |||||
| @uiToggle() declare alphaToCoverage: boolean | |||||
| @uiToggle() declare worldUnits: boolean | |||||
| // @uiToggle() declare fog = true | |||||
| // todo dispose ui config | // todo dispose ui config |
| public static readonly TYPE = 'PhysicalMaterial' // not using .type because it is used by three.js | public static readonly TYPE = 'PhysicalMaterial' // not using .type because it is used by three.js | ||||
| assetType = 'material' as const | assetType = 'material' as const | ||||
| userData: IMaterialUserData | |||||
| declare userData: IMaterialUserData | |||||
| public readonly isPhysicalMaterial = true | public readonly isPhysicalMaterial = true | ||||
| /** | /** | ||||
| * Serializes this material to JSON. | * Serializes this material to JSON. | ||||
| * @param meta - metadata for serialization | * @param meta - metadata for serialization | ||||
| * @param _internal - Calls only super.toJSON, does internal three.js serialization and @serialize tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material | |||||
| * @param _internal - Calls only super.toJSON, does internal three.js serialization and `@serialize` tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material | |||||
| */ | */ | ||||
| toJSON(meta?: SerializationMetaType, _internal = false): any { | toJSON(meta?: SerializationMetaType, _internal = false): any { | ||||
| if (_internal) return { | if (_internal) return { |
| assetType = 'material' as const | assetType = 'material' as const | ||||
| userData: IMaterialUserData | |||||
| declare userData: IMaterialUserData | |||||
| public readonly isAShaderMaterial = true | public readonly isAShaderMaterial = true | ||||
| public static readonly TYPE = 'UnlitLineMaterial' // not using .type because it is used by three.js | public static readonly TYPE = 'UnlitLineMaterial' // not using .type because it is used by three.js | ||||
| assetType = 'material' as const | assetType = 'material' as const | ||||
| userData: IMaterialUserData | |||||
| declare userData: IMaterialUserData | |||||
| public readonly isUnlitLineMaterial = true | public readonly isUnlitLineMaterial = true | ||||
| public static readonly TYPE = 'UnlitMaterial' // not using .type because it is used by three.js | public static readonly TYPE = 'UnlitMaterial' // not using .type because it is used by three.js | ||||
| assetType = 'material' as const | assetType = 'material' as const | ||||
| userData: IMaterialUserData | |||||
| declare userData: IMaterialUserData | |||||
| public readonly isUnlitMaterial = true | public readonly isUnlitMaterial = true | ||||
| /** | /** | ||||
| * Serializes this material to JSON. | * Serializes this material to JSON. | ||||
| * @param meta - metadata for serialization | * @param meta - metadata for serialization | ||||
| * @param _internal - Calls only super.toJSON, does internal three.js serialization and @serialize tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material | |||||
| * @param _internal - Calls only super.toJSON, does internal three.js serialization and `@serialize` tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material | |||||
| */ | */ | ||||
| toJSON(meta?: SerializationMetaType, _internal = false): any { | toJSON(meta?: SerializationMetaType, _internal = false): any { | ||||
| if (_internal) return { | if (_internal) return { |
| setDirty = iObjectCommons.setDirty | setDirty = iObjectCommons.setDirty | ||||
| refreshUi = iObjectCommons.refreshUi | refreshUi = iObjectCommons.refreshUi | ||||
| material: TMaterial | |||||
| readonly materials: IMaterial[] | |||||
| geometry: TGeometry | |||||
| declare material: TMaterial | |||||
| declare readonly materials: IMaterial[] | |||||
| declare geometry: TGeometry | |||||
| /** | /** | ||||
| * @deprecated use `this` instead | * @deprecated use `this` instead | ||||
| iObjectCommons.upgradeObject3D.call(this) | iObjectCommons.upgradeObject3D.call(this) | ||||
| } | } | ||||
| userData: IObject3DUserData | |||||
| declare userData: IObject3DUserData | |||||
| // region inherited type fixes | // region inherited type fixes | ||||
| // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 | // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ILightEvent) => void | dispatchEvent: (event: ILightEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| dispose: (removeFromParent?: boolean) => void | dispose: (removeFromParent?: boolean) => void | ||||
| // endregion | // endregion |
| readonly isRootScene = true | readonly isRootScene = true | ||||
| assetType = 'model' as const | assetType = 'model' as const | ||||
| uiConfig!: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| // private _processors = new ObjectProcessorMap<'environment' | 'background'>() | // private _processors = new ObjectProcessorMap<'environment' | 'background'>() | ||||
| // private _sceneObjects: ISceneObject[] = [] | // private _sceneObjects: ISceneObject[] = [] | ||||
| refreshScene(event?: Partial<ISceneEvent> & ISceneSetDirtyOptions): this { | refreshScene(event?: Partial<ISceneEvent> & ISceneSetDirtyOptions): this { | ||||
| if (event && event.type === 'objectUpdate' && event.object === this) return this // ignore self | if (event && event.type === 'objectUpdate' && event.object === this) return this // ignore self | ||||
| // todo test the isCamera here. this is for animation object plugin | // todo test the isCamera here. this is for animation object plugin | ||||
| if (event?.sceneUpdate === false || event?.refreshScene === false || event.object?.isCamera) return this.setDirty(event) // so that it doesn't trigger frame fade, shadow refresh etc | |||||
| if (event?.sceneUpdate === false || event?.refreshScene === false || event?.object?.isCamera) return this.setDirty(event) // so that it doesn't trigger frame fade, shadow refresh etc | |||||
| // console.warn(event) | // console.warn(event) | ||||
| this.refreshActiveCameraNearFar() | this.refreshActiveCameraNearFar() | ||||
| this._sceneBounds = this.getBounds(false, true) | this._sceneBounds = this.getBounds(false, true) | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ISceneEvent) => void | dispatchEvent: (event: ISceneEvent) => void | ||||
| parent: null | |||||
| children: IObject3D[] | |||||
| declare parent: null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| @uiFolderContainer('GLTF Animations') | @uiFolderContainer('GLTF Animations') | ||||
| export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'checkpointBegin'|'animationStep'> { | export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'checkpointBegin'|'animationStep'> { | ||||
| enabled = true | enabled = true | ||||
| uiConfig!: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| static readonly PluginType = 'GLTFAnimation' | static readonly PluginType = 'GLTFAnimation' | ||||
| /** | /** |
| this._geometry.attributes.uv2 = (this._geometry.attributes.uv as any as BufferAttribute | InterleavedBufferAttribute).clone() | this._geometry.attributes.uv2 = (this._geometry.attributes.uv as any as BufferAttribute | InterleavedBufferAttribute).clone() | ||||
| this._geometry.attributes.uv2.needsUpdate = true | this._geometry.attributes.uv2.needsUpdate = true | ||||
| } | } | ||||
| if (this._mesh) this._mesh.geometry = g | |||||
| if (this._mesh) this._mesh.geometry = this._geometry | |||||
| } | } | ||||
| export class Rhino3dmLoadPlugin extends BaseImporterPlugin implements IUiConfigContainer { | export class Rhino3dmLoadPlugin extends BaseImporterPlugin implements IUiConfigContainer { | ||||
| public static readonly PluginType = 'Rhino3dmLoadPlugin' | public static readonly PluginType = 'Rhino3dmLoadPlugin' | ||||
| protected _importer = new Importer(Rhino3dmLoader2, ['3dm'], ['model/vnd.3dm', 'model/3dm'], true) | protected _importer = new Importer(Rhino3dmLoader2, ['3dm'], ['model/vnd.3dm', 'model/3dm'], true) | ||||
| uiConfig!: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| /** | /** | ||||
| * Import materials from the file based on material source and color source. If false, a default material will be used | * Import materials from the file based on material source and color source. If false, a default material will be used |
| if (filename) { | if (filename) { | ||||
| const zip: Zippable = {} | const zip: Zippable = {} | ||||
| zip[filename] = new Uint8Array(buffer) | zip[filename] = new Uint8Array(buffer) | ||||
| buffer = zipSync(zip).buffer | |||||
| buffer = zipSync(zip).buffer as ArrayBuffer | |||||
| } | } | ||||
| } | } | ||||
| return super.parse(buffer) | return super.parse(buffer) |
| @uiFolderContainer('Dropzone') | @uiFolderContainer('Dropzone') | ||||
| export class DropzonePlugin extends AViewerPluginSync<'drop'> { | export class DropzonePlugin extends AViewerPluginSync<'drop'> { | ||||
| static readonly PluginType = 'Dropzone' | static readonly PluginType = 'Dropzone' | ||||
| uiConfig!: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| @uiToggle() @serialize() enabled = true | @uiToggle() @serialize() enabled = true | ||||
| private _inputEl?: HTMLInputElement | private _inputEl?: HTMLInputElement | ||||
| private _dropzone?: Dropzone | private _dropzone?: Dropzone |
| target?: SSAOPluginTarget | target?: SSAOPluginTarget | ||||
| @uiImage('SSAO Buffer' /* {readOnly: true}*/) texture?: Texture | @uiImage('SSAO Buffer' /* {readOnly: true}*/) texture?: Texture | ||||
| @uiConfig() protected _pass?: SSAOPluginPass | |||||
| @uiConfig() declare protected _pass?: SSAOPluginPass | |||||
| // @onChange2(SSAOPlugin.prototype._createTarget) | // @onChange2(SSAOPlugin.prototype._createTarget) | ||||
| // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig) | // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig) |
| } | } | ||||
| @serialize('viewer') | @serialize('viewer') | ||||
| protected _viewer: ThreeViewer | undefined // todo: fix deserialization throwing error | |||||
| declare protected _viewer: ThreeViewer | undefined // todo: fix deserialization throwing error | |||||
| // toJSON(): any { | // toJSON(): any { | ||||
| // return this._viewer?.toJSON() ?? {} | // return this._viewer?.toJSON() ?? {} |
| export class ExtendedShaderPass extends ShaderPass implements IPass { | export class ExtendedShaderPass extends ShaderPass implements IPass { | ||||
| public static readonly DEFAULT_TEX_ID = 'tDiffuse' | public static readonly DEFAULT_TEX_ID = 'tDiffuse' | ||||
| material!: ShaderMaterial2 | |||||
| declare material: ShaderMaterial2 | |||||
| overrideReadBuffer: {texture?: WebGLRenderTarget['texture']}|null = null | overrideReadBuffer: {texture?: WebGLRenderTarget['texture']}|null = null | ||||
| readonly isExtendedShaderPass = true | readonly isExtendedShaderPass = true |
| @uiToggle('Enabled') enabled = true | @uiToggle('Enabled') enabled = true | ||||
| scene?: IScene | |||||
| declare scene?: IScene | |||||
| before?: IPassID[] | before?: IPassID[] | ||||
| after?: IPassID[] | after?: IPassID[] | ||||
| required?: IPassID[] | required?: IPassID[] |
| @uiFolderContainer('Screen Pass') | @uiFolderContainer('Screen Pass') | ||||
| export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'screen'> { | export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'screen'> { | ||||
| uiConfig!: UiObjectConfig | |||||
| declare uiConfig: UiObjectConfig | |||||
| readonly passId = 'screen' | readonly passId = 'screen' | ||||
| after: IPassID[] = ['render'] | after: IPassID[] = ['render'] | ||||
| required: IPassID[] = ['render'] | required: IPassID[] = ['render'] |
| mimeType = hdr ? 'image/x-exr' : 'image/png' | mimeType = hdr ? 'image/x-exr' : 'image/png' | ||||
| } | } | ||||
| if (!hdrFormats.includes(mimeType)) hdr = false | if (!hdrFormats.includes(mimeType)) hdr = false | ||||
| let buffer: ArrayBufferLike | |||||
| let buffer: ArrayBuffer | |||||
| if (!hdr) { | if (!hdr) { | ||||
| const url = this.renderTargetToDataUrl(target, mimeType === 'auto' ? undefined : mimeType, 90, textureIndex) | const url = this.renderTargetToDataUrl(target, mimeType === 'auto' ? undefined : mimeType, 90, textureIndex) | ||||
| buffer = base64ToArrayBuffer(url.split(',')[1]) | |||||
| buffer = base64ToArrayBuffer(url.split(',')[1]) as ArrayBuffer | |||||
| mimeType = url.split(';')[0].split(':')[1] | mimeType = url.split(';')[0].split(':')[1] | ||||
| } else { | } else { | ||||
| if (mimeType !== 'image/x-exr') { | if (mimeType !== 'image/x-exr') { | ||||
| mimeType = 'image/x-exr' | mimeType = 'image/x-exr' | ||||
| } | } | ||||
| const exporter = new EXRExporter2() | const exporter = new EXRExporter2() | ||||
| buffer = exporter.parse(this._renderer, target, {textureIndex}) | |||||
| buffer = exporter.parse(this._renderer, target, {textureIndex}).buffer as ArrayBuffer | |||||
| } | } | ||||
| const b = new Blob([buffer], {type: mimeType}) as BlobExt | const b = new Blob([buffer], {type: mimeType}) as BlobExt | ||||
| b.ext = mimeType === 'image/x-exr' ? 'exr' : mimeType.split('/')[1] | b.ext = mimeType === 'image/x-exr' ? 'exr' : mimeType.split('/')[1] |
| setDirty = iObjectCommons.setDirty.bind(this) | setDirty = iObjectCommons.setDirty.bind(this) | ||||
| refreshUi = iObjectCommons.refreshUi.bind(this) | refreshUi = iObjectCommons.refreshUi.bind(this) | ||||
| object: IObject3D | undefined | |||||
| declare object: IObject3D | undefined | |||||
| private _keyDownListener(event: KeyboardEvent) { | private _keyDownListener(event: KeyboardEvent) { | ||||
| if (!this.enabled) return | if (!this.enabled) return | ||||
| if (!this.object) return | if (!this.object) return | ||||
| // region properties | // region properties | ||||
| enabled: boolean | |||||
| declare enabled: boolean | |||||
| // axis: 'X' | 'Y' | 'Z' | 'E' | 'XY' | 'YZ' | 'XZ' | 'XYZ' | 'XYZE' | null | // axis: 'X' | 'Y' | 'Z' | 'E' | 'XY' | 'YZ' | 'XZ' | 'XYZ' | 'XYZE' | null | ||||
| // onChange not required for before since they fire 'change' event on changed. see TransformControls.js | // onChange not required for before since they fire 'change' event on changed. see TransformControls.js | ||||
| @uiDropdown('Mode', ['translate', 'rotate', 'scale'].map(label=>({label}))) | @uiDropdown('Mode', ['translate', 'rotate', 'scale'].map(label=>({label}))) | ||||
| mode: 'translate' | 'rotate' | 'scale' | |||||
| declare mode: 'translate' | 'rotate' | 'scale' | |||||
| translationSnap: number | null | |||||
| rotationSnap: number | null | |||||
| scaleSnap: number | null | |||||
| declare translationSnap: number | null | |||||
| declare rotationSnap: number | null | |||||
| declare scaleSnap: number | null | |||||
| @uiDropdown('Space', ['world', 'local'].map(label=>({label}))) | @uiDropdown('Space', ['world', 'local'].map(label=>({label}))) | ||||
| space: 'world' | 'local' | |||||
| declare space: 'world' | 'local' | |||||
| @uiSlider('Size', [0.1, 10], 0.1) | @uiSlider('Size', [0.1, 10], 0.1) | ||||
| size: number | |||||
| declare size: number | |||||
| @uiToggle('Show X') | @uiToggle('Show X') | ||||
| showX: boolean | |||||
| declare showX: boolean | |||||
| @uiToggle('Show Y') | @uiToggle('Show Y') | ||||
| showY: boolean | |||||
| declare showY: boolean | |||||
| @uiToggle('Show Z') | @uiToggle('Show Z') | ||||
| showZ: boolean | |||||
| declare showZ: boolean | |||||
| // dragging: boolean | // dragging: boolean | ||||
| clone: (recursive?: boolean) => this | clone: (recursive?: boolean) => this | ||||
| remove: (...object: IObject3D[]) => this | remove: (...object: IObject3D[]) => this | ||||
| dispatchEvent: (event: ISceneEvent) => void | dispatchEvent: (event: ISceneEvent) => void | ||||
| parent: IObject3D | null | |||||
| children: IObject3D[] | |||||
| declare parent: IObject3D | null | |||||
| declare children: IObject3D[] | |||||
| // endregion | // endregion | ||||
| } | } |
| color: ColorRepresentation|undefined | color: ColorRepresentation|undefined | ||||
| lightPlane: Line2 | lightPlane: Line2 | ||||
| targetLine: Line2 | targetLine: Line2 | ||||
| light: (DirectionalLight&IUiConfigContainer)|undefined | |||||
| declare light: (DirectionalLight&IUiConfigContainer)|undefined | |||||
| @onChange(DirectionalLightHelper2.prototype.update) | @onChange(DirectionalLightHelper2.prototype.update) | ||||
| material: LineMaterial2 | material: LineMaterial2 |
| export class PointLightHelper2 extends ALightHelperWidget { | export class PointLightHelper2 extends ALightHelperWidget { | ||||
| color: ColorRepresentation | undefined | color: ColorRepresentation | undefined | ||||
| lightSphere: Wireframe | lightSphere: Wireframe | ||||
| light: (PointLight & IUiConfigContainer) | undefined | |||||
| declare light: (PointLight & IUiConfigContainer) | undefined | |||||
| @onChange(PointLightHelper2.prototype.update) | @onChange(PointLightHelper2.prototype.update) | ||||
| material: LineMaterial2 | material: LineMaterial2 | ||||
| @onChange(PointLightHelper2.prototype.update) | @onChange(PointLightHelper2.prototype.update) |
| export class SpotLightHelper2 extends ALightHelperWidget { | export class SpotLightHelper2 extends ALightHelperWidget { | ||||
| color: ColorRepresentation | undefined | color: ColorRepresentation | undefined | ||||
| cone: LineSegments2 | cone: LineSegments2 | ||||
| light: (SpotLight & IUiConfigContainer) | undefined | |||||
| declare light: (SpotLight & IUiConfigContainer) | undefined | |||||
| @onChange(SpotLightHelper2.prototype.update) | @onChange(SpotLightHelper2.prototype.update) | ||||
| material: LineMaterial2 | material: LineMaterial2 | ||||
| @onChange(SpotLightHelper2.prototype.update) | @onChange(SpotLightHelper2.prototype.update) |
| * @param paddingByte (Optional) | * @param paddingByte (Optional) | ||||
| * @returns The same buffer if it's already aligned to 4-byte boundary or a new buffer | * @returns The same buffer if it's already aligned to 4-byte boundary or a new buffer | ||||
| */ | */ | ||||
| function getPaddedArrayBuffer(arrayBuffer: ArrayBuffer, paddingByte = 0): ArrayBuffer { | |||||
| function getPaddedArrayBuffer(arrayBuffer: Uint8Array<ArrayBuffer>, paddingByte = 0): ArrayBuffer { | |||||
| const paddedLength = getPaddedBufferSize(arrayBuffer.byteLength) | const paddedLength = getPaddedBufferSize(arrayBuffer.byteLength) | ||||
| if (paddedLength !== arrayBuffer.byteLength) { | if (paddedLength !== arrayBuffer.byteLength) { | ||||
| const array = new Uint8Array(paddedLength) | const array = new Uint8Array(paddedLength) | ||||
| } | } | ||||
| return array.buffer | return array.buffer | ||||
| } | } | ||||
| return arrayBuffer | |||||
| return arrayBuffer.buffer | |||||
| } | } | ||||
| const GLB_CHUNK_TYPE_BIN = 0x004E4942 | const GLB_CHUNK_TYPE_BIN = 0x004E4942 | ||||
| // https://github.com/mrdoob/three.js/blob/4dbd0065f2ec29b89c250d8582f61e9f4792e077/examples/jsm/exporters/GLTFExporter.js#L558 | // https://github.com/mrdoob/three.js/blob/4dbd0065f2ec29b89c250d8582f61e9f4792e077/examples/jsm/exporters/GLTFExporter.js#L558 | ||||
| export function makeGLBFile(buffers: ArrayBuffer, json: any): BlobExt { | |||||
| export function makeGLBFile(buffers: Uint8Array<ArrayBuffer>, json: any): BlobExt { | |||||
| // Binary chunk. | // Binary chunk. | ||||
| const binaryChunk = getPaddedArrayBuffer(buffers) | const binaryChunk = getPaddedArrayBuffer(buffers) | ||||
| const binaryChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES)) | const binaryChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES)) | ||||
| binaryChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_BIN, true) | binaryChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_BIN, true) | ||||
| // JSON chunk. | // JSON chunk. | ||||
| const jsonChunk = getPaddedArrayBuffer(new TextEncoder().encode(JSON.stringify(json || {})).buffer, 0x20) | |||||
| const buffer1 = new TextEncoder().encode(JSON.stringify(json || {})) as Uint8Array<ArrayBuffer> | |||||
| const jsonChunk = getPaddedArrayBuffer(buffer1, 0x20) | |||||
| const jsonChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES)) | const jsonChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES)) | ||||
| jsonChunkPrefix.setUint32(0, jsonChunk.byteLength, true) | jsonChunkPrefix.setUint32(0, jsonChunk.byteLength, true) | ||||
| jsonChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_JSON, true) | jsonChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_JSON, true) |